a5f8382ee3
* Rename page runtime edge to experimental-edge * fix ut * fix lint * PageRuntime -> ServerRuntime * rename constant
197 lines
4.7 KiB
TypeScript
197 lines
4.7 KiB
TypeScript
import type {
|
|
ArrayExpression,
|
|
BooleanLiteral,
|
|
ExportDeclaration,
|
|
Identifier,
|
|
KeyValueProperty,
|
|
Module,
|
|
Node,
|
|
NullLiteral,
|
|
NumericLiteral,
|
|
ObjectExpression,
|
|
RegExpLiteral,
|
|
StringLiteral,
|
|
VariableDeclaration,
|
|
} from '@swc/core'
|
|
|
|
/**
|
|
* Extracts the value of an exported const variable named `exportedName`
|
|
* (e.g. "export const config = { runtime: 'experimental-edge' }") from swc's AST.
|
|
* The value must be one of (or throws UnsupportedValueError):
|
|
* - string
|
|
* - boolean
|
|
* - number
|
|
* - null
|
|
* - undefined
|
|
* - array containing values listed in this list
|
|
* - object containing values listed in this list
|
|
*
|
|
* Throws NoSuchDeclarationError if the declaration is not found.
|
|
*/
|
|
export function extractExportedConstValue(
|
|
module: Module,
|
|
exportedName: string
|
|
): any {
|
|
for (const moduleItem of module.body) {
|
|
if (!isExportDeclaration(moduleItem)) {
|
|
continue
|
|
}
|
|
|
|
const declaration = moduleItem.declaration
|
|
if (!isVariableDeclaration(declaration)) {
|
|
continue
|
|
}
|
|
|
|
if (declaration.kind !== 'const') {
|
|
continue
|
|
}
|
|
|
|
for (const decl of declaration.declarations) {
|
|
if (
|
|
isIdentifier(decl.id) &&
|
|
decl.id.value === exportedName &&
|
|
decl.init
|
|
) {
|
|
return extractValue(decl.init)
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new NoSuchDeclarationError()
|
|
}
|
|
|
|
/**
|
|
* A wrapper on top of `extractExportedConstValue` that returns undefined
|
|
* instead of throwing when the thrown error is known.
|
|
*/
|
|
export function tryToExtractExportedConstValue(
|
|
module: Module,
|
|
exportedName: string
|
|
) {
|
|
try {
|
|
return extractExportedConstValue(module, exportedName)
|
|
} catch (error) {
|
|
if (
|
|
error instanceof UnsupportedValueError ||
|
|
error instanceof NoSuchDeclarationError
|
|
) {
|
|
return undefined
|
|
}
|
|
}
|
|
}
|
|
|
|
function isExportDeclaration(node: Node): node is ExportDeclaration {
|
|
return node.type === 'ExportDeclaration'
|
|
}
|
|
|
|
function isVariableDeclaration(node: Node): node is VariableDeclaration {
|
|
return node.type === 'VariableDeclaration'
|
|
}
|
|
|
|
function isIdentifier(node: Node): node is Identifier {
|
|
return node.type === 'Identifier'
|
|
}
|
|
|
|
function isBooleanLiteral(node: Node): node is BooleanLiteral {
|
|
return node.type === 'BooleanLiteral'
|
|
}
|
|
|
|
function isNullLiteral(node: Node): node is NullLiteral {
|
|
return node.type === 'NullLiteral'
|
|
}
|
|
|
|
function isStringLiteral(node: Node): node is StringLiteral {
|
|
return node.type === 'StringLiteral'
|
|
}
|
|
|
|
function isNumericLiteral(node: Node): node is NumericLiteral {
|
|
return node.type === 'NumericLiteral'
|
|
}
|
|
|
|
function isArrayExpression(node: Node): node is ArrayExpression {
|
|
return node.type === 'ArrayExpression'
|
|
}
|
|
|
|
function isObjectExpression(node: Node): node is ObjectExpression {
|
|
return node.type === 'ObjectExpression'
|
|
}
|
|
|
|
function isKeyValueProperty(node: Node): node is KeyValueProperty {
|
|
return node.type === 'KeyValueProperty'
|
|
}
|
|
|
|
function isRegExpLiteral(node: Node): node is RegExpLiteral {
|
|
return node.type === 'RegExpLiteral'
|
|
}
|
|
|
|
class UnsupportedValueError extends Error {}
|
|
class NoSuchDeclarationError extends Error {}
|
|
|
|
function extractValue(node: Node): any {
|
|
if (isNullLiteral(node)) {
|
|
return null
|
|
} else if (isBooleanLiteral(node)) {
|
|
// e.g. true / false
|
|
return node.value
|
|
} else if (isStringLiteral(node)) {
|
|
// e.g. "abc"
|
|
return node.value
|
|
} else if (isNumericLiteral(node)) {
|
|
// e.g. 123
|
|
return node.value
|
|
} else if (isRegExpLiteral(node)) {
|
|
// e.g. /abc/i
|
|
return new RegExp(node.pattern, node.flags)
|
|
} else if (isIdentifier(node)) {
|
|
switch (node.value) {
|
|
case 'undefined':
|
|
return undefined
|
|
default:
|
|
throw new UnsupportedValueError()
|
|
}
|
|
} else if (isArrayExpression(node)) {
|
|
// e.g. [1, 2, 3]
|
|
const arr = []
|
|
for (const elem of node.elements) {
|
|
if (elem) {
|
|
if (elem.spread) {
|
|
// e.g. [ ...a ]
|
|
throw new UnsupportedValueError()
|
|
}
|
|
|
|
arr.push(extractValue(elem.expression))
|
|
} else {
|
|
// e.g. [1, , 2]
|
|
// ^^
|
|
arr.push(undefined)
|
|
}
|
|
}
|
|
return arr
|
|
} else if (isObjectExpression(node)) {
|
|
// e.g. { a: 1, b: 2 }
|
|
const obj: any = {}
|
|
for (const prop of node.properties) {
|
|
if (!isKeyValueProperty(prop)) {
|
|
// e.g. { ...a }
|
|
throw new UnsupportedValueError()
|
|
}
|
|
|
|
let key
|
|
if (isIdentifier(prop.key)) {
|
|
// e.g. { a: 1, b: 2 }
|
|
key = prop.key.value
|
|
} else if (isStringLiteral(prop.key)) {
|
|
// e.g. { "a": 1, "b": 2 }
|
|
key = prop.key.value
|
|
} else {
|
|
throw new UnsupportedValueError()
|
|
}
|
|
|
|
obj[key] = extractValue(prop.value)
|
|
}
|
|
|
|
return obj
|
|
} else {
|
|
throw new UnsupportedValueError()
|
|
}
|
|
}
|