1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
import boxen from 'boxen';
import { execa } from 'execa';
import { bold, cyan, dim, magenta } from 'kleur/colors';
import { createRequire } from 'node:module';
import ora from 'ora';
import prompts from 'prompts';
import whichPm from 'which-pm';
import { debug, info, type LogOptions } from '../core/logger/core.js';
type GetPackageOptions = {
skipAsk?: boolean;
cwd?: string;
};
export async function getPackage<T>(
packageName: string,
logging: LogOptions,
options: GetPackageOptions,
otherDeps: string[] = []
): Promise<T | undefined> {
const require = createRequire(options.cwd ?? process.cwd());
let packageImport;
try {
require.resolve(packageName);
// The `require.resolve` is required as to avoid Node caching the failed `import`
packageImport = await import(packageName);
} catch (e) {
info(
logging,
'',
`To continue, Astro requires the following dependency to be installed: ${bold(packageName)}.`
);
const result = await installPackage([packageName, ...otherDeps], options, logging);
if (result) {
packageImport = await import(packageName);
} else {
return undefined;
}
}
return packageImport as T;
}
function getInstallCommand(packages: string[], packageManager: string) {
switch (packageManager) {
case 'npm':
return { pm: 'npm', command: 'install', flags: [], dependencies: packages };
case 'yarn':
return { pm: 'yarn', command: 'add', flags: [], dependencies: packages };
case 'pnpm':
return { pm: 'pnpm', command: 'add', flags: [], dependencies: packages };
default:
return null;
}
}
async function installPackage(
packageNames: string[],
options: GetPackageOptions,
logging: LogOptions
): Promise<boolean> {
const cwd = options.cwd ?? process.cwd();
const packageManager = (await whichPm(cwd)).name ?? 'npm';
const installCommand = getInstallCommand(packageNames, packageManager);
if (!installCommand) {
return false;
}
const coloredOutput = `${bold(installCommand.pm)} ${installCommand.command}${[
'',
...installCommand.flags,
].join(' ')} ${cyan(installCommand.dependencies.join(' '))}`;
const message = `\n${boxen(coloredOutput, {
margin: 0.5,
padding: 0.5,
borderStyle: 'round',
})}\n`;
info(
logging,
null,
`\n ${magenta('Astro will run the following command:')}\n ${dim(
'If you skip this step, you can always run it yourself later'
)}\n${message}`
);
let response;
if (options.skipAsk) {
response = true;
} else {
response = (
await prompts({
type: 'confirm',
name: 'askToContinue',
message: 'Continue?',
initial: true,
})
).askToContinue;
}
if (Boolean(response)) {
const spinner = ora('Installing dependencies...').start();
try {
await execa(
installCommand.pm,
[installCommand.command, ...installCommand.flags, ...installCommand.dependencies],
{ cwd: cwd }
);
spinner.succeed();
return true;
} catch (err) {
debug('add', 'Error installing dependencies', err);
spinner.fail();
return false;
}
} else {
return false;
}
}
|