dustinspecker / generator-ng-poly

Yeoman generator for modular AngularJS apps with Gulp and optional Polymer support
MIT License
237 stars 50 forks source link

Gulp tasks to minify CSS, JS, and HTML - Polymer #2

Open dustinspecker opened 10 years ago

dustinspecker commented 10 years ago
  1. Simply minify CSS and HTML
  2. ngMin and minify AngularJS
  3. Vulcanize Polymer (or inject minified CSS and JS into minified HTML)
  4. Use minified Bower assets
  5. Gulp target or similar to build for development or production (minified)
dustinspecker commented 10 years ago

7d769cbd846a5dc575d0b0194e408f353c6d9371 Implements 1, 2 (ng-annotate), and 5.

Minified Bower assets are manual.

sunphg commented 10 years ago

gulp build

/* Request 404 error for .map on dev / Request URL:http://127.0.0.1:8080/platform.js.map Remote Address:127.0.0.1:8080 Request Method:GET Status Code:404 Not Found Host:127.0.0.1:8080 Referer:http://127.0.0.1:8080/ X-Source-Map-Request-From:inspector

/* CSS working on body tag on dev */ home.css:1 body { background-color: #e5e5e5; }

bootstrap.css:881 body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #333333; background-color: #ffffff; }

gulp build --stage prod

/* Request 404 error for .map on prod / Request URL:http://127.0.0.1:8080/platform.js.map Request URL:http://127.0.0.1:8080/angular.min.js.map Request URL:http://127.0.0.1:8080/angular-animate.min.js.map Request URL:http://127.0.0.1:8080/angular-cookies.min.js.map Request URL:http://127.0.0.1:8080/angular-resource.min.js.map Request URL:http://127.0.0.1:8080/angular-touch.min.js.map Request URL:http://127.0.0.1:8080/angular-sanitize.min.js.map Remote Address:127.0.0.1:8080 Request Method:GET Status Code:404 Not Found Host:127.0.0.1:8080 Referer:http://127.0.0.1:8080/ X-Source-Map-Request-From:inspector

/* CSS not working on body tag on prod (after concat and minify CSS to one file) */ style.css:1 body { font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; line-height: 1.42857143; color: #333; background-color: #fff; }

dustinspecker commented 10 years ago

Good catch!

Here's the original Gulp style task,

...
  // less
  stream.queue(gulp.src([
    appStyleFiles,
    bowerDir + 'bootstrap/less/bootstrap.less',
    '!**/*.{scss,styl}'
  ])
    .pipe(less({
      paths: [
        bowerDir + 'bootstrap/less/**/*.less'
      ]
    })))
  ;
...

We need to put add bootstrap.less to the stream first. So changing it to:

...
  // less
  stream.queue(gulp.src([
    bowerDir + 'bootstrap/less/bootstrap.less',
    appStyleFiles,
    '!**/*.{scss,styl}'
  ])
    .pipe(less({
      paths: [
        bowerDir + 'bootstrap/less/**/*.less'
      ]
    })))
  ;
...

will fix the issue of the custom CSS being overridden by Bootstrap.

Thank you for bringing that up.

sunphg commented 10 years ago

Following angular module minifid version will request map file in last line of code (//# sourceMappingURL=angular.min.js.map), browser's network console will show '404 Not Found' message.

gulp build --stage prod

Request URL:http://127.0.0.1:8080/js/angular.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-animate.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-cookies.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-resource.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-route.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-touch.min.js.map
Request URL:http://127.0.0.1:8080/js/angular-sanitize.min.js.map
sunphg commented 10 years ago

BrowserSync open browser window too fast before gulp build task finished on first time, so we just only got a blank page and need to click browser's reload button to refresh manually. Force BrowserSync to reload before any gulp watch tasks will help to resolve the problem.

gulp.task('watch', function () {
+  browserSync.reload();
  gulp.watch([unitTestsFiles], ['unitTest']);
  gulp.watch([appMarkupFiles, appScriptFiles, appStyleFiles], ['build', browserSync.reload]);
});

//Before: no force BrowserSync reload on watch task

gulp

[15:12:43] Starting 'clean'...
[15:12:43] Starting 'jshint'...
[15:12:43] Starting 'browser-sync'...
[15:12:43] Finished 'browser-sync' after 3.02 ms
[BS] [DEBUG]: Checking if there's an internet connection...
[BS] [DEBUG]: Resolved, setting online: true
[BS] [DEBUG]: Static Server running (http) ...
[BS] [DEBUG]: Running mode: SERVER
[BS] Local: >>> http://localhost:3000
[BS] External: >>> http://192.168.0.1:3000
[BS] Serving files from: build/
[15:12:43] Finished 'clean' after 93 ms
[15:12:43] Starting 'markup'...
[15:12:43] Starting 'style'...
[15:12:43] Starting 'fonts'...
[15:12:44] Finished 'markup' after 190 ms
[15:12:44] Finished 'style' after 749 ms
[15:12:44] Finished 'fonts' after 748 ms
[15:12:44] gulp-plato: Report generated in report.
[15:12:44] Finished 'jshint' after 895 ms
[15:12:44] Starting 'scripts'...
[BS] [DEBUG]: Browser Connected! (Chrome, version: 36.0.1985.143)
[15:12:44] Finished 'scripts' after 50 ms
[15:12:44] Starting 'inject'...
[15:12:44] Finished 'inject' after 4.61 ms
[15:12:44] Starting 'angularInject'...
[15:12:44] Finished 'angularInject' after 101 ms
[15:12:44] Starting 'build'...
[15:12:44] Finished 'build' after 5.02 μs
[15:12:44] Starting 'dev'...
[15:12:44] Starting 'watch'...
[15:12:44] Finished 'watch' after 14 ms
[15:12:44] Finished 'dev' after 15 ms
[15:12:44] Starting 'default'...
[15:12:44] Finished 'default' after 7.68 μs

//After: force BrowserSync reload on watch task

gulp

[15:15:01] Starting 'clean'...
[15:15:01] Starting 'jshint'...
[15:15:01] Starting 'browser-sync'...
[15:15:01] Finished 'browser-sync' after 3.17 ms
[BS] [DEBUG]: Checking if there's an internet connection...
[BS] [DEBUG]: Resolved, setting online: true
[BS] [DEBUG]: Static Server running (http) ...
[BS] [DEBUG]: Running mode: SERVER
[BS] Local: >>> http://localhost:3000
[BS] External: >>> http://192.168.0.1:3000
[BS] Serving files from: build/
[15:15:01] Finished 'clean' after 87 ms
[15:15:01] Starting 'markup'...
[15:15:01] Starting 'style'...
[15:15:01] Starting 'fonts'...
[15:15:02] Finished 'markup' after 587 ms
[15:15:02] Finished 'style' after 761 ms
[15:15:02] Finished 'fonts' after 772 ms
[15:15:02] gulp-plato: Report generated in report.
[15:15:02] Finished 'jshint' after 906 ms
[15:15:02] Starting 'scripts'...
[BS] [DEBUG]: Browser Connected! (Chrome, version: 36.0.1985.143)
[15:15:02] Finished 'scripts' after 45 ms
[15:15:02] Starting 'inject'...
[15:15:02] Finished 'inject' after 5.66 ms
[15:15:02] Starting 'angularInject'...
[15:15:02] Finished 'angularInject' after 101 ms
[15:15:02] Starting 'build'...
[15:15:02] Finished 'build' after 6.79 μs
[15:15:02] Starting 'dev'...
[15:15:02] Starting 'watch'...
[15:15:02] Finished 'watch' after 19 ms
[15:15:02] Finished 'dev' after 20 ms
[15:15:02] Starting 'default'...
[15:15:02] Finished 'default' after 4.43 μs
[BS] [DEBUG]: Browser Connected! (Chrome, version: 36.0.1985.143)
dustinspecker commented 10 years ago

06a9736e8599dcec3ea2783ce6afd314e2e263e2 The browserSync issue has been fixed. Thank you!

In regards to using sourcemaps, I'm going to raise this up as another issue to also include support for sourcemaps when compiling, concating, and minifying app code.

dustinspecker commented 10 years ago

0d8df10508d3e07c716af37389f9e444115ea5da Bower components are automatically injected and minified

sunphg commented 10 years ago

Something wrong between v0.2.10 to v0.2.13

//  *   gulp/build.js:92
    .pipe($.if(isProd, $.ngHtml2js({
      moduleName: require('../package.json').name,
      declareModule: false
    })))

//  *   gulp/build.js:97
//  (Bug)
    .pipe(jsFilter)
    .pipe($.if(isProd, $.angularFilesort()))
    .pipe($.if(isProd, $.concat('app.js')))
    .pipe($.if(isProd, $.ngAnnotate()))
    .pipe($.if(isProd, $.uglify()))
    .pipe($.if(isProd, $.rev()))
-    .pipe(gulp.dest(buildJs));
+    .pipe(gulp.dest(buildJs))
+    .pipe(jsFilter.restore());

//  *   protractor.config.js:4 
//  (e2e test for Chrome version over 35)
-    browserName: 'chrome'
+    browserName: 'chrome',
+    chromeOptions: {
+      args: ['--test-type']
+    }

//  *   package.json:2 
//  (ngHtml2js get from this)
-"name": "track",
+"name": "<%= appName %>",
dustinspecker commented 10 years ago

6a1e2535076782f15bd648a2e3f01c1218e51881 Thank you. This adds your fixes.

sunphg commented 10 years ago

The wiredep should not be using the bootstrap.js with the angular-ui-bootstrap.js (ui-bootstrap-tpls.js) library for 'bowerCopy' gulp task . http://stackoverflow.com/questions/24104336/dropdown-of-navbar-is-double-clicked

dustinspecker commented 10 years ago

Thank you. If you're needing a fix right now, you can add an exclude option to wiredep:

$.wiredep({exclude: [/bootstrap[.]js/]})

This will prevent bootstrap.js from being injected. I'll try to get this added to the generator tomorrow.

dustinspecker commented 10 years ago

f54cc6bc7cccff7812e72bdf936dac0358d27aea Add wiredep exclude

sunphg commented 10 years ago

'browserSync' task should be start after 'build', not parallel task run with 'build'.

// * Gulpfile.js:7
-gulp.task('dev', ['build', 'browserSync'], function () {
+gulp.task('dev', ['build'], function () {
+  gulp.start('browserSync');
  gulp.start('watch');
});

jsFilter.restore() should be put in last line, otherwise all .js and .html will be copy to js folder.

// * gulp\build.js:99
    .pipe(jsFilter)
    .pipe($.if(isProd, $.angularFilesort()))
    .pipe($.if(isProd, $.concat('app.js')))
    .pipe($.if(isProd, $.ngAnnotate()))
    .pipe($.if(isProd, $.uglify()))
    .pipe($.if(isProd, $.rev()))
-    .pipe(jsFilter.restore())
-    .pipe(gulp.dest(buildConfig.buildJs));
+    .pipe(gulp.dest(buildConfig.buildJs))
+    .pipe(jsFilter.restore());
dustinspecker commented 10 years ago

d4dcafa6748b27961af5c6f6bf643747a8521fcd Fixes both of these issues. Thank you!

sunphg commented 10 years ago

The jsFilter.restore() must be added at last line, because 'gulp --stage prod' will report ngHtml2js error on Chrome console when didn't have jsFilter.restore() at last line.

// * gulp/build.js:104
  .pipe($.if(isProd, $.concat('app.js'))) 
  .pipe($.if(isProd, $.ngAnnotate()))
  .pipe($.if(isProd, $.uglify()))
  .pipe($.if(isProd, $.rev()))<% if (polymer) { %>
  .pipe($.addSrc($.mainBowerFiles({filter: /platform/})))<% } %> 
-  .pipe(gulp.dest(buildConfig.buildJs));
+  .pipe(gulp.dest(buildConfig.buildJs))
+  .pipe(jsFilter.restore());
dustinspecker commented 10 years ago

9489b7c0f4e5daa8bfddd6abe459ad360b61abbd Adds this line

That's odd. I'm not getting the same error with the line missing. Adding the line didn't seem to change anything for me either, so adding it for those getting the issue. Thanks again.

sunphg commented 10 years ago

Recommand imagemin only run on production stage to save time for build up on development stage. It's only save small amount of space, but spent more time for build and watch tasks.

// * gulp/build.js
// copy and optimize images into build directory
gulp.task('images', ['clean'], function () {
  return gulp.src(appImages)
-    .pipe($.imagemin())
+    .pipe($.if(isProd, $.imagemin()))
    .pipe(gulp.dest(buildConfig.buildImages));
})

Recommand to replace the link and script tag's src path from hardcode to build.config.sys, It's easy for resource folder customization.

// * build.config.js
module.exports = {
+  // link directories
+  linkCss: 'css/',
+  linkJs: 'js/',
  // build directories
  buildDir: 'build/',
  buildCss: 'build/css/',
  buildFonts: 'build/fonts/',
  buildImages: 'build/images/',
  buildJs: 'build/js/'
};

// * gulp/build.js
    return gulp.src(buildConfig.buildDir + 'index.html')
      .pipe($.wiredep.stream({
        exclude: [/bootstrap[.]js/],
        fileTypes: {
          html: {
            replace: {
              css: function (filePath) {
-                return '<link rel="stylesheet" href="' + 'css/' + filePath.split('/').pop() + '">';
+                return '<link rel="stylesheet" href="' + buildConfig.linkCss + filePath.split('/').pop() + '">';
              },
              js: function (filePath) {
-                return '<script src="' + 'js/' + filePath.split('/').pop() + '"></script>';
+                return '<script src="' + buildConfig.linkJs + filePath.split('/').pop() + '"></script>';
              }
            }
          }
        }
      }))
      .pipe(gulp.dest(buildConfig.buildDir));
dustinspecker commented 10 years ago

64210c428ab658c1f77a2f2aebf20383dae65c3f Imagemin is ran in prod only

1f6d773c17732e51ddf88dd3dbff6f9499aa8dd8 I took a different approach with the wiredep replace. I used the buildCss and buildJs, but removed the buildDir portion from both. This way users don't have to repeat themselves as much. If there's something I'm overlooking with this approach and your method takes care of that we can definitely change it to yours.

Thank you again!

sunphg commented 9 years ago

No error message at gullp-plumber and console for lint task and styles task.

// * gulp/analyze.js
  var coffeeFilter = $.filter('**/*.coffee')
    , jsFilter = $.filter('**/*.js')
    , onError = function (err) {
      $.notify.onError({
-        title: 'Error linting the JS',
+        title: 'Error linting at <%= error.plugin %>',
        subtitle: ' ', //overrides defaults
-        message: ' ', //overrides defaults
+        message: '\n' + err.message.replace(/\u001b\[.*?m/g, ''), //overrides defaults
        sound: ' ' //overrides defaults
      })(err);

      this.emit('end');
    };

  return gulp.src([
    appScriptFiles,
    e2eFiles,
    unitTests
  ])
    .pipe($.plumber({errorHandler: onError}))
    .pipe(coffeeFilter)
    .pipe($.coffeelint())
    .pipe($.coffeelint.reporter())
    .pipe($.coffeelint.reporter('fail'))
    .pipe(coffeeFilter.restore())
    .pipe(jsFilter)
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.jshint.reporter('fail'))
    .pipe($.jscs());
// * gulp/build.js
  var lessFilter = $.filter('**/*.less')
    , scssFilter = $.filter('**/*.scss')
    , stylusFilter = $.filter('**/*.styl')
    , onError = function (err) {
      $.notify.onError({
-        title: 'Syntax error in CSS',
+        title: 'Syntax error at <%= error.plugin %>',
        subtitle: ' ', //overrides defaults
-        message: ' ', //overrides defaults
+        message: '\n' + err.message.replace(/\u001b\[.*?m/g, ''), //overrides defaults
        sound: ' ' //overrides defaults
      })(err);

      this.emit('end');
    };

  return gulp.src([
    appStyleFiles
  ])
    .pipe($.plumber({errorHandler: onError}))
    .pipe(lessFilter)
    .pipe($.less())
    .pipe(lessFilter.restore())
    .pipe(scssFilter)
    .pipe($.sass())
    .pipe(scssFilter.restore())
    .pipe(stylusFilter)
    .pipe($.stylus({
      use: $.nib()
    }))
    .pipe(stylusFilter.restore())

After version 0.4.19, 'npm install -g generator-ng-poly' only have '.npmignore' and no '.gitignore' in generator-ng-poly\app\templates\ folder, and can't build successful.

dustinspecker commented 9 years ago

Thanks. I'll add those updated notify messages.

Can you try updating generator-ng-poly? I'm not sure a .npmignore file is getting in there. I don't believe we have any of those in this project. Also odd about the missing .gitignore file.

sunphg commented 9 years ago

When packaging using just a .gitignore file, npm seems to convert it to an .npmignore file and then include it with the package.

https://github.com/npm/npm/issues/3763

npm info it worked if it ends with ok
npm info using npm@2.1.11
npm info using node@v0.10.33
npm info attempt registry request try #1 at 10:41:39
npm info install generator-ng-poly@0.4.21 into d:\javascript\npm
npm info installOne generator-ng-poly@0.4.21
npm info preuninstall generator-ng-poly@0.4.21
npm info uninstall generator-ng-poly@0.4.21
npm info postuninstall generator-ng-poly@0.4.21

npm verb tar unpack d:\javascript\npm-cache\generator-ng-poly\0.4.21\package.tgz
npm verb tar unpacking to d:\javascript\npm\node_modules\generator-ng-poly
npm verb gentlyRm vacuuming d:\javascript\npm\node_modules\generator-ng-poly

npm sill gunzTarPerm extractEntry genBase/index.js
npm sill gunzTarPerm extractEntry app/index.js
npm sill gunzTarPerm extractEntry app/templates/.npmignore
npm sill gunzTarPerm extractEntry app/templates/watch.js
npm sill gunzTarPerm extractEntry app/templates/protractor.config.js
npm sill gunzTarPerm extractEntry app/templates/analyze.js
npm sill gunzTarPerm extractEntry app/templates/_test.js
npm sill gunzTarPerm extractEntry app/templates/_app.js
npm sill gunzTarPerm extractEntry app/templates/_karma.config.js
npm sill gunzTarPerm extractEntry app/templates/_build.config.js
npm sill gunzTarPerm extractEntry app/templates/_build.js
npm sill gunzTarPerm extractEntry app/templates/_gulpfile.js
npm sill gunzTarPerm extractEntry app/templates/_package.json
npm sill gunzTarPerm extractEntry app/templates/.editorconfig
npm sill gunzTarPerm extractEntry app/templates/_index.html
npm sill gunzTarPerm extractEntry app/templates/_index.jade
npm sill gunzTarPerm extractEntry app/templates/_bower.json
npm sill gunzTarPerm extractEntry app/templates/_index.haml
npm sill gunzTarPerm extractEntry app/templates/_readme.md
npm sill gunzTarPerm extractEntry app/templates/_app.coffee
npm sill gunzTarPerm extractEntry app/templates/.jshintrc
npm sill gunzTarPerm extractEntry app/templates/.jscsrc
npm sill gunzTarPerm extractEntry app/templates/.bowerrc
npm sill gunzTarPerm extractEntry value/index.js
dustinspecker commented 9 years ago

bb4600fc9ddc9b957aceaf29bc8ac4a71ec5d745 Added gulp-notify info

03f8b564315570aa7ecc62f40ac9e2b9a7b5b245 Added .npmignore file. Thanks for catching that!

dustinspecker commented 9 years ago

888d1497d5ff6752f72dc4c1ff525e0b54438bc3 Ok, now it's really fixed.

sunphg commented 9 years ago

cssRebaseUrls and cssUrlAdjuster are not working with URL and DataURI

// * build/app/css/app.css
  background-image: url(https://www.npmjs.com/static/images/npm-logo.svg);
=>
  background-image: url("../https:/www.npmjs.com/static/images/npm-logo.svg");

  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhUAAAFAAQMAAAAxgRzkAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAACxJREFUeNrtwTEBAAAAwqD1T20KP6AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BlUAAAEYkol+AAAAAElFTkSuQmCC);
=>
  background-image: url("../app/data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhUAAAFAAQMAAAAxgRzkAAAAA1BMVEX/+nxBvIAAAAAXRSTlMAQObYZgAAACxJREFUeNrtwTEBAAAAwqD1T20KP6AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BlUAAAEYkol+AAAAAElFTkSuQmCC");
// * gulp/build.js
-    .pipe($.if(isProd, $.cssRebaseUrls()))
+    .pipe($.if(isProd, $.cssRebaseUrls({
      root: appBase
    })))
-    .pipe($.if(isProd, $.cssUrlAdjuster({
      prepend: '../'
    })))
dustinspecker commented 9 years ago

361651aa83b00d4a54e9819e72e463689f09afca Sorry for the late response. Thank you for bringing this up.