}"
`;
+exports[`sfc reactive props destructure > var declaration shadowing before declaration in for loop 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+export default /*@__PURE__*/_defineComponent({
+ props: {
+ foo: { type: String, required: false, default: "a" }
+ },
+ setup(__props: any) {
+
+
+let bar: string | undefined;
+function init() {
+ bar = foo;
+ for (var foo = "b"; false;) {}
+}
+init();
+
+return () => {}
+}
+
+})"
+`;
+
+exports[`sfc reactive props destructure > var declaration shadowing before declaration in nested block 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+export default /*@__PURE__*/_defineComponent({
+ props: {
+ foo: { type: String, required: false, default: "a" }
+ },
+ setup(__props: any) {
+
+
+let bar: string | undefined;
+function init() {
+ bar = foo;
+ {
+ var foo = "b";
+ }
+}
+init();
+
+return () => {}
+}
+
+})"
+`;
+
+exports[`sfc reactive props destructure > var declaration shadowing does not cross function scope 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+export default /*@__PURE__*/_defineComponent({
+ props: {
+ foo: { type: String, required: false, default: "a" }
+ },
+ setup(__props: any) {
+
+
+let bar: string | undefined;
+function init() {
+ function nested() {
+ {
+ var foo = "b";
+ }
+ return foo;
+ }
+ bar = __props.foo;
+ nested();
+}
+init();
+
+return () => {}
+}
+
+})"
+`;
+
+exports[`sfc reactive props destructure > var declaration shadowing in nested block 1`] = `
+"import { defineComponent as _defineComponent } from 'vue'
+
+export default /*@__PURE__*/_defineComponent({
+ props: {
+ foo: { type: String, required: false, default: "a" }
+ },
+ setup(__props: any) {
+
+
+let bar: string | undefined;
+function init() {
+ {
+ var foo = "b";
+ }
+ bar = foo;
+}
+init();
+
+return () => {}
+}
+
+})"
+`;
+
exports[`sfc reactive props destructure > with TSInstantiationExpression 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Foo = <T extends string | number>(data: T) => void
assertCode(content)
})
+ test('var declaration shadowing in nested block', () => {
+ const { content } = compile(`<script setup lang="ts">
+const { foo = "a" } = defineProps<{ foo?: string }>();
+let bar: string | undefined;
+function init() {
+ {
+ var foo = "b";
+ }
+ bar = foo;
+}
+init();
+</script>`)
+ expect(content).toMatch(`var foo = "b"`)
+ expect(content).toMatch(`bar = foo`)
+ expect(content).not.toMatch(`bar = __props.foo`)
+ assertCode(content)
+ })
+
+ test('var declaration shadowing before declaration in nested block', () => {
+ const { content } = compile(`<script setup lang="ts">
+const { foo = "a" } = defineProps<{ foo?: string }>();
+let bar: string | undefined;
+function init() {
+ bar = foo;
+ {
+ var foo = "b";
+ }
+}
+init();
+</script>`)
+ expect(content).toMatch(`bar = foo`)
+ expect(content).toMatch(`var foo = "b"`)
+ expect(content).not.toMatch(`bar = __props.foo`)
+ assertCode(content)
+ })
+
+ test('var declaration shadowing before declaration in for loop', () => {
+ const { content } = compile(`<script setup lang="ts">
+const { foo = "a" } = defineProps<{ foo?: string }>();
+let bar: string | undefined;
+function init() {
+ bar = foo;
+ for (var foo = "b"; false;) {}
+}
+init();
+</script>`)
+ expect(content).toMatch(`bar = foo`)
+ expect(content).toMatch(`for (var foo = "b"; false;)`)
+ expect(content).not.toMatch(`bar = __props.foo`)
+ assertCode(content)
+ })
+
+ test('var declaration shadowing does not cross function scope', () => {
+ const { content } = compile(`<script setup lang="ts">
+const { foo = "a" } = defineProps<{ foo?: string }>();
+let bar: string | undefined;
+function init() {
+ function nested() {
+ {
+ var foo = "b";
+ }
+ return foo;
+ }
+ bar = foo;
+ nested();
+}
+init();
+</script>`)
+ expect(content).toMatch(`return foo`)
+ expect(content).toMatch(`bar = __props.foo`)
+ assertCode(content)
+ })
+
test('default values w/ array runtime declaration', () => {
const { content } = compile(`
<script setup>
const rootScope: Scope = Object.create(null)
const scopeStack: Scope[] = [rootScope]
+ const functionScopeStack: Scope[] = [rootScope]
let currentScope: Scope = rootScope
const excludedIds = new WeakSet<Identifier>()
const parentStack: Node[] = []
propsLocalToPublicMap[local] = key
}
- function pushScope() {
- scopeStack.push((currentScope = Object.create(currentScope)))
+ function pushScope(isFunctionScope = false) {
+ const scope = (currentScope = Object.create(currentScope))
+ scopeStack.push(scope)
+ if (isFunctionScope) {
+ functionScopeStack.push(scope)
+ }
}
- function popScope() {
+ function popScope(isFunctionScope = false) {
scopeStack.pop()
+ if (isFunctionScope) {
+ functionScopeStack.pop()
+ }
currentScope = scopeStack[scopeStack.length - 1] || null
}
- function registerLocalBinding(id: Identifier) {
+ function registerLocalBinding(id: Identifier, scope = currentScope) {
excludedIds.add(id)
- if (currentScope) {
- currentScope[id.name] = false
+ if (scope) {
+ scope[id.name] = false
} else {
ctx.error(
'registerBinding called without active scope, something is wrong.',
}
}
- function walkVariableDeclaration(stmt: VariableDeclaration, isRoot = false) {
+ function walkVariableDeclaration(
+ stmt: VariableDeclaration,
+ isRoot = false,
+ scope = stmt.kind === 'var'
+ ? functionScopeStack[functionScopeStack.length - 1]
+ : currentScope,
+ ) {
if (stmt.declare) {
return
}
// are already passed in as knownProps
excludedIds.add(id)
} else {
- registerLocalBinding(id)
+ registerLocalBinding(id, scope)
}
}
}
}
+ function walkFunctionScopeVarDeclarations(
+ scopeNode: Program | BlockStatement,
+ isRoot = false,
+ ) {
+ const scope = functionScopeStack[functionScopeStack.length - 1]
+ walk(scopeNode, {
+ enter(node: Node, parent: Node | null) {
+ if (
+ parent &&
+ parent.type.startsWith('TS') &&
+ !TS_NODE_TYPES.includes(parent.type)
+ ) {
+ return this.skip()
+ }
+
+ if (
+ isFunctionType(node) ||
+ node.type === 'ClassDeclaration' ||
+ node.type === 'ClassExpression'
+ ) {
+ return this.skip()
+ }
+
+ if (node.type === 'VariableDeclaration' && node.kind === 'var') {
+ walkVariableDeclaration(node, isRoot && parent === scopeNode, scope)
+ }
+ },
+ })
+ }
+
function rewriteId(id: Identifier, parent: Node, parentStack: Node[]) {
if (
(parent.type === 'AssignmentExpression' && id === parent.left) ||
// check root scope first
const ast = ctx.scriptSetupAst!
+ walkFunctionScopeVarDeclarations(ast, true)
walkScope(ast, true)
walk(ast, {
enter(node: Node, parent: Node | null) {
// function scopes
if (isFunctionType(node)) {
- pushScope()
+ pushScope(true)
walkFunctionParams(node, registerLocalBinding)
if (node.body.type === 'BlockStatement') {
+ walkFunctionScopeVarDeclarations(node.body)
walkScope(node.body)
}
return
},
leave(node: Node, parent: Node | null) {
parent && parentStack.pop()
- if (
- (node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
- isFunctionType(node) ||
+ if (isFunctionType(node)) {
+ popScope(true)
+ } else if (node.type === 'BlockStatement' && !isFunctionType(parent!)) {
+ popScope()
+ } else if (
node.type === 'CatchClause' ||
node.type === 'ForOfStatement' ||
node.type === 'ForInStatement' ||