Skip to content

Commit

Permalink
Refactors context passing (#5623)
Browse files Browse the repository at this point in the history
**What's the problem this PR addresses?**

Passing the context to the CLI instance was impractical, as it required
to know exactly what was the context that the commands expect. I think
there's something to improve here in Clipanion itself (perhaps so that
the context can be defined when instantiating the CLI, not running the
first command), but that's for a separate time.

**How did you fix it?**

I made `getCliBase` add a `defaultContext` field to the CLI instance. I
considered returning them as separate fields, but wasn't sure it'd be
worth the hassle of conveying two different values. Might revisit in a
follow-up PR.

**Checklist**
<!--- Don't worry if you miss something, chores are automatically
tested. -->
<!--- This checklist exists to help you remember doing the chores when
you submit a PR. -->
<!--- Put an `x` in all the boxes that apply. -->
- [x] I have read the [Contributing
Guide](https://yarnpkg.com/advanced/contributing).

<!-- See
https://yarnpkg.com/advanced/contributing#preparing-your-pr-to-be-released
for more details. -->
<!-- Check with `yarn version check` and fix with `yarn version check
-i` -->
- [x] I have set the packages that need to be released for my changes to
be effective.

<!-- The "Testing chores" workflow validates that your PR follows our
guidelines. -->
<!-- If it doesn't pass, click on it to see details as to what your PR
might be missing. -->
- [x] I will check that all automated PR checks pass before the PR gets
reviewed.
  • Loading branch information
arcanis authored Jul 28, 2023
1 parent 0ff9d24 commit 7a433ba
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 44 deletions.
23 changes: 23 additions & 0 deletions .yarn/versions/bfe31a8a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
releases:
"@yarnpkg/cli": major

declined:
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/core"
- "@yarnpkg/doctor"
3 changes: 2 additions & 1 deletion packages/yarnpkg-cli/sources/cli.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import '@yarnpkg/cli/polyfills';
import {npath} from '@yarnpkg/fslib';
import {npath, ppath} from '@yarnpkg/fslib';

import {runExit} from './lib';
import {getPluginConfiguration} from './tools/getPluginConfiguration';

runExit(process.argv.slice(2), {
cwd: ppath.cwd(),
selfPath: npath.toPortablePath(npath.resolve(process.argv[1])),
pluginConfiguration: getPluginConfiguration(),
});
16 changes: 9 additions & 7 deletions packages/yarnpkg-cli/sources/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export {BaseCommand} from './tools/BaseCommand';
export {WorkspaceRequiredError} from './tools/WorkspaceRequiredError';
export {getDynamicLibs} from './tools/getDynamicLibs';
export {getPluginConfiguration} from './tools/getPluginConfiguration';
export {openWorkspace} from './tools/openWorkspace';
export {getCli, runExit} from './lib';
export {pluginCommands} from './pluginCommands';
export {type CommandContext} from '@yarnpkg/core';

export {BaseCommand} from './tools/BaseCommand';
export {WorkspaceRequiredError} from './tools/WorkspaceRequiredError';
export {getDynamicLibs} from './tools/getDynamicLibs';
export {getPluginConfiguration} from './tools/getPluginConfiguration';
export {openWorkspace} from './tools/openWorkspace';
export {type YarnCli, getCli, runExit} from './lib';
export {pluginCommands} from './pluginCommands';
78 changes: 42 additions & 36 deletions packages/yarnpkg-cli/sources/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,31 @@ import {isCI}
import {Cli, UsageError} from 'clipanion';

import {pluginCommands} from './pluginCommands';
import {getPluginConfiguration} from './tools/getPluginConfiguration';

function getBaseCli() {
return new Cli<CommandContext>({
export type YarnCli = ReturnType<typeof getBaseCli>;

function getBaseCli({cwd, pluginConfiguration}: {cwd: PortablePath, pluginConfiguration: PluginConfiguration}) {
const cli = new Cli<CommandContext>({
binaryLabel: `Yarn Package Manager`,
binaryName: `yarn`,
binaryVersion: YarnVersion ?? `<unknown>`,
});

return Object.assign(cli, {
defaultContext: {
...Cli.defaultContext,
cwd,
plugins: pluginConfiguration,
quiet: false,
stdin: process.stdin,
stdout: process.stdout,
stderr: process.stderr,
},
});
}

function validateNodejsVersion(cli: Cli<CommandContext>) {
function validateNodejsVersion(cli: YarnCli) {
// YARN_IGNORE_NODE is special because this code needs to execute as early as possible.
// It's not a regular core setting because Configuration.find may use functions not available
// on older Node versions.
Expand Down Expand Up @@ -46,7 +61,7 @@ async function getCoreConfiguration({selfPath, pluginConfiguration}: {selfPath:
});
}

function runYarnPath(cli: Cli<CommandContext>, argv: Array<string>, {yarnPath}: {yarnPath: PortablePath}) {
function runYarnPath(cli: YarnCli, argv: Array<string>, {yarnPath}: {yarnPath: PortablePath}) {
if (!xfs.existsSync(yarnPath)) {
(cli.error(new Error(`The "yarn-path" option has been set, but the specified location doesn't exist (${yarnPath}).`)));
return 1;
Expand Down Expand Up @@ -74,17 +89,23 @@ function runYarnPath(cli: Cli<CommandContext>, argv: Array<string>, {yarnPath}:
return 0;
}

function checkCwd(argv: Array<string>): [PortablePath, Array<string>] {
if (argv.length >= 2 && argv[0] === `--cwd`)
return [xfs.realpathSync(npath.toPortablePath(argv[1])), argv.slice(2)];
function checkCwd(cli: YarnCli, argv: Array<string>) {
let cwd: PortablePath | null = null;

if (argv.length >= 1 && argv[0].startsWith(`--cwd=`))
return [xfs.realpathSync(npath.toPortablePath(argv[0].slice(6))), argv.slice(1)];
let postCwdArgv = argv;
if (argv.length >= 2 && argv[0] === `--cwd`) {
cwd = xfs.realpathSync(npath.toPortablePath(argv[1]));
postCwdArgv = argv.slice(2);
} else if (argv.length >= 1 && argv[0].startsWith(`--cwd=`)) {
cwd = xfs.realpathSync(npath.toPortablePath(argv[0].slice(6)));
postCwdArgv = argv.slice(1);
}

return [ppath.cwd(), argv];
cli.defaultContext.cwd = cwd ?? ppath.cwd();
return postCwdArgv;
}

function initTelemetry(cli: Cli<CommandContext>, {configuration}: {configuration: Configuration}) {
function initTelemetry(cli: YarnCli, {configuration}: {configuration: Configuration}) {
const isTelemetryEnabled = configuration.get(`enableTelemetry`);
if (!isTelemetryEnabled || isCI || !process.stdout.isTTY)
return;
Expand All @@ -100,31 +121,15 @@ function initTelemetry(cli: Cli<CommandContext>, {configuration}: {configuration
}
}

function initCommands(cli: Cli<CommandContext>, {configuration}: {configuration: Configuration}) {
function initCommands(cli: YarnCli, {configuration}: {configuration: Configuration}) {
for (const plugin of configuration.plugins.values()) {
for (const command of plugin.commands || []) {
cli.register(command);
}
}
}

function processArgv(cli: Cli<CommandContext>, argv: Array<string>, {cwd, pluginConfiguration}: {cwd: PortablePath, pluginConfiguration: PluginConfiguration}) {
const context = {
cwd,
plugins: pluginConfiguration,
quiet: false,
stdin: process.stdin,
stdout: process.stdout,
stderr: process.stderr,
};

return {
context,
command: cli.process(argv, context),
};
}

async function run(cli: Cli<CommandContext>, argv: Array<string>, {selfPath, pluginConfiguration}: {selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
async function run(cli: YarnCli, argv: Array<string>, {selfPath, pluginConfiguration}: {selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
if (!validateNodejsVersion(cli))
return 1;

Expand All @@ -141,20 +146,21 @@ async function run(cli: Cli<CommandContext>, argv: Array<string>, {selfPath, plu

delete process.env.YARN_IGNORE_PATH;

const [cwd, postCwdArgv] = checkCwd(argv);
const postCwdArgv = checkCwd(cli, argv);

initTelemetry(cli, {configuration});
initCommands(cli, {configuration});

const {command, context} = processArgv(cli, postCwdArgv, {cwd, pluginConfiguration});
const command = cli.process(postCwdArgv, cli.defaultContext);

if (!command.help)
Configuration.telemetry?.reportCommandName(command.path.join(` `));

return await cli.run(command, context);
return await cli.run(command, cli.defaultContext);
}

export async function getCli({pluginConfiguration}: {pluginConfiguration: PluginConfiguration}) {
const cli = getBaseCli();
export async function getCli({cwd = ppath.cwd(), pluginConfiguration = getPluginConfiguration()}: {cwd?: PortablePath, pluginConfiguration?: PluginConfiguration} = {}) {
const cli = getBaseCli({cwd, pluginConfiguration});

const configuration = await getCoreConfiguration({
pluginConfiguration,
Expand All @@ -166,8 +172,8 @@ export async function getCli({pluginConfiguration}: {pluginConfiguration: Plugin
return cli;
}

export async function runExit(argv: Array<string>, {selfPath, pluginConfiguration}: {selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
const cli = getBaseCli();
export async function runExit(argv: Array<string>, {cwd = ppath.cwd(), selfPath, pluginConfiguration}: {cwd: PortablePath, selfPath: PortablePath | null, pluginConfiguration: PluginConfiguration}) {
const cli = getBaseCli({cwd, pluginConfiguration});

try {
process.exitCode = await run(cli, argv, {selfPath, pluginConfiguration});
Expand Down

0 comments on commit 7a433ba

Please sign in to comment.