diff --git a/babel.config.js b/babel.config.js index eb14e52856..167bcdb65d 100644 --- a/babel.config.js +++ b/babel.config.js @@ -8,6 +8,9 @@ module.exports = function (api) { const presets = [ ['@babel/preset-env', { + exclude: [ + 'transform-dynamic-import' + ], targets: { node: 'current' } diff --git a/package-lock.json b/package-lock.json index 37e505163d..d152bc3f86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27789,6 +27789,7 @@ "devDependencies": { "@axe-core/puppeteer": "^4.7.3", "cheerio": "^1.0.0-rc.12", + "govuk-frontend": "*", "govuk-frontend-config": "*", "govuk-frontend-lib": "*", "jest-axe": "^7.0.1", diff --git a/packages/govuk-frontend-review/rollup.config.mjs b/packages/govuk-frontend-review/rollup.config.mjs index 5f11ad618b..4ba6b356e4 100644 --- a/packages/govuk-frontend-review/rollup.config.mjs +++ b/packages/govuk-frontend-review/rollup.config.mjs @@ -14,7 +14,7 @@ export default defineConfig(({ i: input }) => ({ * Output options */ output: { - format: 'iife', + format: 'es', /** * Output plugins diff --git a/packages/govuk-frontend-review/src/app.mjs b/packages/govuk-frontend-review/src/app.mjs index c6e8ce4541..158014e4aa 100644 --- a/packages/govuk-frontend-review/src/app.mjs +++ b/packages/govuk-frontend-review/src/app.mjs @@ -112,7 +112,7 @@ export default async () => { const componentName = req.params.componentName const exampleName = req.params.exampleName || 'default' - const previewLayout = res.locals.componentData?.previewLayout || 'layout' + const previewLayout = res.locals.componentData?.previewLayout const exampleConfig = res.locals.componentData?.examples.find( example => example.name.replace(/ /g, '-') === exampleName diff --git a/packages/govuk-frontend-review/src/javascripts/all.mjs b/packages/govuk-frontend-review/src/javascripts/all.mjs index 77bd73ab2b..6a7fbfc08f 100644 --- a/packages/govuk-frontend-review/src/javascripts/all.mjs +++ b/packages/govuk-frontend-review/src/javascripts/all.mjs @@ -1,4 +1 @@ -import * as GOVUKFrontend from 'govuk-frontend' - -// @ts-expect-error Manually add globals to window for tests -window.GOVUKFrontend = GOVUKFrontend +export * from 'govuk-frontend' diff --git a/packages/govuk-frontend-review/src/views/component-preview.njk b/packages/govuk-frontend-review/src/views/component-preview.njk index 9a7b52d1eb..96602d19b6 100644 --- a/packages/govuk-frontend-review/src/views/component-preview.njk +++ b/packages/govuk-frontend-review/src/views/component-preview.njk @@ -1,4 +1,4 @@ -{% extends "layouts/" + previewLayout + ".njk" %} +{% extends "layouts/" + previewLayout | default('layout') + ".njk" %} {% set bodyClasses %} {{ bodyClasses }} diff --git a/packages/govuk-frontend-review/src/views/examples/scoped-initialisation/index.njk b/packages/govuk-frontend-review/src/views/examples/scoped-initialisation/index.njk index 89aabf7a10..d96baf295b 100644 --- a/packages/govuk-frontend-review/src/views/examples/scoped-initialisation/index.njk +++ b/packages/govuk-frontend-review/src/views/examples/scoped-initialisation/index.njk @@ -45,11 +45,10 @@ {% endblock %} {% block bodyEnd %} - + {% endblock %} diff --git a/packages/govuk-frontend-review/src/views/examples/template-custom/index.njk b/packages/govuk-frontend-review/src/views/examples/template-custom/index.njk index a5e5e2a566..2838ef1ed2 100644 --- a/packages/govuk-frontend-review/src/views/examples/template-custom/index.njk +++ b/packages/govuk-frontend-review/src/views/examples/template-custom/index.njk @@ -121,7 +121,10 @@ {% block bodyEnd %} - - + + {% endblock %} diff --git a/packages/govuk-frontend-review/src/views/examples/template-default/index.njk b/packages/govuk-frontend-review/src/views/examples/template-default/index.njk index facd7b041d..7c72c80cff 100644 --- a/packages/govuk-frontend-review/src/views/examples/template-default/index.njk +++ b/packages/govuk-frontend-review/src/views/examples/template-default/index.njk @@ -12,7 +12,10 @@ {% block bodyEnd %} - - + + {% endblock %} diff --git a/packages/govuk-frontend-review/src/views/examples/translated/index.njk b/packages/govuk-frontend-review/src/views/examples/translated/index.njk index 15b87da586..f4c38ccb75 100644 --- a/packages/govuk-frontend-review/src/views/examples/translated/index.njk +++ b/packages/govuk-frontend-review/src/views/examples/translated/index.njk @@ -937,18 +937,19 @@ {% endblock %} {% block bodyEnd %} - + diff --git a/packages/govuk-frontend-review/src/views/layouts/_generic.njk b/packages/govuk-frontend-review/src/views/layouts/_generic.njk index 6a940053b3..f1d89284f3 100644 --- a/packages/govuk-frontend-review/src/views/layouts/_generic.njk +++ b/packages/govuk-frontend-review/src/views/layouts/_generic.njk @@ -15,6 +15,9 @@ {% set mainClasses = 'govuk-main-wrapper--auto-spacing' %} {% block bodyEnd %} - - + + {% endblock %} diff --git a/packages/govuk-frontend-review/src/views/tests/boilerplate.njk b/packages/govuk-frontend-review/src/views/tests/boilerplate.njk index e8b9c179ed..9832d919d2 100644 --- a/packages/govuk-frontend-review/src/views/tests/boilerplate.njk +++ b/packages/govuk-frontend-review/src/views/tests/boilerplate.njk @@ -1,17 +1,25 @@ - - - - - Test Boilerplate - - - - +{% extends "component-preview.njk" %} + +{% set pageTitle = "Test boilerplate" %} +{% block pageTitle %}{{ pageTitle }} - GOV.UK{% endblock %} + +{% block head %} + {{ super() }} + +{% endblock %} + +{% block content %} +

Test boilerplate

Used during testing to inject rendered components and test specific configurations

- - - +
+{% endblock %} + +{% block bodyEnd %} + +{% endblock %} diff --git a/packages/govuk-frontend-review/tasks/scripts.mjs b/packages/govuk-frontend-review/tasks/scripts.mjs index 9ec4badaf0..3ae26f46f4 100644 --- a/packages/govuk-frontend-review/tasks/scripts.mjs +++ b/packages/govuk-frontend-review/tasks/scripts.mjs @@ -18,9 +18,9 @@ export const compile = (options) => gulp.series( destPath: join(options.destPath, 'javascripts'), configPath: join(options.basePath, 'rollup.config.mjs'), - // Rename with `*.min.js` extension + // Rename with `*.bundle.min.mjs` extension filePath ({ dir, name }) { - return join(dir, `${name}.min.js`) + return join(dir, `${name}.bundle.min.mjs`) } }) ), diff --git a/packages/govuk-frontend/postcss.config.mjs b/packages/govuk-frontend/postcss.config.mjs index f725e8b4a8..d4e3ee2f8a 100644 --- a/packages/govuk-frontend/postcss.config.mjs +++ b/packages/govuk-frontend/postcss.config.mjs @@ -2,7 +2,7 @@ import autoprefixer from 'autoprefixer' import cssnano from 'cssnano' import cssnanoPresetDefault from 'cssnano-preset-default' import { pkg } from 'govuk-frontend-config' -import { isDev } from 'govuk-frontend-tasks/helpers/task-arguments.mjs' +import { isDev } from 'govuk-frontend-tasks/helpers/task-arguments.js' import postcss from 'postcss' import scss from 'postcss-scss' diff --git a/packages/govuk-frontend/src/govuk/components/button/button.test.js b/packages/govuk-frontend/src/govuk/components/button/button.test.js index 0e302fc02c..96c6f9c602 100644 --- a/packages/govuk-frontend/src/govuk/components/button/button.test.js +++ b/packages/govuk-frontend/src/govuk/components/button/button.test.js @@ -18,14 +18,12 @@ describe('/components/button', () => { it('does not prevent further JavaScript from running', async () => { await goTo(page, '/tests/boilerplate') - const result = await page.evaluate((component) => { - const namespace = 'GOVUKFrontend' in window - ? window.GOVUKFrontend - : {} + const result = await page.evaluate(async (exportName) => { + const namespace = await import('govuk-frontend') // `undefined` simulates the element being missing, // from an unchecked `document.querySelector` for example - new namespace[component](undefined).init() + new namespace[exportName](undefined).init() // If our component initialisation breaks, this won't run return true diff --git a/packages/govuk-frontend/src/govuk/components/character-count/character-count.test.js b/packages/govuk-frontend/src/govuk/components/character-count/character-count.test.js index c3c2e6af78..0c0e8e603a 100644 --- a/packages/govuk-frontend/src/govuk/components/character-count/character-count.test.js +++ b/packages/govuk-frontend/src/govuk/components/character-count/character-count.test.js @@ -648,17 +648,14 @@ describe('Character count', () => { // Override maxlength to 10 maxlength: 10 }, - initialiser ({ config, namespace }) { - const $component = document.querySelector('[data-module]') - + initialiser ($module) { // Set locale to Welsh, which expects translations for 'one', 'two', // 'few' 'many' and 'other' forms – with the default English strings // provided we only have translations for 'one' and 'other'. // // We want to make sure we handle this gracefully in case users have // an existing character count inside an incorrect locale. - $component.setAttribute('lang', 'cy') - new namespace.CharacterCount($component, config).init() + $module.setAttribute('lang', 'cy') } }) diff --git a/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.test.js b/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.test.js index ba2211abbd..9321e1a1e1 100644 --- a/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.test.js +++ b/packages/govuk-frontend/src/govuk/components/error-summary/error-summary.test.js @@ -1,4 +1,4 @@ -const { goToComponent, goToExample, renderAndInitialise } = require('govuk-frontend-helpers/puppeteer') +const { goToComponent, goToExample, renderAndInitialise, goTo } = require('govuk-frontend-helpers/puppeteer') const { getExamples } = require('govuk-frontend-lib/files') describe('Error Summary', () => { @@ -82,14 +82,14 @@ describe('Error Summary', () => { describe('using JavaScript configuration, with no elements on the page', () => { it('does not prevent further JavaScript from running', async () => { - const result = await page.evaluate((component) => { - const namespace = 'GOVUKFrontend' in window - ? window.GOVUKFrontend - : {} + await goTo(page, '/tests/boilerplate') + + const result = await page.evaluate(async (exportName) => { + const namespace = await import('govuk-frontend') // `undefined` simulates the element being missing, // from an unchecked `document.querySelector` for example - new namespace[component](undefined).init() + new namespace[exportName](undefined).init() // If our component initialisation breaks, this won't run return true diff --git a/packages/govuk-frontend/src/govuk/components/globals.test.mjs b/packages/govuk-frontend/src/govuk/components/globals.test.mjs index cc5b328865..22571a2514 100644 --- a/packages/govuk-frontend/src/govuk/components/globals.test.mjs +++ b/packages/govuk-frontend/src/govuk/components/globals.test.mjs @@ -5,23 +5,19 @@ describe('GOV.UK Frontend', () => { let exported beforeEach(async () => { - await goTo(page, '/') + await goTo(page, '/tests/boilerplate') - // Exported via global (e.g GOVUKFrontend.initAll) - exported = await page.evaluate(() => 'GOVUKFrontend' in window - ? Object.keys(window.GOVUKFrontend) - : undefined + // Exports available via browser dynamic import + exported = await page.evaluate(async () => + Object.keys(await import('govuk-frontend')) ) }) it('exports `initAll` function', async () => { - await goTo(page, '/') - - const typeofInitAll = await page.evaluate((utility) => { - const namespace = 'GOVUKFrontend' in window - ? window.GOVUKFrontend - : {} + await goTo(page, '/tests/boilerplate') + const typeofInitAll = await page.evaluate(async (utility) => { + const namespace = await import('govuk-frontend') return typeof namespace[utility] }, 'initAll') @@ -52,10 +48,8 @@ describe('GOV.UK Frontend', () => { const components = exported .filter(method => !['initAll', 'version'].includes(method)) - const componentsWithoutInitFunctions = await page.evaluate((components) => { - const namespace = 'GOVUKFrontend' in window - ? window.GOVUKFrontend - : {} + const componentsWithoutInitFunctions = await page.evaluate(async (components) => { + const namespace = await import('govuk-frontend') return components.filter(component => { const prototype = namespace[component].prototype diff --git a/shared/helpers/package.json b/shared/helpers/package.json index ddc4a983d8..3dcf9b14d8 100644 --- a/shared/helpers/package.json +++ b/shared/helpers/package.json @@ -16,6 +16,7 @@ "devDependencies": { "@axe-core/puppeteer": "^4.7.3", "cheerio": "^1.0.0-rc.12", + "govuk-frontend": "*", "govuk-frontend-config": "*", "govuk-frontend-lib": "*", "jest-axe": "^7.0.1", diff --git a/shared/helpers/puppeteer.js b/shared/helpers/puppeteer.js index c593bd6d75..18e549a390 100644 --- a/shared/helpers/puppeteer.js +++ b/shared/helpers/puppeteer.js @@ -76,9 +76,8 @@ async function axe (page, overrides = {}) { * @param {object} options - Render and initialise options * @param {object} options.params - Nunjucks macro params * @param {object} [options.config] - Component instantiation config - * @param {(context: { config?: object, namespace: object }) => void} [options.initialiser] - A function that'll run in the - * browser to execute arbitrary initialisation. Receives an object with the - * passed configuration as `config` and the GOVUKFrontend global as `namespace` + * @param {($module: Element) => void} [options.initialiser] - A function that'll run in the + * browser to execute arbitrary initialisation * @returns {Promise} Puppeteer page object */ async function renderAndInitialise (page, componentName, options) { @@ -92,22 +91,15 @@ async function renderAndInitialise (page, componentName, options) { }, html) // Run a script to init the JavaScript component - await page.evaluate((componentClassName, options) => { - const $component = document.querySelector('[data-module]') - - // Check for window global - if (!('GOVUKFrontend' in window) || !window.GOVUKFrontend[componentClassName]) { - throw new Error(`Global 'window.GOVUKFrontend.${componentClassName}' not found`) - } + await page.evaluate(async (exportName, options) => { + const $module = document.querySelector('[data-module]') if (options.initialiser) { - return options.initialiser({ - config: options.config, - namespace: window.GOVUKFrontend - }) + options.initialiser($module) } - new window.GOVUKFrontend[componentClassName]($component, options.config).init() + const namespace = await import('govuk-frontend') + new namespace[exportName]($module, options.config).init() }, componentNameToClassName(componentName), options) return page diff --git a/shared/helpers/tsconfig.json b/shared/helpers/tsconfig.json index 4073f258b8..a711d76daf 100644 --- a/shared/helpers/tsconfig.json +++ b/shared/helpers/tsconfig.json @@ -1,4 +1,11 @@ { "extends": "../../tsconfig.base.json", - "include": ["**/*.js", "**/*.mjs", "../config", "../lib"] + "include": [ + "**/*.js", + "**/*.mjs", + "../config", + "../lib", + "../../packages/govuk-frontend" + ], + "exclude": ["./dist/**", "../../packages/govuk-frontend/dist/**"] } diff --git a/shared/tasks/helpers/task-arguments.mjs b/shared/tasks/helpers/task-arguments.js similarity index 55% rename from shared/tasks/helpers/task-arguments.mjs rename to shared/tasks/helpers/task-arguments.js index 420537dd8e..75333012ff 100644 --- a/shared/tasks/helpers/task-arguments.mjs +++ b/shared/tasks/helpers/task-arguments.js @@ -1,7 +1,7 @@ -import parser from 'yargs-parser' +const parser = require('yargs-parser') // Non-flag arguments as tasks const { _: tasks } = parser(process.argv) // Check for development task -export const isDev = tasks.includes('dev') +module.exports.isDev = tasks.includes('dev') diff --git a/shared/tasks/helpers/task-arguments.unit.test.mjs b/shared/tasks/helpers/task-arguments.unit.test.js similarity index 80% rename from shared/tasks/helpers/task-arguments.unit.test.mjs rename to shared/tasks/helpers/task-arguments.unit.test.js index 17e57e2b61..043e4a66a6 100644 --- a/shared/tasks/helpers/task-arguments.unit.test.mjs +++ b/shared/tasks/helpers/task-arguments.unit.test.js @@ -26,35 +26,35 @@ describe('Task arguments', () => { it('is flagged false', async () => { process.argv = [...argv] - const { isDev } = await import('./task-arguments.mjs') + const { isDev } = require('./task-arguments.js') expect(isDev).toBe(false) }) it("is flagged false for 'gulp build:app'", async () => { process.argv = [...argv, 'build:app'] - const { isDev } = await import('./task-arguments.mjs') + const { isDev } = require('./task-arguments.js') expect(isDev).toBe(false) }) it("is flagged false for 'gulp build:package'", async () => { process.argv = [...argv, 'build:package'] - const { isDev } = await import('./task-arguments.mjs') + const { isDev } = require('./task-arguments.js') expect(isDev).toBe(false) }) it("is flagged false for 'gulp build:release'", async () => { process.argv = [...argv, 'build:release'] - const { isDev } = await import('./task-arguments.mjs') + const { isDev } = require('./task-arguments.js') expect(isDev).toBe(false) }) it("is flagged true for 'gulp dev'", async () => { process.argv = [...argv, 'dev'] - const { isDev } = await import('./task-arguments.mjs') + const { isDev } = require('./task-arguments.js') expect(isDev).toBe(true) }) }) diff --git a/shared/tasks/npm.mjs b/shared/tasks/npm.mjs index 5bec8c9b94..90d1c39d63 100644 --- a/shared/tasks/npm.mjs +++ b/shared/tasks/npm.mjs @@ -2,7 +2,7 @@ import runScript from '@npmcli/run-script' import { paths } from 'govuk-frontend-config' import PluginError from 'plugin-error' -import { isDev } from './helpers/task-arguments.mjs' +import { isDev } from './helpers/task-arguments.js' import { task } from './index.mjs' /**