feat(kit): support config extends using unjs/c12 (#3008)

This commit is contained in:
pooya parsa 2022-01-31 22:13:58 +01:00 committed by GitHub
parent 083f90b719
commit 1672148a87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 128 additions and 177 deletions

View 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>

View File

@ -0,0 +1,10 @@
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
publicRuntimeConfig: {
theme: {
primaryColor: 'base_primary',
secondaryColor: 'base_secondary'
}
}
})

View File

@ -0,0 +1,13 @@
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
extends: './base',
publicRuntimeConfig: {
theme: {
primaryColor: 'user_primary'
}
},
modules: [
'@nuxt/ui'
]
})

View 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"
}
}

View File

@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}

View File

@ -14,9 +14,9 @@
}, },
"dependencies": { "dependencies": {
"@nuxt/schema": "^3.0.0", "@nuxt/schema": "^3.0.0",
"c12": "^0.1.1",
"consola": "^2.15.3", "consola": "^2.15.3",
"defu": "^5.0.1", "defu": "^5.0.1",
"dotenv": "^15.0.0",
"globby": "^13.1.0", "globby": "^13.1.0",
"hash-sum": "^2.0.0", "hash-sum": "^2.0.0",
"jiti": "^1.12.15", "jiti": "^1.12.15",
@ -24,7 +24,6 @@
"mlly": "^0.4.1", "mlly": "^0.4.1",
"pathe": "^0.2.0", "pathe": "^0.2.0",
"pkg-types": "^0.3.2", "pkg-types": "^0.3.2",
"rc9": "^1.2.0",
"scule": "^0.2.1", "scule": "^0.2.1",
"semver": "^7.3.5", "semver": "^7.3.5",
"unctx": "^1.0.2", "unctx": "^1.0.2",

View File

@ -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))
}
}

View File

@ -1,12 +1,10 @@
import { existsSync } from 'fs'
import { resolve } from 'pathe' import { resolve } from 'pathe'
import defu from 'defu'
import { applyDefaults } from 'untyped' import { applyDefaults } from 'untyped'
import * as rc from 'rc9' import { loadConfig, DotenvOptions } from 'c12'
import type { NuxtOptions } from '@nuxt/schema' import type { NuxtOptions } from '@nuxt/schema'
import { NuxtConfigSchema } from '@nuxt/schema' import { NuxtConfigSchema } from '@nuxt/schema'
import { tryResolveModule, requireModule, scanRequireTree } from '../internal/cjs' // TODO
import { setupDotenv, DotenvOptions } from '../internal/dotenv' // import { tryResolveModule, requireModule, scanRequireTree } from '../internal/cjs'
export interface LoadNuxtConfigOptions { export interface LoadNuxtConfigOptions {
/** Your project root directory (either absolute or relative to the current working directory). */ /** 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> { export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
const rootDir = resolve(process.cwd(), opts.rootDir || '.') const rootDir = resolve(process.cwd(), opts.rootDir || '.')
if (opts.dotenv !== false) { const { config: nuxtConfig, configFile, layers } = await loadConfig({
await setupDotenv({ rootDir, ...opts.dotenv }) 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.layers = layers
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
}
// Resolve and apply defaults // Resolve and apply defaults
return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions

View File

@ -19,6 +19,7 @@
"unbuild": "latest" "unbuild": "latest"
}, },
"dependencies": { "dependencies": {
"c12": "^0.1.1",
"create-require": "^1.1.1", "create-require": "^1.1.1",
"defu": "^5.0.1", "defu": "^5.0.1",
"jiti": "^1.12.15", "jiti": "^1.12.15",

View File

@ -6,6 +6,19 @@ import jiti from 'jiti'
import defu from 'defu' import defu from 'defu'
export default { 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. * Define the workspace directory of your application.
* *

View File

@ -1,7 +1,10 @@
import { ConfigSchema } from '../../schema/config' import { ConfigSchema } from '../../schema/config'
import type { ResolvedConfig } from 'c12'
/** Normalized Nuxt options available as `nuxt.options.*` */ /** 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 type DeepPartial<T> = T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> | T[P] } : T

View File

@ -2859,9 +2859,9 @@ __metadata:
"@nuxt/schema": ^3.0.0 "@nuxt/schema": ^3.0.0
"@types/lodash.template": ^4 "@types/lodash.template": ^4
"@types/semver": ^7 "@types/semver": ^7
c12: ^0.1.1
consola: ^2.15.3 consola: ^2.15.3
defu: ^5.0.1 defu: ^5.0.1
dotenv: ^15.0.0
globby: ^13.1.0 globby: ^13.1.0
hash-sum: ^2.0.0 hash-sum: ^2.0.0
jiti: ^1.12.15 jiti: ^1.12.15
@ -2869,7 +2869,6 @@ __metadata:
mlly: ^0.4.1 mlly: ^0.4.1
pathe: ^0.2.0 pathe: ^0.2.0
pkg-types: ^0.3.2 pkg-types: ^0.3.2
rc9: ^1.2.0
scule: ^0.2.1 scule: ^0.2.1
semver: ^7.3.5 semver: ^7.3.5
unbuild: latest unbuild: latest
@ -3021,6 +3020,7 @@ __metadata:
dependencies: dependencies:
"@types/lodash.template": ^4 "@types/lodash.template": ^4
"@types/semver": ^7 "@types/semver": ^7
c12: ^0.1.1
create-require: ^1.1.1 create-require: ^1.1.1
defu: ^5.0.1 defu: ^5.0.1
jiti: ^1.12.15 jiti: ^1.12.15
@ -6744,6 +6744,21 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "cacache@npm:^12.0.2":
version: 12.0.4 version: 12.0.4
resolution: "cacache@npm:12.0.4" resolution: "cacache@npm:12.0.4"
@ -8908,20 +8923,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"dotenv@npm:^14.3.0": "dotenv@npm:^14.3.0, dotenv@npm:^14.3.2":
version: 14.3.2 version: 14.3.2
resolution: "dotenv@npm:14.3.2" resolution: "dotenv@npm:14.3.2"
checksum: 86c06758915d6facc35275f4a7fafc16705b6f3b44befaa8abca91367991efc8ff8db5437d3cc14778231d19fb97610fe82d60f8a53ba723cdb69fe4171439aa checksum: 86c06758915d6facc35275f4a7fafc16705b6f3b44befaa8abca91367991efc8ff8db5437d3cc14778231d19fb97610fe82d60f8a53ba723cdb69fe4171439aa
languageName: node languageName: node
linkType: hard 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": "dotenv@npm:^8.2.0":
version: 8.6.0 version: 8.6.0
resolution: "dotenv@npm:8.6.0" resolution: "dotenv@npm:8.6.0"
@ -10081,6 +10089,15 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "example-hello-world@workspace:examples/hello-world":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "example-hello-world@workspace:examples/hello-world" resolution: "example-hello-world@workspace:examples/hello-world"
@ -11116,6 +11133,16 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "glob-parent@npm:^3.1.0":
version: 3.1.0 version: 3.1.0
resolution: "glob-parent@npm:3.1.0" resolution: "glob-parent@npm:3.1.0"
@ -19608,7 +19635,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 4.4.19
resolution: "tar@npm:4.4.19" resolution: "tar@npm:4.4.19"
dependencies: dependencies: