Skip to content

Commit

Permalink
feat: support options-setup
Browse files Browse the repository at this point in the history
  • Loading branch information
zcf0508 committed Aug 24, 2023
1 parent f33a620 commit 6b7eb7f
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ module.exports = {
},
ignorePatterns: [
'**.min.*',
'test/TestComponent.vue',
'test/**/TestComponent.vue',
'*.d.ts',
'dist',
'output',
Expand Down
4 changes: 4 additions & 0 deletions src/analyze/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export {
export {
analyze as analyzeSetupScript,
} from './setupScript';

export {
analyze as analyzeOptions,
} from './options';
192 changes: 192 additions & 0 deletions src/analyze/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { babelParse } from '@vue/compiler-sfc';
import _traverse from '@babel/traverse';
const traverse: typeof _traverse =
//@ts-ignore
_traverse.default?.default || _traverse.default || _traverse;

export function analyze(
content: string
) {
// console.log(content);
const ast = babelParse(content, { sourceType: 'module',
plugins: [
'typescript',
],
});

// ---

const graph = {
nodes: new Set<string>(),
edges: new Map<string, Set<string>>(),
};

traverse(ast, {
ExportDefaultDeclaration(path) {
// export default {}
if(path.node.declaration.type === 'ObjectExpression') {
path.node.declaration.properties.forEach(prop => {
if(prop.type === 'ObjectProperty'
&& prop.key.type === 'Identifier'
&& prop.key.name === 'props'
&& prop.value.type === 'ObjectExpression'
) {
const propsNodes = prop.value.properties;
propsNodes.forEach(propNode => {
// @ts-ignore
const name = propNode.key.name;
if(name && !graph.nodes.has(name)) {
graph.nodes.add(name);
if(!graph.edges.get(name)) {
graph.edges.set(name, new Set());
}
}
});
}

if(prop.type === 'ObjectMethod'
&& prop.key.type === 'Identifier'
&& prop.key.name === 'setup'
) {

const setupNode = prop.body.body;

const declarNodes = new Set<string>();
const declarEdges = new Map<string, Set<string>>();

setupNode.forEach(item => {
if(item.type !== 'ReturnStatement') {
if(item.type === 'VariableDeclaration') {
item.declarations.forEach(declar => {

if(declar.type === 'VariableDeclarator'
&& declar.id.type === 'Identifier'
) {
const name = declar.id.name;
if(name && !declarNodes.has(name)) {
declarNodes.add(name);
declarEdges.set(name, new Set());
}
}
});
}
if(item.type === 'FunctionDeclaration') {
if(item.id?.type === 'Identifier') {
const name = item.id.name;
if(name && !declarNodes.has(name)) {
declarNodes.add(name);
declarEdges.set(name, new Set());
}
}
}

}
// if(item.type === 'ReturnStatement'
// && item.argument?.type === 'ObjectExpression'
// ) {

// }
});

setupNode.forEach(item => {
if(item.type !== 'ReturnStatement') {
if(item.type === 'VariableDeclaration') {
item.declarations.forEach(declar => {

if(declar.type === 'VariableDeclarator'
&& declar.id.type === 'Identifier'
&& declar.init?.type === 'CallExpression'
&& declar.init?.callee.type === 'Identifier'
&& ['computed'].includes(declar.init?.callee.name)
) {
const name = declar.id.name;
traverse(declar, {
Identifier(path) {
if(declarNodes.has(path.node.name) && path.node.name !== name) {
declarEdges.get(name)?.add(path.node.name);
}
},
},path.scope, path);
}
});
}
if(['CallExpression', 'ArrowFunctionExpression', 'FunctionDeclaration'].includes(item.type)) {
// @ts-ignore
const name = item.id?.name;
traverse(item, {
Identifier(path) {
if(declarNodes.has(path.node.name) && path.node.name !== name) {
declarEdges.get(name)?.add(path.node.name);
}
},
}, path.scope, path);
}
}
});

setupNode.forEach(item => {
if(item.type === 'ReturnStatement'
&& item.argument?.type === 'ObjectExpression'
) {
item.argument.properties.forEach(prop => {
if(prop.type === 'ObjectProperty') {
if(
prop.key.type === 'Identifier'
&& prop.value.type === 'Identifier'
) {
const name = prop.key.name;
const valuename = prop.value.name;
if(name && !graph.nodes.has(name)) {
graph.nodes.add(name);
if(!graph.edges.has(name)) {
graph.edges.set(name, new Set());
}
if(
declarEdges.get(valuename)
&& declarEdges.get(valuename)?.size
) {
declarEdges.get(valuename)?.forEach(item => {
graph.edges.get(name)?.add(item);
});
}
}
}
if(
prop.key.type === 'Identifier'
&& ['CallExpression', 'ArrowFunctionExpression', 'FunctionDeclaration'].includes(prop.value.type)
) {
const name = prop.key.name;
if(name && !graph.nodes.has(name)) {
graph.nodes.add(name);
traverse(prop.value, {
Identifier(path) {
if(declarNodes.has(path.node.name) && path.node.name !== name) {
graph.edges.get(name)?.add(path.node.name);
}
},
}, path.scope, path);
}
}
}
});
}
});

}
});
}
// export default defineComponent({})
if(path.node.declaration.type === 'CallExpression'
&& path.node.declaration.callee.type === 'Identifier'
&& path.node.declaration.callee.name === 'defineComponent'
&& path.node.declaration.arguments[0].type === 'ObjectExpression'
) {
const code = path.node.declaration.arguments[0];

}
},
});


return graph;
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { parse } from '@vue/compiler-sfc';
export { analyzeTemplate, analyzeSetupScript } from './analyze';
export { analyzeTemplate, analyzeSetupScript, analyzeOptions } from './analyze';
export { getVisData } from './vis';
12 changes: 12 additions & 0 deletions test/options/TestComponent.graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const edges = new Map<string, Set<string>>();

edges.set('msg', new Set([]));
edges.set('data', new Set([]));
edges.set('count', new Set([]));
edges.set('plus', new Set([]));
edges.set('add', new Set(['data']));

export const graph = {
nodes: new Set(['msg', 'data', 'count', 'plus', 'add']),
edges,
};
File renamed without changes.
104 changes: 104 additions & 0 deletions test/options/TestComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<h1>{{ msg }}</h1>

<p>
Recommended IDE setup:
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
+
<a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur" target="_blank"> Vetur </a>
or
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
(if using
<code>&lt;script setup&gt;</code>)
</p>

<p>See <code>README.md</code> for more information.</p>

<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank"> Vite Docs </a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
</p>

<button type="button" @click="plus">
count is: {{ count }}
</button>
<p class="my-2">
<label for="number_input">
<input
id="number_input"
v-model="number"
w:p="l-2"
w:border="~ gray-100"
w:appearance="none"
w:outline="focus:none"
type="number"
>
</label>
<button
w:m="l-2"
w:p="x-2"
w:bg="green-500"
w:text="white"
w:rounded="~"
@click="add"
>
ADD
</button>
</p>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</template>

<script lang="ts">
export default {
name: 'TestComponent',
props: {
msg: {
type: String,
required: true,
},
},
setup() {
const data = reactive({
number: 0,
});
const count = computed(() => counterStore.count);
function plus() {
counterStore.increment();
}
function add() {
counterStore.add(Number(data.number));
}
return {
data,
count,
plus,
add,
}
}
}
</script>

<style scoped>
a {
color: #42b983;
}
label {
margin: 0 0.5em;
font-weight: bold;
}
code {
background-color: #eee;
padding: 2px 4px;
border-radius: 4px;
color: #304455;
}
</style>
19 changes: 19 additions & 0 deletions test/options/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as fs from 'node:fs';
import path from 'node:path';
import { parse, analyzeTemplate, analyzeOptions } from '@/index';

import {graph as graphRes} from './TestComponent.graph';
import {nodes as nodesRes} from './TestComponent.nodes';

describe('test analyze', () => {
const source = fs.readFileSync(path.resolve(__dirname, './TestComponent.vue'), 'utf-8');
const sfc = parse(source);
it('test analyze options', () => {
const graph = analyzeOptions(sfc.descriptor.script?.content!);
expect(graph).toEqual(graphRes);
});
it('test analyze template', () => {
const nodes = analyzeTemplate(sfc.descriptor.template!.content);
expect(nodes).toEqual(nodesRes);
});
});
File renamed without changes.
1 change: 1 addition & 0 deletions test/setup-block/TestComponent.nodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const nodes = new Set(['msg', 'plus', 'count', 'number', 'add']);
File renamed without changes.
File renamed without changes.

0 comments on commit 6b7eb7f

Please sign in to comment.