diff --git a/README.md b/README.md index fc7613d50..f1188ccd6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,31 @@ Ensure the value passed into APP_ENV matches the file name of the .env file you If no value is passed `.env.local` will be used as the default +### Additional flags + +Additional env flags can be set by prepending them with "--". To pass arguments through to the underlying utility (e.g. jest) use "--" as a separator. + +Examples: + +``` +yarn dev --NEXT_PUBLIC_DRUPAL_BASE_URL https://staging.cms.va.gov -- --port 3003 +``` + +``` +yarn test -- path/to/file +``` + +Available env variables and underlying utility help can be viewed by appending `-h` to the yarn script: +Examples: + +``` +yarn test -h +``` + +``` +yarn build -h +``` + ## Local CMS endpoint To use the local CMS as an endpoint, follow the install directions for [the CMS repo here](https://github.com/department-of-veterans-affairs/va.gov-cms/blob/main/READMES/getting-started.md). diff --git a/lint-staged.config.js b/lint-staged.config.js index 8b9e5872b..6c0c55a64 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -15,7 +15,7 @@ module.exports = { // Run unit tests relating to modified files. // todo: Jest should be able to ignore files that don't need tests (*.stories.*, config, etc) '**/*.(ts|tsx|js|jsx)': (filenames) => [ - `yarn lint:staged:test-modified-files '${filenames.join(' ')}'`, + `yarn test -- --findRelatedTests ${filenames.join(' ')} --passWithNoTests`, ], // Format MarkDown and JSON diff --git a/package.json b/package.json index ee4e8c526..ae95c26cb 100644 --- a/package.json +++ b/package.json @@ -22,25 +22,22 @@ "lint": "next lint", "plop": "plop", "postinstall": "husky install", - "typedoc": "typedoc", - "typedoc:serve": "http-server typedocs -p 8002", - "lint:staged:test-modified-files": "yarn build:env-loader && node ./scripts/yarn/lint:staged:test-modified-files.js", "test": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test.js", "test:ci": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:ci.js", "test:coverage": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:coverage.js", - "test:playwright": "playwright test --project=main", - "test:playwright:interactive": "playwright test --ui", - "test:playwright:a11y": "playwright test --project=a11y", + "test:debug": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:debug.js", + "test:watch": "yarn test -- --watch", "test:examples": "APP_ENV=test jest --rootDir ./example_tests --config ./example_tests/jest.config.js", "test:example": "APP_ENV=test yarn test:examples example_tests/${1}/", - "test:debug": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:debug.js", "test:format": "APP_ENV=test prettier --check .", + "test:playwright": "playwright test --project=main", + "test:playwright:interactive": "playwright test --ui", + "test:playwright:a11y": "playwright test --project=a11y", "test:types": "yarn build:env-loader && APP_ENV=test tsc --noEmit", - "test:watch": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:watch.js", - "test:update-snapshots": "yarn build:env-loader && APP_ENV=test node ./scripts/yarn/test:update-snapshots.js", - "test:u": "yarn test:update-snapshots", "storybook": "storybook dev -p 6006", - "storybook:build": "storybook build -o out/storybook-static --quiet" + "storybook:build": "storybook build -o out/storybook-static --quiet", + "typedoc": "typedoc", + "typedoc:serve": "http-server typedocs -p 8002" }, "dependencies": { "@department-of-veterans-affairs/component-library": "^31.0.0", diff --git a/packages/env-loader/src/cli-options.ts b/packages/env-loader/src/cli-options.ts index c7ec11be4..3db498de7 100644 --- a/packages/env-loader/src/cli-options.ts +++ b/packages/env-loader/src/cli-options.ts @@ -1,67 +1,71 @@ import { Command, Option } from 'commander' import { EnvVars } from '.' +type CliOptionsAndArgs = { + options: EnvVars + args: string[] +} + type AdditionalHelp = { heading: string commands: string[] } -const ADDITIONAL_HELP: { [key: string]: AdditionalHelp[] } = { - test: [ - { - heading: 'Watch mode', - commands: ['yarn test:watch'], - }, - { - heading: 'With coverage report', - commands: ['yarn test:coverage'], - }, - { - heading: 'Update test snapshots', - commands: ['yarn test:update-snapshots', 'yarn test:u'], - }, - { - heading: 'Run Playwright E2E tests', - commands: ['yarn test:playwright'], - }, - ], -} +const ADDITIONAL_HELP: AdditionalHelp[] = [ + { + heading: 'Run unit tests in watch mode', + commands: ['yarn test -- --watch'], + }, + { + heading: 'Unit test specific file', + commands: ['yarn test -- path/to/file'], + }, + { + heading: 'Update unit-test snapshots', + commands: ['yarn test -- -u'], + }, + { + heading: 'Get help for Next build', + commands: ['yarn build -- -h'], + }, + { + heading: + 'Run Next dev server on a specific port and pull data from a specific Drupal instance', + commands: [ + 'yarn dev --NEXT_PUBLIC_DRUPAL_BASE_URL -- -p ', + ], + }, +] -const formatHelpText = ( - scriptName: string, - helpCommands: AdditionalHelp[] -): string => { - const outputLines: string[] = [] - outputLines.push(`Additional \`${scriptName}\` commands:`) - helpCommands.forEach((helpCommand) => { - outputLines.push(`\n ${helpCommand.heading}:`) +const getAdditionalHelpText = (): string => { + const helpText: string[] = [] + helpText.push( + 'Args can be passed through to the underlying utility by adding them to the end of the command.' + ) + helpText.push( + '\nUse "--" to separate the env flags from the args meant for the underlying utility.' + ) + helpText.push('\nEx:') + ADDITIONAL_HELP.forEach((helpCommand) => { + helpText.push(`\n ${helpCommand.heading}:`) helpCommand.commands.forEach((terminalCommand) => { - outputLines.push(`\n $ ${terminalCommand}`) + helpText.push(`\n $ ${terminalCommand}`) }) }) - outputLines.push('\n') - - return outputLines.join('') -} - -const configureAdditionalHelpText = ( - program: Command, - scriptName: string -): void => { - const additionalHelp = ADDITIONAL_HELP[scriptName] - if (additionalHelp) { - program.addHelpText('beforeAll', formatHelpText(scriptName, additionalHelp)) - } + helpText.push('\n') + return helpText.join('') } /** - * Parses CLI options from the command line into an object. + * Parses CLI options and arguments from the command line into an object. * * See https://www.npmjs.com/package/commander#common-option-types-boolean-and-value */ -export const getCliOptions = (scriptName: string): EnvVars => { +export const getCliOptionsAndArgs = (): CliOptionsAndArgs => { const program = new Command() + program + .passThroughOptions() .option('--NEXT_IMAGE_DOMAIN ', 'Drupal image domain') .option( '--NEXT_PUBLIC_ASSETS_URL ', @@ -95,7 +99,30 @@ export const getCliOptions = (scriptName: string): EnvVars => { ).choices(['true', 'false']) ) - configureAdditionalHelpText(program, scriptName) + const additionalHelpText = getAdditionalHelpText() + program.addHelpText('beforeAll', additionalHelpText) + + program.exitOverride() + try { + const parsed = program.parse() + const options = parsed.opts() + const args = parsed.args - return program.parse().opts() + return { + options, + args, + } + } catch (err) { + if (err.code === 'commander.helpDisplayed') { + return { + options: {}, + args: ['-h'], + } + } + + return { + options: {}, + args: [], + } + } } diff --git a/packages/env-loader/src/index.ts b/packages/env-loader/src/index.ts index 453649947..c56d8d3cc 100644 --- a/packages/env-loader/src/index.ts +++ b/packages/env-loader/src/index.ts @@ -1,5 +1,5 @@ /*eslint-disable no-console*/ -import { getCliOptions } from './cli-options' +import { getCliOptionsAndArgs } from './cli-options' import { getEnvFileVars } from './env-file' import { getCmsFeatureFlags } from './cms-feature-flags' import { spawn } from 'child_process' @@ -8,53 +8,36 @@ export type EnvVars = { [key: string]: string } -const getAllEnvVars = async (scriptName: string): Promise => { - // CLI OPTIONS - const cliOptions = getCliOptions(scriptName) +export const processEnv = async (command: string): Promise => { + // CLI + const { args: cliArgs, options: cliOptions } = getCliOptionsAndArgs() - // ENV FILE VARS + // ENV FILE const envVars = getEnvFileVars(process.env.APP_ENV) - // For now, don't fetch CMS feature flags for tests. - // Will fail CI. + // CMS FEATURE FLAGS + let cmsFeatureFlags if (process.env.APP_ENV === 'test') { - return { - ...envVars, - ...cliOptions, - } + // For now, don't fetch CMS feature flags for tests. Will fail CI. + cmsFeatureFlags = {} + } else { + const drupalBaseUrlProp = 'NEXT_PUBLIC_DRUPAL_BASE_URL' + const drupalBaseUrl = + cliOptions[drupalBaseUrlProp] || envVars[drupalBaseUrlProp] + cmsFeatureFlags = await getCmsFeatureFlags(drupalBaseUrl) } - // CMS FEATURE FLAGS - const drupalBaseUrlProp = 'NEXT_PUBLIC_DRUPAL_BASE_URL' - const drupalBaseUrl = - cliOptions[drupalBaseUrlProp] || envVars[drupalBaseUrlProp] - const cmsFeatureFlags = await getCmsFeatureFlags(drupalBaseUrl) - - // Return all options with proper cascading: - // 1. CLI options have highest precedence - // 2. CMS feature flags next - // 3. ENV file vars last - return { - ...envVars, - ...cmsFeatureFlags, - ...cliOptions, - } -} - -const getYarnScriptName = (path: string): string => { - const match = path.match(/\/scripts\/yarn\/(.*)\.js/) - return match?.[1] || '' -} - -export const processEnv = async (command: string): Promise => { - const yarnScriptName = getYarnScriptName(process.argv[1]) - process.env = { ...process.env, - ...(await getAllEnvVars(yarnScriptName)), + ...{ + ...envVars, + ...cmsFeatureFlags, + ...cliOptions, + }, } - spawn(command, { + // Pass additional arguments through to the underlying command + spawn(`${command} ${cliArgs.join(' ')}`, { shell: true, stdio: 'inherit', }) diff --git a/scripts/yarn/lint:staged:test-modified-files.js b/scripts/yarn/lint:staged:test-modified-files.js deleted file mode 100644 index c6626a445..000000000 --- a/scripts/yarn/lint:staged:test-modified-files.js +++ /dev/null @@ -1,4 +0,0 @@ -const { processEnv } = require('env-loader') - -const modifiedFiles = process.argv[2] -processEnv(`jest --findRelatedTests ${modifiedFiles} --passWithNoTests`) diff --git a/scripts/yarn/test:update-snapshots.js b/scripts/yarn/test:update-snapshots.js deleted file mode 100644 index e04ec5f1d..000000000 --- a/scripts/yarn/test:update-snapshots.js +++ /dev/null @@ -1,3 +0,0 @@ -const { processEnv } = require('env-loader') - -processEnv('jest --updateSnapshot') diff --git a/scripts/yarn/test:watch.js b/scripts/yarn/test:watch.js deleted file mode 100644 index 299423a0d..000000000 --- a/scripts/yarn/test:watch.js +++ /dev/null @@ -1,3 +0,0 @@ -const { processEnv } = require('env-loader') - -processEnv('jest --watch')