@@ -5,7 +5,15 @@ import pico from 'picocolors';
55
66import { ScriptName } from '../config' ;
77import { 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
1018export 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