|
|
@@ -252,6 +252,7 @@ export function processExpressions(
|
|
|
seenIdentifier,
|
|
|
updatedVariable,
|
|
|
} = analyzeExpressions(expressions)
|
|
|
+ const reservedNames = new Set<string>(seenIdentifier)
|
|
|
|
|
|
// process repeated identifiers and member expressions
|
|
|
// e.g., `foo[baz]` will be transformed into `foo_baz`
|
|
|
@@ -262,6 +263,7 @@ export function processExpressions(
|
|
|
expToVariableMap,
|
|
|
seenIdentifier,
|
|
|
updatedVariable,
|
|
|
+ reservedNames,
|
|
|
)
|
|
|
|
|
|
// process duplicate expressions after identifier and member expression handling.
|
|
|
@@ -272,6 +274,7 @@ export function processExpressions(
|
|
|
varDeclarations,
|
|
|
updatedVariable,
|
|
|
expToVariableMap,
|
|
|
+ reservedNames,
|
|
|
)
|
|
|
|
|
|
return genDeclarations(
|
|
|
@@ -387,6 +390,7 @@ function processRepeatedVariables(
|
|
|
>,
|
|
|
seenIdentifier: Set<string>,
|
|
|
updatedVariable: Set<string>,
|
|
|
+ reservedNames: Set<string>,
|
|
|
): DeclarationValue[] {
|
|
|
const declarations: DeclarationValue[] = []
|
|
|
const expToReplacementMap = new Map<
|
|
|
@@ -404,7 +408,9 @@ function processRepeatedVariables(
|
|
|
if (isGloballyAllowed(name)) continue
|
|
|
if (seenVariable[name] > 1 && exps.size > 0) {
|
|
|
const isIdentifier = seenIdentifier.has(name)
|
|
|
- const varName = isIdentifier ? name : genVarName(name)
|
|
|
+ const varName = isIdentifier
|
|
|
+ ? name
|
|
|
+ : getUniqueDeclarationName(genVarName(name), reservedNames)
|
|
|
|
|
|
// replaces all non-identifiers with the new name. if node content
|
|
|
// includes only one member expression, it will become an identifier,
|
|
|
@@ -526,6 +532,7 @@ function processRepeatedExpressions(
|
|
|
SimpleExpressionNode,
|
|
|
Array<{ name: string; loc?: { start: number; end: number } }>
|
|
|
>,
|
|
|
+ reservedNames: Set<string>,
|
|
|
): DeclarationValue[] {
|
|
|
const declarations: DeclarationValue[] = []
|
|
|
const seenExp = expressions.reduce(
|
|
|
@@ -554,41 +561,43 @@ function processRepeatedExpressions(
|
|
|
Object.entries(seenExp).forEach(([content, count]) => {
|
|
|
if (count > 1) {
|
|
|
// foo + baz -> foo_baz
|
|
|
- const varName = genVarName(content)
|
|
|
- if (!declarations.some(d => d.name === varName)) {
|
|
|
- // if foo and baz have no other references, we don't need to declare separate variables
|
|
|
- // instead of:
|
|
|
- // const foo = _ctx.foo
|
|
|
- // const baz = _ctx.baz
|
|
|
- // const foo_baz = foo + baz
|
|
|
- // we can generate:
|
|
|
- // const foo_baz = _ctx.foo + _ctx.baz
|
|
|
- const delVars: Record<string, string> = {}
|
|
|
- for (let i = varDeclarations.length - 1; i >= 0; i--) {
|
|
|
- const item = varDeclarations[i]
|
|
|
- if (!item.exps || !item.seenCount) continue
|
|
|
-
|
|
|
- const shouldRemove = [...item.exps].every(
|
|
|
- node => node.content === content && item.seenCount === count,
|
|
|
- )
|
|
|
- if (shouldRemove) {
|
|
|
- delVars[item.name] = item.rawName!
|
|
|
- varDeclarations.splice(i, 1)
|
|
|
- }
|
|
|
- }
|
|
|
- const value = extend(
|
|
|
- {},
|
|
|
- expressions.find(exp => exp.content === content)!,
|
|
|
+ // if foo and baz have no other references, we don't need to declare separate variables
|
|
|
+ // instead of:
|
|
|
+ // const foo = _ctx.foo
|
|
|
+ // const baz = _ctx.baz
|
|
|
+ // const foo_baz = foo + baz
|
|
|
+ // we can generate:
|
|
|
+ // const foo_baz = _ctx.foo + _ctx.baz
|
|
|
+ const delVars: Record<string, string> = {}
|
|
|
+ for (let i = varDeclarations.length - 1; i >= 0; i--) {
|
|
|
+ const item = varDeclarations[i]
|
|
|
+ if (!item.exps || !item.seenCount) continue
|
|
|
+
|
|
|
+ const shouldRemove = [...item.exps].every(
|
|
|
+ node => node.content === content && item.seenCount === count,
|
|
|
)
|
|
|
- Object.keys(delVars).forEach(name => {
|
|
|
- value.content = value.content.replace(name, delVars[name])
|
|
|
- if (value.ast) value.ast = parseExp(context, value.content)
|
|
|
- })
|
|
|
- declarations.push({
|
|
|
- name: varName,
|
|
|
- value: value,
|
|
|
- })
|
|
|
+ if (shouldRemove) {
|
|
|
+ delVars[item.name] = item.rawName!
|
|
|
+ reservedNames.delete(item.name)
|
|
|
+ varDeclarations.splice(i, 1)
|
|
|
+ }
|
|
|
}
|
|
|
+ const value = extend(
|
|
|
+ {},
|
|
|
+ expressions.find(exp => exp.content === content)!,
|
|
|
+ )
|
|
|
+ Object.keys(delVars).forEach(name => {
|
|
|
+ value.content = value.content.replace(name, delVars[name])
|
|
|
+ if (value.ast) value.ast = parseExp(context, value.content)
|
|
|
+ })
|
|
|
+ const varName = getUniqueDeclarationName(
|
|
|
+ genVarName(content),
|
|
|
+ reservedNames,
|
|
|
+ )
|
|
|
+ declarations.push({
|
|
|
+ name: varName,
|
|
|
+ value,
|
|
|
+ })
|
|
|
|
|
|
// assume content equals to `foo + baz`
|
|
|
expressions.forEach(exp => {
|
|
|
@@ -674,6 +683,20 @@ export function genVarName(exp: string): string {
|
|
|
.replace(/_+$/, '')}`
|
|
|
}
|
|
|
|
|
|
+function getUniqueDeclarationName(
|
|
|
+ baseName: string,
|
|
|
+ reservedNames: Set<string>,
|
|
|
+): string {
|
|
|
+ const normalizedBase = baseName || 'exp'
|
|
|
+ let name = normalizedBase
|
|
|
+ let i = 1
|
|
|
+ while (reservedNames.has(name)) {
|
|
|
+ name = `${normalizedBase}_${i++}`
|
|
|
+ }
|
|
|
+ reservedNames.add(name)
|
|
|
+ return name
|
|
|
+}
|
|
|
+
|
|
|
function extractMemberExpression(
|
|
|
exp: Node,
|
|
|
onIdentifier: (id: Identifier) => void,
|