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)
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/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 1608b53a6..e0d808406 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')
@@ -5,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-embedded')
const webpack = require('webpack-stream')
@@ -35,26 +41,66 @@ 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', (error) => {
+ done(
+ new PluginError('compileCSS', error.messageFormatted, {
+ showProperties: false
+ })
+ )
+ })
+ )
+ .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/'))
+ .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: '.'
+ })
+ )
}
/**
@@ -62,11 +108,14 @@ function minifyCSS() {
*/
/* Use Webpack to build and minify the NHS.UK components JS. */
-function webpackJS() {
+function webpackJS(done) {
return gulp
- .src('./packages/nhsuk.js')
+ .src('./packages/nhsuk.js', {
+ sourcemaps: true
+ })
.pipe(
webpack({
+ devtool: 'source-map',
mode: 'production',
module: {
rules: [
@@ -84,24 +133,49 @@ 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)
+ }
+ },
+ stats: {
+ colors: true,
+ errors: false
},
target: 'browserslist'
+ }).on('error', (error) => {
+ done(
+ new PluginError('webpackJS', error, {
+ showProperties: false
+ })
+ )
+ })
+ )
+ .pipe(
+ gulp.dest('./dist', {
+ sourcemaps: '.'
})
)
- .pipe(gulp.dest('./dist'))
}
/* 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 +184,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 +213,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'))
}
@@ -183,17 +253,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]), versionJS])
-)
+gulp.task('build', gulp.series(['clean', gulp.parallel(['style', 'script'])]))
gulp.task(
'zip',
@@ -206,8 +269,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 e63e2d3c5..1fd72e84e 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",
diff --git a/tasks/docs.js b/tasks/docs.js
index 815bb13de..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'], copyCSS),
- gulp.watch(['dist/*.js'], copyJS),
+ gulp.watch(['dist/*.min.{css,css.map}'], copyCSS),
+ gulp.watch(['dist/*.min.{js,js.map}'], copyJS),
gulp.watch(['packages/assets/**/*'], copyBinaryAssets)
])
)