Skip to content

Commit

Permalink
Resolve govuk-frontend at workspace level by default
Browse files Browse the repository at this point in the history
Node.js `require.resolve()` paths are optionally provided, for example:

1. Review app uses local `node_modules` before project level
2. Package stats uses local `node_modules` before project level

This is necessary for workspace `govuk-frontend@4` installs to be found
  • Loading branch information
colinrotherham committed Jun 6, 2023
1 parent 5343240 commit 5eb1e82
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 20 deletions.
2 changes: 1 addition & 1 deletion packages/govuk-frontend-review/nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"events": {
"restart": "browser-sync reload --config browsersync.config.js"
},
"ext": "mjs,json,yaml",
"ext": "js,mjs,json,yaml",
"quiet": true
}
5 changes: 4 additions & 1 deletion packages/govuk-frontend-review/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import resolve from '@rollup/plugin-node-resolve'
import terser from '@rollup/plugin-terser'
import { paths, resolver } from 'govuk-frontend-config'
import { defineConfig } from 'rollup'

/**
Expand Down Expand Up @@ -39,6 +40,8 @@ export default defineConfig(({ i: input }) => ({
* Input plugins
*/
plugins: [
resolve()
resolve({
modulePaths: resolver(paths.app).modules
})
]
}))
4 changes: 3 additions & 1 deletion shared/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ const pkg = require('govuk-frontend/package.json')
*/
const paths = require('./paths')
const ports = require('./ports')
const resolver = require('./resolver')

module.exports = {
paths,
pkg,
ports
ports,
resolver
}
50 changes: 50 additions & 0 deletions shared/config/resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { dirname, join } = require('path')

// Repository root directory
const paths = require('./paths')

/**
* Default lookup resolver paths
*
* @param {string} basePath - Base directory, for example `/path/to/package`
* @returns {{ modules: string[], stylesheets: string[], views: string[] }} Resolver paths
*/
module.exports = (basePath) => {
const requirePaths = [
join(basePath, 'node_modules'),
join(paths.root, 'node_modules')
]

// Locate GOV.UK Frontend
const packagePath = dirname(
require.resolve('govuk-frontend/package.json', {
paths: requirePaths
})
)

return {
/**
* Node.js module paths
*/
modules: [
join(packagePath, 'node_modules'),
...requirePaths
],

/**
* Sass stylesheets
*/
stylesheets: [
join(packagePath, 'dist'),
...requirePaths
],

/**
* Nunjucks view paths
*/
views: [
join(basePath, 'src/views'),
join(packagePath, 'dist')
]
}
}
20 changes: 13 additions & 7 deletions shared/lib/names.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ function componentPathToModuleName (componentPath) {
* @param {PackageOptions} [options] - Package resolution options
* @returns {string} Path to installed npm package entry
*/
function packageEntryToPath (packageEntry, { modulePath } = {}) {
const packagePath = require.resolve(packageEntry)
function packageEntryToPath (packageEntry, { modulePath, requirePaths } = {}) {
const packagePath = require.resolve(packageEntry, {
paths: requirePaths
})

// Append optional module path
return modulePath !== undefined ? join(dirname(packagePath), modulePath) : packagePath
Expand All @@ -82,16 +84,17 @@ function packageEntryToPath (packageEntry, { modulePath } = {}) {
* @param {PackageOptions} [options] - Package resolution options
* @returns {string} Path to installed npm package field
*/
function packageFieldToPath (packageName, { modulePath, field = 'main' } = {}) {
function packageFieldToPath (packageName, { modulePath, requirePaths, field = 'main' } = {}) {
const packageEntry = `${packageName}/package.json`

// Package field as child path
const entryPath = require(packageEntryToPath(packageEntry))[field]
const entryPath = require(packageEntryToPath(packageEntry, { requirePaths }))[field]
const childPath = modulePath !== undefined ? join(dirname(entryPath), modulePath) : entryPath

// Append optional module path
return packageEntryToPath(packageEntry, {
modulePath: childPath
modulePath: childPath,
requirePaths
})
}

Expand All @@ -103,11 +106,13 @@ function packageFieldToPath (packageName, { modulePath, field = 'main' } = {}) {
*
* @param {string} packageName - Installed npm package name
* @param {string} [childPath] - Child directory path (optional, relative to package.json), for example `src/govuk/all.mjs`
* @param {PackageOptions} [options] - Package resolution options
* @returns {string} Path to installed npm package
*/
function packageNameToPath (packageName, childPath = '') {
function packageNameToPath (packageName, childPath = '', { requirePaths } = {}) {
return packageEntryToPath(`${packageName}/package.json`, {
modulePath: childPath
modulePath: childPath,
requirePaths
})
}

Expand All @@ -124,4 +129,5 @@ module.exports = {
* @typedef {object} PackageOptions
* @property {string} [field] - Package field name from package.json, for example `module`
* @property {string} [modulePath] - Module path (optional, relative to package entry), for example `all.mjs`
* @property {string[]} [requirePaths] - Node.js require 'node_modules` lookup paths
*/
32 changes: 32 additions & 0 deletions shared/lib/names.unit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,35 @@ describe('packageNameToPath', () => {
.toBe(resolvedPath)
})
})

describe("packageNameToPath (with custom 'node_module' paths)", () => {
const packages = [
{
packageName: 'govuk-frontend',
childPath: 'src/govuk/all.mjs',
options: { requirePaths: [join(paths.root, 'node_modules')] },
resolvedPath: join(paths.package, 'src/govuk/all.mjs')
},
{
packageName: 'govuk-frontend-review',
childPath: 'src/app.mjs',
options: { requirePaths: [join(paths.root, 'node_modules')] },
resolvedPath: join(paths.app, 'src/app.mjs')
},
{
packageName: 'autoprefixer',
options: { requirePaths: [join(paths.package, 'node_modules')] },
resolvedPath: join(paths.root, 'node_modules/autoprefixer')
},
{
packageName: 'postcss',
options: { requirePaths: [join(paths.app, 'node_modules')] },
resolvedPath: join(paths.root, 'node_modules/postcss')
}
]

it.each(packages)("locates path for npm package '$packageName'", ({ packageName, childPath, options = {}, resolvedPath }) => {
expect(packageNameToPath(packageName, childPath, options))
.toBe(resolvedPath)
})
})
4 changes: 3 additions & 1 deletion shared/stats/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export default defineConfig(modulePaths
* Input plugins
*/
plugins: [
resolve({ modulePaths: [paths.root] }),
resolve({
modulePaths: packageOptions.requirePaths
}),

// Stats: File size
visualizer({
Expand Down
5 changes: 3 additions & 2 deletions shared/stats/src/index.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { join, parse } from 'path'

import { paths } from 'govuk-frontend-config'
import { paths, resolver } from 'govuk-frontend-config'
import { getComponentNames, filterPath, getYaml } from 'govuk-frontend-lib/files'

/**
Expand All @@ -16,7 +16,8 @@ const componentNamesWithJavaScript = await getComponentNames((componentName, com
*/
export const packageOptions = {
field: 'module',
modulePath: 'all.mjs'
modulePath: 'all.mjs',
requirePaths: resolver(paths.stats).modules
}

/**
Expand Down
10 changes: 3 additions & 7 deletions shared/tasks/styles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { readFile } from 'fs/promises'
import { join, parse } from 'path'

import chalk from 'chalk'
import { paths } from 'govuk-frontend-config'
import { resolver } from 'govuk-frontend-config'
import { getListing } from 'govuk-frontend-lib/files'
import { packageNameToPath } from 'govuk-frontend-lib/names'
import PluginError from 'plugin-error'
import postcss from 'postcss'
// eslint-disable-next-line import/default
Expand Down Expand Up @@ -39,7 +38,7 @@ export async function compile (pattern, options) {
*
* @param {AssetEntry} assetEntry - Asset entry
*/
export async function compileStylesheet ([modulePath, { configPath, srcPath, destPath, filePath }]) {
export async function compileStylesheet ([modulePath, { basePath, configPath, srcPath, destPath, filePath }]) {
const moduleSrcPath = join(srcPath, modulePath)
const moduleDestPath = join(destPath, filePath ? filePath(parse(modulePath)) : modulePath)

Expand Down Expand Up @@ -80,10 +79,7 @@ export async function compileStylesheet ([modulePath, { configPath, srcPath, des
sourceMapIncludeSources: true,

// Resolve @imports via
loadPaths: [
packageNameToPath('govuk-frontend', 'dist'),
join(paths.root, 'node_modules')
],
loadPaths: resolver(basePath).stylesheets,

// Sass custom logger
logger: logger({
Expand Down

0 comments on commit 5eb1e82

Please sign in to comment.