diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/add-cache-busting.js b/add-cache-busting.js new file mode 100644 index 0000000..fe825d4 --- /dev/null +++ b/add-cache-busting.js @@ -0,0 +1,38 @@ +'use strict'; + +const filter = require('gulp-filter'); +const lazypipe = require('lazypipe'); +const rev = require('gulp-rev'); +const revReplace = require('gulp-rev-replace'); +const size = require('gulp-size'); + +const elementsFilter = filter(['elements/*'], { restore: true }); +const indexFilter = filter(['index.html', 'elements/*'], { restore: true }); + +/** + * The elements.js file is imported from elements.html in the same directly. + * The path prefix must therefore be removed. + */ +function removePathFromJSfile(filename) { + if (filename.endsWith('js')) { + return filename.replace('elements/', ''); + } + + return filename; +} + +/** + * Adds cache busting to the element/elements.html file + */ +module.exports = lazypipe() + .pipe(() => elementsFilter) + .pipe(rev) + .pipe(() => size({title: 'add-cache-busting'})) + .pipe(() => elementsFilter.restore) + .pipe(() => indexFilter) + .pipe(() => revReplace({ + modifyUnreved: removePathFromJSfile, + modifyReved: removePathFromJSfile, + })) + .pipe(() => size({title: 'add-cache-busting (replace)'})) + .pipe(() => indexFilter.restore); diff --git a/add-csp-compliance.js b/add-csp-compliance.js new file mode 100644 index 0000000..45b8239 --- /dev/null +++ b/add-csp-compliance.js @@ -0,0 +1,17 @@ +'use strict'; + +const crisper = require('gulp-crisper'); +const filter = require('gulp-filter'); +const lazypipe = require('lazypipe'); +const size = require('gulp-size'); + +const elementsFilter = filter(['elements/elements.html'], { restore: true }); + +/** + * Splits the combined Polymer file for CSP compliance + */ +module.exports = lazypipe() + .pipe(() => elementsFilter) + .pipe(() => size({title: 'add-csp-compliance'})) + .pipe(() => crisper({scriptInHead:false})) + .pipe(() => elementsFilter.restore); diff --git a/index.js b/index.js new file mode 100644 index 0000000..3bef5f7 --- /dev/null +++ b/index.js @@ -0,0 +1,10 @@ +'use strict'; + +exports.addCacheBusting = require('./add-cache-busting.js'); +exports.addCspCompliance = require('./add-csp-compliance.js'); +exports.injectCustomElementsEs5Adapter = require('./inject-custom-elements-es5-adapter.js'); +exports.optimizeAssets = require('./optimize-assets.js'); +exports.polymerBuild = require('./polymer-build.js'); + +// Import tasks +require('./inline-references-task.js'); diff --git a/inject-custom-elements-es5-adapter.js b/inject-custom-elements-es5-adapter.js new file mode 100644 index 0000000..f708eca --- /dev/null +++ b/inject-custom-elements-es5-adapter.js @@ -0,0 +1,22 @@ +'use strict'; + +const lazypipe = require('lazypipe'); +const replace = require('gulp-replace'); +const size = require('gulp-size'); + +/** + * Creates a gulp.pipe() function that replaces the custom-elements-es5-adapter + * placeholder with the code loading the adapter. + * + * @returns + */ +function injectCustomElementsES5Adaptor() { + return replace( + '', + '' + ); +}; + +module.exports = lazypipe() + .pipe(injectCustomElementsES5Adaptor) + .pipe(() => size({title: 'inject-custom-elements-es5-adapter'})); diff --git a/inline-references-task.js b/inline-references-task.js new file mode 100644 index 0000000..4aebb79 --- /dev/null +++ b/inline-references-task.js @@ -0,0 +1,24 @@ +'use strict'; + +const gulp = require('gulp'); +const inline = require('gulp-inline'); +const size = require('gulp-size'); + +/** + * Inline referenced files into the index.html + * + * gulp-inline can't work with stream but needs to have all files written to the file system + */ +gulp.task('inline-references', function() { + return gulp.src('dist/index.html') + .pipe(inline({ + base: 'dist/', + ignore: [ + 'config.js', + 'bower_components/webcomponentsjs/webcomponents-loader.js', + 'elements/elements.html', + ], + })) + .pipe(size({title: 'inline:'})) + .pipe(gulp.dest('dist')); +}); diff --git a/optimize-assets.js b/optimize-assets.js new file mode 100644 index 0000000..301dec6 --- /dev/null +++ b/optimize-assets.js @@ -0,0 +1,38 @@ +'use strict'; + +const babel = require('gulp-babel'); +const gulpIf = require('gulp-if'); +const gulpIgnore = require('gulp-ignore'); +const htmlmin = require('gulp-htmlmin'); +const lazypipe = require('lazypipe'); +const minifyCss = require('gulp-minify-css'); +const uglify = require('gulp-uglify'); + +const size = require('gulp-size'); +const gutil = require('gulp-util'); + +function logError(err) { + gutil.log(gutil.colors.red('[Error]'), err.toString()); + this.emit('end'); +} + +module.exports = lazypipe() + // Convert ES6 -> ES5 code for IE11 + .pipe(() => gulpIf('*.js', babel({ + presets: [ [ 'es2015', { modules: false } ] ], + compact: true, + ignore: 'custom-elements-es5-adapter.js,webcomponents-*.js' + }))) + .pipe(() => size({title: 'Babel ES6->ES5'})) + + // Minify code and HTML + .pipe(() => gulpIf('*.js', gulpIgnore.exclude('*custom-elements-es5-adapter*', uglify().on('error', logError)))) + .pipe(() => size({title: 'Add es5-adapter'})) + .pipe(() => gulpIf('*.css', minifyCss())) + .pipe(() => size({title: 'Minimize CSS'})) + .pipe(() => gulpIf('*.html', htmlmin({ + removeAttributeQuotes: false, + removeEmptyAttributes: false, + removeRedundantAttributes: false + }))) + .pipe(() => size({title: 'Minimize HTML'})); diff --git a/package.json b/package.json new file mode 100644 index 0000000..e7b9fe6 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "gulp-polymer-build-utils", + "version": "1.0.0", + "description": "Utils to simplify building Polymer applications", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Collaborne/gulp-polymer-build-utils.git" + }, + "author": "Ronny Roeller (Collaborne)", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/Collaborne/gulp-polymer-build-utils/issues" + }, + "homepage": "https://github.com/Collaborne/gulp-polymer-build-utils#readme", + "devDependencies": { + "gulp-babel": "^6.1.2", + "gulp-crisper": "^1.1.0", + "gulp-filter": "^5.0.1", + "gulp-htmlmin": "^3.0.0", + "gulp-if": "^2.0.2", + "gulp-ignore": "^2.0.2", + "gulp-inline": "^0.1.3", + "gulp-minify-css": "^1.2.4", + "gulp-replace": "^0.6.1", + "gulp-rev": "^8.0.0", + "gulp-rev-replace": "^0.4.3", + "gulp-size": "^2.1.0", + "gulp-uglify": "^3.0.0", + "gulp-util": "^3.0.8", + "lazypipe": "^1.0.1", + "merge-stream": "^1.0.1", + "polymer-build": "^2.0.0" + } +} diff --git a/polymer-build.js b/polymer-build.js new file mode 100644 index 0000000..ec89183 --- /dev/null +++ b/polymer-build.js @@ -0,0 +1,26 @@ +'use strict'; + +const build = require('polymer-build'); +const merge = require('merge-stream'); +const size = require('gulp-size'); + +/** + * Executes the polymer-build, which a.o. vulcanizes and minifies the HTML + * + * @returns + */ +function polymerBuild(config) { + const project = new build.PolymerProject(config); + + const bundler = project.bundler({ + // XXX: sourcemaps makes V8 run out of memory + sourcemaps: false, + stripComments: true, + }).on('error', e => console.error(e)); + + return merge(project.sources(), project.dependencies()) + .pipe(bundler) + .pipe(size({title: 'polymer-bundler'})); +}; + +module.exports = polymerBuild;