diff --git a/app/assets/javascripts/modules/all.mjs b/app/assets/javascripts/modules/all.mjs index 13837f56a..7940c17df 100644 --- a/app/assets/javascripts/modules/all.mjs +++ b/app/assets/javascripts/modules/all.mjs @@ -2,10 +2,10 @@ // a bit like `app/__init__` in the Flask app. // // When processed by a bundler, this is turned into a Immediately Invoked Function Expression (IIFE) -// and saved as `all.js` in the same folder as this file. The IIFE format allows it to run in -// browsers that don't support JS Modules. +// The IIFE format allows it to run in browsers that don't support JS Modules. // // Exported items will be added to the window.GOVUK namespace. +// For example, `export { Frontend }` will assign `Frontend` to `window.Frontend` import Header from 'govuk-frontend/components/header/header'; // Copy of the initAll function from https://github.com/alphagov/govuk-frontend/blob/v2.13.0/src/all.js diff --git a/gulpfile.js b/gulpfile.js index 010dcc90d..16b2a843d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -6,16 +6,16 @@ // 1. LIBRARIES // - - - - - - - - - - - - - - - const { src, pipe, dest, series, parallel, watch } = require('gulp'); -const rollup = require('rollup'); const rollupPluginCommonjs = require('rollup-plugin-commonjs'); const rollupPluginNodeResolve = require('rollup-plugin-node-resolve'); +const streamqueue = require('streamqueue'); const stylish = require('jshint-stylish'); -const del = require('del'); const plugins = {}; plugins.addSrc = require('gulp-add-src'); plugins.babel = require('gulp-babel'); plugins.base64 = require('gulp-base64-inline'); +plugins.rollup = require('gulp-better-rollup') plugins.concat = require('gulp-concat'); plugins.cssUrlAdjuster = require('gulp-css-url-adjuster'); plugins.jshint = require('gulp-jshint'); @@ -54,70 +54,78 @@ const copy = { }; -const bundleJavaScriptModules = async function () { - const bundle = await rollup.rollup({ - input: paths.src + 'javascripts/modules/all.mjs', - plugins: [ - // determine module entry points from either 'module' or 'main' fields in package.json - rollupPluginNodeResolve({ - mainFields: ['module', 'main'] - }), - // gulp rollup runs on nodeJS so reads modules in commonJS format - // this adds node_modules to the require path so it can find the GOVUK Frontend modules - rollupPluginCommonjs({ - include: 'node_modules/**' - }) - ] - }); - - // write resulting module to Immediately Invoked Function Expression (IIFE) format - // map the exported code to the window.GOVUK namespace - await bundle.write({ - file: paths.src + 'javascripts/modules/all.js', - format: 'iife', - name: 'GOVUK' - }); -}; const javascripts = () => { - return src([ - paths.toolkit + 'javascripts/govuk/modules.js', - paths.toolkit + 'javascripts/govuk/show-hide-content.js', - paths.src + 'javascripts/govuk/cookie-functions.js', - paths.src + 'javascripts/cookieMessage.js', - paths.src + 'javascripts/stick-to-window-when-scrolling.js', - paths.src + 'javascripts/detailsPolyfill.js', - paths.src + 'javascripts/apiKey.js', - paths.src + 'javascripts/autofocus.js', - paths.src + 'javascripts/enhancedTextbox.js', - paths.src + 'javascripts/fileUpload.js', - paths.src + 'javascripts/radioSelect.js', - paths.src + 'javascripts/updateContent.js', - paths.src + 'javascripts/listEntry.js', - paths.src + 'javascripts/liveSearch.js', - paths.src + 'javascripts/errorTracking.js', - paths.src + 'javascripts/preventDuplicateFormSubmissions.js', - paths.src + 'javascripts/fullscreenTable.js', - paths.src + 'javascripts/previewPane.js', - paths.src + 'javascripts/colourPreview.js', - paths.src + 'javascripts/templateFolderForm.js', - paths.src + 'javascripts/collapsibleCheckboxes.js', - paths.src + 'javascripts/main.js' - ]) - .pipe(plugins.prettyerror()) - .pipe(plugins.babel({ - presets: ['@babel/preset-env'] - })) + // JS from third-party sources + // We assume none of it will need to pass through Babel + const vendored = src(paths.src + 'javascripts/modules/all.mjs') + // Use Rollup to combine all JS in JS module format into a Immediately Invoked Function + // Expression (IIFE) to: + // - deliver it in one bundle + // - allow it to run in browsers without support for JS Modules + .pipe(plugins.rollup( + { + plugins: [ + // determine module entry points from either 'module' or 'main' fields in package.json + rollupPluginNodeResolve({ + mainFields: ['module', 'main'] + }), + // gulp rollup runs on nodeJS so reads modules in commonJS format + // this adds node_modules to the require path so it can find the GOVUK Frontend modules + rollupPluginCommonjs({ + include: 'node_modules/**' + }) + ] + }, + { + format: 'iife', + name: 'GOVUK' + } + )) + // return a stream which pipes these files before the JS modules bundle .pipe(plugins.addSrc.prepend([ paths.npm + 'hogan.js/dist/hogan-3.0.2.js', paths.npm + 'jquery/dist/jquery.min.js', paths.npm + 'query-command-supported/dist/queryCommandSupported.min.js', paths.npm + 'diff-dom/diffDOM.js', paths.npm + 'timeago/jquery.timeago.js', - paths.npm + 'textarea-caret/index.js', - paths.src + 'javascripts/modules/all.js' - ])) + paths.npm + 'textarea-caret/index.js' + ])); + + // JS local to this application + const local = src([ + paths.toolkit + 'javascripts/govuk/modules.js', + paths.toolkit + 'javascripts/govuk/show-hide-content.js', + paths.src + 'javascripts/govuk/cookie-functions.js', + paths.src + 'javascripts/cookieMessage.js', + paths.src + 'javascripts/stick-to-window-when-scrolling.js', + paths.src + 'javascripts/detailsPolyfill.js', + paths.src + 'javascripts/apiKey.js', + paths.src + 'javascripts/autofocus.js', + paths.src + 'javascripts/enhancedTextbox.js', + paths.src + 'javascripts/fileUpload.js', + paths.src + 'javascripts/radioSelect.js', + paths.src + 'javascripts/updateContent.js', + paths.src + 'javascripts/listEntry.js', + paths.src + 'javascripts/liveSearch.js', + paths.src + 'javascripts/errorTracking.js', + paths.src + 'javascripts/preventDuplicateFormSubmissions.js', + paths.src + 'javascripts/fullscreenTable.js', + paths.src + 'javascripts/previewPane.js', + paths.src + 'javascripts/colourPreview.js', + paths.src + 'javascripts/templateFolderForm.js', + paths.src + 'javascripts/collapsibleCheckboxes.js', + paths.src + 'javascripts/main.js' + ]) + .pipe(plugins.prettyerror()) + .pipe(plugins.babel({ + presets: ['@babel/preset-env'] + })); + + // return single stream of all vinyl objects piped from the end of the vendored stream, then + // those from the end of the local stream + return streamqueue({ objectMode: true }, vendored, local) .pipe(plugins.uglify()) .pipe(plugins.concat('all.js')) .pipe(dest(paths.dist + 'javascripts/')) @@ -188,8 +196,7 @@ const lint = { }, 'js': (cb) => { return src( - paths.src + 'javascripts/**/*.js', - { ignore: paths.src + 'javascripts/modules/*.js' } // ignore bundler boilerplate JS + paths.src + 'javascripts/**/*.js' ) .pipe(plugins.jshint()) .pipe(plugins.jshint.reporter(stylish)) @@ -198,13 +205,6 @@ const lint = { }; -const clean = { - javascripts: (cb) => { - return del([paths.src + 'javascripts/modules/all.js']) - } -} - - // Default: compile everything const defaultTask = parallel( series( @@ -214,9 +214,7 @@ const defaultTask = parallel( series( copy.error_pages, series( - bundleJavaScriptModules, - javascripts, - clean.javascripts + javascripts ), sass ) diff --git a/package.json b/package.json index ecfda4fde..4426449ed 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "gulp-add-src": "1.0.0", "gulp-babel": "8.0.0", "gulp-base64-inline": "1.0.4", + "gulp-better-rollup": "4.0.1", "gulp-concat": "2.6.1", "gulp-include": "2.3.1", "gulp-sass": "4.0.2", @@ -38,6 +39,7 @@ "jquery": "3.4.1", "query-command-supported": "1.0.0", "rollup": "1.23.1", + "streamqueue": "1.1.2", "textarea-caret": "3.1.0", "timeago": "1.6.5" },