Skip to content

Commit

Permalink
Use locally installed govuk-frontend for component names, files, data
Browse files Browse the repository at this point in the history
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
  • Loading branch information
colinrotherham committed Aug 14, 2023
1 parent 5bf4460 commit b762b5a
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 32 deletions.
15 changes: 11 additions & 4 deletions packages/govuk-frontend-review/src/app.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import express from 'express'
import { paths } from 'govuk-frontend-config'
import {
getComponentsFixtures,
getComponentNames,
Expand All @@ -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,
Expand All @@ -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(),
Expand Down
16 changes: 11 additions & 5 deletions packages/govuk-frontend-review/src/common/nunjucks/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
60 changes: 38 additions & 22 deletions shared/lib/components.js
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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, {
Expand All @@ -33,59 +36,71 @@ function nunjucksEnv(searchPaths = [], nunjucksOptions = {}) {
* Load single component fixtures
*
* @param {string} componentName - Component name
* @param {import('./names').PackageOptions} [packageOptions] - Package options (optional)
* @returns {Promise<ComponentFixtures>} 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<string[]>} 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<string[]>} 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/'
)
)
}

/**
* 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<string[]>} 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) =>
Expand All @@ -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 = {}
Expand Down
3 changes: 2 additions & 1 deletion shared/stats/src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
)

/**
Expand Down

0 comments on commit b762b5a

Please sign in to comment.