Commit 7bd6e78f authored by Robert Knight's avatar Robert Knight

Replace webassets with a simple static view and YAML config loader

After replacing the asset build pipeline with Gulp, webassets'
only function was to generate URLs for files defined in
named bundles and add cache-busting query strings.

It wasn't doing the latter because webassets does not
generate cache-busting query strings for files it serves
unless at least one filter is used or an output path is defined.

This commit replaces webassets with a Pyramid static view
and a small custom asset config environment, h.assets.Environment.

h.assets.Environment reads a set of named bundles defined
in the h/assets.yaml config file and a JSON manifest which maps
file paths to URLs with cache-busting query strings, generated
by the Gulp build process.

In the process, the logic for adding CORS headers to static
asset requests has been rewritten to 1) Explain _why_
a CORS header is needed and 2) Add the headers in a way
that doesn't rely on internal details of how Pyramid works.

pyramid.static.static_view is used directly rather than
config.add_static_view() because add_static_view() does
not provide a way to decorate requests in order to add
the CORS header.

See https://github.com/Pylons/pyramid/issues/1486
parent 60345ee0
'use strict';
require('core-js/es6/promise');
require('core-js/fn/object/assign');
require('core-js/fn/string');
var path = require('path');
var batch = require('gulp-batch');
var changed = require('gulp-changed');
var endOfStream = require('end-of-stream');
var gulp = require('gulp');
var gulpIf = require('gulp-if');
var gulpUtil = require('gulp-util');
var sass = require('gulp-sass');
var postcss = require('gulp-postcss');
var runSequence = require('run-sequence');
var sourcemaps = require('gulp-sourcemaps');
var manifest = require('./scripts/gulp/manifest');
var createBundle = require('./scripts/gulp/create-bundle');
var vendorBundles = require('./scripts/gulp/vendor-bundles');
......@@ -31,7 +36,7 @@ var vendorModules = Object.keys(vendorBundles.bundles)
}, []);
/** Builds the bundles containing vendor JS code */
gulp.task('build-vendor-js', function (done) {
gulp.task('build-vendor-js', function () {
var finished = [];
Object.keys(vendorBundles.bundles).forEach(function (name) {
finished.push(createBundle({
......@@ -118,7 +123,6 @@ gulp.task('build-css', function () {
};
return gulp.src(styleFiles)
.pipe(changed(STYLE_DIR, {extension: '.css'}))
.pipe(sourcemaps.init())
.pipe(gulpIf(isSASSFile, sass(sassOpts).on('error', sass.logError)))
.pipe(postcss([require('autoprefixer')]))
......@@ -127,7 +131,7 @@ gulp.task('build-css', function () {
});
gulp.task('watch-css', function () {
gulp.watch(styleFiles, ['build-css']);
gulp.watch('./h/static/styles/**/*.scss', ['build-css']);
});
var fontFiles = 'h/static/styles/vendor/fonts/*.woff';
......@@ -153,7 +157,36 @@ gulp.task('watch-images', function () {
gulp.watch(imageFiles, ['build-images']);
});
gulp.task('build', ['build-app-js', 'build-css',
'build-fonts', 'build-images']);
var MANIFEST_SOURCE_FILES = 'build/@(fonts|images|scripts|styles)/*.@(js|css|woff|jpg|png|svg)';
// Generate a JSON manifest mapping file paths to
// URLs containing cache-busted
function generateManifest() {
var stream = gulp.src(MANIFEST_SOURCE_FILES)
.pipe(manifest({name: 'manifest.json'}))
.pipe(gulp.dest('build/'));
stream.on('end', function () {
gulpUtil.log('Updated asset manifest');
});
}
gulp.task('generate-manifest', generateManifest);
gulp.task('watch-manifest', function () {
gulp.watch(MANIFEST_SOURCE_FILES, batch(function (events, done) {
endOfStream(generateManifest(), function () {
done();
});
}));
});
gulp.task('build', function (callback) {
runSequence(['build-app-js', 'build-css',
'build-fonts', 'build-images'],
'generate-manifest',
callback);
});
gulp.task('watch', ['watch-app-js', 'watch-css',
'watch-fonts', 'watch-images']);
'watch-fonts', 'watch-images',
'watch-manifest']);
......@@ -53,8 +53,10 @@
"devDependencies": {
"chai": "^3.2.0",
"compass-mixins": "^0.12.7",
"end-of-stream": "^1.1.0",
"exorcist": "^0.4.0",
"gulp": "^3.9.1",
"gulp-batch": "^1.0.5",
"gulp-changed": "^1.3.0",
"gulp-cli": "^1.2.1",
"gulp-if": "^2.0.0",
......@@ -78,7 +80,9 @@
"proxyquire": "^1.6.0",
"proxyquire-universal": "^1.0.8",
"proxyquireify": "^3.0.0",
"run-sequence": "^1.1.5",
"sinon": "1.16.1",
"through2": "^2.0.1",
"uglifyify": "^3.0.1",
"watchify": "^3.7.0",
"whatwg-fetch": "^0.10.1"
......
'use strict';
var path = require('path');
var crypto = require('crypto');
var through = require('through2');
var File = require('vinyl');
/**
* Gulp plugin that generates a cache-busting manifest file.
*
* Returns a function that creates a stream which takes
* a stream of Vinyl files as inputs and outputs a JSON
* manifest mapping input paths (eg. "scripts/foo.js")
* to URLs with cache-busting query parameters (eg. "scripts/foo.js?af95bd").
*/
module.exports = function (opts) {
var manifest = {};
return through.obj(function (file, enc, callback) {
var hash = crypto.createHash('sha1');
hash.update(file.contents);
var hashSuffix = hash.digest('hex').slice(0, 6);
var relPath = path.relative('build/', file.path);
manifest[relPath] = relPath + '?' + hashSuffix;
callback();
}, function (callback) {
var manifestFile = new File({
path: opts.name,
contents: new Buffer(JSON.stringify(manifest, null, 2), 'utf-8'),
});
this.push(manifestFile);
callback();
});
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment