Nuxt/packages/kit/src/config/env.ts

121 lines
3.3 KiB
TypeScript
Raw Normal View History

import { resolve } from 'path'
import { existsSync, promises as fsp } from 'fs'
import dotenv from 'dotenv'
export interface LoadDotEnvOptions {
2021-04-15 18:49:29 +00:00
/** The project root directory (either absolute or relative to the current working directory). */
rootDir: string
2021-04-15 18:49:29 +00:00
/**
* What file to look in for environment variables (either absolute or relative
* to the current working directory). For example, `.env`.
*/
dotenvFile: string
2021-04-15 18:49:29 +00:00
/**
* Whether to interpolate variables within .env.
*
* @example
* ```env
* BASE_DIR="/test"
* # resolves to "/test/further"
* ANOTHER_DIR="${BASE_DIR}/further"
* ```
*/
expand: boolean
2021-04-15 18:49:29 +00:00
/** An object describing environment variables (key, value pairs). */
env: NodeJS.ProcessEnv
}
2021-04-15 18:49:29 +00:00
/**
* Load and interpolate environment variables into `process.env`.
* If you need more control (or access to the values), consider using `loadDotenv` instead
*
* @param rootDir - The project root directory (either absolute or relative to the current working directory).
*/
export async function loadEnv (rootDir: string) {
// Load env
2021-04-15 18:49:29 +00:00
const env = await loadDotenv({
rootDir,
dotenvFile: '.env',
env: process.env,
expand: true
})
// Fill process.env so it is accessible in nuxt.config
for (const key in env) {
if (!key.startsWith('_') && process.env[key] === undefined) {
process.env[key] = env[key]
}
}
}
2021-04-15 18:49:29 +00:00
/** Load environment variables into an object. */
export async function loadDotenv (opts: LoadDotEnvOptions) {
const env = Object.create(null)
const dotenvFile = resolve(opts.rootDir, opts.dotenvFile)
2021-04-15 18:49:29 +00:00
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
2021-04-15 18:49:29 +00:00
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]
}
2021-04-15 18:49:29 +00:00
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]
2021-04-15 18:49:29 +00:00
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))
}
}