UNPKG

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
"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)