[middleware] support destructuring for env vars in static analysis (#37556)
This commit enables the following patterns in Middleware: ```ts // with a dot notation const { ENV_VAR, "ENV-VAR": myEnvVar } = process.env; // or with an object access const { ENV_VAR2, "ENV-VAR2": myEnvVar2 } = process["env"]; ``` ### Related - @cramforce asked this fixed here: https://github.com/vercel/next.js/pull/37514#discussion_r892437257 ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md`
This commit is contained in:
parent
76083210ed
commit
9155201807
3 changed files with 76 additions and 7 deletions
|
@ -164,18 +164,26 @@ function getCodeAnalizer(params: {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares an environment variable that is being used in this module
|
||||
* through this static analysis.
|
||||
*/
|
||||
const addUsedEnvVar = (envVarName: string) => {
|
||||
const buildInfo = getModuleBuildInfo(parser.state.module)
|
||||
if (buildInfo.nextUsedEnvVars === undefined) {
|
||||
buildInfo.nextUsedEnvVars = new Set()
|
||||
}
|
||||
|
||||
buildInfo.nextUsedEnvVars.add(envVarName)
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler for calls to `process.env` where we identify the name of the
|
||||
* ENV variable being assigned and store it in the module info.
|
||||
*/
|
||||
const handleCallMemberChain = (_: unknown, members: string[]) => {
|
||||
if (members.length >= 2 && members[0] === 'env') {
|
||||
const buildInfo = getModuleBuildInfo(parser.state.module)
|
||||
if (buildInfo.nextUsedEnvVars === undefined) {
|
||||
buildInfo.nextUsedEnvVars = new Set()
|
||||
}
|
||||
|
||||
buildInfo.nextUsedEnvVars.add(members[1])
|
||||
addUsedEnvVar(members[1])
|
||||
if (!isInMiddlewareLayer(parser)) {
|
||||
return true
|
||||
}
|
||||
|
@ -226,6 +234,37 @@ function getCodeAnalizer(params: {
|
|||
hooks.new.for('NextResponse').tap(NAME, handleNewResponseExpression)
|
||||
hooks.callMemberChain.for('process').tap(NAME, handleCallMemberChain)
|
||||
hooks.expressionMemberChain.for('process').tap(NAME, handleCallMemberChain)
|
||||
|
||||
/**
|
||||
* Support static analyzing environment variables through
|
||||
* destructuring `process.env` or `process["env"]`:
|
||||
*
|
||||
* const { MY_ENV, "MY-ENV": myEnv } = process.env
|
||||
* ^^^^^^ ^^^^^^
|
||||
*/
|
||||
hooks.declarator.tap(NAME, (declarator) => {
|
||||
if (
|
||||
declarator.init?.type === 'MemberExpression' &&
|
||||
isProcessEnvMemberExpression(declarator.init) &&
|
||||
declarator.id?.type === 'ObjectPattern'
|
||||
) {
|
||||
for (const property of declarator.id.properties) {
|
||||
if (property.type === 'RestElement') continue
|
||||
if (
|
||||
property.key.type === 'Literal' &&
|
||||
typeof property.key.value === 'string'
|
||||
) {
|
||||
addUsedEnvVar(property.key.value)
|
||||
} else if (property.key.type === 'Identifier') {
|
||||
addUsedEnvVar(property.key.name)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInMiddlewareLayer(parser)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
registerUnsupportedApiHooks(parser, compilation)
|
||||
}
|
||||
}
|
||||
|
@ -538,3 +577,14 @@ function isNullLiteral(expr: any) {
|
|||
function isUndefinedIdentifier(expr: any) {
|
||||
return expr.name === 'undefined'
|
||||
}
|
||||
|
||||
function isProcessEnvMemberExpression(memberExpression: any): boolean {
|
||||
return (
|
||||
memberExpression.object?.type === 'Identifier' &&
|
||||
memberExpression.object.name === 'process' &&
|
||||
((memberExpression.property?.type === 'Literal' &&
|
||||
memberExpression.property.value === 'env') ||
|
||||
(memberExpression.property?.type === 'Identifier' &&
|
||||
memberExpression.property.name === 'env'))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -66,6 +66,19 @@ export async function middleware(request) {
|
|||
// The next line is required to allow to find the env variable
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
process.env.MIDDLEWARE_TEST
|
||||
|
||||
// The next line is required to allow to find the env variable
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
const { ANOTHER_MIDDLEWARE_TEST } = process.env
|
||||
if (!ANOTHER_MIDDLEWARE_TEST) {
|
||||
console.log('missing ANOTHER_MIDDLEWARE_TEST')
|
||||
}
|
||||
|
||||
const { 'STRING-ENV-VAR': stringEnvVar } = process['env']
|
||||
if (!stringEnvVar) {
|
||||
console.log('missing STRING-ENV-VAR')
|
||||
}
|
||||
|
||||
return serializeData(JSON.stringify({ process: { env: process.env } }))
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ describe('Middleware Runtime', () => {
|
|||
context.buildId = 'development'
|
||||
context.app = await launchApp(context.appDir, context.appPort, {
|
||||
env: {
|
||||
ANOTHER_MIDDLEWARE_TEST: 'asdf2',
|
||||
'STRING-ENV-VAR': 'asdf3',
|
||||
MIDDLEWARE_TEST: 'asdf',
|
||||
NEXT_RUNTIME: 'edge',
|
||||
},
|
||||
|
@ -100,6 +102,8 @@ describe('Middleware Runtime', () => {
|
|||
context.appPort = await findPort()
|
||||
context.app = await nextStart(context.appDir, context.appPort, {
|
||||
env: {
|
||||
ANOTHER_MIDDLEWARE_TEST: 'asdf2',
|
||||
'STRING-ENV-VAR': 'asdf3',
|
||||
MIDDLEWARE_TEST: 'asdf',
|
||||
NEXT_RUNTIME: 'edge',
|
||||
},
|
||||
|
@ -120,7 +124,7 @@ describe('Middleware Runtime', () => {
|
|||
)
|
||||
expect(manifest.middleware).toEqual({
|
||||
'/': {
|
||||
env: ['MIDDLEWARE_TEST'],
|
||||
env: ['MIDDLEWARE_TEST', 'ANOTHER_MIDDLEWARE_TEST', 'STRING-ENV-VAR'],
|
||||
files: ['server/edge-runtime-webpack.js', 'server/middleware.js'],
|
||||
name: 'middleware',
|
||||
page: '/',
|
||||
|
@ -215,6 +219,8 @@ function tests(context, locale = '') {
|
|||
expect(readMiddlewareJSON(res)).toEqual({
|
||||
process: {
|
||||
env: {
|
||||
ANOTHER_MIDDLEWARE_TEST: 'asdf2',
|
||||
'STRING-ENV-VAR': 'asdf3',
|
||||
MIDDLEWARE_TEST: 'asdf',
|
||||
NEXT_RUNTIME: 'edge',
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue