From b762b5a82e467a5a56cdae28b38ac41c2fbab5ca Mon Sep 17 00:00:00 2001 From: Colin Rotherham Date: Thu, 3 Aug 2023 15:37:31 +0100 Subject: [PATCH] Use locally installed `govuk-frontend` for component names, files, data This fixes an issue where `packages/govuk-frontend` provides a list of component names, files or even YAML component data even though we have a locally installed legacy version --- packages/govuk-frontend-review/src/app.mjs | 15 +++-- .../src/common/nunjucks/index.mjs | 16 +++-- shared/lib/components.js | 60 ++++++++++++------- shared/stats/src/index.mjs | 3 +- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/packages/govuk-frontend-review/src/app.mjs b/packages/govuk-frontend-review/src/app.mjs index 45f8dd7a25..0164ab149b 100644 --- a/packages/govuk-frontend-review/src/app.mjs +++ b/packages/govuk-frontend-review/src/app.mjs @@ -1,4 +1,5 @@ import express from 'express' +import { paths } from 'govuk-frontend-config' import { getComponentsFixtures, getComponentNames, @@ -16,6 +17,10 @@ import * as routes from './routes/index.mjs' export default async () => { const app = express() + // Resolve GOV.UK Frontend from review app `node_modules` + // to allow previous versions to be installed locally + const packageOptions = { moduleRoot: paths.app } + // Cache mapped components and examples const [ componentsFixtures, @@ -24,14 +29,16 @@ export default async () => { exampleNames, fullPageExamples ] = await Promise.all([ - getComponentsFixtures(), + getComponentsFixtures(packageOptions), // Components list - getComponentNames(), + getComponentNames(packageOptions), // Components list (with JavaScript only) - getComponentNamesFiltered((componentName, componentFiles) => - componentFiles.some(filterPath([`**/${componentName}.mjs`])) + getComponentNamesFiltered( + (componentName, componentFiles) => + componentFiles.some(filterPath([`**/${componentName}.mjs`])), + packageOptions ), getExampleNames(), diff --git a/packages/govuk-frontend-review/src/common/nunjucks/index.mjs b/packages/govuk-frontend-review/src/common/nunjucks/index.mjs index 981006af38..b52bcdd4db 100644 --- a/packages/govuk-frontend-review/src/common/nunjucks/index.mjs +++ b/packages/govuk-frontend-review/src/common/nunjucks/index.mjs @@ -13,11 +13,17 @@ import * as globals from './globals/index.mjs' * @returns {import('nunjucks').Environment} Nunjucks Environment */ export function renderer(app) { - const env = nunjucksEnv([join(paths.app, 'src/views')], { - express: app, // the Express.js review app that nunjucks should install to - noCache: true, // never use a cache and recompile templates each time - watch: true // reload templates when they are changed. needs chokidar dependency to be installed - }) + const env = nunjucksEnv( + [join(paths.app, 'src/views')], + { + express: app, // the Express.js review app that nunjucks should install to + noCache: true, // never use a cache and recompile templates each time + watch: true // reload templates when they are changed. needs chokidar dependency to be installed + }, + { + moduleRoot: paths.app + } + ) // Set view engine app.set('view engine', 'njk') diff --git a/shared/lib/components.js b/shared/lib/components.js index d9279f7fbd..e5a538e10f 100644 --- a/shared/lib/components.js +++ b/shared/lib/components.js @@ -1,9 +1,9 @@ -const { join } = require('path') +const { dirname, join } = require('path') const nunjucks = require('nunjucks') const { getListing, getDirectories } = require('./files') -const { packageNameToPath, componentNameToMacroName } = require('./names') +const { packageTypeToPath, componentNameToMacroName } = require('./names') // Nunjucks default environment const env = nunjucksEnv() @@ -13,13 +13,16 @@ const env = nunjucksEnv() * * @param {string[]} [searchPaths] - Nunjucks search paths (optional) * @param {import('nunjucks').ConfigureOptions} [nunjucksOptions] - Nunjucks options (optional) + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {import('nunjucks').Environment} Nunjucks environment */ -function nunjucksEnv(searchPaths = [], nunjucksOptions = {}) { - const packagePath = packageNameToPath('govuk-frontend') +function nunjucksEnv(searchPaths = [], nunjucksOptions = {}, packageOptions) { + const packagePath = dirname( + packageTypeToPath('govuk-frontend', packageOptions) + ) - // Add to Nunjucks search paths - searchPaths.push(join(packagePath, 'src')) + // Add to Nunjucks search paths (without 'govuk' suffix) + searchPaths.push(join(packagePath, '../')) // Nunjucks environment return nunjucks.configure(searchPaths, { @@ -33,47 +36,58 @@ function nunjucksEnv(searchPaths = [], nunjucksOptions = {}) { * Load single component fixtures * * @param {string} componentName - Component name + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise} Component data */ -const getComponentFixtures = async (componentName) => { +const getComponentFixtures = async (componentName, packageOptions) => { return require(join( - packageNameToPath('govuk-frontend'), - `dist/govuk/components/${componentName}/fixtures.json` + dirname(packageTypeToPath('govuk-frontend', packageOptions)), + `components/${componentName}/fixtures.json` )) } /** * Load all components' data * + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise<(ComponentFixtures)[]>} Components' data */ -const getComponentsFixtures = async () => { - const componentNames = await getComponentNames() - return Promise.all(componentNames.map(getComponentFixtures)) +const getComponentsFixtures = async (packageOptions) => { + const componentNames = await getComponentNames(packageOptions) + return Promise.all( + componentNames.map((componentName) => + getComponentFixtures(componentName, packageOptions) + ) + ) } /** * Get component files * * @param {string} [componentName] - Component name + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise} Component files */ -const getComponentFiles = (componentName = '*') => +const getComponentFiles = (componentName = '*', packageOptions) => getListing( join( - packageNameToPath('govuk-frontend'), - `dist/govuk/components/${componentName}/**/*` + dirname(packageTypeToPath('govuk-frontend', packageOptions)), + `components/${componentName}/**/*` ) ) /** * Get component names * + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise} Component names */ -async function getComponentNames() { +async function getComponentNames(packageOptions) { return getDirectories( - join(packageNameToPath('govuk-frontend'), '**/dist/govuk/components/') + join( + dirname(packageTypeToPath('govuk-frontend', packageOptions)), + 'components/' + ) ) } @@ -81,11 +95,12 @@ async function getComponentNames() { * Get component names, filtered * * @param {(componentName: string, componentFiles: string[]) => boolean} filter - Component names array filter + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise} Component names */ -async function getComponentNamesFiltered(filter) { - const componentNames = await getComponentNames() - const componentFiles = await getComponentFiles() +async function getComponentNamesFiltered(filter, packageOptions) { + const componentNames = await getComponentNames(packageOptions) + const componentFiles = await getComponentFiles('*', packageOptions) // Apply component names filter return componentNames.filter((componentName) => @@ -97,10 +112,11 @@ async function getComponentNamesFiltered(filter) { * Get examples from component fixtures * * @param {string} componentName - Component name + * @param {import('./names').PackageOptions} [packageOptions] - Package options (optional) * @returns {Promise<{ [name: string]: ComponentFixture['options'] }>} Component examples as an object */ -async function getExamples(componentName) { - const { fixtures } = await getComponentFixtures(componentName) +async function getExamples(componentName, packageOptions) { + const { fixtures } = await getComponentFixtures(componentName, packageOptions) /** @type {{ [name: string]: ComponentFixture['options'] }} */ const examples = {} diff --git a/shared/stats/src/index.mjs b/shared/stats/src/index.mjs index 1db5613f3d..5b4d374480 100644 --- a/shared/stats/src/index.mjs +++ b/shared/stats/src/index.mjs @@ -9,7 +9,8 @@ import { filterPath, getYaml } from 'govuk-frontend-lib/files' */ const componentNamesWithJavaScript = await getComponentNamesFiltered( (componentName, componentFiles) => - componentFiles.some(filterPath([`**/${componentName}.mjs`])) + componentFiles.some(filterPath([`**/${componentName}.mjs`])), + { moduleRoot: paths.stats } ) /**