vue-hook-optimizer
Version:
a tool that helps refactor and optimize hook abstractions in Vue components
1,079 lines (1,073 loc) • 98.2 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
NodeType: () => NodeType,
SuggestionType: () => SuggestionType,
analyzeOptions: () => analyze2,
analyzeSetupScript: () => analyze,
analyzeStyle: () => analyze3,
analyzeTemplate: () => analyze4,
analyzeTsx: () => analyze5,
gen: () => gen,
getMermaidText: () => getMermaidText,
getVisData: () => getVisData,
parse: () => import_compiler_sfc5.parse
});
module.exports = __toCommonJS(src_exports);
// src/analyze/options.ts
var import_traverse2 = __toESM(require("@babel/traverse"));
var import_compiler_sfc2 = require("@vue/compiler-sfc");
// src/analyze/setupScript.ts
var import_traverse = __toESM(require("@babel/traverse"));
var import_compiler_sfc = require("@vue/compiler-sfc");
// src/analyze/utils.ts
var NodeType = /* @__PURE__ */ ((NodeType3) => {
NodeType3["var"] = "var";
NodeType3["fun"] = "fun";
return NodeType3;
})(NodeType || {});
var NodeCollection = class {
lineOffset = 0;
addInfo = true;
constructor(_lineOffset = 0, _addInfo = true) {
this.lineOffset = _lineOffset;
this.addInfo = _addInfo;
}
nodes = /* @__PURE__ */ new Map();
addNode(label, node, options = { isComputed: false, isMethod: false, comment: "" }) {
if (this.nodes.has(label)) {
return;
}
if (!options.isComputed && (node.type === "VariableDeclarator" && [
"ArrowFunctionExpression",
"FunctionDeclaration",
"FunctionExpression"
].includes(node.init?.type || "") || node.type === "ObjectProperty" && [
"ArrowFunctionExpression",
"FunctionDeclaration",
"FunctionExpression"
].includes(node.value?.type || "") || node.type === "FunctionDeclaration" || node.type === "ObjectMethod" || node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") || options.isMethod) {
this.nodes.set(label, {
label,
type: "fun" /* fun */,
...this.addInfo ? {
info: {
line: (node.loc?.start.line || 1) - 1 + this.lineOffset,
column: node.loc?.start.column || 0,
...options.comment ? { comment: options.comment } : {}
}
} : {}
});
} else {
this.nodes.set(label, {
label,
type: "var" /* var */,
...this.addInfo ? {
info: {
line: (node.loc?.start.line || 1) - 1 + this.lineOffset,
column: node.loc?.start.column || 0,
...options.comment ? { comment: options.comment } : {}
}
} : {}
});
}
}
addTypedNode(label, node) {
this.nodes.set(label, {
label,
type: node.type,
...this.addInfo ? {
info: {
...node.info || {}
}
} : {}
});
}
getNode(label) {
return this.nodes.get(label);
}
map(graph) {
const nodes = new Set(Array.from(graph.nodes).map((node) => {
return this.nodes.get(node);
}).filter((node) => !!node));
const edges = new Map(Array.from(graph.edges).map(([from, to]) => {
const labelMap = /* @__PURE__ */ new Map();
for (const item of to) {
const node = this.nodes.get(item.label);
if (!node) {
continue;
}
const existing = labelMap.get(item.label);
if (!existing || existing.type === "get" && item.type === "set") {
labelMap.set(item.label, { node, type: item.type });
}
}
const items = Array.from(labelMap.values());
return [this.nodes.get(from), new Set(items)];
}));
return {
nodes,
edges
};
}
};
function getComment(node) {
let comment = "";
node.leadingComments?.forEach((_comment) => {
if (_comment.loc.end.line > node.loc.start.line) {
return;
}
if (_comment.value.trim().startsWith("*")) {
comment += `${_comment.value.trim().replace(/^\s*\*+\s*\**/gm, "").trim()}
`;
}
});
node.trailingComments?.forEach((_comment) => {
if (_comment.loc.end.line > node.loc.start.line) {
return;
}
if (_comment.value.trim().startsWith("*")) {
comment += `${_comment.value.trim().replace(/^\s*\*+\s*\**/gm, "").trim()}
`;
} else {
comment += `${_comment.value.trim()}
`;
}
});
return comment.trim();
}
function isWritingNode(path) {
const assignParent = path.findParent((p) => p.isAssignmentExpression());
if (assignParent) {
const leftNode = assignParent.node.left;
if (leftNode.start != null && path.node.start >= leftNode.start && path.node.end <= leftNode.end) {
return true;
}
}
const updateParent = path.findParent((p) => p.isUpdateExpression());
if (updateParent) {
const argNode = updateParent.node.argument;
if (argNode.start != null && path.node.start >= argNode.start && path.node.end <= argNode.end) {
return true;
}
}
return false;
}
function isCallingNode(path) {
const parent = path.parentPath;
if (parent && parent.isCallExpression()) {
return parent.node.callee === path.node;
}
return false;
}
function getRelationType(path) {
if (path.node.type === "Identifier" && isCallingNode(path)) {
return "call";
}
if (isWritingNode(path)) {
return "set";
}
return "get";
}
// src/analyze/setupScript.ts
var traverse = import_traverse.default.default?.default || import_traverse.default.default || import_traverse.default;
var ignoreFunctionsName = ["defineProps", "defineEmits", "withDefaults"];
var watchHooks = [
"watch",
// from `@vueuse/core`
"watchArray",
"watchAtMost",
"watchDebounced",
"watchDeep",
"watchIgnorable",
"watchImmediate",
"watchOnce",
"watchPausable",
"watchThrottled",
"watchTriggerable",
"watchWithFilter"
];
function processSetup(ast, parentScope, parentPath, _spread, _lineOffset = 0) {
const spread = _spread || [];
const nodeCollection = new NodeCollection(_lineOffset);
const graph = {
nodes: /* @__PURE__ */ new Set(),
edges: /* @__PURE__ */ new Map(),
spread: /* @__PURE__ */ new Map()
};
traverse(ast, {
VariableDeclaration(path) {
path.node.declarations.forEach((declaration) => {
if (declaration.id.type === "ArrayPattern") {
declaration.id.elements.forEach((element) => {
if (element?.type === "Identifier") {
const name = element.name;
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent) && !(declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && ignoreFunctionsName.includes(declaration.init?.callee.name))) {
graph.nodes.add(name);
nodeCollection.addNode(name, element, {
comment: getComment(path.node)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
if (element?.type === "RestElement" && element.argument.type === "Identifier") {
const name = element.argument.name;
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent) && !(declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && ignoreFunctionsName.includes(declaration.init?.callee.name))) {
graph.nodes.add(name);
nodeCollection.addNode(name, element.argument, {
comment: getComment(path.node)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
if (declaration.id.type === "ObjectPattern") {
declaration.id.properties.forEach((property) => {
if (property.type === "ObjectProperty" && property.value.type === "Identifier") {
const name = property.value.name;
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent) && !(declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && ignoreFunctionsName.includes(declaration.init?.callee.name))) {
graph.nodes.add(name);
nodeCollection.addNode(name, property.value, {
comment: getComment(property)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
if (property.type === "RestElement" && property.argument.type === "Identifier") {
const name = property.argument.name;
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent) && !(declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && ignoreFunctionsName.includes(declaration.init?.callee.name))) {
graph.nodes.add(name);
nodeCollection.addNode(name, property.argument, {
comment: getComment(property)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
if (declaration.id?.type === "Identifier") {
const name = declaration.id.name;
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent) && !(declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && ignoreFunctionsName.includes(declaration.init?.callee.name))) {
graph.nodes.add(name);
nodeCollection.addNode(name, declaration, {
comment: getComment(path.node)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
if (spread.includes(name)) {
if (declaration.init?.type === "ObjectExpression") {
declaration.init?.properties.forEach((prop) => {
if ((prop.type === "ObjectProperty" || prop.type === "ObjectMethod") && prop.key.type === "Identifier") {
const keyName = prop.key.name;
graph.nodes.add(keyName);
nodeCollection.addNode(keyName, prop, {
comment: getComment(prop)
});
if (!graph.edges.get(keyName)) {
graph.edges.set(keyName, /* @__PURE__ */ new Set());
}
if (graph.spread.has(name)) {
graph.spread.get(name)?.add(keyName);
} else {
graph.spread.set(name, /* @__PURE__ */ new Set([keyName]));
}
} else if (prop.type === "SpreadElement") {
console.warn("not support spread in spread");
}
});
}
if (declaration.init?.type === "CallExpression" && declaration.init?.callee.type === "Identifier" && declaration.init?.callee.name === "reactive") {
const arg = declaration.init?.arguments[0];
if (arg.type === "ObjectExpression") {
arg.properties.forEach((prop) => {
if ((prop.type === "ObjectProperty" || prop.type === "ObjectMethod") && prop.key.type === "Identifier") {
const keyName = prop.key.name;
graph.nodes.add(keyName);
nodeCollection.addNode(keyName, prop, {
comment: getComment(prop)
});
if (!graph.edges.get(keyName)) {
graph.edges.set(keyName, /* @__PURE__ */ new Set());
}
if (graph.spread.has(name)) {
graph.spread.get(name)?.add(keyName);
} else {
graph.spread.set(name, /* @__PURE__ */ new Set([keyName]));
}
} else if (prop.type === "SpreadElement") {
console.warn("not support spread in spread");
}
});
}
}
}
}
}
});
},
FunctionDeclaration(path) {
const name = path.node.id?.name;
if (name) {
const binding = path.scope.getBinding(name);
if (binding && (path.parent.type === "Program" || parentPath?.type === "ObjectMethod" && parentPath.body === path.parent)) {
graph.nodes.add(name);
nodeCollection.addNode(name, path.node.id, {
isMethod: true,
comment: getComment(path.node)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
}
}, parentScope, parentPath);
function traverseHooks(node, patentScope) {
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.type === "Identifier" || node.type === "CallExpression" && node.callee.type === "Identifier") {
const hookName = (() => {
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.type === "Identifier") {
return node.expression.callee.name;
}
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
return node.callee.name;
}
})() || "";
if (!hookName) {
return;
}
const hookBinding = patentScope.getBinding(hookName);
if (!(hookBinding === void 0 || hookBinding?.scope.block.type === "Program" || parentScope === hookBinding?.scope)) {
return;
}
const expression = node.type === "ExpressionStatement" ? node.expression : node;
const watchArgs = /* @__PURE__ */ new Set();
if (hookName === "provide") {
traverse(expression, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
const _node = nodeCollection.getNode(path1.node.name);
if (_node?.info?.used) {
_node?.info?.used?.add(hookName);
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set([hookName])
};
}
}
}
}, patentScope, node);
} else if (watchHooks.includes(hookName)) {
if (expression.arguments[0].type === "Identifier") {
const binding = patentScope.getBinding(expression.arguments[0].name);
if (graph.nodes.has(expression.arguments[0].name) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
watchArgs.add(expression.arguments[0]);
}
} else {
traverse(expression.arguments[0], {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
watchArgs.add(path1.node);
}
}
}, patentScope, node);
}
} else if (hookName === "useEffect" && expression.arguments[1].type === "ArrayExpression") {
traverse(expression.arguments[1], {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
watchArgs.add(path1.node);
}
}
}, patentScope, node);
}
expression.arguments.forEach((argNode, index) => {
if (watchHooks.includes(hookName) && index === 0 && argNode.type === "Identifier") {
const _node = nodeCollection.getNode(argNode.name);
if (_node?.info?.used) {
_node?.info?.used?.add(hookName);
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set([hookName])
};
}
return;
}
if (argNode.type === "Identifier") {
const binding = patentScope.getBinding(argNode.name);
if (graph.nodes.has(argNode.name) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
const _node = nodeCollection.getNode(argNode.name);
if (_node?.info?.used) {
_node?.info?.used?.add(hookName);
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set([hookName])
};
}
}
} else {
traverse(argNode, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
if ([...watchHooks, "useEffect"].includes(hookName) && watchArgs.size > 0) {
const watchArgsNames = Array.from(watchArgs).map((arg) => arg.name);
watchArgs.forEach((watchArg) => {
if (!watchArgsNames.includes(path1.node.name)) {
graph.edges.get(watchArg.name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
});
}
const _node = nodeCollection.getNode(path1.node.name);
if (_node?.info?.used) {
_node?.info?.used?.add(hookName);
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set([hookName])
};
}
}
}
}, patentScope, node);
}
});
}
}
traverse(ast, {
FunctionDeclaration(path) {
const name = path.node.id?.name;
if (name && graph.nodes.has(name)) {
traverse(path.node.body, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
},
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
},
VariableDeclarator(path) {
if (path.node.init) {
if (path.node.id.type === "ArrayPattern") {
path.node.id.elements.forEach((element) => {
if (element?.type === "Identifier") {
const name = element.name;
if (name && graph.nodes.has(name) && path.node.init?.type === "CallExpression") {
traverse(path.node.init, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
},
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
}
});
} else if (path.node.id.type === "ObjectPattern") {
path.node.id.properties.forEach((property) => {
if (property.type === "ObjectProperty" && property.value.type === "Identifier") {
const name = property.value.name;
if (name && graph.nodes.has(name) && path.node.init) {
traverse(path.node.init, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
},
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
}
});
} else if ([
"CallExpression",
"ArrowFunctionExpression",
"FunctionDeclaration"
].includes(path.node.init.type) && path.node.id.type === "Identifier") {
if (path.node.init.type === "CallExpression" && path.node.init.callee.type === "Identifier" && [...watchHooks, "watchEffect"].includes(path.node.init.callee.name)) {
traverseHooks(path.node.init, path.scope);
}
const name = path.node.id?.name;
if (name && graph.nodes.has(name)) {
traverse(path.node.init, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
},
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
} else if (path.node.id.type === "Identifier") {
const name = path.node.id.name;
if (path.node.init.type === "Identifier") {
const binding = path.scope.getBinding(path.node.init.name);
if (graph.nodes.has(path.node.init.name) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path.node.init.name,
type: getRelationType(path)
});
}
} else {
traverse(path.node.init, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
}
}, path.scope, path);
}
}
}
},
ObjectMethod(path) {
if (path.node.key.type === "Identifier" && graph.nodes.has(path.node.key.name)) {
const name = path.node.key.name;
traverse(path.node.body, {
Identifier(path1) {
const binding = path1.scope.getBinding(path1.node.name);
if (graph.nodes.has(path1.node.name) && (path1.parent.type !== "MemberExpression" && path1.parent.type !== "OptionalMemberExpression" || path1.parent.object === path1.node) && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.name,
type: getRelationType(path1)
});
}
},
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
},
ObjectProperty(path) {
if (path.node.key.type === "Identifier" && graph.nodes.has(path.node.key.name)) {
const name = path.node.key.name;
traverse(path.node.value, {
MemberExpression(path1) {
if (path1.node.object.type === "Identifier" && spread.includes(path1.node.object.name)) {
const binding = path1.scope.getBinding(path1.node.object.name);
if (spread.includes(path1.node.object.name) && path1.node.property.type === "Identifier" && (binding?.scope.block.type === "Program" || parentScope === binding?.scope)) {
graph.edges.get(name)?.add({
label: path1.node.property.name,
type: getRelationType(path1)
});
}
}
}
}, path.scope, path);
}
},
ExpressionStatement(path) {
if (path.type === "ExpressionStatement" && path.node.expression.type === "CallExpression" && path.node.expression.callee.type === "Identifier") {
const name = path.node.expression.callee.name;
if (graph.nodes.has(name) && path.scope.block.type === "Program") {
const _node = nodeCollection.getNode(name);
if (_node?.info?.used) {
_node?.info?.used?.add("Call Expression");
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set(["Call Expression"])
};
}
} else {
traverseHooks(path.node.expression, path.scope);
}
}
if (path.type === "ExpressionStatement" && path.node.expression.type === "AssignmentExpression" && path.node.expression.right.type === "CallExpression" && path.node.expression.right.callee.type === "Identifier") {
traverseHooks(path.node.expression.right, path.scope);
}
}
}, parentScope, parentPath);
return {
graph,
nodeCollection
};
}
function analyze(content, lineOffset = 0, jsx = false) {
const ast = (0, import_compiler_sfc.babelParse)(content, { sourceType: "module", plugins: [
"typescript",
...jsx ? ["jsx"] : []
] });
const { graph, nodeCollection } = processSetup(ast, void 0, void 0, void 0, lineOffset);
return nodeCollection.map(graph);
}
// src/analyze/options.ts
var traverse2 = import_traverse2.default.default?.default || import_traverse2.default.default || import_traverse2.default;
var vueLifeCycleHooks = [
"beforeCreate",
"created",
"beforeMount",
"mounted",
"beforeUpdate",
"updated",
"beforeDestroy",
"destroyed",
"activated",
"deactivated",
"errorCaptured",
"renderTracked",
"renderTriggered",
"provide"
];
function analyze2(content, lineOffset = 0, jsx = false) {
const ast = (0, import_compiler_sfc2.babelParse)(content, { sourceType: "module", plugins: [
"typescript",
...jsx ? ["jsx"] : []
] });
let nodeCollection = new NodeCollection(lineOffset);
const tNodes = /* @__PURE__ */ new Map();
const graph = {
nodes: /* @__PURE__ */ new Set(),
edges: /* @__PURE__ */ new Map()
};
const nodesUsedInTemplate = /* @__PURE__ */ new Set();
function process(node, path) {
traverse2(node, {
ObjectProperty(path1) {
if (path.node.declaration.type === "ObjectExpression" && path1.parent === path.node.declaration || path.node.declaration.type === "CallExpression" && path1.parent === path.node.declaration.arguments[0]) {
if (path1.node.key.type === "Identifier" && path1.node.key.name === "data" && (path1.node.value.type === "ArrowFunctionExpression" || path1.node.value.type === "FunctionExpression")) {
const dataNode = path1.node.value;
traverse2(dataNode, {
ReturnStatement(path2) {
if (path2.parent === dataNode.body) {
if (path2.node.argument?.type === "ObjectExpression") {
path2.node.argument.properties.forEach((prop) => {
if (prop.type === "ObjectProperty") {
if (prop.key.type === "Identifier") {
const name = prop.key.name;
graph.nodes.add(name);
tNodes.set(name, prop.key);
nodeCollection.addNode(name, prop, {
comment: getComment(prop)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
}
}
}, path1.scope, path1);
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "computed") {
const computedNode = path1.node;
if (computedNode.value.type === "ObjectExpression") {
computedNode.value.properties.forEach((prop) => {
if (prop.type === "ObjectProperty" || prop.type === "ObjectMethod") {
if (prop.key.type === "Identifier") {
const name = prop.key.name;
graph.nodes.add(name);
tNodes.set(name, prop.key);
nodeCollection.addNode(name, prop, {
isComputed: true,
comment: getComment(prop)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "methods") {
const methodsNode = path1.node;
if (methodsNode.value.type === "ObjectExpression") {
methodsNode.value.properties.forEach((prop) => {
if (prop.type === "ObjectProperty" || prop.type === "ObjectMethod") {
if (prop.key.type === "Identifier") {
const name = prop.key.name;
graph.nodes.add(name);
tNodes.set(name, prop.key);
nodeCollection.addNode(name, prop, {
isMethod: true,
comment: getComment(prop)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "render" && (path1.node.value.type === "ArrowFunctionExpression" || path1.node.value.type === "FunctionExpression")) {
traverse2(path1.node.value, {
ReturnStatement(path2) {
const templateNode = path2.node;
traverse2(templateNode, {
MemberExpression(path3) {
if (path3.node.object && path3.node.object.type === "ThisExpression") {
if (path3.node.property && path3.node.property.type === "Identifier") {
nodesUsedInTemplate.add(path3.node.property.name);
}
}
}
}, path2.scope, path2);
}
}, path1.scope, path1);
}
}
},
ObjectMethod(path1) {
if (path.node.declaration.type === "ObjectExpression" && path1.parent === path.node.declaration || path.node.declaration.type === "CallExpression" && path1.parent === path.node.declaration.arguments[0]) {
if (path1.node.key.type === "Identifier" && path1.node.key.name === "setup") {
const setupNode = path1.node;
const spread = [];
traverse2(setupNode, {
ReturnStatement(path2) {
if (path2.node.argument?.type === "ObjectExpression") {
const returnNode = path2.node.argument;
traverse2(returnNode, {
SpreadElement(path3) {
if (path3.node.argument.type === "CallExpression" && path3.node.argument.callee.type === "Identifier" && path3.node.argument.callee.name === "toRefs" && path3.node.argument.arguments[0].type === "Identifier") {
spread.push(path3.node.argument.arguments[0].name);
} else if (path3.node.argument.type === "Identifier") {
spread.push(path3.node.argument.name);
}
}
}, path2.scope, path2);
}
if (path2.node.argument?.type === "FunctionExpression" || path2.node.argument?.type === "ArrowFunctionExpression") {
const templateNode = path2.node.argument.body;
traverse2(templateNode, {
Identifier(path3) {
const binding = path3.scope.getBinding(path3.node.name);
if (binding?.scope === path1.scope) {
nodesUsedInTemplate.add(path3.node.name);
}
},
JSXIdentifier(path3) {
const binding = path3.scope.getBinding(path3.node.name);
if (binding?.scope === path1.scope) {
nodesUsedInTemplate.add(path3.node.name);
}
}
}, path2.scope, path2);
}
}
}, path1.scope, path1);
const {
graph: {
nodes: tempNodes,
edges: tempEdges,
spread: tempSpread
},
nodeCollection: tempNodeCollection
} = processSetup(setupNode, path1.scope, setupNode, spread, lineOffset);
traverse2(setupNode, {
ReturnStatement(path2) {
if (path2.scope !== path1.scope) {
return;
}
if (path2.node.argument?.type === "ObjectExpression") {
const returnNode = path2.node.argument;
traverse2(returnNode, {
ObjectProperty(path3) {
if (path3.parent === returnNode) {
if (path3.node.key.type === "Identifier" && path3.node.value.type === "Identifier" && tempNodes.has(path3.node.value.name)) {
const valName = path3.node.value.name;
if (!graph.nodes.has(valName)) {
graph.nodes.add(valName);
tNodes.set(valName, path3.node.value);
nodeCollection.addTypedNode(
valName,
tempNodeCollection.nodes.get(valName)
);
}
if (!graph.edges.has(valName)) {
graph.edges.set(valName, /* @__PURE__ */ new Set([...Array.from(
tempEdges.get(valName) || /* @__PURE__ */ new Set()
)]));
}
const name = path3.node.key.name;
if (name !== valName) {
graph.nodes.add(name);
tNodes.set(name, path3.node.key);
nodeCollection.addNode(name, path3.node.key, {
comment: getComment(path3.node)
});
graph.edges.set(name, /* @__PURE__ */ new Set([{
label: valName,
type: getRelationType(path3)
}]));
}
}
}
},
SpreadElement(path3) {
if (path3.node.argument.type === "CallExpression" && path3.node.argument.callee.type === "Identifier" && path3.node.argument.callee.name === "toRefs" && path3.node.argument.arguments[0].type === "Identifier" && tempSpread.get(path3.node.argument.arguments[0].name)) {
tempSpread.get(path3.node.argument.arguments[0].name)?.forEach((name) => {
graph.nodes.add(name);
tNodes.set(name, path3.node.argument.arguments[0]);
nodeCollection.addTypedNode(name, tempNodeCollection.nodes.get(name));
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
tempEdges.get(name)?.forEach((edge) => {
graph.edges.get(name)?.add(edge);
});
}
});
} else if (path3.node.argument.type === "Identifier" && tempSpread.get(path3.node.argument.name)) {
tempSpread.get(path3.node.argument.name)?.forEach((name) => {
graph.nodes.add(name);
tNodes.set(name, path3.node.argument);
nodeCollection.addTypedNode(name, tempNodeCollection.nodes.get(name));
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
tempEdges.get(name)?.forEach((edge) => {
graph.edges.get(name)?.add(edge);
});
}
});
}
}
}, path2.scope, path2);
} else {
graph.edges = tempEdges;
graph.nodes = tempNodes;
nodeCollection = tempNodeCollection;
}
}
}, path1.scope, path1);
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "data") {
const dataNode = path1.node;
traverse2(dataNode, {
ReturnStatement(path2) {
if (path2.parent === dataNode.body) {
if (path2.node.argument?.type === "ObjectExpression") {
path2.node.argument.properties.forEach((prop) => {
if (prop.type === "ObjectProperty") {
if (prop.key.type === "Identifier") {
const name = prop.key.name;
graph.nodes.add(name);
tNodes.set(name, prop.key);
nodeCollection.addNode(name, prop, {
comment: getComment(prop)
});
if (!graph.edges.get(name)) {
graph.edges.set(name, /* @__PURE__ */ new Set());
}
}
}
});
}
}
}
}, path1.scope, path1);
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "render") {
traverse2(path1.node, {
ReturnStatement(path2) {
const templateNode = path2.node;
traverse2(templateNode, {
MemberExpression(path3) {
if (path3.node.object && path3.node.object.type === "ThisExpression") {
if (path3.node.property && path3.node.property.type === "Identifier") {
nodesUsedInTemplate.add(path3.node.property.name);
}
}
}
}, path2.scope, path2);
}
}, path1.scope, path1);
}
}
}
}, path.scope, path);
traverse2(node, {
ObjectMethod(path1) {
if (path.node.declaration.type === "ObjectExpression" && path1.parent === path.node.declaration || path.node.declaration.type === "CallExpression" && path1.parent === path.node.declaration.arguments[0]) {
if (path1.node.key.type === "Identifier" && vueLifeCycleHooks.includes(path1.node.key.name)) {
const hookName = path1.node.key.name;
traverse2(path1.node.body, {
MemberExpression(path2) {
if (path2.node.object.type === "ThisExpression" && path2.node.property.type === "Identifier") {
const _node = nodeCollection.getNode(path2.node.property.name);
if (_node?.info?.used) {
_node?.info?.used?.add(hookName);
} else if (_node) {
_node.info = {
..._node?.info,
used: /* @__PURE__ */ new Set([hookName])
};
}
}
}
}, path1.scope, path1);
}
}
},
ObjectProperty(path1) {
if (path.node.declaration.type === "ObjectExpression" && path1.parent === path.node.declaration || path.node.declaration.type === "CallExpression" && path1.parent === path.node.declaration.arguments[0]) {
if (path1.node.key.type === "Identifier" && path1.node.key.name === "computed") {
const computedNode = path1.node;
if (computedNode.value.type === "ObjectExpression") {
computedNode.value.properties.forEach((prop) => {
if (prop.type === "ObjectMethod" && prop.key.type === "Identifier") {
const name = prop.key.name;
traverse2(prop, {
MemberExpression(path2) {
if (path2.node.object.type === "ThisExpression" && path2.node.property.type === "Identifier") {
graph.edges.get(name)?.add({
label: path2.node.property.name,
type: getRelationType(path2)
});
}
}
}, path1.scope, path1);
}
if (prop.type === "ObjectProperty" && prop.key.type === "Identifier" && prop.value.type === "ObjectExpression") {
const name = prop.key.name;
prop.value.properties.forEach((prop1) => {
if (prop1.type === "ObjectProperty" && prop1.key.type === "Identifier" && prop1.key.name === "get") {
traverse2(prop1, {
MemberExpression(path2) {
if (path2.node.object.type === "ThisExpression" && path2.node.property.type === "Identifier") {
graph.edges.get(name)?.add({
label: path2.node.property.name,
type: getRelationType(path2)
});
}
}
}, path1.scope, path1);
}
});
}
});
}
}
if (path1.node.key.type === "Identifier" && path1.node.key.name === "methods") {
const methodsNode = path1.node;
if (methodsNode.value.type === "ObjectExpression") {
methodsNode.value.properties.forEach((prop) => {
if ((prop.type === "ObjectMethod" || prop.type === "ObjectProperty") && prop.key.type === "Identifier") {
const name = prop.key.name;
traverse2(prop, {
MemberExpression(path2)