blob: b935e8fc3bdfb770b30dabcf2a709f5a94201c05 [file] [log] [blame]
/*
Copyright 2016 The LUCI Authors. All rights reserved.
Use of this source code is governed under the Apache License, Version 2.0
that can be found in the LICENSE file.
This document has been largely derived from the Polymer Starter Kit:
https://github.com/PolymerElements/polymer-starter-kit
*/
'use strict';
var path = require('path');
var argv = require('yargs').argv;
var exports = module.exports = {}
exports.base = path.join(__dirname, '..');
exports.out = (argv.out || exports.base);
exports.plugins = require('gulp-load-plugins')({
config: path.join(exports.base, 'package.json'),
});
// Include Gulp & tools we'll use
var $ = exports.plugins;
var browserSync = require('browser-sync');
var cleanCSS = require('gulp-clean-css');
var debug = require('gulp-debug');
var del = require('del');
var format = require('gulp-clang-format');
var fs = require('fs');
var glob = require('glob-all');
var gulpIf = require('gulp-if');
var useref = require('gulp-useref');
var htmlmin = require('gulp-htmlmin');
var historyApiFallback = require('connect-history-api-fallback');
var hyd = require('hydrolysis');
var merge = require('merge-stream');
var reload = browserSync.reload;
var ts = require('gulp-typescript');
var sourcemaps = require('gulp-sourcemaps');
var tslint = require('gulp-tslint');
var AUTOPREFIXER_BROWSERS = [
'ie >= 10',
'ie_mob >= 10',
'ff >= 30',
'chrome >= 34',
'safari >= 7',
'opera >= 23',
'ios >= 7',
'android >= 4.4',
'bb >= 10'
];
// Include directory (/web/inc/)
exports.incDir = path.join(exports.base, 'inc');
// Common (global) tasks.
exports.setup_common = function(gulp) {
// Verify TypeScript file integrity and formatting.
gulp.task('tslint', function() {
process.chdir(exports.incDir);
return gulp.src(['**/*.ts', '!bower_components/**'])
.pipe(tslint({
configuration: './tslint.json',
formatter: 'verbose',
}))
.pipe(tslint.report());
});
gulp.task('check-ts', function() {
process.chdir(exports.incDir);
// Transpile each TypeScript module independently into JavaScript.
var tsconfigPath = path.join(exports.incDir, 'tsconfig.json');
// Compile the files in "scripts-ts/*.ts" into a single out file.
var tsProj = ts.createProject(tsconfigPath, {
typeRoots: [path.join(exports.base, 'node_modules', '@types')],
});
return gulp.src(['**/*.ts', '!bower_components/**'])
.pipe(tsProj());
});
gulp.task('check-format', function() {
process.chdir(exports.incDir);
return gulp.src(['**/*.ts', '!bower_components/**'])
.pipe(format.checkFormat())
.on('warning', function(e) {
process.stdout.write(e.message);
process.exit(1)
});
});
gulp.task('format', function() {
process.chdir(exports.incDir);
// The base option ensures the glob doesn't strip prefixes.
return gulp.src(['**/*.ts', '!bower_components/**'])
.pipe(format.format())
.pipe(gulp.dest('.'));
});
// Build production files, the default task
gulp.task('lint', gulp.series(['tslint']));
// Build production files, the default task
gulp.task('presubmit', gulp.parallel(['lint', 'check-format', 'check-ts']));
};
// Project-specific tasks.
exports.setup = function(gulp, appDir, config) {
var APP = path.basename(appDir);
var DIST = path.join(exports.out, 'dist', APP);
var layout = {
app: APP,
dir: process.cwd(),
web: '../..',
inc: './inc',
distPath: DIST,
// NOTE: Takes vararg via "arguments".
dist: function() {
return extendPath(DIST).apply(null, arguments);
},
};
var extendPath = function() {
var base = [].slice.call(arguments);
return function() {
// Simple case: only base, no additional elements.
if (base.length === 1 && arguments.length === 0) {
return base[0];
}
var parts = base.concat();
parts.push.apply(parts, arguments)
return path.join.apply(null, parts);
};
};
var styleTask = function(stylesPath, srcs) {
return gulp.src(srcs.map(function(src) {
return path.join(stylesPath, src);
}))
.pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
.pipe(gulp.dest('.tmp/' + stylesPath))
.pipe(cleanCSS())
.pipe(gulp.dest(layout.dist(stylesPath)))
.pipe($.size({title: stylesPath}));
};
var imageOptimizeTask = function(src, dest) {
return gulp.src(src)
.pipe($.imagemin({
progressive: true,
interlaced: true
}))
.pipe(gulp.dest(dest))
.pipe($.size({title: 'images'}));
};
var optimizeHtmlTask = function(src, dest) {
return gulp.src(src)
.pipe(useref({ searchPath: ['.tmp', '.'] }))
// Concatenate and minify JavaScript
.pipe($.if('*.js', $.uglify({
preserveComments: 'some'
})))
// Concatenate and minify styles
// In case you are still using useref build blocks
.pipe($.if('*.css', cleanCSS()))
.pipe(useref({ searchPath: ['.tmp', '.'] }))
// Minify any HTML
.pipe($.if('*.html', htmlmin({
remoteAttributeQuotes: false,
remoteEmptyAttributes: false,
remoteRedundantAttributes: false,
})))
// Output files
.pipe(gulp.dest(dest))
.pipe($.size({
title: 'html'
}));
};
// Transpiles "inc/*/*.ts" and deposits the result alongside their source
// "ts" files.
gulp.task('ts', function() {
// Compile the files in "scripts-ts/*.ts" into a single out file.
var appTsDir = path.join(layout.inc, 'apps', layout.app);
var tsProj = ts.createProject(path.join(layout.inc, 'tsconfig.json'), {
typeRoots: [path.join(exports.base, 'node_modules', '@types')],
outFile: path.join(appTsDir, 'main.js'),
});
return gulp.src(path.join(layout.inc, 'apps', layout.app, '*.ts'), {
exclude: ['*_test.ts'],
})
.pipe(sourcemaps.init())
.pipe(tsProj())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(layout.inc))
});
// Compile and automatically prefix stylesheets
gulp.task('styles', function() {
return styleTask('styles', ['**/*.css']);
});
gulp.task('elements', function() {
return styleTask('elements', ['**/*.css']);
});
// Optimize images
gulp.task('images', function() {
return imageOptimizeTask('images/**/*', layout.dist('images'));
});
// Copy all files at the root level (app)
gulp.task('copy', function() {
// Application files.
var app = gulp.src([
'*',
'!inc',
'!test',
'!elements',
'!inc/bower_components',
'!cache-config.json',
'!**/.DS_Store',
'!gulpfile.js',
'!package.json',
]).pipe(gulp.dest(layout.dist()));
// Copy over only the bower_components we need
// These are things which cannot be vulcanized
var webcomponentsjs = gulp.src([
'inc/bower_components/webcomponentsjs/webcomponents-lite.min.js',
]).pipe(gulp.dest(layout.dist('inc/bower_components/webcomponentsjs/')));
var requirejs = gulp.src([
'inc/bower_components/requirejs/require.js',
]).pipe(gulp.dest(layout.dist('inc/bower_components/requirejs/')));
var includes = (config.includes) ? (config.includes(gulp, layout)) : ([]);
return merge(app, includes, webcomponentsjs, requirejs)
.pipe($.size({
title: 'copy'
}));
});
// Copy web fonts to dist
gulp.task('fonts', function() {
return gulp.src(['fonts/**'])
.pipe(gulp.dest(layout.dist('fonts')))
.pipe($.size({
title: 'fonts'
}));
});
// Scan your HTML for assets & optimize them
gulp.task('html', function() {
return optimizeHtmlTask(
['**/*.html', '!{elements,test,inc}/**/*.html'],
layout.dist());
});
gulp.task('compile', gulp.series(['ts']));
// Vulcanize granular configuration
gulp.task('vulcanize', gulp.series(['compile'], function vulcanize() {
var fsResolver = hyd.FSResolver
return gulp.src('elements/elements.html')
.pipe($.vulcanize({
stripComments: true,
inlineCss: true,
inlineScripts: true,
}))
.pipe(gulp.dest(layout.dist('elements')))
.pipe($.size({title: 'vulcanize'}));
}));
// Clean output directory
gulp.task('clean', function() {
var dist = layout.dist();
var remove = ['.tmp', path.join(dist, '*')];
var keep = '!'+path.join(dist, '.keep');
return del(remove.concat(keep), {force: true, dot:true});
});
// Build production files, the default task
gulp.task('default', gulp.series([
'clean',
gulp.parallel(['copy', 'styles', 'images', 'fonts', 'html', 'compile', 'vulcanize']),
]));
// Watch files for changes & reload
gulp.task('serve', gulp.series(['default'], function() {
browserSync({
port: 5000,
ui: {
port: 5080,
},
notify: false,
logPrefix: 'PSK',
snippetOptions: {
rule: {
match: '<span id="browser-sync-binding"></span>',
fn: function(snippet) {
return snippet;
}
}
},
// Run as an https by uncommenting 'https: true'
// Note: this uses an unsigned certificate which on first access
// will present a certificate warning in the browser.
// https: true,
server: {
baseDir: ['.'],
middleware: [historyApiFallback()]
}
});
gulp.watch(['**/*.html'], ['html', reload]);
gulp.watch(['styles/**/*.css'], ['styles', reload]);
gulp.watch(['elements/**/*.css'], ['elements', reload]);
gulp.watch(['images/**/*'], [reload]);
gulp.watch(['inc/**/*.ts'], { cwd: exports.base }, ['compile', reload]);
}));
// Build and serve the output from the dist build
gulp.task('serve:dist', gulp.series(['default'], function() {
browserSync({
port: 5000,
ui: {
port: 5080,
},
notify: false,
logPrefix: 'PSK',
snippetOptions: {
rule: {
match: '<span id="browser-sync-binding"></span>',
fn: function(snippet) {
return snippet;
}
}
},
// Run as an https by uncommenting 'https: true'
// Note: this uses an unsigned certificate which on first access
// will present a certificate warning in the browser.
// https: true,
server: layout.dist(),
middleware: [historyApiFallback()]
});
}));
// Install common targets.
exports.setup_common(gulp);
};
// Load custom tasks from the `tasks` directory
try {
require('require-dir')('tasks');
} catch (err) {}