diff --git a/packages/api-server/package.json b/packages/api-server/package.json index e25159f93f74..e2c1ee7a7117 100644 --- a/packages/api-server/package.json +++ b/packages/api-server/package.json @@ -28,7 +28,7 @@ "pretty-ms": "7.0.1", "qs": "6.10.3", "split2": "4.1.0", - "yargs": "16.2.0" + "yargs": "17.3.1" }, "repository": { "type": "git", diff --git a/packages/cli/package.json b/packages/cli/package.json index 796b58acd2e7..41c6d90bf095 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -49,7 +49,7 @@ "rimraf": "3.0.2", "secure-random-password": "0.2.3", "terminal-link": "2.1.1", - "yargs": "16.2.0" + "yargs": "17.3.1" }, "devDependencies": { "@babel/cli": "7.16.7", diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index cb9ce5ae2623..0a79312b4d63 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -1,31 +1,74 @@ #!/usr/bin/env node +/** + * This file is the CLI's main entry point. + * (This is specified in the package.json's `bin` property.) + * + * ``` + * yarn rw # <-- you are here + * ``` + * + * This file: + * + * 1) sets up up middleware + * 2) loads the rest of the CLI via this package's command-module structure. + * + * @see {@link https://github.com/yargs/yargs/blob/main/docs/advanced.md#providing-a-command-module} + * + * @remarks + * + * The yargs codebase can appear to be a bit complicated at first. + * If you ever have to dig into the source, know that most of the methods are defined here: + * {@link https://github.com/yargs/yargs/blob/3d2a6aa8c954a58589d7a199b2496bd894dcde25/lib/yargs-factory.ts} + */ import fs from 'fs' import path from 'path' import { config } from 'dotenv-defaults' -import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' +/** + * This is the blessed way of doing things now, as opposed to: + * + * ``` + * import yargs from 'yargs + * ``` + * + * The above is the older, singleton API, which hasn't been deprecated but has been discouraged: + * {@link https://github.com/yargs/yargs/issues/2045#issuecomment-942442554} + */ +import yargs from 'yargs/yargs' import { getPaths, getConfigPath } from '@redwoodjs/internal' /** - * The current working directory can be set via: - * 1. A `--cwd` option - * 2. The `RWJS_CWD` env-var - * 3. Found by traversing directories upwards for the first `redwood.toml` + * Yargs middleware. + * + * Middleware functions get access to `argv`: + * {@link https://yargs.js.org/docs/#api-reference-middlewarecallbacks-applybeforevalidation} + * + * This middleware parses, validates, and sets current working directory in the following order: + * + * 1. the `--cwd` option + * 2. the `RWJS_CWD` env var + * 3. by traversing directories upwards for the first `redwood.toml` * - * This middleware parses, validates, and sets current working directory - * in the order above. + * This is mainly for contributors. + * + * @param {import('yargs').Argv} argv */ -const getCwdMiddleware = (argv) => { +const setCwdMiddleware = (argv) => { let configPath try { let cwd + if (argv.cwd) { cwd = argv.cwd - // We delete the argument because it's not actually referenced in CLI, - // we use the `RWJS_CWD` env-var, - // and it conflicts with "forwarding" commands such as test and prisma. + + /** + * We delete `cwd` because it's not actually referenced in the CLI. + * We use the `RWJS_CWD` env var instead. + * That, and it conflicts with "forwarding" commands such as test and prisma. + */ delete argv.cwd } else if (process.env.RWJS_CWD) { cwd = process.env.RWJS_CWD @@ -34,6 +77,7 @@ const getCwdMiddleware = (argv) => { } configPath = path.resolve(process.cwd(), cwd, 'redwood.toml') + if (!fs.existsSync(configPath)) { throw new Error('Could not find `redwood.toml` config file.') } @@ -51,25 +95,75 @@ const getCwdMiddleware = (argv) => { } } +/** + * Loads the env vars in `.env` and `.env.defaults`. + * + * @remarks + * + * We should only do this if we're in a Redwood project, + * which is why this is a middleware function that comes after `setCwdMiddleware`. + */ const loadDotEnvDefaultsMiddleware = () => { + const { base } = getPaths() + config({ - path: path.join(getPaths().base, '.env'), - defaults: path.join(getPaths().base, '.env.defaults'), + path: path.join(base, '.env'), + defaults: path.join(base, '.env.defaults'), encoding: 'utf8', }) } // eslint-disable-next-line no-unused-expressions -yargs +/** + * `yargs` takes an array (which is what `process.argv` is), + * but yargs expects this array to only have the args that come after program name. + * So if the command run at the CLI is: + * + * ``` + * yarn rw --help + * ``` + * + * yargs expects `['--help']`, not `['rw', '--help']`. + * + * `hideBin` is a yargs helper-function that removes the program name from the array for us: + * {@link https://yargs.js.org/docs/#api-reference} + * + * @remarks + * + * Assigning the return of this to a variable + * because we should be able to run the cli "programmatically" this way: + * - {@link https://github.com/yargs/yargs/issues/1605} + * - {@link https://yargs.js.org/docs/#api-reference-parseargs-context-parsecallback} + */ +const parser = yargs(hideBin(process.argv)) .scriptName('rw') - .middleware([getCwdMiddleware, loadDotEnvDefaultsMiddleware]) + .middleware([setCwdMiddleware, loadDotEnvDefaultsMiddleware]) .option('cwd', { - describe: 'Working directory to use (where `redwood.toml` is located.)', + demandOption: false, + description: + 'Current working directory to use (i.e., where `redwood.toml` is located). Useful for development', + type: 'string', }) - .commandDir('./commands') .example( - 'yarn rw g page home /', - "\"Create a page component named 'Home' at path '/'\"" + '$0 g page home /', + "Create a page component named 'Home' at path '/'" ) - .demandCommand() - .strict().argv + .commandDir('./commands') + .demandCommand(1, '') + .recommendCommands() + .strict() + +/** + * `yargs` seems to be moving away from `argv`: + * {@link https://github.com/yargs/yargs/pull/2036} + * + * Note that calling `parse` with no args is equivalent to `argv`. + * - {@link https://yargs.js.org/docs/#api-reference} + * - {@link https://github.com/yargs/yargs/blob/3d2a6aa8c954a58589d7a199b2496bd894dcde25/lib/yargs-factory.ts#L72-L77} + * + * @remarks + * + * This should pretty much only ever be used here, at the top level, according to: + * {@link https://yargs.js.org/docs/#api-reference-argv} + */ +parser.parse() diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 0d6f18cbae00..a9c9c57183c8 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -23,7 +23,7 @@ "tasuku": "1.0.2", "toml": "3.0.0", "vscode-ripgrep": "1.13.2", - "yargs": "16.2.0" + "yargs": "17.3.1" }, "repository": { "type": "git", diff --git a/packages/create-redwood-app/package.json b/packages/create-redwood-app/package.json index e257db841f04..7b6d7bd5238c 100644 --- a/packages/create-redwood-app/package.json +++ b/packages/create-redwood-app/package.json @@ -19,7 +19,7 @@ "fs-extra": "10.0.0", "listr": "0.14.3", "tmp": "0.2.1", - "yargs": "16.2.0" + "yargs": "17.3.1" }, "repository": { "type": "git", diff --git a/packages/create-redwood-app/src/create-redwood-app.js b/packages/create-redwood-app/src/create-redwood-app.js index f0161cbc55ec..b167b8cb32ea 100644 --- a/packages/create-redwood-app/src/create-redwood-app.js +++ b/packages/create-redwood-app/src/create-redwood-app.js @@ -13,7 +13,8 @@ import checkNodeVersion from 'check-node-version' import execa from 'execa' import fs from 'fs-extra' import Listr from 'listr' -import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' +import yargs from 'yargs/yargs' import { name, version } from '../package' @@ -48,10 +49,16 @@ const { 'yarn-install': yarnInstall, typescript, overwrite, -} = yargs +} = yargs(hideBin(process.argv)) .scriptName(name) - .usage('Usage: $0 [option]') - .example('$0 newapp') + .usage('Usage: yarn create redwood-app [option]') + .example([ + ['yarn create redwood-app my-app', 'Create a Redwood App in `./my-app`'], + [ + 'yarn create redwood-app my-ts-app --ts', + 'Create a Redwood App in TypeScript', + ], + ]) .option('yarn-install', { default: true, type: 'boolean', @@ -70,9 +77,10 @@ const { describe: 'Create even if target directory is empty', }) .version(version) - .strict().argv + .parse() const targetDir = String(args).replace(/,/g, '-') + if (!targetDir) { console.error('Please specify the project directory') console.log( diff --git a/tasks/run-e2e b/tasks/run-e2e index abb73bb9c241..da14b8481f3b 100755 --- a/tasks/run-e2e +++ b/tasks/run-e2e @@ -6,7 +6,8 @@ const path = require('path') const execa = require('execa') const fs = require('fs-extra') -const yargs = require('yargs') +const { hideBin } = require('yargs/helpers') +const yargs = require('yargs/yargs') // This script sets up a blank RedwoodJS app into a directory. // It uses the packages from the RedwoodJS framework (../packages). @@ -188,7 +189,7 @@ const initGit = () => { }) } -const args = yargs +const args = yargs(hideBin(process.argv)) .option('create-project', { default: true, type: 'boolean', alias: 'create' }) .option('build-framework', { default: true, type: 'boolean', alias: 'build' }) .option('copy-framework', { default: true, type: 'boolean', alias: 'copy' }) @@ -198,7 +199,7 @@ const args = yargs .example('run-e2e') .example('run-e2e /tmp/redwood-app --ts') .help() - .strict().argv + .parse() const REDWOODJS_FRAMEWORK_PATH = path.resolve(__dirname, '..') let REDWOOD_PROJECT_DIRECTORY = diff --git a/yarn.lock b/yarn.lock index 9e0c988b6108..a2d510b69559 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5342,7 +5342,7 @@ __metadata: qs: 6.10.3 split2: 4.1.0 typescript: 4.5.4 - yargs: 16.2.0 + yargs: 17.3.1 bin: rw-api-server: ./dist/index.js rw-api-server-watch: ./dist/watch.js @@ -5442,7 +5442,7 @@ __metadata: secure-random-password: 0.2.3 terminal-link: 2.1.1 typescript: 4.5.4 - yargs: 16.2.0 + yargs: 17.3.1 bin: redwood: ./dist/index.js rw: ./dist/index.js @@ -5475,7 +5475,7 @@ __metadata: tempy: 1.0.1 toml: 3.0.0 vscode-ripgrep: 1.13.2 - yargs: 16.2.0 + yargs: 17.3.1 bin: codemods: ./dist/codemods.js languageName: unknown @@ -12170,7 +12170,7 @@ __metadata: listr: 0.14.3 tmp: 0.2.1 typescript: 4.5.4 - yargs: 16.2.0 + yargs: 17.3.1 bin: create-redwood-app: ./dist/create-redwood-app.js languageName: unknown @@ -30219,18 +30219,18 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"yargs@npm:16.2.0, yargs@npm:^16.1.1, yargs@npm:^16.2.0": - version: 16.2.0 - resolution: "yargs@npm:16.2.0" +"yargs@npm:17.3.1, yargs@npm:^17.0.0, yargs@npm:^17.3.0": + version: 17.3.1 + resolution: "yargs@npm:17.3.1" dependencies: cliui: ^7.0.2 escalade: ^3.1.1 get-caller-file: ^2.0.5 require-directory: ^2.1.1 - string-width: ^4.2.0 + string-width: ^4.2.3 y18n: ^5.0.5 - yargs-parser: ^20.2.2 - checksum: b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + yargs-parser: ^21.0.0 + checksum: 2c5ff77132468093a1872b8a9798cdcc5da0bcf7a2b0660264ffa91766324b0926c3346e091d249dc3a86caf7e8e623aa0f8de660c9baf440188d4da7d4378c4 languageName: node linkType: hard @@ -30253,18 +30253,18 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"yargs@npm:^17.0.0, yargs@npm:^17.3.0": - version: 17.3.1 - resolution: "yargs@npm:17.3.1" +"yargs@npm:^16.1.1, yargs@npm:^16.2.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" dependencies: cliui: ^7.0.2 escalade: ^3.1.1 get-caller-file: ^2.0.5 require-directory: ^2.1.1 - string-width: ^4.2.3 + string-width: ^4.2.0 y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 2c5ff77132468093a1872b8a9798cdcc5da0bcf7a2b0660264ffa91766324b0926c3346e091d249dc3a86caf7e8e623aa0f8de660c9baf440188d4da7d4378c4 + yargs-parser: ^20.2.2 + checksum: b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 languageName: node linkType: hard