From a04c67b67f4b37649f0d76919d512c6d307df7c7 Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Tue, 14 Mar 2023 13:11:52 +0000 Subject: [PATCH] Namespace tasks for import into workspaces --- config/jest/browser/open.mjs | 2 +- gulpfile.mjs | 25 ++++++---- package.json | 2 +- tasks/{compile-assets.mjs => assets.mjs} | 2 +- ...{screenshot-components.mjs => browser.mjs} | 48 +++++++++++++++---- ...ad.unit.test.mjs => browser.unit.test.mjs} | 2 +- tasks/browser/download.mjs | 27 ----------- tasks/build/dist.mjs | 18 ++++--- tasks/build/package.mjs | 23 +++++---- tasks/build/public.mjs | 7 ++- tasks/clean.mjs | 23 --------- tasks/{compile-configs.mjs => configs.mjs} | 2 +- tasks/{file.mjs => files.mjs} | 22 +++++++++ tasks/gulp/watch.mjs | 6 +-- tasks/{ => helpers}/task-arguments.mjs | 0 .../task-arguments.unit.test.mjs | 0 tasks/index.mjs | 10 ++++ tasks/{run.mjs => npm.mjs} | 10 ++-- .../{compile-javascripts.mjs => scripts.mjs} | 6 +-- tasks/{compile-stylesheets.mjs => styles.mjs} | 6 +-- 20 files changed, 126 insertions(+), 115 deletions(-) rename tasks/{compile-assets.mjs => assets.mjs} (96%) rename tasks/{screenshot-components.mjs => browser.mjs} (75%) rename tasks/{browser/download.unit.test.mjs => browser.unit.test.mjs} (97%) delete mode 100644 tasks/browser/download.mjs delete mode 100644 tasks/clean.mjs rename tasks/{compile-configs.mjs => configs.mjs} (90%) rename tasks/{file.mjs => files.mjs} (51%) rename tasks/{ => helpers}/task-arguments.mjs (100%) rename tasks/{ => helpers}/task-arguments.unit.test.mjs (100%) create mode 100644 tasks/index.mjs rename tasks/{run.mjs => npm.mjs} (87%) rename tasks/{compile-javascripts.mjs => scripts.mjs} (95%) rename tasks/{compile-stylesheets.mjs => styles.mjs} (96%) diff --git a/config/jest/browser/open.mjs b/config/jest/browser/open.mjs index 1abcf4fb8fa..0d01c00d5d8 100644 --- a/config/jest/browser/open.mjs +++ b/config/jest/browser/open.mjs @@ -1,6 +1,6 @@ import setup from 'jest-environment-puppeteer/setup' -import { download } from '../../../tasks/browser/download.mjs' +import { download } from '../../../tasks/browser.mjs' /** * Open browser diff --git a/gulpfile.mjs b/gulpfile.mjs index 9669506c32d..3ad5624a821 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -4,17 +4,14 @@ import gulp from 'gulp' import { paths } from './config/index.js' import * as build from './tasks/build/index.mjs' -import { compileJavaScripts } from './tasks/compile-javascripts.mjs' -import { compileStylesheets } from './tasks/compile-stylesheets.mjs' -import { watch } from './tasks/gulp/watch.mjs' -import { npmScriptTask } from './tasks/run.mjs' +import { browser, files, scripts, styles, npm } from './tasks/index.mjs' /** * Umbrella scripts tasks (for watch) * Runs JavaScript code quality checks, documentation, compilation */ gulp.task('scripts', gulp.series( - compileJavaScripts('all.mjs', { + scripts.compile('all.mjs', { srcPath: join(paths.src, 'govuk'), destPath: paths.public, @@ -23,7 +20,7 @@ gulp.task('scripts', gulp.series( } }), - npmScriptTask('build:jsdoc') + npm.run('build:jsdoc') )) /** @@ -31,7 +28,7 @@ gulp.task('scripts', gulp.series( * Runs Sass code quality checks, documentation, compilation */ gulp.task('styles', gulp.series( - compileStylesheets('[!_]*.scss', { + styles.compile('[!_]*.scss', { srcPath: join(paths.app, 'src'), destPath: paths.public, @@ -40,7 +37,7 @@ gulp.task('styles', gulp.series( } }), - npmScriptTask('build:sassdoc') + npm.run('build:sassdoc') )) /** @@ -56,6 +53,14 @@ gulp.task('build:dist', build.dist()) */ gulp.task('dev', gulp.series( 'build:public', - watch, - npmScriptTask('serve', ['--workspace', 'app']) + files.watch, + npm.run('serve', ['--workspace', 'app']) +)) + +/** + * Screenshots task + * Sends screenshots to Percy for visual regression testing + */ +gulp.task('screenshots', gulp.series( + browser.screenshots )) diff --git a/package.json b/package.json index 6933e4d991b..8a76c8855b7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "postbuild:dist": "jest --color --selectProjects \"Build tasks\" --testMatch \"**/*dist.test*\"", "pretest": "npm run build:public", "test": "jest --color --ignoreProjects \"Build tasks\" --maxWorkers=50%", - "test:screenshots": "node ./tasks/screenshot-components.mjs", + "test:screenshots": "gulp screenshots", "lint": "npm run lint:editorconfig && npm run lint:prettier && npm run lint:js && npm run lint:scss", "lint:editorconfig": "npm run lint:editorconfig:cli", "lint:editorconfig:cli": "editorconfig-checker", diff --git a/tasks/compile-assets.mjs b/tasks/assets.mjs similarity index 96% rename from tasks/compile-assets.mjs rename to tasks/assets.mjs index e4fafa7f37b..7f59d791561 100644 --- a/tasks/compile-assets.mjs +++ b/tasks/assets.mjs @@ -8,7 +8,7 @@ import { dirname } from 'path' * @param {AssetOutput} result - Generated or minified bundle * @returns {Promise} */ -export async function writeAsset (filePath, result) { +export async function write (filePath, result) { await mkdir(dirname(filePath), { recursive: true }) const writeTasks = [] diff --git a/tasks/screenshot-components.mjs b/tasks/browser.mjs similarity index 75% rename from tasks/screenshot-components.mjs rename to tasks/browser.mjs index b803ba80982..e4bf3284271 100644 --- a/tasks/screenshot-components.mjs +++ b/tasks/browser.mjs @@ -2,13 +2,44 @@ import { join } from 'path' import percySnapshot from '@percy/puppeteer' import { isPercyEnabled } from '@percy/sdk-utils' -import { launch } from 'puppeteer' +import puppeteer from 'puppeteer' import { paths } from '../config/index.js' import { filterPath, getDirectories, getListing } from '../lib/file-helper.js' import { goToComponent, goToExample } from '../lib/puppeteer-helpers.js' +import configPuppeteer from '../puppeteer.config.js' -import { download } from './browser/download.mjs' +/** + * Puppeteer browser downloader + */ +export async function download () { + const fetcher = puppeteer.createBrowserFetcher({ + path: join(configPuppeteer.cacheDirectory, 'chrome') + }) + + // Downloaded versions + const versions = fetcher.localRevisions() + + // Download latest browser (unless cached) + if (!versions.includes(puppeteer.defaultBrowserRevision)) { + await fetcher.download(puppeteer.defaultBrowserRevision) + + // Remove outdated browser versions + for (const version of versions) { + await fetcher.remove(version) + } + } +} + +/** + * Puppeteer browser launcher + */ +export async function launch () { + await download() + + // Open browser + return puppeteer.launch() +} /** * Send screenshots in concurrent batches to Percy @@ -17,6 +48,10 @@ import { download } from './browser/download.mjs' * @returns {Promise} */ export async function screenshots () { + if (!await isPercyEnabled()) { + throw new Error('Percy healthcheck failed') + } + const browser = await launch() // Screenshot stack @@ -99,19 +134,12 @@ export async function screenshotExample (page, exampleName) { return page.close() } -if (!await isPercyEnabled()) { - throw new Error('Percy healthcheck failed') -} - const [componentNames, componentsFiles] = await Promise.all([ getDirectories(join(paths.src, 'govuk/components')), - getListing(join(paths.src, 'govuk/components')), - download() // Download browser + getListing(join(paths.src, 'govuk/components')) ]) const exampleNames = [ 'text-alignment', 'typography' ] - -await screenshots() // Take screenshots diff --git a/tasks/browser/download.unit.test.mjs b/tasks/browser.unit.test.mjs similarity index 97% rename from tasks/browser/download.unit.test.mjs rename to tasks/browser.unit.test.mjs index a9627ec9a8c..b73559cfd1b 100644 --- a/tasks/browser/download.unit.test.mjs +++ b/tasks/browser.unit.test.mjs @@ -1,6 +1,6 @@ import puppeteer from 'puppeteer' -import { download } from './download.mjs' +import { download } from './browser.mjs' jest.mock('puppeteer') diff --git a/tasks/browser/download.mjs b/tasks/browser/download.mjs deleted file mode 100644 index 0ed22a0932b..00000000000 --- a/tasks/browser/download.mjs +++ /dev/null @@ -1,27 +0,0 @@ -import { join } from 'path' - -import puppeteer from 'puppeteer' - -import configPuppeteer from '../../puppeteer.config.js' - -/** - * Puppeteer browser downloader - */ -export async function download () { - const fetcher = puppeteer.createBrowserFetcher({ - path: join(configPuppeteer.cacheDirectory, 'chrome') - }) - - // Downloaded versions - const versions = fetcher.localRevisions() - - // Download latest browser (unless cached) - if (!versions.includes(puppeteer.defaultBrowserRevision)) { - await fetcher.download(puppeteer.defaultBrowserRevision) - - // Remove outdated browser versions - for (const version of versions) { - await fetcher.remove(version) - } - } -} diff --git a/tasks/build/dist.mjs b/tasks/build/dist.mjs index 2799e66891a..1c0a900a751 100644 --- a/tasks/build/dist.mjs +++ b/tasks/build/dist.mjs @@ -3,11 +3,9 @@ import { join } from 'path' import gulp from 'gulp' import { paths, pkg } from '../../config/index.js' -import { clean } from '../clean.mjs' -import { compileJavaScripts } from '../compile-javascripts.mjs' -import { compileStylesheets } from '../compile-stylesheets.mjs' -import { version } from '../file.mjs' -import { copyAssets } from '../gulp/copy-to-destination.mjs' +import * as files from '../files.mjs' +import * as scripts from '../scripts.mjs' +import * as styles from '../styles.mjs' /** * Build dist task @@ -16,18 +14,18 @@ import { copyAssets } from '../gulp/copy-to-destination.mjs' * @returns {() => import('gulp').TaskFunction} Task function */ export default () => gulp.series( - clean('**/*', { + files.clean('**/*', { destPath: paths.dist }), // Copy GOV.UK Frontend static assets - copyAssets('*/**', { + files.copyAssets('*/**', { srcPath: join(paths.src, 'govuk/assets'), destPath: join(paths.dist, 'assets') }), // Compile GOV.UK Frontend JavaScript - compileJavaScripts('all.mjs', { + scripts.compile('all.mjs', { srcPath: join(paths.src, 'govuk'), destPath: paths.dist, @@ -37,7 +35,7 @@ export default () => gulp.series( }), // Compile GOV.UK Frontend Sass - compileStylesheets('[!_]*.scss', { + styles.compile('[!_]*.scss', { srcPath: join(paths.src, 'govuk'), destPath: paths.dist, @@ -47,7 +45,7 @@ export default () => gulp.series( }), // Update GOV.UK Frontend version - version('VERSION.txt', { + files.version('VERSION.txt', { destPath: paths.dist }) ) diff --git a/tasks/build/package.mjs b/tasks/build/package.mjs index 0f5ecc0e5ce..ceff818edf5 100644 --- a/tasks/build/package.mjs +++ b/tasks/build/package.mjs @@ -3,11 +3,10 @@ import { join } from 'path' import gulp from 'gulp' import { paths } from '../../config/index.js' -import { clean } from '../clean.mjs' -import { compileConfig } from '../compile-configs.mjs' -import { compileJavaScripts } from '../compile-javascripts.mjs' -import { compileStylesheets } from '../compile-stylesheets.mjs' -import { copyAssets, copyFiles } from '../gulp/copy-to-destination.mjs' +import * as configs from '../configs.mjs' +import * as files from '../files.mjs' +import * as scripts from '../scripts.mjs' +import * as styles from '../styles.mjs' /** * Build package task @@ -16,7 +15,7 @@ import { copyAssets, copyFiles } from '../gulp/copy-to-destination.mjs' * @returns {() => import('gulp').TaskFunction} Task function */ export default () => gulp.series( - clean('**/*', { + files.clean('**/*', { destPath: paths.package, ignore: [ '**/package.json', @@ -25,19 +24,19 @@ export default () => gulp.series( }), // Copy GOV.UK Frontend files - copyFiles({ + files.copyFiles({ srcPath: paths.src, destPath: paths.package }), // Copy GOV.UK Frontend JavaScript (ES modules) - copyAssets('**/!(*.test).mjs', { + files.copyAssets('**/!(*.test).mjs', { srcPath: join(paths.src, 'govuk'), destPath: join(paths.package, 'govuk-esm') }), // Compile GOV.UK Frontend JavaScript (AMD modules) - compileJavaScripts('**/!(*.test).mjs', { + scripts.compile('**/!(*.test).mjs', { srcPath: join(paths.src, 'govuk'), destPath: join(paths.package, 'govuk'), @@ -47,7 +46,7 @@ export default () => gulp.series( }), // Apply CSS prefixes to GOV.UK Frontend Sass - compileStylesheets('**/*.scss', { + styles.compile('**/*.scss', { srcPath: join(paths.src, 'govuk'), destPath: join(paths.package, 'govuk'), @@ -57,7 +56,7 @@ export default () => gulp.series( }), // Apply CSS prefixes to GOV.UK Prototype Kit Sass - compileStylesheets('init.scss', { + styles.compile('init.scss', { srcPath: join(paths.src, 'govuk-prototype-kit'), destPath: join(paths.package, 'govuk-prototype-kit'), @@ -67,7 +66,7 @@ export default () => gulp.series( }), // Compile GOV.UK Prototype Kit config - compileConfig('govuk-prototype-kit.config.mjs', { + configs.compile('govuk-prototype-kit.config.mjs', { srcPath: join(paths.src, 'govuk-prototype-kit'), destPath: paths.package, diff --git a/tasks/build/public.mjs b/tasks/build/public.mjs index 3e25f38166f..0d21bc136bb 100644 --- a/tasks/build/public.mjs +++ b/tasks/build/public.mjs @@ -3,8 +3,7 @@ import { join } from 'path' import gulp from 'gulp' import { paths } from '../../config/index.js' -import { clean } from '../clean.mjs' -import { copyAssets } from '../gulp/copy-to-destination.mjs' +import * as files from '../files.mjs' /** * Build public task @@ -13,12 +12,12 @@ import { copyAssets } from '../gulp/copy-to-destination.mjs' * @returns {() => import('gulp').TaskFunction} Task function */ export default () => gulp.series( - clean('**/*', { + files.clean('**/*', { destPath: paths.public }), // Copy GOV.UK Frontend static assets - copyAssets('**/*', { + files.copyAssets('**/*', { srcPath: join(paths.src, 'govuk/assets'), destPath: join(paths.public, 'assets') }), diff --git a/tasks/clean.mjs b/tasks/clean.mjs deleted file mode 100644 index 43d00d9510a..00000000000 --- a/tasks/clean.mjs +++ /dev/null @@ -1,23 +0,0 @@ -import { basename, join } from 'path' - -import { deleteAsync } from 'del' -import slash from 'slash' - -/** - * Delete path globs for a given destination - * - * @param {string} pattern - Pattern to remove - * @param {AssetEntry[1]} options - Asset options - * @returns {() => Promise} Prepared compile task - */ -export function clean (pattern, { destPath, ignore }) { - const task = () => deleteAsync(slash(join(destPath, pattern)), { ignore }) - - task.displayName = `clean:${basename(destPath)}` - - return task -} - -/** - * @typedef {import('./compile-assets.mjs').AssetEntry} AssetEntry - */ diff --git a/tasks/compile-configs.mjs b/tasks/configs.mjs similarity index 90% rename from tasks/compile-configs.mjs rename to tasks/configs.mjs index c1236f7a335..5a44970ec5b 100644 --- a/tasks/compile-configs.mjs +++ b/tasks/configs.mjs @@ -10,7 +10,7 @@ import { basename, parse, join } from 'path' * @param {AssetEntry[1]} options - Asset options * @returns {() => Promise} Prepared compile task */ -export function compileConfig (modulePath, { srcPath, destPath, filePath }) { +export function compile (modulePath, { srcPath, destPath, filePath }) { const configPath = join(destPath, filePath ? filePath(parse(modulePath)) : modulePath) const task = async () => { diff --git a/tasks/file.mjs b/tasks/files.mjs similarity index 51% rename from tasks/file.mjs rename to tasks/files.mjs index d6ae102b66b..44a1bde4f20 100644 --- a/tasks/file.mjs +++ b/tasks/files.mjs @@ -2,8 +2,26 @@ import { writeFile } from 'fs/promises' import { EOL } from 'os' import { basename, join } from 'path' +import { deleteAsync } from 'del' +import slash from 'slash' + import { pkg } from '../config/index.js' +/** + * Delete path globs for a given destination + * + * @param {string} pattern - Pattern to remove + * @param {AssetEntry[1]} options - Asset options + * @returns {() => Promise} Prepared compile task + */ +export function clean (pattern, { destPath, ignore }) { + const task = () => deleteAsync(slash(join(destPath, pattern)), { ignore }) + + task.displayName = `clean:${basename(destPath)}` + + return task +} + /** * Write `package/package.json` version to file * @@ -19,6 +37,10 @@ export function version (assetPath, { destPath }) { return task } +// Include Gulp legacy file tasks +export { watch } from './gulp/watch.mjs' +export { copyAssets, copyFiles } from './gulp/copy-to-destination.mjs' + /** * @typedef {import('./compile-assets.mjs').AssetEntry} AssetEntry */ diff --git a/tasks/gulp/watch.mjs b/tasks/gulp/watch.mjs index ecf66fdb445..280d11bb00f 100644 --- a/tasks/gulp/watch.mjs +++ b/tasks/gulp/watch.mjs @@ -2,7 +2,7 @@ import gulp from 'gulp' import slash from 'slash' import { paths } from '../../config/index.js' -import { npmScriptTask } from '../run.mjs' +import * as npm from '../npm.mjs' /** * Watch task @@ -20,7 +20,7 @@ export function watch () { `${slash(paths.src)}/govuk/**/*.scss`, `!${slash(paths.src)}/govuk/vendor/*` ], gulp.parallel( - npmScriptTask('lint:scss'), + npm.run('lint:scss'), 'styles' )), @@ -28,7 +28,7 @@ export function watch () { 'jsdoc.config.js', `${slash(paths.src)}/govuk/**/*.mjs` ], gulp.parallel( - npmScriptTask('lint:js'), + npm.run('lint:js'), 'scripts' )) ]) diff --git a/tasks/task-arguments.mjs b/tasks/helpers/task-arguments.mjs similarity index 100% rename from tasks/task-arguments.mjs rename to tasks/helpers/task-arguments.mjs diff --git a/tasks/task-arguments.unit.test.mjs b/tasks/helpers/task-arguments.unit.test.mjs similarity index 100% rename from tasks/task-arguments.unit.test.mjs rename to tasks/helpers/task-arguments.unit.test.mjs diff --git a/tasks/index.mjs b/tasks/index.mjs new file mode 100644 index 00000000000..5f62bc82cb7 --- /dev/null +++ b/tasks/index.mjs @@ -0,0 +1,10 @@ +/** + * Shared tasks + */ +export * as assets from './assets.mjs' +export * as browser from './browser.mjs' +export * as configs from './configs.mjs' +export * as files from './files.mjs' +export * as npm from './npm.mjs' +export * as scripts from './scripts.mjs' +export * as styles from './styles.mjs' diff --git a/tasks/run.mjs b/tasks/npm.mjs similarity index 87% rename from tasks/run.mjs rename to tasks/npm.mjs index 58eea234085..c1257b98b52 100644 --- a/tasks/run.mjs +++ b/tasks/npm.mjs @@ -1,6 +1,6 @@ import { spawn } from 'child_process' -import { isDev } from './task-arguments.mjs' +import { isDev } from './helpers/task-arguments.mjs' /** * Spawns Node.js child process for npm script @@ -10,7 +10,7 @@ import { isDev } from './task-arguments.mjs' * @param {string[]} [args] - npm script arguments * @returns {Promise} Exit code */ -export async function npmScript (name, args = []) { +export async function script (name, args = []) { const command = process.platform === 'win32' ? 'npm.cmd' : 'npm' return new Promise((resolve, reject) => { @@ -52,14 +52,14 @@ export async function npmScript (name, args = []) { } /** - * Creates a Gulp task for npmScript() + * Creates a Gulp task for script() * * @param {string} name - npm script name * @param {string[]} [args] - npm script arguments * @returns {() => Promise} Exit code */ -export function npmScriptTask (name, args = []) { - const task = () => npmScript(name, args) +export function run (name, args = []) { + const task = () => script(name, args) // Add task alias // https://gulpjs.com/docs/en/api/task#task-metadata diff --git a/tasks/compile-javascripts.mjs b/tasks/scripts.mjs similarity index 95% rename from tasks/compile-javascripts.mjs rename to tasks/scripts.mjs index 45d1659fdc4..f42352d3100 100644 --- a/tasks/compile-javascripts.mjs +++ b/tasks/scripts.mjs @@ -7,7 +7,7 @@ import { minify } from 'terser' import { getListing } from '../lib/file-helper.js' import { componentPathToModuleName } from '../lib/helper-functions.js' -import { writeAsset } from './compile-assets.mjs' +import * as assets from './assets.mjs' /** * Compile JavaScript ESM to CommonJS task @@ -19,7 +19,7 @@ import { writeAsset } from './compile-assets.mjs' * @param {AssetEntry[1]} [options] - Asset options * @returns {() => Promise} Prepared compile task */ -export function compileJavaScripts (pattern, options) { +export function compile (pattern, options) { const task = async () => { const modulePaths = await getListing(options.srcPath, pattern) @@ -76,7 +76,7 @@ export async function compileJavaScript ([modulePath, { srcPath, destPath, fileP result = await minifyJavaScript(modulePath, result) // Write to files - return writeAsset(moduleDestPath, result) + return assets.write(moduleDestPath, result) } } diff --git a/tasks/compile-stylesheets.mjs b/tasks/styles.mjs similarity index 96% rename from tasks/compile-stylesheets.mjs rename to tasks/styles.mjs index f286a69dde5..4d3f7c9bca5 100644 --- a/tasks/compile-stylesheets.mjs +++ b/tasks/styles.mjs @@ -11,7 +11,7 @@ import { compileAsync } from 'sass-embedded' import { paths } from '../config/index.js' import { getListing } from '../lib/file-helper.js' -import { writeAsset } from './compile-assets.mjs' +import * as assets from './assets.mjs' /** * Compile Sass to CSS task @@ -20,7 +20,7 @@ import { writeAsset } from './compile-assets.mjs' * @param {AssetEntry[1]} [options] - Asset options * @returns {() => Promise} Prepared compile task */ -export function compileStylesheets (pattern, options) { +export function compile (pattern, options) { const task = async () => { const modulePaths = await getListing(options.srcPath, pattern) @@ -104,7 +104,7 @@ export async function compileStylesheet ([modulePath, { srcPath, destPath, fileP .process(css, { ...options, ...config.options }) // Write to files - return writeAsset(moduleDestPath, result) + return assets.write(moduleDestPath, result) } /**