mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-18 01:15:58 +00:00
feat(kit): support config extends
using unjs/c12
(#3008)
This commit is contained in:
parent
083f90b719
commit
1672148a87
16
examples/config-extends/app.vue
Normal file
16
examples/config-extends/app.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
const themeConfig = useRuntimeConfig().theme
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtExampleLayout example="config-extends">
|
||||
theme runtimeConfig
|
||||
<pre>{{ JSON.stringify(themeConfig, null, 2) }}</pre>
|
||||
</NuxtExampleLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
pre {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
10
examples/config-extends/base/nuxt.config.ts
Normal file
10
examples/config-extends/base/nuxt.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineNuxtConfig } from 'nuxt3'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
publicRuntimeConfig: {
|
||||
theme: {
|
||||
primaryColor: 'base_primary',
|
||||
secondaryColor: 'base_secondary'
|
||||
}
|
||||
}
|
||||
})
|
13
examples/config-extends/nuxt.config.ts
Normal file
13
examples/config-extends/nuxt.config.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { defineNuxtConfig } from 'nuxt3'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
extends: './base',
|
||||
publicRuntimeConfig: {
|
||||
theme: {
|
||||
primaryColor: 'user_primary'
|
||||
}
|
||||
},
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
]
|
||||
})
|
13
examples/config-extends/package.json
Normal file
13
examples/config-extends/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "example-config-extends",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest",
|
||||
"nuxt3": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxi dev",
|
||||
"build": "nuxi build",
|
||||
"start": "nuxi preview"
|
||||
}
|
||||
}
|
3
examples/config-extends/tsconfig.json
Normal file
3
examples/config-extends/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
@ -14,9 +14,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/schema": "^3.0.0",
|
||||
"c12": "^0.1.1",
|
||||
"consola": "^2.15.3",
|
||||
"defu": "^5.0.1",
|
||||
"dotenv": "^15.0.0",
|
||||
"globby": "^13.1.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"jiti": "^1.12.15",
|
||||
@ -24,7 +24,6 @@
|
||||
"mlly": "^0.4.1",
|
||||
"pathe": "^0.2.0",
|
||||
"pkg-types": "^0.3.2",
|
||||
"rc9": "^1.2.0",
|
||||
"scule": "^0.2.1",
|
||||
"semver": "^7.3.5",
|
||||
"unctx": "^1.0.2",
|
||||
|
@ -1,128 +0,0 @@
|
||||
import { promises as fsp, existsSync } from 'fs'
|
||||
import { resolve } from 'pathe'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
export interface DotenvOptions {
|
||||
/** The project root directory (either absolute or relative to the current working directory). */
|
||||
rootDir: string
|
||||
|
||||
/**
|
||||
* What file to look in for environment variables (either absolute or relative
|
||||
* to the current working directory). For example, `.env`.
|
||||
*/
|
||||
fileName: string
|
||||
|
||||
/**
|
||||
* Whether to interpolate variables within .env.
|
||||
*
|
||||
* @example
|
||||
* ```env
|
||||
* BASE_DIR="/test"
|
||||
* # resolves to "/test/further"
|
||||
* ANOTHER_DIR="${BASE_DIR}/further"
|
||||
* ```
|
||||
*/
|
||||
expand: boolean
|
||||
|
||||
/** An object describing environment variables (key, value pairs). */
|
||||
env: NodeJS.ProcessEnv
|
||||
}
|
||||
|
||||
export type Env = typeof process.env
|
||||
|
||||
/**
|
||||
* Load and interpolate environment variables into `process.env`.
|
||||
* If you need more control (or access to the values), consider using `loadDotenv` instead
|
||||
*
|
||||
*/
|
||||
export async function setupDotenv (options: DotenvOptions): Promise<Env> {
|
||||
const targetEnv = options.env ?? process.env
|
||||
|
||||
// Load env
|
||||
const env = await loadDotenv({
|
||||
rootDir: options.rootDir,
|
||||
fileName: options.fileName ?? '.env',
|
||||
env: targetEnv,
|
||||
expand: options.expand ?? true
|
||||
})
|
||||
|
||||
// Fill process.env
|
||||
for (const key in env) {
|
||||
if (!key.startsWith('_') && targetEnv[key] === undefined) {
|
||||
targetEnv[key] = env[key]
|
||||
}
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
/** Load environment variables into an object. */
|
||||
export async function loadDotenv (opts: DotenvOptions): Promise<Env> {
|
||||
const env = Object.create(null)
|
||||
|
||||
const dotenvFile = resolve(opts.rootDir, opts.fileName)
|
||||
|
||||
if (existsSync(dotenvFile)) {
|
||||
const parsed = dotenv.parse(await fsp.readFile(dotenvFile, 'utf-8'))
|
||||
Object.assign(env, parsed)
|
||||
}
|
||||
|
||||
// Apply process.env
|
||||
if (!opts.env._applied) {
|
||||
Object.assign(env, opts.env)
|
||||
env._applied = true
|
||||
}
|
||||
|
||||
// Interpolate env
|
||||
if (opts.expand) {
|
||||
expand(env)
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// Based on https://github.com/motdotla/dotenv-expand
|
||||
function expand (target: Record<string, any>, source: Record<string, any> = {}, parse = (v: any) => v) {
|
||||
function getValue (key: string) {
|
||||
// Source value 'wins' over target value
|
||||
return source[key] !== undefined ? source[key] : target[key]
|
||||
}
|
||||
|
||||
function interpolate (value: unknown, parents: string[] = []) {
|
||||
if (typeof value !== 'string') {
|
||||
return value
|
||||
}
|
||||
const matches = value.match(/(.?\${?(?:[a-zA-Z0-9_:]+)?}?)/g) || []
|
||||
return parse(matches.reduce((newValue, match) => {
|
||||
const parts = /(.?)\${?([a-zA-Z0-9_:]+)?}?/g.exec(match)
|
||||
const prefix = parts[1]
|
||||
|
||||
let value, replacePart: string
|
||||
|
||||
if (prefix === '\\') {
|
||||
replacePart = parts[0]
|
||||
value = replacePart.replace('\\$', '$')
|
||||
} else {
|
||||
const key = parts[2]
|
||||
replacePart = parts[0].substring(prefix.length)
|
||||
|
||||
// Avoid recursion
|
||||
if (parents.includes(key)) {
|
||||
console.warn(`Please avoid recursive environment variables ( loop: ${parents.join(' > ')} > ${key} )`)
|
||||
return ''
|
||||
}
|
||||
|
||||
value = getValue(key)
|
||||
|
||||
// Resolve recursive interpolations
|
||||
value = interpolate(value, [...parents, key])
|
||||
}
|
||||
|
||||
return value !== undefined ? newValue.replace(replacePart, value) : newValue
|
||||
}, value))
|
||||
}
|
||||
|
||||
for (const key in target) {
|
||||
target[key] = interpolate(getValue(key))
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
import { existsSync } from 'fs'
|
||||
import { resolve } from 'pathe'
|
||||
import defu from 'defu'
|
||||
import { applyDefaults } from 'untyped'
|
||||
import * as rc from 'rc9'
|
||||
import { loadConfig, DotenvOptions } from 'c12'
|
||||
import type { NuxtOptions } from '@nuxt/schema'
|
||||
import { NuxtConfigSchema } from '@nuxt/schema'
|
||||
import { tryResolveModule, requireModule, scanRequireTree } from '../internal/cjs'
|
||||
import { setupDotenv, DotenvOptions } from '../internal/dotenv'
|
||||
// TODO
|
||||
// import { tryResolveModule, requireModule, scanRequireTree } from '../internal/cjs'
|
||||
|
||||
export interface LoadNuxtConfigOptions {
|
||||
/** Your project root directory (either absolute or relative to the current working directory). */
|
||||
@ -25,39 +23,22 @@ export interface LoadNuxtConfigOptions {
|
||||
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
|
||||
const rootDir = resolve(process.cwd(), opts.rootDir || '.')
|
||||
|
||||
if (opts.dotenv !== false) {
|
||||
await setupDotenv({ rootDir, ...opts.dotenv })
|
||||
}
|
||||
const { config: nuxtConfig, configFile, layers } = await loadConfig({
|
||||
cwd: rootDir,
|
||||
name: 'nuxt',
|
||||
configFile: 'nuxt.config',
|
||||
rcFile: '.nuxtrc',
|
||||
dotenv: opts.dotenv,
|
||||
globalRc: true,
|
||||
overrides: opts.config
|
||||
})
|
||||
|
||||
const nuxtConfigFile = tryResolveModule(resolve(rootDir, opts.configFile || 'nuxt.config'))
|
||||
nuxtConfig.rootDir = nuxtConfig.rootDir || rootDir
|
||||
|
||||
let nuxtConfig: any = {}
|
||||
nuxtConfig._nuxtConfigFile = configFile
|
||||
nuxtConfig._nuxtConfigFiles = [configFile]
|
||||
|
||||
if (nuxtConfigFile && existsSync(nuxtConfigFile)) {
|
||||
nuxtConfig = requireModule(nuxtConfigFile, { clearCache: true })
|
||||
|
||||
if (typeof nuxtConfig === 'function') {
|
||||
nuxtConfig = await nuxtConfig(opts)
|
||||
}
|
||||
|
||||
nuxtConfig = { ...nuxtConfig }
|
||||
nuxtConfig._nuxtConfigFile = nuxtConfigFile
|
||||
nuxtConfig._nuxtConfigFiles = Array.from(scanRequireTree(nuxtConfigFile))
|
||||
}
|
||||
|
||||
// Combine configs
|
||||
// Priority: configOverrides > nuxtConfig > .nuxtrc > .nuxtrc (global)
|
||||
nuxtConfig = defu(
|
||||
opts.config,
|
||||
nuxtConfig,
|
||||
rc.read({ name: '.nuxtrc', dir: rootDir }),
|
||||
rc.readUser('.nuxtrc')
|
||||
)
|
||||
|
||||
// Set rootDir
|
||||
if (!nuxtConfig.rootDir) {
|
||||
nuxtConfig.rootDir = rootDir
|
||||
}
|
||||
nuxtConfig.layers = layers
|
||||
|
||||
// Resolve and apply defaults
|
||||
return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions
|
||||
|
@ -19,6 +19,7 @@
|
||||
"unbuild": "latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"c12": "^0.1.1",
|
||||
"create-require": "^1.1.1",
|
||||
"defu": "^5.0.1",
|
||||
"jiti": "^1.12.15",
|
||||
|
@ -6,6 +6,19 @@ import jiti from 'jiti'
|
||||
import defu from 'defu'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Extend nested configurations from multiple local or remoted sources
|
||||
*
|
||||
* Value should be either a string or array of strings pointing to source directories or config path relative to current config.
|
||||
*
|
||||
* You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository.
|
||||
*
|
||||
* @typedef {string|string[]}
|
||||
*
|
||||
* @version 3
|
||||
*/
|
||||
extends: null,
|
||||
|
||||
/**
|
||||
* Define the workspace directory of your application.
|
||||
*
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { ConfigSchema } from '../../schema/config'
|
||||
import type { ResolvedConfig } from 'c12'
|
||||
|
||||
/** Normalized Nuxt options available as `nuxt.options.*` */
|
||||
export interface NuxtOptions extends ConfigSchema { }
|
||||
export interface NuxtOptions extends ConfigSchema {
|
||||
layers: ResolvedConfig<NuxtOptions>[]
|
||||
}
|
||||
|
||||
type DeepPartial<T> = T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> | T[P] } : T
|
||||
|
||||
|
49
yarn.lock
49
yarn.lock
@ -2859,9 +2859,9 @@ __metadata:
|
||||
"@nuxt/schema": ^3.0.0
|
||||
"@types/lodash.template": ^4
|
||||
"@types/semver": ^7
|
||||
c12: ^0.1.1
|
||||
consola: ^2.15.3
|
||||
defu: ^5.0.1
|
||||
dotenv: ^15.0.0
|
||||
globby: ^13.1.0
|
||||
hash-sum: ^2.0.0
|
||||
jiti: ^1.12.15
|
||||
@ -2869,7 +2869,6 @@ __metadata:
|
||||
mlly: ^0.4.1
|
||||
pathe: ^0.2.0
|
||||
pkg-types: ^0.3.2
|
||||
rc9: ^1.2.0
|
||||
scule: ^0.2.1
|
||||
semver: ^7.3.5
|
||||
unbuild: latest
|
||||
@ -3021,6 +3020,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@types/lodash.template": ^4
|
||||
"@types/semver": ^7
|
||||
c12: ^0.1.1
|
||||
create-require: ^1.1.1
|
||||
defu: ^5.0.1
|
||||
jiti: ^1.12.15
|
||||
@ -6744,6 +6744,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"c12@npm:^0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "c12@npm:0.1.1"
|
||||
dependencies:
|
||||
defu: ^5.0.1
|
||||
dotenv: ^14.3.2
|
||||
gittar: ^0.1.1
|
||||
jiti: ^1.12.14
|
||||
mlly: ^0.4.1
|
||||
pathe: ^0.2.0
|
||||
rc9: ^1.2.0
|
||||
checksum: 628bd42845926249f9dfbaf14371793de72c4b9fa764e8a635ae56f5e67c99d765f8ea09ac44e3876e334095a627ca24d7029ca5d6254829998b9fab67927460
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacache@npm:^12.0.2":
|
||||
version: 12.0.4
|
||||
resolution: "cacache@npm:12.0.4"
|
||||
@ -8908,20 +8923,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dotenv@npm:^14.3.0":
|
||||
"dotenv@npm:^14.3.0, dotenv@npm:^14.3.2":
|
||||
version: 14.3.2
|
||||
resolution: "dotenv@npm:14.3.2"
|
||||
checksum: 86c06758915d6facc35275f4a7fafc16705b6f3b44befaa8abca91367991efc8ff8db5437d3cc14778231d19fb97610fe82d60f8a53ba723cdb69fe4171439aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dotenv@npm:^15.0.0":
|
||||
version: 15.0.0
|
||||
resolution: "dotenv@npm:15.0.0"
|
||||
checksum: be5d852b2ad1708780e7038481c0687ce4bb9edf354d439872511e966a5f28c083f296e1d1c182256d695bfdd67e320382eb3b0f2e9d83efa3615cd1757257ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dotenv@npm:^8.2.0":
|
||||
version: 8.6.0
|
||||
resolution: "dotenv@npm:8.6.0"
|
||||
@ -10081,6 +10089,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"example-config-extends@workspace:examples/config-extends":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-config-extends@workspace:examples/config-extends"
|
||||
dependencies:
|
||||
"@nuxt/ui": "npm:@nuxt/ui-edge@latest"
|
||||
nuxt3: latest
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"example-hello-world@workspace:examples/hello-world":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "example-hello-world@workspace:examples/hello-world"
|
||||
@ -11116,6 +11133,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gittar@npm:^0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "gittar@npm:0.1.1"
|
||||
dependencies:
|
||||
mkdirp: ^0.5.1
|
||||
tar: ^4.4.1
|
||||
checksum: fcc6127a9ce36116f1d2e429b896ab133c27ddd84817a48ccb88b3039caca33e1f8566fe00d14c6e7c16a0aa65cf543c94588427e5b72c52c1cd4d8d87414744
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob-parent@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "glob-parent@npm:3.1.0"
|
||||
@ -19608,7 +19635,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:^4, tar@npm:^4.4.12":
|
||||
"tar@npm:^4, tar@npm:^4.4.1, tar@npm:^4.4.12":
|
||||
version: 4.4.19
|
||||
resolution: "tar@npm:4.4.19"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user