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 4, 2023
1 parent 7853357 commit 38fff45
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 24 deletions.
11 changes: 8 additions & 3 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, getComponentNamesFiltered, renderComponent } from 'govuk-frontend-lib/components'
import { filterPath } from 'govuk-frontend-lib/files'
import { getStats, modulePaths } from 'govuk-frontend-stats'
Expand All @@ -11,16 +12,20 @@ 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, componentNames, componentNamesWithJavaScript, 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`]))),
componentFiles.some(filterPath([`**/${componentName}.mjs`])), packageOptions),

getExampleNames(),
getFullPageExamples()
Expand Down
2 changes: 2 additions & 0 deletions packages/govuk-frontend-review/src/common/nunjucks/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export function renderer (app) {
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
Expand Down
49 changes: 29 additions & 20 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,14 @@ 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,49 +34,56 @@ 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) => {
return require(join(packageNameToPath('govuk-frontend'), `dist/govuk/components/${componentName}/fixtures.json`))
const getComponentFixtures = async (componentName, packageOptions) => {
return require(join(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 = '*') =>
getListing(join(packageNameToPath('govuk-frontend'), `dist/govuk/components/${componentName}/**/*`))
const getComponentFiles = (componentName = '*', packageOptions) =>
getListing(join(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 () {
return getDirectories(join(packageNameToPath('govuk-frontend'), '**/dist/govuk/components/'))
async function getComponentNames (packageOptions) {
return getDirectories(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 @@ -86,10 +94,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
2 changes: 1 addition & 1 deletion shared/stats/src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { filterPath, getYaml } from 'govuk-frontend-lib/files'
* Components with JavaScript
*/
const componentNamesWithJavaScript = await getComponentNamesFiltered((componentName, componentFiles) =>
componentFiles.some(filterPath([`**/${componentName}.mjs`])))
componentFiles.some(filterPath([`**/${componentName}.mjs`])), { moduleRoot: paths.stats })

/**
* Package options
Expand Down

0 comments on commit 38fff45

Please sign in to comment.