Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor file copying tasks to remove Gulp #3456

Merged
merged 3 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 1 addition & 73 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@
"glob": "^10.0.0",
"gulp": "^4.0.2",
"gulp-cli": "^2.3.0",
"gulp-rename": "^2.0.0",
domoscargin marked this conversation as resolved.
Show resolved Hide resolved
"jest-axe": "^7.0.1",
"js-yaml": "^4.1.0",
"jsdoc": "^4.0.2",
"jsdoc-tsimport-plugin": "^1.0.5",
"map-stream": "^0.0.7",
"minimatch": "^8.0.3",
"nunjucks": "^3.2.3",
"outdent": "^0.8.0",
Expand Down Expand Up @@ -113,8 +111,7 @@
"stylelint-config-gds": "^0.2.0",
"stylelint-order": "^6.0.3",
"typed-query-selector": "^2.9.2",
"typescript": "^5.0.3",
"vinyl": "^3.0.0"
"typescript": "^5.0.3"
},
"overrides": {
"chokidar@^2": {
Expand Down
13 changes: 11 additions & 2 deletions tasks/assets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,27 @@ export async function write (filePath, result) {
* @typedef {object} AssetOptions
* @property {string} [srcPath] - Input directory
* @property {string} [destPath] - Output directory
* @property {AssetFormatter} [filePath] - File path formatter
* @property {PathFormatter} [filePath] - File path formatter
* @property {TextFormatter} [fileContents] - File contents formatter
* @property {string[]} [ignore] - File path patterns to ignore
*/

/**
* Asset path formatter
*
* @callback AssetFormatter
* @callback PathFormatter
* @param {import('path').ParsedPath} file - Parsed file path
* @returns {string} Formatted file path
*/

/**
* Asset contents formatter
*
* @callback TextFormatter
* @param {string} [contents] - Parsed file contents
* @returns {Promise<string>} Formatted file contents
*/

/**
* Asset compiled output
*
Expand Down
18 changes: 9 additions & 9 deletions tasks/build/package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { join } from 'path'
import gulp from 'gulp'

import { paths } from '../../config/index.js'
import { configs, files, scripts, styles, task } from '../index.mjs'
import { components, configs, files, scripts, styles, task } from '../index.mjs'

/**
* Build package task
Expand Down Expand Up @@ -41,18 +41,18 @@ export default gulp.series(
),

// Generate GOV.UK Frontend fixtures.json from ${componentName}.yaml
task.name('copy:fixtures', () =>
files.generateFixtures({
srcPath: paths.src,
destPath: paths.package
task.name('compile:fixtures', () =>
components.generateFixtures('**/*.yaml', {
srcPath: join(paths.src, 'govuk/components'),
destPath: join(paths.package, 'govuk/components')
})
),

// Generate GOV.UK Frontend macro-options.json from ${componentName}.yaml
task.name('copy:macro-options', () =>
files.generateMacroOptions({
srcPath: paths.src,
destPath: paths.package
task.name('compile:macro-options', () =>
components.generateMacroOptions('**/*.yaml', {
srcPath: join(paths.src, 'govuk/components'),
destPath: join(paths.package, 'govuk/components')
})
),

Expand Down
136 changes: 136 additions & 0 deletions tasks/components.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { readFile } from 'fs/promises'
import { basename, dirname, join } from 'path'

import yaml from 'js-yaml'
import nunjucks from 'nunjucks'

import { getListing } from '../lib/file-helper.js'

import { files } from './index.mjs'

/**
* Generate fixtures.json from component data
*
* @param {AssetEntry[0]} pattern - Path to ${componentName}.yaml
* @param {AssetEntry[1]} options - Asset options
*/
export async function generateFixtures (pattern, { srcPath, destPath }) {
const componentDataPaths = await getListing(srcPath, pattern)

// Loop component data paths
const fixtures = componentDataPaths.map(async (componentDataPath) => {
const fixture = await generateFixture(join(srcPath, componentDataPath))

// Write to destination
await files.write(componentDataPath, {
srcPath,
destPath,

// Rename to fixtures.json
filePath ({ dir }) {
return join(dir, 'fixtures.json')
},

// Replace contents with JSON
async fileContents () {
return JSON.stringify(fixture, null, 4)
}
})
})

await Promise.all(fixtures)
}

/**
* Generate macro-options.json from component data
*
* @param {AssetEntry[0]} pattern - Path to ${componentName}.yaml
* @param {AssetEntry[1]} options - Asset options
*/
export async function generateMacroOptions (pattern, { srcPath, destPath }) {
const componentDataPaths = await getListing(srcPath, pattern)

// Loop component data paths
const macroOptions = componentDataPaths.map(async (componentDataPath) => {
const macroOption = await generateMacroOption(join(srcPath, componentDataPath))

// Write to destination
await files.write(componentDataPath, {
srcPath,
destPath,

// Rename to 'macro-options.json'
filePath ({ dir }) {
return join(dir, 'macro-options.json')
},

// Replace contents with JSON
async fileContents () {
return JSON.stringify(macroOption, null, 4)
}
})
})

await Promise.all(macroOptions)
}

/**
* Component fixtures YAML to JSON
*
* @param {string} componentDataPath - Path to ${componentName}.yaml
* @returns {Promise<{ component: string; fixtures: Record<string, unknown>[] }>} Component fixtures object
*/
async function generateFixture (componentDataPath) {
const json = await yaml.load(await readFile(componentDataPath, 'utf8'), { json: true })

if (!json?.examples) {
throw new Error(`${componentDataPath} is missing "examples"`)
}

// Nunjucks template
const template = join(dirname(componentDataPath), 'template.njk')
const componentName = basename(dirname(componentDataPath))

// Loop examples
const examples = json.examples.map(async (example) => {
const context = { params: example.data }

return {
name: example.name,
options: example.data,
hidden: Boolean(example.hidden),

// Wait for render to complete
html: await new Promise((resolve, reject) => {
return nunjucks.render(template, context, (error, result) => {
return error ? reject(error) : resolve(result.trim())
})
})
}
})

return {
component: componentName,
fixtures: await Promise.all(examples)
}
}

/**
* Macro options YAML to JSON
*
* @param {string} componentDataPath - Path to ${componentName}.yaml
* @returns {Promise<Record<string, unknown>[]>} Component macro options
*/
async function generateMacroOption (componentDataPath) {
const json = await yaml.load(await readFile(componentDataPath, 'utf8'), { json: true })

if (!json?.params) {
throw new Error(`${componentDataPath} is missing "params"`)
}

return json.params
}

/**
* @typedef {import('./assets.mjs').AssetEntry} AssetEntry
*/
Loading