})
})
})
+
+ describe('switch case variable declarations', () => {
+ test('should handle const declarations in switch case without braces', () => {
+ const { code } = compile(
+ `{{ (() => { switch (1) { case 1: const foo = "bar"; return \`\${foo}\`; } })() }}`,
+ )
+
+ expect(code).toMatch(`const foo = "bar";`)
+ expect(code).toMatch(`return \`\${foo}\`;`)
+ expect(code).not.toMatch(`_ctx.foo`)
+ })
+
+ test('should handle const declarations in switch case with braces (existing behavior)', () => {
+ const { code } = compile(
+ `{{ (() => {
+ switch (true) {
+ case true: {
+ const foo = "bar";
+ return \`\${foo}\`;
+ }
+ }
+ })() }}`,
+ )
+
+ expect(code).toMatch(`const foo = "bar";`)
+ expect(code).toMatch(`return \`\${foo}\`;`)
+ expect(code).not.toMatch(`_ctx.foo`)
+ })
+
+ test('should parse switch case test as local scoped variables', () => {
+ const { code } = compile(
+ `{{ (() => { switch (foo) { case bar: return \`\${bar}\`; } })() }}`,
+ )
+
+ expect(code).toMatch('_ctx.foo')
+ expect(code).toMatch(`_ctx.bar`)
+ })
+ })
})
Node,
ObjectProperty,
Program,
+ SwitchCase,
+ SwitchStatement,
} from '@babel/types'
import { walk } from 'estree-walker'
markScopeIdentifier(node, id, knownIds),
)
}
+ } else if (node.type === 'SwitchStatement') {
+ if (node.scopeIds) {
+ node.scopeIds.forEach(id => markKnownIds(id, knownIds))
+ } else {
+ // record switch case block-level local variables
+ walkSwitchStatement(node, false, id =>
+ markScopeIdentifier(node, id, knownIds),
+ )
+ }
} else if (node.type === 'CatchClause' && node.param) {
- for (const id of extractIdentifiers(node.param)) {
- markScopeIdentifier(node, id, knownIds)
+ if (node.scopeIds) {
+ node.scopeIds.forEach(id => markKnownIds(id, knownIds))
+ } else {
+ for (const id of extractIdentifiers(node.param)) {
+ markScopeIdentifier(node, id, knownIds)
+ }
}
} else if (isForStatement(node)) {
- walkForStatement(node, false, id =>
- markScopeIdentifier(node, id, knownIds),
- )
+ if (node.scopeIds) {
+ node.scopeIds.forEach(id => markKnownIds(id, knownIds))
+ } else {
+ walkForStatement(node, false, id =>
+ markScopeIdentifier(node, id, knownIds),
+ )
+ }
}
},
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | null) {
}
export function walkBlockDeclarations(
- block: BlockStatement | Program,
+ block: BlockStatement | SwitchCase | Program,
onIdent: (node: Identifier) => void,
): void {
- for (const stmt of block.body) {
+ const body = block.type === 'SwitchCase' ? block.consequent : block.body
+ for (const stmt of body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
onIdent(stmt.id)
} else if (isForStatement(stmt)) {
walkForStatement(stmt, true, onIdent)
+ } else if (stmt.type === 'SwitchStatement') {
+ walkSwitchStatement(stmt, true, onIdent)
}
}
}
}
}
+function walkSwitchStatement(
+ stmt: SwitchStatement,
+ isVar: boolean,
+ onIdent: (id: Identifier) => void,
+) {
+ for (const cs of stmt.cases) {
+ for (const stmt of cs.consequent) {
+ if (
+ stmt.type === 'VariableDeclaration' &&
+ (stmt.kind === 'var' ? isVar : !isVar)
+ ) {
+ for (const decl of stmt.declarations) {
+ for (const id of extractIdentifiers(decl.id)) {
+ onIdent(id)
+ }
+ }
+ }
+ }
+ walkBlockDeclarations(cs, onIdent)
+ }
+}
+
export function extractIdentifiers(
param: Node,
nodes: Identifier[] = [],