Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ssr): capture scope declaration correctly #6281

Merged
merged 5 commits into from Dec 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Expand Up @@ -529,3 +529,64 @@ class A {
"
`)
})

test('delcare scope', async () => {
expect(
(
await ssrTransform(
`
import { aaa, bbb, ccc, ddd } from 'vue'

function foobar() {
ddd()

const aaa = () => {
bbb(ccc)
ddd()
}
const bbb = () => {
console.log('hi')
}
const ccc = 1
function ddd() {}

aaa()
bbb()
ccc()
}

aaa()
bbb()
`,
null,
null
)
).code
).toMatchInlineSnapshot(`
"
const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\");


function foobar() {
ddd()

const aaa = () => {
bbb(ccc)
ddd()
}
const bbb = () => {
console.log('hi')
}
const ccc = 1
function ddd() {}

aaa()
bbb()
ccc()
}

__vite_ssr_import_0__.aaa()
__vite_ssr_import_0__.bbb()
"
`)
})
59 changes: 23 additions & 36 deletions packages/vite/src/node/ssr/ssrTransform.ts
Expand Up @@ -180,7 +180,7 @@ export async function ssrTransform(
// 3. convert references to import bindings & import.meta references
walk(ast, {
onIdentifier(id, parent, parentStack) {
const grandparent = parentStack[parentStack.length - 2]
const grandparent = parentStack[1]
const binding = idToImportMap.get(id.name)
if (!binding) {
return
Expand All @@ -203,7 +203,7 @@ export async function ssrTransform(
if (!declaredConst.has(id.name)) {
declaredConst.add(id.name)
// locate the top-most node containing the class declaration
const topNode = parentStack[1]
const topNode = parentStack[parentStack.length - 2]
s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`)
}
} else {
Expand Down Expand Up @@ -266,33 +266,32 @@ function walk(
{ onIdentifier, onImportMeta, onDynamicImport }: Visitors
) {
const parentStack: Node[] = []
const scope: Record<string, number> = Object.create(null)
const scopeMap = new WeakMap<_Node, Set<string>>()
const identifiers: [id: any, stack: Node[]][] = []

const setScope = (node: FunctionNode, name: string) => {
let scopeIds = scopeMap.get(node)
if (scopeIds && scopeIds.has(name)) {
return
}
if (name in scope) {
scope[name]++
} else {
scope[name] = 1
}
if (!scopeIds) {
scopeIds = new Set()
scopeMap.set(node, scopeIds)
}
scopeIds.add(name)
}

function isInScope(name: string, parents: Node[]) {
return parents.some((node) => node && scopeMap.get(node)?.has(name))
}

;(eswalk as any)(root, {
enter(node: Node, parent: Node | null) {
if (node.type === 'ImportDeclaration') {
return this.skip()
}

parent && parentStack.push(parent)
parent && parentStack.unshift(parent)

if (node.type === 'MetaProperty' && node.meta.name === 'import') {
onImportMeta(node)
Expand All @@ -301,8 +300,12 @@ function walk(
}

if (node.type === 'Identifier') {
if (!scope[node.name] && isRefIdentifier(node, parent!, parentStack)) {
onIdentifier(node, parent!, parentStack)
if (
!isInScope(node.name, parentStack) &&
isRefIdentifier(node, parent!, parentStack)
) {
// record the identifier, for DFS -> BFS
identifiers.push([node, parentStack.slice(0)])
}
} else if (isFunction(node)) {
// If it is a function declaration, it could be shadowing an import
Expand Down Expand Up @@ -372,18 +375,15 @@ function walk(
},

leave(node: Node, parent: Node | null) {
parent && parentStack.pop()
const scopeIds = scopeMap.get(node)
if (scopeIds) {
scopeIds.forEach((id: string) => {
scope[id]--
if (scope[id] === 0) {
delete scope[id]
}
})
}
parent && parentStack.shift()
}
})

// emit the identifier events in BFS so the hoisted declarations
// can be captured correctly
identifiers.forEach(([node, stack]) => {
if (!isInScope(node.name, stack)) onIdentifier(node, stack[0], stack)
})
}

function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) {
Expand Down Expand Up @@ -459,12 +459,7 @@ function isFunction(node: _Node): node is FunctionNode {
}

function findParentFunction(parentStack: _Node[]): FunctionNode | undefined {
for (let i = parentStack.length - 1; i >= 0; i--) {
const node = parentStack[i]
if (isFunction(node)) {
return node
}
}
return parentStack.find((i) => isFunction(i)) as FunctionNode
}

function isInDestructuringAssignment(
Expand All @@ -475,15 +470,7 @@ function isInDestructuringAssignment(
parent &&
(parent.type === 'Property' || parent.type === 'ArrayPattern')
) {
let i = parentStack.length
while (i--) {
const p = parentStack[i]
if (p.type === 'AssignmentExpression') {
return true
} else if (p.type !== 'Property' && !p.type.endsWith('Pattern')) {
break
}
}
return parentStack.some((i) => i.type === 'AssignmentExpression')
}
return false
}