| OLD | NEW |
| 1 /* | 1 /* |
| 2 Copyright 2016 The LUCI Authors. All rights reserved. | 2 Copyright 2016 The LUCI Authors. All rights reserved. |
| 3 Use of this source code is governed under the Apache License, Version 2.0 | 3 Use of this source code is governed under the Apache License, Version 2.0 |
| 4 that can be found in the LICENSE file. | 4 that can be found in the LICENSE file. |
| 5 | 5 |
| 6 This document has been largely derived from the Polymer Starter Kit: | 6 This document has been largely derived from the Polymer Starter Kit: |
| 7 https://github.com/PolymerElements/polymer-starter-kit | 7 https://github.com/PolymerElements/polymer-starter-kit |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 var path = require('path'); | 12 var path = require('path'); |
| 13 var argv = require('yargs').argv; | 13 var argv = require('yargs').argv; |
| 14 | 14 |
| 15 var exports = module.exports = {} | 15 var exports = module.exports = {} |
| 16 exports.base = path.join(__dirname, '..'); | 16 exports.base = path.join(__dirname, '..'); |
| 17 exports.out = (argv.out || exports.base); | 17 exports.out = (argv.out || exports.base); |
| 18 exports.plugins = require('gulp-load-plugins')({ | 18 exports.plugins = require('gulp-load-plugins')({ |
| 19 config: path.join(exports.base, 'package.json'), | 19 config: path.join(exports.base, 'package.json'), |
| 20 }); | 20 }); |
| 21 | 21 |
| 22 // Include Gulp & tools we'll use | 22 // Include Gulp & tools we'll use |
| 23 var $ = exports.plugins; | 23 var $ = exports.plugins; |
| 24 var del = require('del'); |
| 25 var requireDir = require('require-dir'); |
| 26 var runSequence = require('run-sequence'); |
| 24 var browserSync = require('browser-sync'); | 27 var browserSync = require('browser-sync'); |
| 25 var debug = require('gulp-debug'); | 28 var reload = browserSync.reload; |
| 26 var del = require('del'); | 29 var merge = require('merge-stream'); |
| 27 var foreach = require('gulp-foreach'); | |
| 28 var fs = require('fs'); | 30 var fs = require('fs'); |
| 29 var glob = require('glob-all'); | 31 var glob = require('glob-all'); |
| 30 var historyApiFallback = require('connect-history-api-fallback'); | 32 var historyApiFallback = require('connect-history-api-fallback'); |
| 31 var hyd = require('hydrolysis'); | 33 var crypto = require('crypto'); |
| 32 var merge = require('merge-stream'); | |
| 33 var pathExists = require('path-exists'); | |
| 34 var reload = browserSync.reload; | |
| 35 var rename = require('gulp-rename'); | |
| 36 var requireDir = require('require-dir'); | |
| 37 var runSequence = require('run-sequence'); | |
| 38 var through = require('through2'); | |
| 39 var ts = require('gulp-typescript'); | |
| 40 | |
| 41 | 34 |
| 42 var AUTOPREFIXER_BROWSERS = [ | 35 var AUTOPREFIXER_BROWSERS = [ |
| 43 'ie >= 10', | 36 'ie >= 10', |
| 44 'ie_mob >= 10', | 37 'ie_mob >= 10', |
| 45 'ff >= 30', | 38 'ff >= 30', |
| 46 'chrome >= 34', | 39 'chrome >= 34', |
| 47 'safari >= 7', | 40 'safari >= 7', |
| 48 'opera >= 23', | 41 'opera >= 23', |
| 49 'ios >= 7', | 42 'ios >= 7', |
| 50 'android >= 4.4', | 43 'android >= 4.4', |
| 51 'bb >= 10' | 44 'bb >= 10' |
| 52 ]; | 45 ]; |
| 53 | 46 |
| 54 exports.setup = function(gulp, config) { | 47 exports.setup = function(gulp, config) { |
| 55 var APP = path.basename(config.dir); | 48 var APP = path.basename(config.dir); |
| 56 var BUILD = path.join('.tmp', 'build'); | |
| 57 var DIST = path.join(exports.out, 'dist', APP); | 49 var DIST = path.join(exports.out, 'dist', APP); |
| 58 | 50 |
| 59 var layout = { | 51 var layout = { |
| 60 app: APP, | 52 app: APP, |
| 61 distPath: DIST, | 53 distPath: DIST, |
| 62 | 54 |
| 63 // NOTE: Takes vararg via "arguments". | 55 // NOTE: Takes vararg via "arguments". |
| 64 dist: function() { | 56 dist: function() { |
| 65 return extendPath(DIST).apply(null, arguments); | 57 return extendPath(DIST).apply(null, arguments); |
| 66 }, | 58 }, |
| 67 }; | 59 }; |
| 68 | 60 |
| 69 var extendPath = function() { | 61 var extendPath = function() { |
| 70 var base = [].slice.call(arguments); | 62 var base = [].slice.call(arguments); |
| 71 return function() { | 63 return function() { |
| 72 // Simple case: only base, no additional elements. | 64 // Simple case: only base, no additional elements. |
| 73 if (base.length === 1 && arguments.length === 0) { | 65 if (base.length === 1 && arguments.length === 0) { |
| 74 return base[0]; | 66 return base[0]; |
| 75 } | 67 } |
| 76 | 68 |
| 77 var parts = base.concat(); | 69 var parts = base.concat(); |
| 78 parts.push.apply(parts, arguments) | 70 parts.push.apply(parts, arguments) |
| 79 return path.join.apply(null, parts); | 71 return path.join.apply(null, parts); |
| 80 }; | 72 }; |
| 81 }; | 73 }; |
| 82 | 74 |
| 83 var styleTask = function(stylesPath, srcs) { | 75 var styleTask = function(stylesPath, srcs) { |
| 84 return gulp.src(srcs.map(function(src) { | 76 return gulp.src(srcs.map(function(src) { |
| 85 return path.join(BUILD, stylesPath, src); | 77 return path.join(stylesPath, src); |
| 86 })) | 78 })) |
| 87 .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS)) | 79 .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS)) |
| 88 .pipe(gulp.dest('.tmp/' + stylesPath)) | 80 .pipe(gulp.dest('.tmp/' + stylesPath)) |
| 89 .pipe($.minifyCss()) | 81 .pipe($.minifyCss()) |
| 90 .pipe(gulp.dest(layout.dist(stylesPath))) | 82 .pipe(gulp.dest(layout.dist(stylesPath))) |
| 91 .pipe($.size({title: stylesPath})); | 83 .pipe($.size({title: stylesPath})); |
| 92 }; | 84 }; |
| 93 | 85 |
| 94 var imageOptimizeTask = function(src, dest) { | 86 var imageOptimizeTask = function(src, dest) { |
| 95 return gulp.src(path.join(BUILD, src)) | 87 return gulp.src(src) |
| 96 .pipe($.imagemin({ | 88 .pipe($.imagemin({ |
| 97 progressive: true, | 89 progressive: true, |
| 98 interlaced: true | 90 interlaced: true |
| 99 })) | 91 })) |
| 100 .pipe(gulp.dest(dest)) | 92 .pipe(gulp.dest(dest)) |
| 101 .pipe($.size({title: 'images'})); | 93 .pipe($.size({title: 'images'})); |
| 102 }; | 94 }; |
| 103 | 95 |
| 104 var optimizeHtmlTask = function(src, dest) { | 96 var optimizeHtmlTask = function(src, dest) { |
| 105 var assets = $.useref.assets({ | 97 var assets = $.useref.assets({ |
| 106 searchPath: ['.tmp', config.dir] | 98 searchPath: ['.tmp', 'app'] |
| 107 }); | 99 }); |
| 108 | 100 |
| 109 return gulp.src(src, {cwd: BUILD}) | 101 return gulp.src(src) |
| 110 .pipe(assets) | 102 .pipe(assets) |
| 111 // Concatenate and minify JavaScript | 103 // Concatenate and minify JavaScript |
| 112 .pipe($.if('*.js', $.uglify({ | 104 .pipe($.if('*.js', $.uglify({ |
| 113 preserveComments: 'some' | 105 preserveComments: 'some' |
| 114 }))) | 106 }))) |
| 115 // Concatenate and minify styles | 107 // Concatenate and minify styles |
| 116 // In case you are still using useref build blocks | 108 // In case you are still using useref build blocks |
| 117 .pipe($.if('*.css', $.minifyCss())) | 109 .pipe($.if('*.css', $.minifyCss())) |
| 118 .pipe(assets.restore()) | 110 .pipe(assets.restore()) |
| 119 .pipe($.useref()) | 111 .pipe($.useref()) |
| 120 // Minify any HTML | 112 // Minify any HTML |
| 121 .pipe($.if('*.html', $.minifyHtml({ | 113 .pipe($.if('*.html', $.minifyHtml({ |
| 122 quotes: true, | 114 quotes: true, |
| 123 empty: true, | 115 empty: true, |
| 124 spare: true | 116 spare: true |
| 125 }))) | 117 }))) |
| 126 // Output files | 118 // Output files |
| 127 .pipe(gulp.dest(dest)) | 119 .pipe(gulp.dest(dest)) |
| 128 .pipe($.size({ | 120 .pipe($.size({ |
| 129 title: 'html' | 121 title: 'html' |
| 130 })); | 122 })); |
| 131 }; | 123 }; |
| 132 | 124 |
| 133 gulp.task('build', function() { | |
| 134 // Copy application directories. | |
| 135 var app = gulp.src([ | |
| 136 '**', | |
| 137 '!inc', | |
| 138 ]).pipe(gulp.dest(BUILD)); | |
| 139 | |
| 140 var inc = gulp.src('inc/**/*', { cwd: exports.base }) | |
| 141 .pipe(gulp.dest(path.join(BUILD, 'inc'))); | |
| 142 return merge(app, inc); | |
| 143 }); | |
| 144 | |
| 145 // Compile and automatically prefix stylesheets | 125 // Compile and automatically prefix stylesheets |
| 146 gulp.task('styles', ['build'], function() { | 126 gulp.task('styles', function() { |
| 147 return styleTask('styles', ['**/*.css']); | 127 return styleTask('styles', ['**/*.css']); |
| 148 }); | 128 }); |
| 149 | 129 |
| 150 gulp.task('elements', ['build'], function() { | 130 gulp.task('elements', function() { |
| 151 return styleTask('elements', ['**/*.css']); | 131 return styleTask('elements', ['**/*.css']); |
| 152 }); | 132 }); |
| 153 | 133 |
| 154 // Optimize images | 134 // Optimize images |
| 155 gulp.task('images', ['build'], function() { | 135 gulp.task('images', function() { |
| 156 return imageOptimizeTask('images/**/*', layout.dist('images')); | 136 return imageOptimizeTask('images/**/*', layout.dist('images')); |
| 157 }); | 137 }); |
| 158 | 138 |
| 159 // Transpiles "inc/*/*.ts" and deposits the result alongside their source | |
| 160 // "ts" files. | |
| 161 gulp.task('tsinline', function() { | |
| 162 // Transpile each TypeScript module independently into JavaScript in the | |
| 163 // BUILD directory. | |
| 164 var incDir = path.join(exports.base, 'inc') | |
| 165 var tsconfigPath = path.join(incDir, 'tsconfig.json'); | |
| 166 var tsProj = ts.createProject(tsconfigPath, { | |
| 167 typeRoots: [path.join(exports.base, 'node_modules', '@types')], | |
| 168 }); | |
| 169 return gulp.src('*/*.ts', { cwd: incDir }) | |
| 170 .pipe(tsProj()) | |
| 171 .pipe(gulp.dest(incDir)); | |
| 172 }); | |
| 173 | |
| 174 var tsCompileSingle = function(tsconfigPath, dest) { | |
| 175 // Transpile each TypeScript module independently into JavaScript in the | |
| 176 // BUILD directory. | |
| 177 return pathExists(tsconfigPath).then( (exists) => { | |
| 178 if ( !exists ) { | |
| 179 return; | |
| 180 } | |
| 181 | |
| 182 var tsProj = ts.createProject(tsconfigPath, { | |
| 183 target: 'ES5', // Vulcanize can't handle ES6 ATM. | |
| 184 removeComments: true, | |
| 185 module: "amd", | |
| 186 outFile: 'ts-app.js', | |
| 187 typeRoots: [path.join(exports.base, 'node_modules', '@types')], | |
| 188 }); | |
| 189 return gulp.src('main.ts', { cwd: path.join(BUILD, 'scripts-ts') }) | |
| 190 .pipe(tsProj()) | |
| 191 .pipe(rename(function(fpath) { | |
| 192 fpath.dirname = "."; | |
| 193 })) | |
| 194 .pipe(gulp.dest(dest)); | |
| 195 } ); | |
| 196 }; | |
| 197 | |
| 198 // Builds the project's "ts-app.js" into the project directory. | |
| 199 gulp.task('tsproject', function() { | |
| 200 return tsCompileSingle( | |
| 201 path.join(BUILD, 'scripts-ts', 'tsconfig.json'), | |
| 202 path.join('scripts') ); | |
| 203 }); | |
| 204 | |
| 205 gulp.task('ts', ['build'], function() { | |
| 206 return tsCompileSingle( | |
| 207 path.join(BUILD, 'scripts-ts', 'tsconfig.json'), | |
| 208 path.join(path.join(BUILD, 'scripts')) ); | |
| 209 }); | |
| 210 | |
| 211 // Copy all files at the root level (app) | 139 // Copy all files at the root level (app) |
| 212 gulp.task('copy', ['build'], function() { | 140 gulp.task('copy', function() { |
| 213 // Application files. | 141 // Application files. |
| 214 var app = gulp.src([ | 142 var app = gulp.src([ |
| 215 '*', | 143 '*', |
| 216 '!inc', | 144 '!inc', |
| 217 '!test', | 145 '!test', |
| 218 '!elements', | 146 '!elements', |
| 219 '!inc/bower_components', | 147 '!bower_components', |
| 220 '!cache-config.json', | 148 '!cache-config.json', |
| 221 '!**/.DS_Store', | 149 '!**/.DS_Store', |
| 222 '!gulpfile.js', | 150 '!gulpfile.js', |
| 223 '!package.json', | 151 '!package.json', |
| 224 '!scripts-ts', | 152 ]).pipe(gulp.dest(layout.dist())); |
| 225 ], { | |
| 226 cwd: BUILD, | |
| 227 }).pipe(gulp.dest(layout.dist())); | |
| 228 | 153 |
| 229 // Copy over only the bower_components we need | 154 // Copy over only the bower_components we need |
| 230 // These are things which cannot be vulcanized | 155 // These are things which cannot be vulcanized |
| 231 var webcomponentsjs = gulp.src([ | 156 var webcomponentsjs = gulp.src([ |
| 232 'inc/bower_components/webcomponentsjs/webcomponents-lite.min.js', | 157 'inc/bower_components/webcomponentsjs/webcomponents-lite.min.js', |
| 233 ], { | 158 ]).pipe(gulp.dest(layout.dist('inc/bower_components/webcomponentsjs/'))); |
| 234 cwd: BUILD, | |
| 235 }).pipe(gulp.dest(layout.dist('inc/bower_components/webcomponentsjs/'))); | |
| 236 | |
| 237 var requirejs = gulp.src([ | |
| 238 'inc/bower_components/requirejs/require.js', | |
| 239 ], { | |
| 240 cwd: BUILD, | |
| 241 }).pipe(gulp.dest(layout.dist('inc/bower_components/requirejs/'))); | |
| 242 | 159 |
| 243 var includes = (config.includes) ? (config.includes(gulp, layout)) : ([]); | 160 var includes = (config.includes) ? (config.includes(gulp, layout)) : ([]); |
| 244 return merge(app, includes, webcomponentsjs, requirejs) | 161 return merge(app, includes, webcomponentsjs) |
| 245 .pipe($.size({ | 162 .pipe($.size({ |
| 246 title: 'copy' | 163 title: 'copy' |
| 247 })); | 164 })); |
| 248 }); | 165 }); |
| 249 | 166 |
| 250 // Copy web fonts to dist | 167 // Copy web fonts to dist |
| 251 gulp.task('fonts', ['build'], function() { | 168 gulp.task('fonts', function() { |
| 252 return gulp.src(['fonts/**'], {cwd: BUILD}) | 169 return gulp.src(['fonts/**']) |
| 253 .pipe(gulp.dest(layout.dist('fonts'))) | 170 .pipe(gulp.dest(layout.dist('fonts'))) |
| 254 .pipe($.size({ | 171 .pipe($.size({ |
| 255 title: 'fonts' | 172 title: 'fonts' |
| 256 })); | 173 })); |
| 257 }); | 174 }); |
| 258 | 175 |
| 259 // Scan your HTML for assets & optimize them | 176 // Scan your HTML for assets & optimize them |
| 260 gulp.task('html', ['build'], function() { | 177 gulp.task('html', function() { |
| 261 return optimizeHtmlTask( | 178 return optimizeHtmlTask( |
| 262 ['**/*.html', '!{elements,test,inc}/**/*.html'], | 179 ['**/*.html', '!{elements,test,inc}/**/*.html'], |
| 263 layout.dist()); | 180 layout.dist()); |
| 264 }); | 181 }); |
| 265 | 182 |
| 266 // Vulcanize granular configuration | 183 // Vulcanize granular configuration |
| 267 gulp.task('vulcanize', ['build', 'ts'], function() { | 184 gulp.task('vulcanize', function() { |
| 268 var fsResolver = hyd.FSResolver | 185 return gulp.src('elements/elements.html') |
| 269 return gulp.src('elements/elements.html', {cwd: BUILD}) | |
| 270 .pipe($.vulcanize({ | 186 .pipe($.vulcanize({ |
| 271 stripComments: true, | 187 stripComments: true, |
| 272 inlineCss: true, | 188 inlineCss: true, |
| 273 inlineScripts: true, | 189 inlineScripts: true |
| 274 })) | 190 })) |
| 275 .pipe(gulp.dest(layout.dist('elements'))) | 191 .pipe(gulp.dest(layout.dist('elements'))) |
| 276 .pipe($.size({title: 'vulcanize'})); | 192 .pipe($.size({title: 'vulcanize'})); |
| 277 }); | 193 }); |
| 278 | 194 |
| 279 // Clean output directory | 195 // Clean output directory |
| 280 gulp.task('clean', function() { | 196 gulp.task('clean', function() { |
| 281 var dist = layout.dist(); | 197 var dist = layout.dist(); |
| 282 var remove = ['.tmp', path.join(dist, '*')]; | 198 var remove = ['.tmp', path.join(dist, '*')]; |
| 283 var keep = '!'+path.join(dist, '.keep'); | 199 var keep = '!'+path.join(dist, '.keep'); |
| 284 return del(remove.concat(keep), {force: true, dot:true}); | 200 return del(remove.concat(keep), {force: true, dot:true}); |
| 285 }); | 201 }); |
| 286 | 202 |
| 287 // Watch files for changes & reload | 203 // Watch files for changes & reload |
| 288 gulp.task('servebuild', ['build'], function() { | 204 gulp.task('serve', ['styles', 'elements'], function() { |
| 289 gulp.watch([ | |
| 290 '**', | |
| 291 '!.tmp', | |
| 292 ], ['build']); | |
| 293 }); | |
| 294 | |
| 295 // Watch files for changes & reload | |
| 296 gulp.task('serve', ['default'], function() { | |
| 297 browserSync({ | 205 browserSync({ |
| 298 port: 8000, | 206 port: 5000, |
| 299 ui: { | |
| 300 port: 8080, | |
| 301 }, | |
| 302 notify: false, | 207 notify: false, |
| 303 logPrefix: 'PSK', | 208 logPrefix: 'PSK', |
| 304 snippetOptions: { | 209 snippetOptions: { |
| 305 rule: { | 210 rule: { |
| 306 match: '<span id="browser-sync-binding"></span>', | 211 match: '<span id="browser-sync-binding"></span>', |
| 307 fn: function(snippet) { | 212 fn: function(snippet) { |
| 308 return snippet; | 213 return snippet; |
| 309 } | 214 } |
| 310 } | 215 } |
| 311 }, | 216 }, |
| 312 // Run as an https by uncommenting 'https: true' | 217 // Run as an https by uncommenting 'https: true' |
| 313 // Note: this uses an unsigned certificate which on first access | 218 // Note: this uses an unsigned certificate which on first access |
| 314 // will present a certificate warning in the browser. | 219 // will present a certificate warning in the browser. |
| 315 // https: true, | 220 // https: true, |
| 316 server: { | 221 server: { |
| 317 baseDir: [BUILD], | 222 baseDir: ['.tmp', 'app'], |
| 318 middleware: [historyApiFallback()] | 223 middleware: [historyApiFallback()] |
| 319 } | 224 } |
| 320 }); | 225 }); |
| 321 | 226 |
| 322 gulp.watch(['**/*.html'], ['html', reload]); | 227 gulp.watch(['**/*.html'], reload); |
| 323 gulp.watch(['styles/**/*.css'], ['styles', reload]); | 228 gulp.watch(['styles/**/*.css'], ['styles', reload]); |
| 324 gulp.watch(['elements/**/*.css'], ['elements', reload]); | 229 gulp.watch(['elements/**/*.css'], ['elements', reload]); |
| 325 gulp.watch(['images/**/*'], ['build', reload]); | 230 gulp.watch(['images/**/*'], reload); |
| 326 gulp.watch(['inc/**/*'], { cwd: exports.base }, ['ts', reload]); | |
| 327 }); | 231 }); |
| 328 | 232 |
| 329 // Build and serve the output from the dist build | 233 // Build and serve the output from the dist build |
| 330 gulp.task('serve:dist', ['default'], function() { | 234 gulp.task('serve:dist', ['default'], function() { |
| 331 browserSync({ | 235 browserSync({ |
| 332 port: 8000, | 236 port: 5001, |
| 333 ui: { | |
| 334 port: 8080, | |
| 335 }, | |
| 336 notify: false, | 237 notify: false, |
| 337 logPrefix: 'PSK', | 238 logPrefix: 'PSK', |
| 338 snippetOptions: { | 239 snippetOptions: { |
| 339 rule: { | 240 rule: { |
| 340 match: '<span id="browser-sync-binding"></span>', | 241 match: '<span id="browser-sync-binding"></span>', |
| 341 fn: function(snippet) { | 242 fn: function(snippet) { |
| 342 return snippet; | 243 return snippet; |
| 343 } | 244 } |
| 344 } | 245 } |
| 345 }, | 246 }, |
| 346 // Run as an https by uncommenting 'https: true' | 247 // Run as an https by uncommenting 'https: true' |
| 347 // Note: this uses an unsigned certificate which on first access | 248 // Note: this uses an unsigned certificate which on first access |
| 348 // will present a certificate warning in the browser. | 249 // will present a certificate warning in the browser. |
| 349 // https: true, | 250 // https: true, |
| 350 server: layout.dist(), | 251 server: layout.dist(), |
| 351 middleware: [historyApiFallback()] | 252 middleware: [historyApiFallback()] |
| 352 }); | 253 }); |
| 353 }); | 254 }); |
| 354 | 255 |
| 355 // Build production files, the default task | 256 // Build production files, the default task |
| 356 gulp.task('default', ['clean'], function(cb) { | 257 gulp.task('default', ['clean'], function(cb) { |
| 357 runSequence( | 258 runSequence( |
| 358 ['ts', 'copy', 'styles', 'images', 'fonts', 'html'], | 259 ['copy', 'styles', 'images', 'fonts', 'html'], |
| 359 'vulcanize', | 260 'vulcanize', |
| 360 cb); | 261 cb); |
| 361 }); | 262 }); |
| 362 }; | 263 }; |
| 363 | 264 |
| 364 require('es6-promise').polyfill(); | 265 require('es6-promise').polyfill(); |
| 365 | 266 |
| 366 // Load custom tasks from the `tasks` directory | 267 // Load custom tasks from the `tasks` directory |
| 367 try { | 268 try { |
| 368 require('require-dir')('tasks'); | 269 require('require-dir')('tasks'); |
| 369 } catch (err) {} | 270 } catch (err) {} |
| OLD | NEW |