rsnext/packages/next-env/index.ts

174 lines
4.6 KiB
TypeScript
Raw Normal View History

/* eslint-disable import/no-extraneous-dependencies */
import * as fs from 'fs'
import * as path from 'path'
import * as dotenv from 'dotenv'
import { expand as dotenvExpand } from 'dotenv-expand'
export type Env = { [key: string]: string | undefined }
export type LoadedEnvFiles = Array<{
path: string
contents: string
}>
export let initialEnv: Env | undefined = undefined
let combinedEnv: Env | undefined = undefined
let cachedLoadedEnvFiles: LoadedEnvFiles = []
2022-08-13 18:55:55 +02:00
let previousLoadedEnvFiles: LoadedEnvFiles = []
export function updateInitialEnv(newEnv: Env) {
Object.assign(initialEnv || {}, newEnv)
}
type Log = {
info: (...args: any[]) => void
error: (...args: any[]) => void
}
Do not re-assign `process.env` (#46914) ## Checklist - [ ] Related issues linked using `fixes #number` - no related issue exists, happy to open one if desired - [x] Tests added - not sure if specific tests are needed? there is an integration test for environment variables, and Next.js relies a lot on passing information through environment variables; i'd expect everything to break if this change broke environment variable handling - [x] Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md - no new errors, does not apply ### What? Re-assigning `process.env` substitutes the "magic object" that sets environment variables at the process level with an ordinary JavaScript object. This causes environment variables that are set after `process.env` is re-assigned to not be visible to native add-ons. See [this Node.js issue][issue] and [this reproduction case][repro] for details. [issue]: https://github.com/nodejs/node/issues/46996 [repro]: https://github.com/unflxw/nodejs-process-env-addons-repro ### Why? In general, paraphrasing the maintainer in the Node.js issue, re-assigning `process.env` is not a thing you should do. More specifically, I'm trying to use Next.js' experimental OpenTelemetry support with AppSignal's Node.js integration, which also uses OpenTelemetry. The AppSignal Node.js package sets environment variables in order to configure a long-running process, which is then launched through a native add-on. Because of the re-assignment of `process.env` that occurs early in Next.js' lifecycle process, by the time the AppSignal Node.js package sets environment variables, it's setting them in an ordinary JavaScript object that Next.js left in the global `process` object, not in the magic one created by the Node.js runtime. This means that these environment variables are not _actually_ being set for the process at the OS level, and therefore they're also not set for the native add-on, or for the long-running process it spawns. ### How? A `replaceProcessEnv` function is implemented that takes an environment object as an argument, and applies the difference between that environment object and the current environment to the existing `process.env` object. This function is used instead of re-assigning `process.env`. Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-03-09 23:41:50 +01:00
function replaceProcessEnv(sourceEnv: Env) {
Object.keys(process.env).forEach((key) => {
// Allow mutating internal Next.js env variables after the server has initiated.
// This is necessary for dynamic things like the IPC server port.
if (!key.startsWith('__NEXT_PRIVATE')) {
if (sourceEnv[key] === undefined || sourceEnv[key] === '') {
delete process.env[key]
}
Do not re-assign `process.env` (#46914) ## Checklist - [ ] Related issues linked using `fixes #number` - no related issue exists, happy to open one if desired - [x] Tests added - not sure if specific tests are needed? there is an integration test for environment variables, and Next.js relies a lot on passing information through environment variables; i'd expect everything to break if this change broke environment variable handling - [x] Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md - no new errors, does not apply ### What? Re-assigning `process.env` substitutes the "magic object" that sets environment variables at the process level with an ordinary JavaScript object. This causes environment variables that are set after `process.env` is re-assigned to not be visible to native add-ons. See [this Node.js issue][issue] and [this reproduction case][repro] for details. [issue]: https://github.com/nodejs/node/issues/46996 [repro]: https://github.com/unflxw/nodejs-process-env-addons-repro ### Why? In general, paraphrasing the maintainer in the Node.js issue, re-assigning `process.env` is not a thing you should do. More specifically, I'm trying to use Next.js' experimental OpenTelemetry support with AppSignal's Node.js integration, which also uses OpenTelemetry. The AppSignal Node.js package sets environment variables in order to configure a long-running process, which is then launched through a native add-on. Because of the re-assignment of `process.env` that occurs early in Next.js' lifecycle process, by the time the AppSignal Node.js package sets environment variables, it's setting them in an ordinary JavaScript object that Next.js left in the global `process` object, not in the magic one created by the Node.js runtime. This means that these environment variables are not _actually_ being set for the process at the OS level, and therefore they're also not set for the native add-on, or for the long-running process it spawns. ### How? A `replaceProcessEnv` function is implemented that takes an environment object as an argument, and applies the difference between that environment object and the current environment to the existing `process.env` object. This function is used instead of re-assigning `process.env`. Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-03-09 23:41:50 +01:00
}
})
Object.entries(sourceEnv).forEach(([key, value]) => {
process.env[key] = value
})
}
export function processEnv(
loadedEnvFiles: LoadedEnvFiles,
dir?: string,
log: Log = console,
Redesign nextjs logging (#54713) The current logging styles has been existed for a while, this PR gives a fresh impression for the logging output from Next.js. We want to achieve few new goals that makes the output clean, modernized, sweet 🍫 . Few goals are addressed with this redesign: ## Refresh Impression & Simplification The new design of logging is much more information centralized and streamlined. * Given a `ready` message at the begining when compilers are bootstrapped. * Only show `compiled` event with green check mark indicating succesful compilation, this will merge the unclear `compiling` event which shows `(client and server)` before, now tell you the route compilation info in one line. hello world app ### `next dev` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/9649b340-8241-4756-a2b3-a989f0b74003" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/ee181263-3dd4-40d0-9ffc-819a56b45900" height="120"> ### `next build` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/5db9829a-9ffc-49f0-b030-93ee92f5c248" width="360"> <img src="https://github.com/vercel/next.js/assets/4800338/b9527b83-27c8-4426-9c0d-c0d4072b7d58" width="360"> ### error status #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/00455226-ace7-468b-8d90-0d36bf038489" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/1be8c451-d3f0-465c-9ef7-6b0dde7cff85" height="120"> ## Streamlization If you have customized envs and experiments Next.js will give the brief in the early summary about your network information, env vars, and enabled experimental features <img src="https://github.com/vercel/next.js/assets/4800338/ca1a7409-1532-46cb-850f-687e61e587b2" width="400"> ## Polish ### fetching logging structure #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/97526397-dffe-4736-88ed-e5cbe5e945bd" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/ab77c907-5ab5-48bb-8347-6146d2e60932" width="400"> ### Dedupe Duplicates The logging is moved from `@next/env` to `next` itself, `@next/env` will only notify the invoker that the env is reloaded. Then the duplicated logs for the env reloading cases can be avoid. #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/04799295-e739-4035-87aa-61cec962fc39" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/e29020c9-0031-4bf3-a21b-8b64633f43a2" width="400"> ### Different indicators Use unicode text icons for different situation: * passed -> check mark * warning -> warning * error -> red cross * loading -> circle <img src="https://github.com/vercel/next.js/assets/4800338/715c34bd-298f-4990-a5d7-e12e455ead44" width="400"> Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com>
2023-09-05 13:40:00 +02:00
forceReload = false,
onReload?: (envFilePath: string) => void
) {
if (!initialEnv) {
initialEnv = Object.assign({}, process.env)
}
// only reload env when forceReload is specified
if (
!forceReload &&
(process.env.__NEXT_PROCESSED_ENV || loadedEnvFiles.length === 0)
) {
return process.env as Env
}
// flag that we processed the environment values already.
process.env.__NEXT_PROCESSED_ENV = 'true'
const origEnv = Object.assign({}, initialEnv)
const parsed: dotenv.DotenvParseOutput = {}
for (const envFile of loadedEnvFiles) {
try {
let result: dotenv.DotenvConfigOutput = {}
result.parsed = dotenv.parse(envFile.contents)
result = dotenvExpand(result)
2022-08-13 18:55:55 +02:00
if (
result.parsed &&
!previousLoadedEnvFiles.some(
(item) =>
item.contents === envFile.contents && item.path === envFile.path
)
) {
Redesign nextjs logging (#54713) The current logging styles has been existed for a while, this PR gives a fresh impression for the logging output from Next.js. We want to achieve few new goals that makes the output clean, modernized, sweet 🍫 . Few goals are addressed with this redesign: ## Refresh Impression & Simplification The new design of logging is much more information centralized and streamlined. * Given a `ready` message at the begining when compilers are bootstrapped. * Only show `compiled` event with green check mark indicating succesful compilation, this will merge the unclear `compiling` event which shows `(client and server)` before, now tell you the route compilation info in one line. hello world app ### `next dev` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/9649b340-8241-4756-a2b3-a989f0b74003" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/ee181263-3dd4-40d0-9ffc-819a56b45900" height="120"> ### `next build` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/5db9829a-9ffc-49f0-b030-93ee92f5c248" width="360"> <img src="https://github.com/vercel/next.js/assets/4800338/b9527b83-27c8-4426-9c0d-c0d4072b7d58" width="360"> ### error status #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/00455226-ace7-468b-8d90-0d36bf038489" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/1be8c451-d3f0-465c-9ef7-6b0dde7cff85" height="120"> ## Streamlization If you have customized envs and experiments Next.js will give the brief in the early summary about your network information, env vars, and enabled experimental features <img src="https://github.com/vercel/next.js/assets/4800338/ca1a7409-1532-46cb-850f-687e61e587b2" width="400"> ## Polish ### fetching logging structure #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/97526397-dffe-4736-88ed-e5cbe5e945bd" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/ab77c907-5ab5-48bb-8347-6146d2e60932" width="400"> ### Dedupe Duplicates The logging is moved from `@next/env` to `next` itself, `@next/env` will only notify the invoker that the env is reloaded. Then the duplicated logs for the env reloading cases can be avoid. #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/04799295-e739-4035-87aa-61cec962fc39" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/e29020c9-0031-4bf3-a21b-8b64633f43a2" width="400"> ### Different indicators Use unicode text icons for different situation: * passed -> check mark * warning -> warning * error -> red cross * loading -> circle <img src="https://github.com/vercel/next.js/assets/4800338/715c34bd-298f-4990-a5d7-e12e455ead44" width="400"> Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com>
2023-09-05 13:40:00 +02:00
onReload?.(envFile.path)
}
for (const key of Object.keys(result.parsed || {})) {
if (
typeof parsed[key] === 'undefined' &&
typeof origEnv[key] === 'undefined'
) {
chore: enable typescript-eslint's recommended and stylistic configs internally (#52948) Spinning out from #37151 and my draft PR #52845, this enables the two basic recommended rulesets from [typescript-eslint](https://typescript-eslint.io) for the Next.js monorepo source code: * [`plugin:@typescript-eslint/recommended`](https://typescript-eslint.io/linting/configs#recommended): Our base recommended rules that detect common bugs or _(non-stylistic)_ TypeScript bad practices * [`plugin:@typescript-eslint/stylistic`](https://typescript-eslint.io/linting/configs#stylistic): Our base starting stylistic recommended for keeping codebases visually consistent and avoiding out-of-practice visual constructs The process I used is pretty standard (see https://github.com/typescript-eslint/typescript-eslint/issues/6760 for other repos it was done on): 1. Enable those base recommended presets 2. Remove any rule settings that are now redundant 3. Reconfigure any rule whose default settings didn't seem to make sense for this codebase 4. Add a `// Todo: ...` comment, and under it, add a disable for any rule that immediately reported a lot of complaints Note that this only enables the presets internally. It doesn't impact what end-users of published packages such as Next.js or `create-next-app` experience. That's a separate task in #52845. I also didn't fix any existing warning from the `canary` branch. Would you like me to do that? My preference would be a separate PR to get it in more quickly. Any code changes are commented inline. --------- Co-authored-by: Steven <steven@ceriously.com>
2023-08-01 01:32:54 +02:00
// We're being imprecise in the type system - assume parsed[key] can be undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
parsed[key] = result.parsed?.[key]!
}
}
} catch (err) {
log.error(
`Failed to load env from ${path.join(dir || '', envFile.path)}`,
err
)
}
}
return Object.assign(process.env, parsed)
}
export function resetEnv() {
if (initialEnv) {
replaceProcessEnv(initialEnv)
}
}
export function loadEnvConfig(
dir: string,
dev?: boolean,
log: Log = console,
Redesign nextjs logging (#54713) The current logging styles has been existed for a while, this PR gives a fresh impression for the logging output from Next.js. We want to achieve few new goals that makes the output clean, modernized, sweet 🍫 . Few goals are addressed with this redesign: ## Refresh Impression & Simplification The new design of logging is much more information centralized and streamlined. * Given a `ready` message at the begining when compilers are bootstrapped. * Only show `compiled` event with green check mark indicating succesful compilation, this will merge the unclear `compiling` event which shows `(client and server)` before, now tell you the route compilation info in one line. hello world app ### `next dev` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/9649b340-8241-4756-a2b3-a989f0b74003" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/ee181263-3dd4-40d0-9ffc-819a56b45900" height="120"> ### `next build` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/5db9829a-9ffc-49f0-b030-93ee92f5c248" width="360"> <img src="https://github.com/vercel/next.js/assets/4800338/b9527b83-27c8-4426-9c0d-c0d4072b7d58" width="360"> ### error status #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/00455226-ace7-468b-8d90-0d36bf038489" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/1be8c451-d3f0-465c-9ef7-6b0dde7cff85" height="120"> ## Streamlization If you have customized envs and experiments Next.js will give the brief in the early summary about your network information, env vars, and enabled experimental features <img src="https://github.com/vercel/next.js/assets/4800338/ca1a7409-1532-46cb-850f-687e61e587b2" width="400"> ## Polish ### fetching logging structure #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/97526397-dffe-4736-88ed-e5cbe5e945bd" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/ab77c907-5ab5-48bb-8347-6146d2e60932" width="400"> ### Dedupe Duplicates The logging is moved from `@next/env` to `next` itself, `@next/env` will only notify the invoker that the env is reloaded. Then the duplicated logs for the env reloading cases can be avoid. #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/04799295-e739-4035-87aa-61cec962fc39" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/e29020c9-0031-4bf3-a21b-8b64633f43a2" width="400"> ### Different indicators Use unicode text icons for different situation: * passed -> check mark * warning -> warning * error -> red cross * loading -> circle <img src="https://github.com/vercel/next.js/assets/4800338/715c34bd-298f-4990-a5d7-e12e455ead44" width="400"> Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com>
2023-09-05 13:40:00 +02:00
forceReload = false,
onReload?: (envFilePath: string) => void
): {
combinedEnv: Env
loadedEnvFiles: LoadedEnvFiles
} {
if (!initialEnv) {
initialEnv = Object.assign({}, process.env)
}
// only reload env when forceReload is specified
if (combinedEnv && !forceReload) {
return { combinedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}
Do not re-assign `process.env` (#46914) ## Checklist - [ ] Related issues linked using `fixes #number` - no related issue exists, happy to open one if desired - [x] Tests added - not sure if specific tests are needed? there is an integration test for environment variables, and Next.js relies a lot on passing information through environment variables; i'd expect everything to break if this change broke environment variable handling - [x] Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md - no new errors, does not apply ### What? Re-assigning `process.env` substitutes the "magic object" that sets environment variables at the process level with an ordinary JavaScript object. This causes environment variables that are set after `process.env` is re-assigned to not be visible to native add-ons. See [this Node.js issue][issue] and [this reproduction case][repro] for details. [issue]: https://github.com/nodejs/node/issues/46996 [repro]: https://github.com/unflxw/nodejs-process-env-addons-repro ### Why? In general, paraphrasing the maintainer in the Node.js issue, re-assigning `process.env` is not a thing you should do. More specifically, I'm trying to use Next.js' experimental OpenTelemetry support with AppSignal's Node.js integration, which also uses OpenTelemetry. The AppSignal Node.js package sets environment variables in order to configure a long-running process, which is then launched through a native add-on. Because of the re-assignment of `process.env` that occurs early in Next.js' lifecycle process, by the time the AppSignal Node.js package sets environment variables, it's setting them in an ordinary JavaScript object that Next.js left in the global `process` object, not in the magic one created by the Node.js runtime. This means that these environment variables are not _actually_ being set for the process at the OS level, and therefore they're also not set for the native add-on, or for the long-running process it spawns. ### How? A `replaceProcessEnv` function is implemented that takes an environment object as an argument, and applies the difference between that environment object and the current environment to the existing `process.env` object. This function is used instead of re-assigning `process.env`. Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-03-09 23:41:50 +01:00
replaceProcessEnv(initialEnv)
2022-08-13 18:55:55 +02:00
previousLoadedEnvFiles = cachedLoadedEnvFiles
cachedLoadedEnvFiles = []
const isTest = process.env.NODE_ENV === 'test'
const mode = isTest ? 'test' : dev ? 'development' : 'production'
const dotenvFiles = [
`.env.${mode}.local`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
mode !== 'test' && `.env.local`,
`.env.${mode}`,
'.env',
].filter(Boolean) as string[]
for (const envFile of dotenvFiles) {
// only load .env if the user provided has an env config file
const dotEnvPath = path.join(dir, envFile)
try {
const stats = fs.statSync(dotEnvPath)
// make sure to only attempt to read files
if (!stats.isFile()) {
continue
}
const contents = fs.readFileSync(dotEnvPath, 'utf8')
cachedLoadedEnvFiles.push({
path: envFile,
contents,
})
} catch (err: any) {
if (err.code !== 'ENOENT') {
log.error(`Failed to load env from ${envFile}`, err)
}
}
}
Redesign nextjs logging (#54713) The current logging styles has been existed for a while, this PR gives a fresh impression for the logging output from Next.js. We want to achieve few new goals that makes the output clean, modernized, sweet 🍫 . Few goals are addressed with this redesign: ## Refresh Impression & Simplification The new design of logging is much more information centralized and streamlined. * Given a `ready` message at the begining when compilers are bootstrapped. * Only show `compiled` event with green check mark indicating succesful compilation, this will merge the unclear `compiling` event which shows `(client and server)` before, now tell you the route compilation info in one line. hello world app ### `next dev` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/9649b340-8241-4756-a2b3-a989f0b74003" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/ee181263-3dd4-40d0-9ffc-819a56b45900" height="120"> ### `next build` #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/5db9829a-9ffc-49f0-b030-93ee92f5c248" width="360"> <img src="https://github.com/vercel/next.js/assets/4800338/b9527b83-27c8-4426-9c0d-c0d4072b7d58" width="360"> ### error status #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/00455226-ace7-468b-8d90-0d36bf038489" height="120"> <img src="https://github.com/vercel/next.js/assets/4800338/1be8c451-d3f0-465c-9ef7-6b0dde7cff85" height="120"> ## Streamlization If you have customized envs and experiments Next.js will give the brief in the early summary about your network information, env vars, and enabled experimental features <img src="https://github.com/vercel/next.js/assets/4800338/ca1a7409-1532-46cb-850f-687e61e587b2" width="400"> ## Polish ### fetching logging structure #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/97526397-dffe-4736-88ed-e5cbe5e945bd" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/ab77c907-5ab5-48bb-8347-6146d2e60932" width="400"> ### Dedupe Duplicates The logging is moved from `@next/env` to `next` itself, `@next/env` will only notify the invoker that the env is reloaded. Then the duplicated logs for the env reloading cases can be avoid. #### After vs Before <img src="https://github.com/vercel/next.js/assets/4800338/04799295-e739-4035-87aa-61cec962fc39" width="400"> <img src="https://github.com/vercel/next.js/assets/4800338/e29020c9-0031-4bf3-a21b-8b64633f43a2" width="400"> ### Different indicators Use unicode text icons for different situation: * passed -> check mark * warning -> warning * error -> red cross * loading -> circle <img src="https://github.com/vercel/next.js/assets/4800338/715c34bd-298f-4990-a5d7-e12e455ead44" width="400"> Co-authored-by: Tim Neutkens <6324199+timneutkens@users.noreply.github.com>
2023-09-05 13:40:00 +02:00
combinedEnv = processEnv(
cachedLoadedEnvFiles,
dir,
log,
forceReload,
onReload
)
return { combinedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}