Skip to content

Commit 8d1888b

Browse files
authored
Check missing dependencies before run CLI command (#776)
This PR adds a new dependency check before running scripts in the `codama run` CLI command. In other words, it'll check that all external dependencies about to be imported from a script are all installed before running these scripts. When some dependencies are missing, it'll prompt the user to install them.
1 parent 3bf719b commit 8d1888b

File tree

2 files changed

+50
-13
lines changed

2 files changed

+50
-13
lines changed

.changeset/cold-colts-agree.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codama/cli': minor
3+
---
4+
5+
Check missing dependencies before `run` CLI commands

packages/cli/src/commands/run.ts

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ import pico from 'picocolors';
55

66
import { ScriptName } from '../config';
77
import { getParsedConfigFromCommand, ParsedConfig } from '../parsedConfig';
8-
import { CliError, getRootNodeVisitors, logInfo, logSuccess, logWarning } from '../utils';
8+
import {
9+
CliError,
10+
getRootNodeVisitors,
11+
installMissingDependencies,
12+
isLocalModulePath,
13+
logInfo,
14+
logSuccess,
15+
logWarning,
16+
} from '../utils';
917

1018
export function setRunCommand(program: Command): void {
1119
program
@@ -43,18 +51,8 @@ async function getPlans(
4351
throw new CliError('There are no scripts or before visitors to run.');
4452
}
4553

46-
const missingScripts = scripts.filter(script => !parsedConfig.scripts[script]);
47-
if (missingScripts.length > 0) {
48-
const scriptPluralized = missingScripts.length === 1 ? 'Script' : 'Scripts';
49-
const message = parsedConfig.configPath
50-
? `${scriptPluralized} not found in configuration file.`
51-
: `${scriptPluralized} not found because no configuration file was found.`;
52-
const items = [
53-
`${pico.bold(scriptPluralized)}: ${missingScripts.join(', ')}`,
54-
...(parsedConfig.configPath ? [`${pico.bold('Path')}: ${parsedConfig.configPath}`] : []),
55-
];
56-
throw new CliError(message, items);
57-
}
54+
checkMissingScripts(parsedConfig, scripts);
55+
await checkMissingDependencies(parsedConfig, scripts);
5856

5957
if (parsedConfig.before.length > 0) {
6058
plans.push({ script: null, visitors: await getRootNodeVisitors(parsedConfig.before) });
@@ -87,3 +85,37 @@ function runPlan(plan: RunPlan, rootNode: RootNode): RootNode {
8785
logSuccess(`Executed ${identifier}!`);
8886
return newRoot;
8987
}
88+
89+
function checkMissingScripts(parsedConfig: Pick<ParsedConfig, 'configPath' | 'scripts'>, scripts: ScriptName[]) {
90+
const missingScripts = scripts.filter(script => !parsedConfig.scripts[script]);
91+
if (missingScripts.length === 0) return;
92+
93+
const scriptPluralized = missingScripts.length === 1 ? 'Script' : 'Scripts';
94+
const message = parsedConfig.configPath
95+
? `${scriptPluralized} not found in configuration file.`
96+
: `${scriptPluralized} not found because no configuration file was found.`;
97+
const items = [
98+
`${pico.bold(scriptPluralized)}: ${missingScripts.join(', ')}`,
99+
...(parsedConfig.configPath ? [`${pico.bold('Path')}: ${parsedConfig.configPath}`] : []),
100+
];
101+
throw new CliError(message, items);
102+
}
103+
104+
async function checkMissingDependencies(
105+
parsedConfig: Pick<ParsedConfig, 'before' | 'configPath' | 'scripts'>,
106+
scripts: ScriptName[],
107+
) {
108+
const dependencies = new Set<string>([
109+
...parsedConfig.before.map(v => v.path),
110+
...scripts.flatMap(script => parsedConfig.scripts[script]?.map(v => v.path) ?? []),
111+
]);
112+
const externalDependencies = [...dependencies].filter(dep => !isLocalModulePath(dep));
113+
const scriptsRequirePluralized = scripts.length === 1 ? 'script requires' : 'scripts require';
114+
const installed = await installMissingDependencies(
115+
`Your ${scriptsRequirePluralized} additional dependencies.`,
116+
externalDependencies,
117+
);
118+
if (!installed) {
119+
throw new CliError('Cannot proceed without missing dependencies.');
120+
}
121+
}

0 commit comments

Comments
 (0)