-
Notifications
You must be signed in to change notification settings - Fork 120
Expand file tree
/
Copy pathnuxt-config.ts
More file actions
111 lines (101 loc) · 3.59 KB
/
Copy pathnuxt-config.ts
File metadata and controls
111 lines (101 loc) · 3.59 KB
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
import type { NuxtConfig } from '@nuxt/schema'
import process from 'node:process'
import { pathToFileURL } from 'node:url'
import { consola } from 'consola'
import { resolveModulePath } from 'exsolve'
import { join } from 'pathe'
const MODULE_NOT_FOUND_CODES = new Set(['ERR_MODULE_NOT_FOUND', 'MODULE_NOT_FOUND'])
const CONFIG_EXTENSIONS = ['.js', '.ts', '.mjs', '.cjs', '.mts', '.cts']
/**
* Errors that mean the config needs a loader rather than that it is broken:
* unresolvable specifiers (`~`/`@` aliases), TypeScript that cannot be stripped
* (`enum`, `namespace`, parameter properties), and Node versions predating
* built-in type stripping.
*/
const LOADER_REQUIRED_CODES = new Set([
'ERR_MODULE_NOT_FOUND',
'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX',
'ERR_UNKNOWN_FILE_EXTENSION',
])
export async function getNuxtConfig(rootDir: string) {
const configFile = resolveModulePath('./nuxt.config', {
try: true,
from: pathToFileURL(join(rootDir, '/')).href,
extensions: CONFIG_EXTENSIONS,
})
if (!configFile) {
return {}
}
;(globalThis as any).defineNuxtConfig = (c: any) => c
try {
try {
return await importWithoutTypelessWarning(pathToFileURL(configFile).href)
}
catch (error) {
if (!LOADER_REQUIRED_CODES.has((error as NodeJS.ErrnoException).code!)) {
throw error
}
return await importConfigWithJiti(rootDir, error)
}
}
catch (error) {
consola.warn(`Failed to load \`${configFile}\`: ${error instanceof Error ? error.message : String(error)}`)
return {}
}
finally {
delete (globalThis as any).defineNuxtConfig
}
}
/**
* Node warns when it has to reparse a `.ts` file as ESM because the nearest
* `package.json` has no `type`. Reading the config is an internal detail of
* `nuxt info` and the warning describes our loader rather than the user's
* project, so it is dropped for the duration of the import.
*/
async function importWithoutTypelessWarning(href: string): Promise<NuxtConfig> {
const emitWarning = process.emitWarning
process.emitWarning = (warning: string | Error, ...args: any[]) => {
const code = args.find(arg => typeof arg === 'object' && arg)?.code ?? args[1]
if (code !== 'MODULE_TYPELESS_PACKAGE_JSON') {
(emitWarning as any)(warning, ...args)
}
}
try {
return await import(href).then(m => m.default) as NuxtConfig
}
finally {
process.emitWarning = emitWarning
}
}
/**
* `jiti` is an optional peer dependency, so the plain import is the normal
* path when the CLI is installed in a project. When the CLI runs from outside
* the project (`npx nuxi`), it is instead resolved from the user's project,
* where Nuxt provides it.
*/
async function importConfigWithJiti(rootDir: string, cause: unknown) {
const { createJiti } = await import('jiti').catch(async (error) => {
// a `jiti` that resolves but fails to evaluate is a real error, not a missing peer
const code = (error as NodeJS.ErrnoException).code
if (code && !MODULE_NOT_FOUND_CODES.has(code)) {
throw error
}
const jitiPath = resolveModulePath('jiti', {
try: true,
from: pathToFileURL(join(rootDir, '/')).href,
})
if (!jitiPath) {
throw new Error(`${(cause as Error)?.message}. Hint: install \`jiti\` for compatibility.`, { cause })
}
return await import(pathToFileURL(jitiPath).href) as typeof import('jiti')
})
const jiti = createJiti(rootDir, {
interopDefault: true,
// allow using `~` and `@` in `nuxt.config`
alias: {
'~': rootDir,
'@': rootDir,
},
})
return await jiti.import('./nuxt.config', { default: true }) as NuxtConfig
}