【babel】提取vue文件函数注释

提取vue文件函数注释

相关文章

提取vue文件函数注释


一、vue组件的常用语法格式

选项式API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template></template>

<script>
export default {
data() {
return {
// 初始化计数
count: 0,
// 消息
message: "",
};
},
computed: {
// 计算属性,计算计数的两倍
doubledCount() {
return this.count * 2;
},
},
methods: {
// 增加计数的方法
increment() {
this.count++;
},
// 减少计数的方法
decrement() {
this.count--;
},
},
watch: {
// 监听计数的变化
count(newValue, oldValue) {
this.message = `Count changed from ${oldValue} to ${newValue}`;
},
},
mounted() {
// 组件挂载后输出日志
console.log("Component mounted");
},
beforeUnmount() {
// 组件将要卸载前输出日志
console.log("Component will unmount");
},
};
</script>

组合式API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<template></template>

<script>
export default {
data() {
return {
// 初始化计数
count: 0,
// 消息
message: "",
};
},
computed: {
// 计算属性,计算计数的两倍
doubledCount() {
return this.count * 2;
},
},
methods: {
// 增加计数的方法
increment() {
this.count++;
},
// 减少计数的方法
decrement() {
this.count--;
},
},
watch: {
// 监听计数的变化
count(newValue, oldValue) {
this.message = `Count changed from ${oldValue} to ${newValue}`;
},
},
mounted() {
// 组件挂载后输出日志
console.log("Component mounted");
},
beforeUnmount() {
// 组件将要卸载前输出日志
console.log("Component will unmount");
},
};
</script>

通过defineComponent定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<template></template>

<script>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";

export default defineComponent({
props: {
// 定义 props,初始值为 0
initialCount: {
type: Number,
default: 0,
},
},
setup(props) {
// 响应式数据
const count = ref(props.initialCount); // 使用 ref 创建响应式数据 count
const message = ref(""); // 使用 ref 创建响应式数据 message

// 计算属性
const doubledCount = computed(() => count.value * 2);

// 方法:增加 count
function increment() {
count.value++;
}

// 方法:减少 count
function decrement() {
count.value--;
}

// 监听 count 变化
watch(count, (newValue, oldValue) => {
message.value = `Count changed from ${oldValue} to ${newValue}`; // 更新消息
});

// 生命周期钩子:组件挂载时执行
onMounted(() => {
console.log("Component mounted");
});

// 生命周期钩子:组件卸载前执行
onBeforeUnmount(() => {
console.log("Component will unmount");
});

// 返回需要暴露的内容
return {
count,
doubledCount,
increment,
decrement,
message,
};
},
});
</script>

使用setup语法糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template></template>

<script setup>
// 引入需要的函数
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";

// props
const initialCount = props.initialCount; // 接收初始计数的 props

// 响应式数据
const count = ref(initialCount); // 创建一个响应式计数
const message = ref(""); // 创建一个响应式消息

// 计算属性
const doubledCount = computed(() => count.value * 2); // 计算计数的两倍

// 方法:增加计数
function increment() {
count.value++;
}

// 方法:减少计数
function decrement() {
count.value--;
}

// 监听器:监听计数的变化,更新消息
watch(count, (newValue, oldValue) => {
message.value = `Count changed from ${oldValue} to ${newValue}`; // 更新消息
});

// 生命周期钩子:组件挂载时执行
onMounted(() => {
console.log("Component mounted");
});

// 生命周期钩子:组件卸载前执行
onBeforeUnmount(() => {
console.log("Component will unmount");
});
</script>

使用jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { defineComponent, ref, reactive, computed, watch } from 'vue';

export default defineComponent({
setup() {
// 响应式数据
const inputValue = ref('');
const todos = reactive([]);
const filteredTodos = computed(() => {
const filter = inputValue.value.toLowerCase();
return todos.filter(todo => todo.title.toLowerCase().includes(filter));
});

// 方法
const addTodo = () => {
if (inputValue.value.trim() !== '') {
todos.push({ title: inputValue.value, completed: false });
inputValue.value = '';
}
};

const removeTodo = (index) => {
todos.splice(index, 1);
};

// 监听器
watch(filteredTodos, (newTodos, oldTodos) => {
console.log('Todos changed:', newTodos);
});

// 返回渲染函数
return () => (
<div>
<h1>Todo List</h1>
<input
type="text"
placeholder="Add new todo"
value={inputValue.value}
onInput={(e) => inputValue.value = e.target.value}
/>
<button onClick={addTodo}>Add</button>
<ul>
{filteredTodos.value.map((todo, index) => (
<li key={index}>
<span>{todo.title}</span>
<button onClick={() => removeTodo(index)}>Remove</button>
</li>
))}
</ul>
</div>
);
}
});

二、编译大方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
const { parse } = require("@babel/parser"); // 导入 Babel 的解析器
const { parse: vueParse } = require("@vue/compiler-sfc"); // 导入 Vue 单文件组件的解析器
const fs = require("fs"); // 导入文件系统模块

// 检查节点类型是否为函数表达式或箭头函数表达式
const isFunctionExpression = (type) => {
return type === "FunctionExpression" || type === "ArrowFunctionExpression";
};

// 解析 AST,并处理函数节点
const parseAST = (node, handleFunction) => {
// 文件节点,遍历子节点
if (node.type === "File") {
node.program.body.forEach((n) => parseAST(n, handleFunction));
// 导出默认声明节点,解析声明内容
// export default
} else if (node.type === "ExportDefaultDeclaration") {
parseAST(node.declaration, handleFunction);
// 函数调用节点,解析参数
// add(2, 3);
} else if (node.type === "CallExpression") {
node.arguments.forEach((arg) => parseAST(arg, handleFunction));
// 对象方法节点,处理函数节点或 setup 方法节点
// const obj = {
// method() {
// // method body
// }
// };
// obj中的method就是ObjectMethod
} else if (node.type === "ObjectMethod") {
if (node.key.name === "setup") {
// 如果是 setup 方法节点,遍历函数体
node.body.body.forEach((n) => parseAST(n, handleFunction));
} else {
// 其他情况处理为普通函数节点
handleFunction(node.key.name, node.leadingComments);
}
// 对象表达式节点,遍历属性节点
// const obj = {
// key1: value1,
// key2: value2,
// };
// { key1: value1, key2: value2, } 就是 ObjectExpression;
} else if (node.type === "ObjectExpression") {
node.properties.forEach((property) => parseAST(property, handleFunction));
// 函数声明节点,处理函数节点
// function myFunction() {
// // 函数体
// }
// myFunction就是FunctionDeclaration
} else if (node.type === "FunctionDeclaration") {
handleFunction(node.id.name, node.leadingComments);
// 变量声明节点,处理初始化表达式节点
// var x = 10;
// let y = 20;
// const z = 30;
// x\y\z就是VariableDeclaration
} else if (node.type === "VariableDeclaration") {
const declaration = node.declarations[0];
// 如果是函数表达式节点,处理为函数节点
if (declaration.init && isFunctionExpression(declaration.init.type)) {
handleFunction(declaration.id.name, node.leadingComments);
// 其他情况继续解析初始化表达式
} else {
parseAST(declaration.init, handleFunction);
}
// 对象属性节点,处理属性值为函数表达式的情况
// const obj = {
// key1: value1,
// key2: value2,
// // ...
// };
// obj中的key1\key2就是ObjectProperty
} else if (node.type === "ObjectProperty") {
// 如果是函数表达式节点,处理为函数节点
if (isFunctionExpression(node.value.type)) {
handleFunction(node.key.name, node.leadingComments);
// 如果是对象表达式节点,递归解析对象属性
} else if (node.value.type === "ObjectExpression") {
node.value.properties.forEach((property) =>
parseAST(property, handleFunction)
);
}
}
};

// 读取文件内容
const getFileContent = (filePath) => {
return fs.readFileSync(filePath, "utf8");
};

// 获取 Vue 单文件组件的脚本内容
const getScriptContent = (descriptor) => {
return descriptor.script
? descriptor.script.content
: descriptor.scriptSetup
? descriptor.scriptSetup.content
: descriptor.source
? descriptor.source
: "";
};

// 导出函数,用于解析 Vue 文件并提取函数注释
module.exports.parseVueComments = (filePath, handleFunction) => {
// 获取文件内容
const fileContent = getFileContent(filePath);
// 解析 Vue 文件
const { descriptor } = vueParse(fileContent);
// 使用 Babel 解析 Vue 文件的脚本内容,并使用 Vue JSX 插件处理代码
const scriptContent = parse(getScriptContent(descriptor), {
sourceType: "module",
plugins: ["jsx"],
});
// 解析 AST 并提取函数注释
parseAST(scriptContent, handleFunction);
};

三、调用编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const path = require("path");
const { parseVueComments } = require("./parseAST");

// 提取函数的注释
const handleFunction = (name, leadingComments = []) => {
if (leadingComments.length > 0) {
const comments = leadingComments
.map((comment) =>
comment.value
.trim()
.split("\n")
.map((line) => line.trim().replace(/^\*+\s*/, ""))
.filter(Boolean)
)
.flat();
console.error(name, comments);
}
};

const arr = [
"vue 选项式api.vue",
"vue 组合式api.vue",
"vue 组合式api setup语法糖.vue",
"vue 组合式api defineComponent.vue",
"vue 组合式api defineComponent.jsx",
];

// 读取文件内容
const getFilePath = (fileName) => {
return path.resolve(__dirname, "example", fileName);
};

for (const o of arr) {
// 示例调用
const filePath = getFilePath(o);
console.error("目标文件:", filePath);
parseVueComments(filePath, handleFunction);
}

输出结果


喜欢这篇文章?打赏一下支持一下作者吧!
【babel】提取vue文件函数注释
https://www.cccccl.com/20230115/babel/提取vue和jsx文件函数注释/
作者
Jeffrey
发布于
2023年1月15日
许可协议