diff --git a/gulpfile.mjs b/gulpfile.mjs index f275be9ba1..286381a9ba 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -65,6 +65,7 @@ gulp.task('build:package', gulp.series( clean, copyFiles, compileJavaScripts, + compileStylesheets, updatePrototypeKitConfig )) diff --git a/package-lock.json b/package-lock.json index 8197435dae..c4c2abcbcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,6 @@ "glob": "^8.1.0", "gulp": "^4.0.2", "gulp-cli": "^2.3.0", - "gulp-postcss": "^9.0.1", "gulp-rename": "^2.0.0", "gulp-task-listing": "^1.1.1", "js-yaml": "^4.1.0", @@ -10861,115 +10860,6 @@ "object.assign": "^4.1.0" } }, - "node_modules/gulp-postcss": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-9.0.1.tgz", - "integrity": "sha512-9QUHam5JyXwGUxaaMvoFQVT44tohpEFpM8xBdPfdwTYGM0AItS1iTQz0MpsF8Jroh7GF5Jt2GVPaYgvy8qD2Fw==", - "dependencies": { - "fancy-log": "^1.3.3", - "plugin-error": "^1.0.1", - "postcss-load-config": "^3.0.0", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/gulp-postcss/node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-postcss/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-postcss/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-postcss/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-postcss/node_modules/plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dependencies": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-postcss/node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/gulp-postcss/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, "node_modules/gulp-rename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", @@ -23314,22 +23204,6 @@ "node": ">= 0.10" } }, - "node_modules/vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", - "dependencies": { - "source-map": "^0.5.1" - } - }, - "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/vinyl-string": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vinyl-string/-/vinyl-string-1.0.2.tgz", @@ -32284,74 +32158,6 @@ } } }, - "gulp-postcss": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-9.0.1.tgz", - "integrity": "sha512-9QUHam5JyXwGUxaaMvoFQVT44tohpEFpM8xBdPfdwTYGM0AItS1iTQz0MpsF8Jroh7GF5Jt2GVPaYgvy8qD2Fw==", - "requires": { - "fancy-log": "^1.3.3", - "plugin-error": "^1.0.1", - "postcss-load-config": "^3.0.0", - "vinyl-sourcemaps-apply": "^0.2.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - } - }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - } - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - } - } - }, "gulp-rename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", @@ -41629,21 +41435,6 @@ } } }, - "vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", - "requires": { - "source-map": "^0.5.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - } - } - }, "vinyl-string": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vinyl-string/-/vinyl-string-1.0.2.tgz", diff --git a/package.json b/package.json index 0d7d91dc87..552936c4cd 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "glob": "^8.1.0", "gulp": "^4.0.2", "gulp-cli": "^2.3.0", - "gulp-postcss": "^9.0.1", "gulp-rename": "^2.0.0", "gulp-task-listing": "^1.1.1", "js-yaml": "^4.1.0", diff --git a/postcss.config.js b/postcss.config.js index 281145ab29..9f46f1627d 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -4,7 +4,9 @@ const autoprefixer = require('autoprefixer') const cssnano = require('cssnano') const cssnanoPresetDefault = require('cssnano-preset-default') const { minimatch } = require('minimatch') +const postcss = require('postcss') const pseudoclasses = require('postcss-pseudo-classes') +const scss = require('postcss-scss') const unmq = require('postcss-unmq') const unopacity = require('postcss-unopacity') const unrgba = require('postcss-unrgba') @@ -17,6 +19,7 @@ const unrgba = require('postcss-unrgba') */ module.exports = (ctx) => { const plugins = [] + const syntax = ctx.to?.endsWith('.scss') ? scss : postcss // PostCSS 'from' (or webpack 'file') source path // https://github.com/postcss/postcss-load-config#options @@ -55,15 +58,18 @@ module.exports = (ctx) => { } // Always minify CSS - plugins.push(cssnano({ - preset: [cssnanoPresetDefault, { - // Sorted CSS is smaller when gzipped, but we sort using Stylelint - // https://cssnano.co/docs/optimisations/cssdeclarationsorter/ - cssDeclarationSorter: false - }] - })) + if (syntax !== scss) { + plugins.push(cssnano({ + preset: [cssnanoPresetDefault, { + // Sorted CSS is smaller when gzipped, but we sort using Stylelint + // https://cssnano.co/docs/optimisations/cssdeclarationsorter/ + cssDeclarationSorter: false + }] + })) + } return { + syntax, plugins } } diff --git a/postcss.config.unit.test.js b/postcss.config.unit.test.js index 63d9ea2007..02c97d825c 100644 --- a/postcss.config.unit.test.js +++ b/postcss.config.unit.test.js @@ -159,6 +159,22 @@ describe('PostCSS config', () => { }) }) + describe('Sass syntax parser', () => { + it.each([ + { + from: 'src/govuk/components/accordion/_accordion.scss', + to: 'package/govuk/components/accordion/_accordion.scss' + } + ])('Adds plugins for $from', ({ from, to }) => { + const config = configFn({ env, from, to }) + + expect(getPluginNames(config)) + .toEqual([ + 'autoprefixer' + ]) + }) + }) + describe('Review app only', () => { it.each([ { diff --git a/tasks/compile-stylesheets.mjs b/tasks/compile-stylesheets.mjs index e80f0af603..4943b5767c 100644 --- a/tasks/compile-stylesheets.mjs +++ b/tasks/compile-stylesheets.mjs @@ -1,3 +1,4 @@ +import { readFile } from 'fs/promises' import { join, parse } from 'path' import { promisify } from 'util' @@ -24,6 +25,14 @@ const sassRender = promisify(render) export async function compileStylesheets () { const importEntries = await getImportEntries() + // Manually add GOV.UK Prototype kit stylesheet + if (isPackage) { + importEntries.push(['init.scss', { + srcPath: join(paths.src, 'govuk-prototype-kit'), + destPath: join(paths.package, 'govuk-prototype-kit') + }]) + } + try { await Promise.all(importEntries.map(compileStylesheet)) } catch (cause) { @@ -42,35 +51,48 @@ export async function compileStylesheet ([modulePath, { srcPath, destPath }]) { const moduleSrcPath = join(srcPath, modulePath) const moduleDestPath = join(destPath, getPathByDestination(modulePath)) - // Render Sass - const { css, map } = await sassRender({ - file: moduleSrcPath, - outFile: moduleDestPath, - - // Enable source maps - sourceMap: true, - sourceMapContents: true, - - // Resolve @imports via - includePaths: [ - join(paths.src, 'govuk'), - join(paths.node_modules, 'govuk_frontend_toolkit/stylesheets'), - paths.node_modules - ] - }) + let css + let map + // Configure PostCSS const options = { from: moduleSrcPath, - to: moduleDestPath, - map: { + to: moduleDestPath + } + + // Render Sass + if (!isPackage) { + ({ css, map } = await sassRender({ + file: moduleSrcPath, + outFile: moduleDestPath, + + // Enable source maps + sourceMap: true, + sourceMapContents: true, + + // Resolve @imports via + includePaths: [ + join(paths.src, 'govuk'), + join(paths.node_modules, 'govuk_frontend_toolkit/stylesheets'), + paths.node_modules + ] + })) + + // Pass source maps to PostCSS + options.map = { inline: false, prev: map.toString() } } + if (!css) { + css = await readFile(moduleSrcPath) + } + // Transform with PostCSS const config = await postcssrc(options) - const result = await postcss(config.plugins).process(css, options) + const result = await postcss(config.plugins) + .process(css, { ...options, ...config.options }) // Write to files return writeAsset(moduleDestPath, result) @@ -85,8 +107,12 @@ export async function getImportEntries () { const srcPath = isPublic ? paths.app : join(paths.src, 'govuk') const destPath = isPackage ? join(destination, 'govuk') : destination - // Source stylesheets - const importPaths = await getListing(srcPath, '[!_]*.scss') + // Perform a search and return an array of matching file names + // but for 'dist' and 'public' we only want top-level stylesheets + const importPaths = await getListing(srcPath, isPackage + ? '**/*.scss' + : '[!_]*.scss' + ) return importPaths .map((modulePath) => ([modulePath, { diff --git a/tasks/gulp/copy-to-destination.mjs b/tasks/gulp/copy-to-destination.mjs index 1f3fbb6e91..17acc861ce 100644 --- a/tasks/gulp/copy-to-destination.mjs +++ b/tasks/gulp/copy-to-destination.mjs @@ -1,14 +1,11 @@ import { basename, join } from 'path' -import autoprefixer from 'autoprefixer' import gulp from 'gulp' -import postcss from 'gulp-postcss' import rename from 'gulp-rename' import yaml from 'js-yaml' import map from 'map-stream' import merge from 'merge-stream' import nunjucks from 'nunjucks' -import postcssScss from 'postcss-scss' import slash from 'slash' import configPaths from '../../config/paths.js' @@ -63,17 +60,13 @@ export function copyFiles () { // https://github.com/alphagov/govuk-frontend/tree/main/package#readme `!${slash(configPaths.src)}/govuk/README.md`, - // Exclude Sass files handled by PostCSS stream below + // Exclude Sass files handled by Gulp 'compile:scss' `!${slash(configPaths.src)}/**/*.scss`, // Exclude source YAML handled by JSON streams below `!${slash(configPaths.components)}/**/*.yaml` ]), - // Add CSS prefixes to Sass - gulp.src(`${slash(configPaths.src)}/**/*.scss`) - .pipe(postcss([autoprefixer], { syntax: postcssScss })), - // Generate fixtures.json from ${componentName}.yaml gulp.src(`${slash(configPaths.components)}/**/*.yaml`, { base: slash(configPaths.src)