Skip to content

Commit effb7c3

Browse files
bluwyantfu
authored andcommitted
fix(ssr): correctly track scope (#10300)
1 parent dc140af commit effb7c3

File tree

2 files changed

+94
-19
lines changed

2 files changed

+94
-19
lines changed

packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { expect, test } from 'vitest'
22
import { transformWithEsbuild } from '../../plugins/esbuild'
3-
import { traverseHtml } from '../../plugins/html'
43
import { ssrTransform } from '../ssrTransform'
54

65
const ssrTransformSimple = async (code: string, url = '') =>
@@ -728,3 +727,63 @@ console.log("it can parse the hashbang")`
728727
console.log(\\"it can parse the hashbang\\")"
729728
`)
730729
})
730+
731+
// #10289
732+
test('track scope by class, function, condition blocks', async () => {
733+
const code = `
734+
import { foo, bar } from 'foobar'
735+
if (false) {
736+
const foo = 'foo'
737+
console.log(foo)
738+
} else if (false) {
739+
const [bar] = ['bar']
740+
console.log(bar)
741+
} else {
742+
console.log(foo)
743+
console.log(bar)
744+
}
745+
export class Test {
746+
constructor() {
747+
if (false) {
748+
const foo = 'foo'
749+
console.log(foo)
750+
} else if (false) {
751+
const [bar] = ['bar']
752+
console.log(bar)
753+
} else {
754+
console.log(foo)
755+
console.log(bar)
756+
}
757+
}
758+
};`.trim()
759+
760+
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
761+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\");
762+
763+
if (false) {
764+
const foo = 'foo'
765+
console.log(foo)
766+
} else if (false) {
767+
const [bar] = ['bar']
768+
console.log(bar)
769+
} else {
770+
console.log(__vite_ssr_import_0__.foo)
771+
console.log(__vite_ssr_import_0__.bar)
772+
}
773+
class Test {
774+
constructor() {
775+
if (false) {
776+
const foo = 'foo'
777+
console.log(foo)
778+
} else if (false) {
779+
const [bar] = ['bar']
780+
console.log(bar)
781+
} else {
782+
console.log(__vite_ssr_import_0__.foo)
783+
console.log(__vite_ssr_import_0__.bar)
784+
}
785+
}
786+
}
787+
Object.defineProperty(__vite_ssr_exports__, \\"Test\\", { enumerable: true, configurable: true, get(){ return Test }});;"
788+
`)
789+
})

packages/vite/src/node/ssr/ssrTransform.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ function walk(
322322
const scopeMap = new WeakMap<_Node, Set<string>>()
323323
const identifiers: [id: any, stack: Node[]][] = []
324324

325-
const setScope = (node: FunctionNode, name: string) => {
325+
const setScope = (node: _Node, name: string) => {
326326
let scopeIds = scopeMap.get(node)
327327
if (scopeIds && scopeIds.has(name)) {
328328
return
@@ -337,29 +337,29 @@ function walk(
337337
function isInScope(name: string, parents: Node[]) {
338338
return parents.some((node) => node && scopeMap.get(node)?.has(name))
339339
}
340-
function handlePattern(p: Pattern, parentFunction: FunctionNode) {
340+
function handlePattern(p: Pattern, parentScope: _Node) {
341341
if (p.type === 'Identifier') {
342-
setScope(parentFunction, p.name)
342+
setScope(parentScope, p.name)
343343
} else if (p.type === 'RestElement') {
344-
handlePattern(p.argument, parentFunction)
344+
handlePattern(p.argument, parentScope)
345345
} else if (p.type === 'ObjectPattern') {
346346
p.properties.forEach((property) => {
347347
if (property.type === 'RestElement') {
348-
setScope(parentFunction, (property.argument as Identifier).name)
348+
setScope(parentScope, (property.argument as Identifier).name)
349349
} else {
350-
handlePattern(property.value, parentFunction)
350+
handlePattern(property.value, parentScope)
351351
}
352352
})
353353
} else if (p.type === 'ArrayPattern') {
354354
p.elements.forEach((element) => {
355355
if (element) {
356-
handlePattern(element, parentFunction)
356+
handlePattern(element, parentScope)
357357
}
358358
})
359359
} else if (p.type === 'AssignmentPattern') {
360-
handlePattern(p.left, parentFunction)
360+
handlePattern(p.left, parentScope)
361361
} else {
362-
setScope(parentFunction, (p as any).name)
362+
setScope(parentScope, (p as any).name)
363363
}
364364
}
365365

@@ -369,7 +369,14 @@ function walk(
369369
return this.skip()
370370
}
371371

372-
parent && parentStack.unshift(parent)
372+
// track parent stack, skip for "else-if"/"else" branches as acorn nests
373+
// the ast within "if" nodes instead of flattening them
374+
if (
375+
parent &&
376+
!(parent.type === 'IfStatement' && node === parent.alternate)
377+
) {
378+
parentStack.unshift(parent)
379+
}
373380

374381
if (node.type === 'MetaProperty' && node.meta.name === 'import') {
375382
onImportMeta(node)
@@ -389,9 +396,9 @@ function walk(
389396
// If it is a function declaration, it could be shadowing an import
390397
// Add its name to the scope so it won't get replaced
391398
if (node.type === 'FunctionDeclaration') {
392-
const parentFunction = findParentFunction(parentStack)
393-
if (parentFunction) {
394-
setScope(parentFunction, node.id!.name)
399+
const parentScope = findParentScope(parentStack)
400+
if (parentScope) {
401+
setScope(parentScope, node.id!.name)
395402
}
396403
}
397404
// walk function expressions and add its arguments to known identifiers
@@ -430,15 +437,21 @@ function walk(
430437
// mark property in destructuring pattern
431438
setIsNodeInPattern(node)
432439
} else if (node.type === 'VariableDeclarator') {
433-
const parentFunction = findParentFunction(parentStack)
440+
const parentFunction = findParentScope(parentStack)
434441
if (parentFunction) {
435442
handlePattern(node.id, parentFunction)
436443
}
437444
}
438445
},
439446

440447
leave(node: Node, parent: Node | null) {
441-
parent && parentStack.shift()
448+
// untrack parent stack from above
449+
if (
450+
parent &&
451+
!(parent.type === 'IfStatement' && node === parent.alternate)
452+
) {
453+
parentStack.shift()
454+
}
442455
}
443456
})
444457

@@ -521,12 +534,15 @@ const isStaticProperty = (node: _Node): node is Property =>
521534
const isStaticPropertyKey = (node: _Node, parent: _Node) =>
522535
isStaticProperty(parent) && parent.key === node
523536

537+
const functionNodeTypeRE = /Function(?:Expression|Declaration)$|Method$/
524538
function isFunction(node: _Node): node is FunctionNode {
525-
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
539+
return functionNodeTypeRE.test(node.type)
526540
}
527541

528-
function findParentFunction(parentStack: _Node[]): FunctionNode | undefined {
529-
return parentStack.find((i) => isFunction(i)) as FunctionNode
542+
const scopeNodeTypeRE =
543+
/(?:Function|Class)(?:Expression|Declaration)$|Method$|^IfStatement$/
544+
function findParentScope(parentStack: _Node[]): _Node | undefined {
545+
return parentStack.find((i) => scopeNodeTypeRE.test(i.type))
530546
}
531547

532548
function isInDestructuringAssignment(

0 commit comments

Comments
 (0)