-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
Copy pathconfig.ts
249 lines (223 loc) · 6.91 KB
/
config.ts
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import type { Arguments as Flags } from 'yargs-parser';
import type { AstroConfig, AstroUserConfig, CLIFlags } from '../../@types/astro';
import * as colors from 'kleur/colors';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { mergeConfig } from './merge.js';
import { createRelativeSchema } from './schema.js';
import { loadConfigWithVite } from './vite-load.js';
export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
'projectRoot',
'src',
'pages',
'public',
'dist',
'styleOptions',
'markdownOptions',
'buildOptions',
'devOptions',
]);
/** Turn raw config values into normalized values */
export async function validateConfig(
userConfig: any,
root: string,
cmd: string
): Promise<AstroConfig> {
// Manual deprecation checks
/* eslint-disable no-console */
if (userConfig.hasOwnProperty('renderers')) {
console.error('Astro "renderers" are now "integrations"!');
console.error('Update your configuration and install new dependencies:');
try {
const rendererKeywords = userConfig.renderers.map((r: string) =>
r.replace('@astrojs/renderer-', '')
);
const rendererImports = rendererKeywords
.map((r: string) => ` import ${r} from '@astrojs/${r === 'solid' ? 'solid-js' : r}';`)
.join('\n');
const rendererIntegrations = rendererKeywords.map((r: string) => ` ${r}(),`).join('\n');
console.error('');
console.error(colors.dim(' // astro.config.js'));
if (rendererImports.length > 0) {
console.error(colors.green(rendererImports));
}
console.error('');
console.error(colors.dim(' // ...'));
if (rendererIntegrations.length > 0) {
console.error(colors.green(' integrations: ['));
console.error(colors.green(rendererIntegrations));
console.error(colors.green(' ],'));
} else {
console.error(colors.green(' integrations: [],'));
}
console.error('');
} catch (err) {
// We tried, better to just exit.
}
process.exit(1);
}
let legacyConfigKey: string | undefined;
for (const key of Object.keys(userConfig)) {
if (LEGACY_ASTRO_CONFIG_KEYS.has(key)) {
legacyConfigKey = key;
break;
}
}
if (legacyConfigKey) {
throw new AstroError({
...AstroErrorData.ConfigLegacyKey,
message: AstroErrorData.ConfigLegacyKey.message(legacyConfigKey),
});
}
/* eslint-enable no-console */
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
// First-Pass Validation
const result = await AstroConfigRelativeSchema.parseAsync(userConfig);
// If successful, return the result as a verified AstroConfig object.
return result;
}
/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */
export function resolveFlags(flags: Partial<Flags>): CLIFlags {
return {
root: typeof flags.root === 'string' ? flags.root : undefined,
site: typeof flags.site === 'string' ? flags.site : undefined,
base: typeof flags.base === 'string' ? flags.base : undefined,
port: typeof flags.port === 'number' ? flags.port : undefined,
open: typeof flags.open === 'boolean' ? flags.open : undefined,
config: typeof flags.config === 'string' ? flags.config : undefined,
host:
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : undefined,
experimentalAssets:
typeof flags.experimentalAssets === 'boolean' ? flags.experimentalAssets : undefined,
};
}
export function resolveRoot(cwd?: string | URL): string {
if (cwd instanceof URL) {
cwd = fileURLToPath(cwd);
}
return cwd ? path.resolve(cwd) : process.cwd();
}
/** Merge CLI flags & user config object (CLI flags take priority) */
function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
return mergeConfig(astroConfig, {
site: flags.site,
base: flags.base,
markdown: {
drafts: flags.drafts,
},
server: {
port: flags.port,
host: flags.host,
open: flags.open,
},
});
}
async function search(fsMod: typeof fs, root: string) {
const paths = [
'astro.config.mjs',
'astro.config.js',
'astro.config.ts',
'astro.config.mts',
'astro.config.cjs',
'astro.config.cts',
].map((p) => path.join(root, p));
for (const file of paths) {
if (fsMod.existsSync(file)) {
return file;
}
}
}
interface LoadConfigOptions {
cwd?: string;
flags?: Flags;
cmd: string;
validate?: boolean;
/** Invalidate when reloading a previously loaded config */
isRestart?: boolean;
fsMod?: typeof fs;
}
interface ResolveConfigPathOptions {
cwd?: string;
flags?: Flags;
fs: typeof fs;
}
/**
* Resolve the file URL of the user's `astro.config.js|cjs|mjs|ts` file
*/
export async function resolveConfigPath(
configOptions: ResolveConfigPathOptions
): Promise<string | undefined> {
const root = resolveRoot(configOptions.cwd);
const flags = resolveFlags(configOptions.flags || {});
let userConfigPath: string | undefined;
if (flags?.config) {
userConfigPath = /^\.*\//.test(flags.config) ? flags.config : `./${flags.config}`;
userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
if (!configOptions.fs.existsSync(userConfigPath)) {
throw new AstroError({
...AstroErrorData.ConfigNotFound,
message: AstroErrorData.ConfigNotFound.message(flags.config),
});
}
} else {
userConfigPath = await search(configOptions.fs, root);
}
return userConfigPath;
}
interface OpenConfigResult {
userConfig: AstroUserConfig;
astroConfig: AstroConfig;
flags: CLIFlags;
root: string;
}
/** Load a configuration file, returning both the userConfig and astroConfig */
export async function openConfig(configOptions: LoadConfigOptions): Promise<OpenConfigResult> {
const root = resolveRoot(configOptions.cwd);
const flags = resolveFlags(configOptions.flags || {});
const userConfig = await loadConfig(configOptions, root);
const astroConfig = await resolveConfig(userConfig, root, flags, configOptions.cmd);
return {
astroConfig,
userConfig,
flags,
root,
};
}
async function loadConfig(
configOptions: LoadConfigOptions,
root: string
): Promise<Record<string, any>> {
const fsMod = configOptions.fsMod ?? fs;
const configPath = await resolveConfigPath({
cwd: configOptions.cwd,
flags: configOptions.flags,
fs: fsMod,
});
if (!configPath) return {};
// Create a vite server to load the config
return await loadConfigWithVite({
configPath,
fs: fsMod,
root,
});
}
/** Attempt to resolve an Astro configuration object. Normalize, validate, and return. */
export async function resolveConfig(
userConfig: AstroUserConfig,
root: string,
flags: CLIFlags = {},
cmd: string
): Promise<AstroConfig> {
const mergedConfig = mergeCLIFlags(userConfig, flags);
const validatedConfig = await validateConfig(mergedConfig, root, cmd);
return validatedConfig;
}
export function createDefaultDevConfig(
userConfig: AstroUserConfig = {},
root: string = process.cwd()
) {
return resolveConfig(userConfig, root, undefined, 'dev');
}