Skip to content

Commit

Permalink
fix(parser): handle more cases
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Aug 22, 2021
1 parent 98cf455 commit 1c42237
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 14 deletions.
45 changes: 39 additions & 6 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Parser as HTMLParser } from 'htmlparser2'
import { parse } from '@babel/parser'
import { PrivateName, Expression, Statement } from '@babel/types'
import { PrivateName, Expression, Statement, SpreadElement } from '@babel/types'
import { camelize, capitalize, isHTMLTag, isSVGTag, isVoidTag } from '@vue/shared'
import { ParseResult, TagMeta } from './types'

Expand Down Expand Up @@ -34,6 +34,9 @@ export function parseVueSFC(code: string, id?: string): ParseResult {

const parser = new HTMLParser({
onopentag(name, attributes) {
if (!name)
return

if (name === 'template')
templateLevel += 1

Expand Down Expand Up @@ -131,8 +134,25 @@ export function getIdentifiersDeclaration(nodes: Statement[], identifiers = new
}
else if (node.type === 'VariableDeclaration') {
for (const declarator of node.declarations) {
// @ts-expect-error
identifiers.add(declarator.id.name)
if (declarator.id.type === 'Identifier') {
identifiers.add(declarator.id.name)
}
else if (declarator.id.type === 'ObjectPattern') {
for (const property of declarator.id.properties) {
if (property.type === 'ObjectProperty' && property.key.type === 'Identifier')
identifiers.add(property.key.name)
else if (property.type === 'RestElement' && property.argument.type === 'Identifier')
identifiers.add(property.argument.name)
}
}
else if (declarator.id.type === 'ArrayPattern') {
for (const element of declarator.id.elements) {
if (element?.type === 'Identifier')
identifiers.add(element.name)
else if (element?.type === 'RestElement' && element.argument.type === 'Identifier')
identifiers.add(element.argument.name)
}
}
}
}
else if (node.type === 'FunctionDeclaration') {
Expand All @@ -146,7 +166,7 @@ export function getIdentifiersDeclaration(nodes: Statement[], identifiers = new
return identifiers
}

export function getIdentifiersUsage(node?: Expression | PrivateName | Statement, identifiers = new Set<string>()) {
export function getIdentifiersUsage(node?: Expression | SpreadElement | PrivateName | Statement | null, identifiers = new Set<string>()) {
if (!node)
return identifiers

Expand All @@ -171,6 +191,9 @@ export function getIdentifiersUsage(node?: Expression | PrivateName | Statement,
getIdentifiersUsage(node.left, identifiers)
getIdentifiersUsage(node.right, identifiers)
}
else if (node.type === 'UnaryExpression') {
getIdentifiersUsage(node.argument, identifiers)
}
else if (node.type === 'ForOfStatement' || node.type === 'ForInStatement') {
getIdentifiersUsage(node.right, identifiers)
}
Expand All @@ -181,10 +204,20 @@ export function getIdentifiersUsage(node?: Expression | PrivateName | Statement,
}
else if (node.type === 'ObjectExpression') {
node.properties.forEach((prop) => {
// @ts-expect-error
getIdentifiersUsage(prop.value, identifiers)
if (prop.type === 'ObjectProperty')
getIdentifiersUsage(prop.key, identifiers)
else if (prop.type === 'SpreadElement')
getIdentifiersUsage(prop, identifiers)
})
}
else if (node.type === 'ArrayExpression') {
node.elements.forEach((element) => {
getIdentifiersUsage(element, identifiers)
})
}
else if (node.type === 'SpreadElement') {
getIdentifiersUsage(node.argument, identifiers)
}
// else {
// console.log(node)
// }
Expand Down
16 changes: 10 additions & 6 deletions src/transformScriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ export function transformScriptSetup(result: ParseResult) {
// get all identifiers in `<script setup>`
getIdentifiersDeclaration([...imports, ...nodes], identifiers)

const returns = Array.from(identifiers).filter(i => result.template.identifiers.has(i))
const components = Array.from(identifiers).filter(i => result.template.components.has(i)
|| result.template.components.has(camelize(i))
|| result.template.components.has(capitalize(camelize(i))),
)
// filter out identifiers that are used in `<template>`
const returns = Array.from(identifiers)
.filter(Boolean)
.filter(i => result.template.identifiers.has(i))
const components = Array.from(identifiers)
.filter(Boolean)
.filter(i => result.template.components.has(i)
|| result.template.components.has(camelize(i))
|| result.template.components.has(capitalize(camelize(i))),
)

// TODO: apply macros
// append `<script setup>` imports to `<script>`
scriptAst.program.body.unshift(...imports)

Expand Down
24 changes: 24 additions & 0 deletions test/__snapshots__/transform.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,30 @@ export default __sfc_main;
"
`;
exports[`transform fixture ObjectDestructure.vue 1`] = `
"<template>
<div></div>
</template>
<script >
import { toRefs, reactive } from 'vue';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const state = reactive({
data: 'hello'
});
const {
data
} = toRefs(state);
return {};
};
export default __sfc_main;
</script>
"
`;

exports[`transform fixture TemplateOnly.vue 1`] = `
"<template>
<div>
Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/ObjectDestructure.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>
<div></div>
</template>

<script setup>
import { toRefs, reactive } from 'vue'
const state = reactive({ data: 'hello' })
const { data } = toRefs(state)
</script>
11 changes: 9 additions & 2 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ describe('parse', () => {
['var a = 1', ['a']],
['import { foo, t as bar } from "z"', ['foo', 'bar']],
['import foo from "z"', ['foo']],
['import * as foo from "z"', ['foo']],
['function foo(bar) {const a = z}', ['foo']],
['console.log(foo)', []],
['const { data } = toRefs(state)', ['data']],
['const { data, ...args } = bar', ['data', 'args']],
['const { foo: bar } = bar', ['foo']],
['let [a, b,, ...c] = bar', ['a', 'b', 'c']],
]

for (const [input, output] of cases) {
Expand All @@ -30,9 +35,11 @@ describe('parse', () => {
['for (let x in foo) {}', ['foo']],
['for (let [x, idx] of foo) {}', ['foo']],
['a + b', ['a', 'b']],
['a ? "" : b', ['a', 'b']],
['a ? "" : b < c', ['a', 'b', 'c']],
['a == b && a === c', ['a', 'b', 'c']],
['({ a, b })', ['a', 'b']],
['({ a, b, ...args, [c]: 1 })', ['a', 'b', 'args', 'c']],
['!a', ['a']],
['[a,b,...args]', ['a', 'b', 'args']],
]

for (const [input, output] of cases) {
Expand Down

0 comments on commit 1c42237

Please sign in to comment.