From 3abe144be7e2652e91e1faca6b009484a229ba4a Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 09:57:38 +0000 Subject: [PATCH 1/8] Add source map support --- gulpfile.js | 97 ++++++++++++++++++++++++++++++++------------------- tasks/docs.js | 4 +-- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index e651d564f..327535782 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -35,26 +35,44 @@ const sass = gulpSass(dartSass) /* Build the CSS from source */ function compileCSS(done) { return gulp - .src(['packages/nhsuk.scss']) - .pipe(sass().on('error', done)) + .src(['packages/nhsuk.scss'], { + sourcemaps: true + }) + .pipe( + sass({ + sourceMap: true, + sourceMapIncludeSources: true + }).on('error', done) + ) .pipe(postcss([autoprefixer()])) - .pipe(gulp.dest('dist/')) + .pipe( + gulp.dest('dist/', { + sourcemaps: '.' + }) + ) } /* Minify CSS and add a min.css suffix */ function minifyCSS() { return gulp - .src([ - 'dist/*.css', - '!dist/*.min.css' // don't re-minify minified css - ]) + .src( + [ + 'dist/*.css', + '!dist/*.min.css' // don't re-minify minified css + ], + { sourcemaps: true } + ) .pipe(postcss([cssnano()])) .pipe( rename({ suffix: `-${version}.min` }) ) - .pipe(gulp.dest('dist/')) + .pipe( + gulp.dest('dist/', { + sourcemaps: '.' + }) + ) } /** @@ -64,9 +82,12 @@ function minifyCSS() { /* Use Webpack to build and minify the NHS.UK components JS. */ function webpackJS() { return gulp - .src('./packages/nhsuk.js') + .src('./packages/nhsuk.js', { + sourcemaps: true + }) .pipe( webpack({ + devtool: 'source-map', mode: 'production', module: { rules: [ @@ -89,19 +110,29 @@ function webpackJS() { target: 'browserslist' }) ) - .pipe(gulp.dest('./dist')) + .pipe( + gulp.dest('./dist', { + sourcemaps: '.' + }) + ) } /* Minify the JS file for release */ function minifyJS() { return gulp - .src([ - 'dist/*.js', - '!dist/*.min.js' // don't re-minify minified javascript - ]) + .src( + [ + 'dist/*.js', + '!dist/*.min.js' // don't re-minify minified javascript + ], + { sourcemaps: true } + ) .pipe( terser({ format: { comments: false }, + sourceMap: { + includeSources: true + }, // Compatibility workarounds ecma: 5, @@ -110,23 +141,14 @@ function minifyJS() { ) .pipe( rename({ - suffix: '.min' + suffix: `-${version}.min` }) ) - .pipe(gulp.dest('dist/')) -} - -/* Version the JS file for release */ -function versionJS() { - return gulp - .src('dist/nhsuk.min.js') .pipe( - rename({ - basename: `nhsuk-${version}`, - extname: '.min.js' + gulp.dest('dist/', { + sourcemaps: '.' }) ) - .pipe(gulp.dest('dist/')) } /** @@ -148,25 +170,30 @@ function assets() { /* Copy JS files into their relevant folders */ function jsFolder() { - return gulp - .src('dist/*.min.js', { ignore: 'dist/nhsuk.min.js' }) - .pipe(gulp.dest('dist/js/')) + return gulp.src('dist/*.min.{js,js.map}').pipe(gulp.dest('dist/js/')) } /* Copy CSS files into their relevant folders */ function cssFolder() { - return gulp.src('dist/*.min.css').pipe(gulp.dest('dist/css/')) + return gulp.src('dist/*.min.{css,css.map}').pipe(gulp.dest('dist/css/')) } async function createZip() { const { default: zip } = await import('gulp-zip') return gulp - .src(['dist/css/*.min.css', 'dist/js/*.min.js', 'dist/assets/**'], { - base: 'dist', - encoding: false - }) + .src( + [ + 'dist/css/*.min.{css,css.map}', + 'dist/js/*.min.{js,js.map}', + 'dist/assets/**' + ], + { + base: 'dist', + encoding: false + } + ) .pipe(zip(`nhsuk-frontend-${version}.zip`)) .pipe(gulp.dest('dist')) } @@ -192,7 +219,7 @@ gulp.task( gulp.task( 'bundle', - gulp.series(['build', gulp.parallel([minifyCSS, minifyJS]), versionJS]) + gulp.series(['build', gulp.parallel([minifyCSS, minifyJS])]) ) gulp.task( diff --git a/tasks/docs.js b/tasks/docs.js index 815bb13de..f405f7de5 100644 --- a/tasks/docs.js +++ b/tasks/docs.js @@ -137,8 +137,8 @@ gulp.task('docs:watch', () => Promise.all([ gulp.watch(['app/**/*.njk'], buildHTML), gulp.watch(['dist/**/*.html']).on('change', browserSync.reload), - gulp.watch(['dist/*.css'], copyCSS), - gulp.watch(['dist/*.js'], copyJS), + gulp.watch(['dist/*.{css,css.map}'], copyCSS), + gulp.watch(['dist/*.{js,js.map}'], copyJS), gulp.watch(['packages/assets/**/*'], copyBinaryAssets) ]) ) From e672fdd32fd615961c14cbc542480ecdb135ba48 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 09:57:47 +0000 Subject: [PATCH 2/8] Consolidate `build` and `bundle` tasks --- docs/contributing/tooling.md | 8 ++++---- gulpfile.js | 17 +++++------------ package.json | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/docs/contributing/tooling.md b/docs/contributing/tooling.md index fb9e562cc..ee586d44b 100644 --- a/docs/contributing/tooling.md +++ b/docs/contributing/tooling.md @@ -28,11 +28,11 @@ To run a gulp task, run `npx gulp ` on the command line. | task | action | | ------------ | ---------------------------------------------------------------------- | | `default` | Serve the documentation on port 3000. Recompile when there are changes | -| `style` | Compiles CSS | -| `build` | Compiles CSS and JS | -| `bundle` | Creates distributable CSS and JS files in `dist/` | +| `style` | Compiles CSS only, including minified files in `dist/` | +| `script` | Compiles JS only, including minified files in `dist/` | +| `build` | Deletes `dist/` contents then runs `style` and `script` | | `zip` | Creates a distributable zip file in `dist/` | -| `watch` | Recompile distributables when there are changes | +| `watch` | Runs `style` and `script` when there are changes | | `docs:build` | Recompile documentation | | `docs:watch` | Recompile documentation when there are changes | | `docs:serve` | Serve documentation on port 3000 | diff --git a/gulpfile.js b/gulpfile.js index 327535782..a55a9e72d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -210,17 +210,10 @@ gulp.task('clean:zip', async () => { return clean(['dist/{assets,css,js}', 'dist/*.zip']) }) -gulp.task('style', compileCSS) +gulp.task('style', gulp.series([compileCSS, minifyCSS])) +gulp.task('script', gulp.series([webpackJS, minifyJS])) -gulp.task( - 'build', - gulp.series(['clean', gulp.parallel([compileCSS, webpackJS])]) -) - -gulp.task( - 'bundle', - gulp.series(['build', gulp.parallel([minifyCSS, minifyJS])]) -) +gulp.task('build', gulp.series(['clean', gulp.parallel(['style', 'script'])])) gulp.task( 'zip', @@ -233,8 +226,8 @@ gulp.task( gulp.task('watch', () => Promise.all([ - gulp.watch(['packages/**/*.scss'], compileCSS), - gulp.watch(['packages/**/*.js'], webpackJS) + gulp.watch(['packages/**/*.scss'], gulp.series(['style'])), + gulp.watch(['packages/**/*.js'], gulp.series(['script'])) ]) ) diff --git a/package.json b/package.json index ccb422afc..73ae793e8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "node": ">=20.0.0" }, "scripts": { - "build": "gulp bundle docs:build --color --series", + "build": "gulp build docs:build --color --series", "prestart": "npm run build", "start": "gulp --color", "lint": "npm run lint:js && npm run lint:css && npm run lint:prettier", From b6931b19e5c38b976f968b9ab4f7f67a5cdef44e Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 09:55:52 +0000 Subject: [PATCH 3/8] Use minified files for docs --- app/_templates/layout.njk | 4 ++-- tasks/docs.js | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/_templates/layout.njk b/app/_templates/layout.njk index 09fafc66f..603633a3d 100644 --- a/app/_templates/layout.njk +++ b/app/_templates/layout.njk @@ -17,9 +17,9 @@ - + - + diff --git a/tasks/docs.js b/tasks/docs.js index f405f7de5..3e24d3ecf 100644 --- a/tasks/docs.js +++ b/tasks/docs.js @@ -9,6 +9,7 @@ const nunjucks = require('nunjucks') const PluginError = require('plugin-error') const validatorConfig = require('../.htmlvalidate') +const { version } = require('../package.json') /** * Compile Nunjucks into HTML @@ -29,7 +30,8 @@ async function buildHTML() { const { name, dir } = parse(path) const html = env.render(path, { - baseUrl: '/nhsuk-frontend/' + baseUrl: '/nhsuk-frontend/', + version }) const destPath = join('dist/app', dir) @@ -71,8 +73,8 @@ async function validateHTML() { */ function copyCSS() { return gulp - .src('dist/*.css') - .pipe(gulp.dest('dist/app/assets')) + .src('dist/*.min.{css,css.map}') + .pipe(gulp.dest('dist/app/stylesheets')) .pipe(browserSync.stream()) } @@ -81,8 +83,8 @@ function copyCSS() { */ function copyJS() { return gulp - .src('dist/*.js') - .pipe(gulp.dest('dist/app/assets')) + .src('dist/*.min.{js,js.map}') + .pipe(gulp.dest('dist/app/javascripts')) .pipe(browserSync.stream()) } @@ -137,8 +139,8 @@ gulp.task('docs:watch', () => Promise.all([ gulp.watch(['app/**/*.njk'], buildHTML), gulp.watch(['dist/**/*.html']).on('change', browserSync.reload), - gulp.watch(['dist/*.{css,css.map}'], copyCSS), - gulp.watch(['dist/*.{js,js.map}'], copyJS), + gulp.watch(['dist/*.min.{css,css.map}'], copyCSS), + gulp.watch(['dist/*.min.{js,js.map}'], copyJS), gulp.watch(['packages/assets/**/*'], copyBinaryAssets) ]) ) From bb45937083b84d318bbc5d952d60f69a876dd884 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 10:49:55 +0000 Subject: [PATCH 4/8] Ensure Dart Sass `file://` sources use relative paths https://github.com/alphagov/govuk-frontend/pull/3527 --- gulpfile.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index a55a9e72d..c73f33eb2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,8 @@ +const { join, relative } = require('path') +const { cwd } = require('process') +const { Transform } = require('stream') +const { fileURLToPath } = require('url') + const autoprefixer = require('autoprefixer') const cssnano = require('cssnano') const gulp = require('gulp') @@ -44,6 +49,22 @@ function compileCSS(done) { sourceMapIncludeSources: true }).on('error', done) ) + .pipe( + new Transform({ + objectMode: true, + + // Make source file:// paths relative + transform(file, enc, cb) { + if (file.sourceMap?.sources) { + file.sourceMap.sources = file.sourceMap.sources.map((path) => + relative(join(cwd(), 'dist'), fileURLToPath(path)) + ) + } + + cb(null, file) + } + }) + ) .pipe(postcss([autoprefixer()])) .pipe( gulp.dest('dist/', { From 5b18c71f4ea6a5506d41c5608f89d8b37ab61962 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 11:02:18 +0000 Subject: [PATCH 5/8] Ensure JavaScript `webpack://` sources use relative paths --- gulpfile.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index c73f33eb2..c98c6ae6f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -126,7 +126,12 @@ function webpackJS() { minimize: false // minification is handled by terser }, output: { - filename: 'nhsuk.js' + filename: 'nhsuk.js', + + // Make source webpack:// paths relative + devtoolModuleFilenameTemplate(info) { + return relative(join(cwd(), 'dist'), info.absoluteResourcePath) + } }, target: 'browserslist' }) From 4ba77cfa16272b61e9bf0bf4a179b5fd572e2205 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 11:00:38 +0000 Subject: [PATCH 6/8] Add changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99e5eb47b..9e649266b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +:new: **New features** + +- Output source maps and use minified code in examples ([PR 1152](https://github.com/nhsuk/nhsuk-frontend/pull/1152)) + :wrench: **Fixes** We've configured our build tasks to use [Browserslist](https://browsersl.ist) for browser compatibility. This change was introduced in [pull request #1135: Configure Browserslist for build tooling](https://github.com/nhsuk/nhsuk-frontend/issues/1135) From 630bad169936796304e3cf943f2840aad2c03da6 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 11:13:43 +0000 Subject: [PATCH 7/8] Resume Gulp watch after webpack error --- gulpfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index c98c6ae6f..d1065fbd6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -101,7 +101,7 @@ function minifyCSS() { */ /* Use Webpack to build and minify the NHS.UK components JS. */ -function webpackJS() { +function webpackJS(done) { return gulp .src('./packages/nhsuk.js', { sourcemaps: true @@ -134,7 +134,7 @@ function webpackJS() { } }, target: 'browserslist' - }) + }).on('error', done) ) .pipe( gulp.dest('./dist', { From 8fa840cc058bc986850941a4c254ba308eb14a89 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 20 Feb 2025 12:21:33 +0000 Subject: [PATCH 8/8] Format build errors using Gulp --- gulpfile.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index d1065fbd6..8f7e107b8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,6 +10,7 @@ const postcss = require('gulp-postcss') const rename = require('gulp-rename') const gulpSass = require('gulp-sass') const terser = require('gulp-terser') +const PluginError = require('plugin-error') const dartSass = require('sass') const webpack = require('webpack-stream') @@ -47,7 +48,13 @@ function compileCSS(done) { sass({ sourceMap: true, sourceMapIncludeSources: true - }).on('error', done) + }).on('error', (error) => { + done( + new PluginError('compileCSS', error.messageFormatted, { + showProperties: false + }) + ) + }) ) .pipe( new Transform({ @@ -133,8 +140,18 @@ function webpackJS(done) { return relative(join(cwd(), 'dist'), info.absoluteResourcePath) } }, + stats: { + colors: true, + errors: false + }, target: 'browserslist' - }).on('error', done) + }).on('error', (error) => { + done( + new PluginError('webpackJS', error, { + showProperties: false + }) + ) + }) ) .pipe( gulp.dest('./dist', {