SCAST MCP Server

by davidkingzyb
Verified
var ESTREEPY = (function () { var types = { } var Code = '' function getAst(code) { var ESTree = filbert.parse(code) return ESTree } function setCode(code){ Code = code; } function traverseAst(node, callback) { // console.log('traverseAst',node.type,node) var isreturn=callback(node) if(isreturn===true)return Object.keys(node).forEach((key) => { const item = node[key] if (Array.isArray(item) && key != 'children') { item.forEach((sub) => { sub.type && traverseAst(sub, callback) }) } item && item.type && traverseAst(item, callback) }) } function loc2poi(node) { return { line: node.loc.start.line, start: node.loc.start.column } } function getRangeCode(node) { if (node == null) return '' var result = Code.slice(node.range[0], node.range[1]) return result } function getArgs(nodes) { var result = '' for (let node of nodes) { result += getValue(node) + ',' } return result } function getValue(node) { if (node === null || node === undefined) return '' if (d3config.estreeops[node.type] === false && d3config.estreeops.all == false) { return '' } switch (node.type) { case "Program": return node.filename case "Identifier": return node.name case "Literal": return node.raw case "TemplateElement": return node.value.raw case "FunctionDeclaration": return `function ${getValue(node.id)}(${getArgs(node.params)})` case "FunctionExpression": return `function(${getArgs(node.params)})` case "ArrowFunctionExpression": return `(${getArgs(node.params)})=>{}` case "CallExpression": return getValue(node.callee) case "NewExpression": return getValue(node.callee) case "VariableDeclaration": return node.kind case "VariableDeclarator": return getValue(node.id) case "ExpressionStatement": return ''//getRangeCode(node) case "BlockStatement": return "{}" case "MemberExpression": return getValue(node.property) case "RegExpLiteral": return node.regex.pattern case "IfStatement": return 'if ' + getRangeCode(node.test) case "SwitchStatement": return 'switch ' + getRangeCode(node.discriminant) case "SwitchCase": return 'case ' + getRangeCode(node.test) case "CatchClause": return 'catch ' + getRangeCode(node.param) case "WhileStatement": return 'while ' + getRangeCode(node.test) case "DoWhileStatement": return 'do ' + getRangeCode(node.test) case "ForStatement": return 'for ' + getRangeCode(node.test) case "ForInStatement": return `${getRangeCode(node.left)} in ${getRangeCode(node.right)}` case "ForOfStatement": return `${getRangeCode(node.left)} in ${getRangeCode(node.right)}` case "Property": return getValue(node.key) case "UnaryExpression": return node.operator case "UpdateExpression": return getValue(node.argument) + node.operator case 'LogicalExpression': return node.operator case "ConditionalExpression": return getRangeCode(node.test) case "ArrayPattern": return getArgs(node.elements) case "ObjectPattern": return getArgs(node.properties) case "TemplateLiteral": return getArgs(node.quasis) case "RestElement": return "..." + getValue(node.argument) case "BinaryExpression": return node.operator case "ClassDeclaration": return `class ${getValue(node.id)}${':' + getValue(node.superClass)}` case "MethodDefinition": return `${node.static ? 'static' : ''} ${node.kind} ${getValue(node.key)}` case "ImportDeclaration": return getValue(node.source) case "ImportSpecifier": return getValue(node.imported) case "ImportDefaultSpecifier": return node.local.name case "ImportNamespaceSpecifier": return node.local.name case "ExportNamedDeclaration": return getValue(node.source) case "ExportSpecifier": return getValue(node.exported) case "ExportAllDeclaration": return getValue(node.source) case "ExportDefaultDeclaration": return getValue(node.declaration) case "PropertyDefinition": return getValue(node.key) default: return node.type.replace('Statement', '').replace('Declaration', '').replace('Expression', ''); } } function analysisD3(node, file) { node.name = getValue(node) node.poi = loc2poi(node) // console.log('js analysisD3', node.type, node.name, node) switch (node.type) { case "Program": node.children = node.body break case "Identifier": break case "FunctionDeclaration": node.children = [node.body] break case "FunctionExpression": node.children = [node.body] break case "ArrowFunctionExpression": node.children = [node.body] break case "CallExpression": node.children = node.arguments break case "NewExpression": node.children = node.arguments break case "VariableDeclaration": node.children = node.declarations || [] break case "VariableDeclarator": node.children = [node.init] break case "ExpressionStatement": node.children = [node.expression] break case "BlockStatement": node.children = node.body break case "MemberExpression": node.children = [node.object] break case "ReturnStatement": node.children = [node.argument] break case "IfStatement": node.children = [node.consequent || {}, node.alternate || {}] break case "SwitchStatement": node.children = node.cases || [] break case "SwitchCase": node.children = node.consequent break case "ThrowStatement": node.children = [node.argument] break case "TryStatement": node.children = [node.block, node.handler, node.finalizer || {}] break case "CatchClause": node.children = [node.body] break case "WhileStatement": node.children = [node.body] break case "DoWhileStatement": node.children = [node.body] break case "ForStatement": node.children = [node.body] break case "ForInStatement": node.children = [node.body] break case "ForOfStatement": node.children = [node.body] break case "ArrayExpression": node.children = node.elements break case "ObjectExpression": node.children = node.properties; break; case "Property": node.children = [node.value] break case "SequenceExpression": node.children = node.expressions break case "AwaitExpression": node.children = [node.argument] break case "SpreadElement": node.children = [node.argument] break case "AssignmentExpression": node.children = [node.left, node.right] break case "LogicalExpression": node.children = [node.left, node.right]; break case "UnaryExpression": node.children = [node.argument] break case "ConditionalExpression": node.children = [node.consequent, node.alternate] break case "YieldExpression": node.children = [node.argument] break case "TemplateLiteral": node.children = node.expressions break case "BinaryExpression": node.children = [node.left, node.right] break case "AssignmentPattern": node.children = [node.left, node.right] break case "ClassDeclaration": node.children = [node.body] break case "ClassBody": node.children = node.body break case "MethodDefinition": node.children = [node.value] break case "ImportDeclaration": node.children = node.specifiers break case "ExportNamedDeclaration": node.children = node.specifiers break case "ChainExpression": node.children = [node.expression] break case "PropertyDefinition": node.children = [node.value] break } if (d3config.estreeops[node.type] === false && d3config.estreeops.all == false) { node.children = [] } } function analysisMermaid(node, file, r) { // console.log('analysisMermaid',node) switch (node.type) { case "FunctionDeclaration": traverseFunction(node, {}, file) return true case "ClassDeclaration": if (!r.showMethod) break// traverseClass(node, file) return true case "VariableDeclaration": traverseAst(node, (n) => { if (n.type == "VariableDeclarator") { node._name = getValue(n.id) } if (n.type == "ObjectExpression") { traverseObject(n, node, file) return true } if (n.type == "FunctionExpression") { n.id = { type: "Identifier", name: node._name, loc: { start: {}, end: {} } } traverseFunction(n, {}, file) } }) return true } function traverseClass(node, file) { node._file = file node._value = getValue(node.id) node._flow_id = node._value r.FlowNode[node._flow_id] = node; r.FlowOne[node._flow_id] = node._value r.Flow += ` ${node._value}[${node._value}]\nclick ${node._value} "javascript:void(onFlowClick('${node._value}','${file}'))"\n` r.FDPNode[node._flow_id] = { id: node._value, w: node._value.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: `[${node._value}]` } r.UMLClass[node._value] = {} r.UML += ` class ${node._value}{\n`; for (let member of node.body.body) { switch (member.type) { case "MethodDefinition": member._value = getValue(member) member._flow_id = member._value let func = member.value let _name = getValue(member.key) == "constructor" ? '#' + node._value : getValue(member.key) func.id = { type: "Identifier", name: _name, loc: { start: {}, end: {} } } traverseFunction(func, node, file, true) break; case "PropertyDefinition": traverseProperty(member, node, file, r) break } } r.UML += ' }\n' if (node.superClass) { var f = getValue(node.superClass) if (r.FDPNode[f] === undefined) { r.FDPNode[f] = { id: f, w: f.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: `[${f}]` } } r.UML += ` ${f} <|-- ${node._value}\n` r.FlowLink += `${f} ==o ${node._value}\n` r.FDPLinks.push({ source: f, target: node._value, value: 6, dist: 200, dash: "2,2" }) } } function traverseProperty(member, cls, file, r) { member._value = getValue(member) member._flow_id = member._value member._flow_prop = `|${member._value.replaceAll('|', '\|').replaceAll('[', '').replaceAll(']', '')}|` // if(r.FlowFilter[member._flow_id]===false)return; // r.FlowNode[member._flow_id]=member; r.UML += ` ${member._value}\n` // console.log('traverse property', n) if (!r.showCall) return true var n = member.value if (n.type == "CallExpression") { n._file = file n._value = getValue(n) n._flow_id = n._value + '_' + member._flow_id n._flow_from = cls._flow_id n._flow_prop = member._flow_prop r.FlowNode[n._flow_id] = n if (!r.FlowFilter[n._flow_id]) return true n._flow_str = ` ${n._flow_id}([${n._value}])\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: n._value.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: n._value + '()' } r.Flow += n._flow_str return true } else if (n.type == "NewExpression") { n._file = file n._value = getValue(n) n._flow_id = n._value + '_' + member._flow_id n._flow_from = cls._flow_id n._flow_prop = member._flow_prop r.FlowNode[n._flow_id] = n if (!r.FlowFilter[n._flow_id]) return true r.UMLClass[cls._value][n._flow_id] = n; n._flow_str = ` ${n._flow_id}[${n._value}]\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: n._value.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: `[${n._value}]` } r.Flow += n._flow_str return true } } function traverseObject(n, node, file) { for (let prop of n.properties) { if (prop.value.type == "FunctionExpression" || prop.value.type == "ArrowFunctionExpression") { prop.value.id = { type: "Identifier", name: getValue(prop.key), loc: { start: {}, end: {} } } traverseFunction(prop.value, {}, file) } } } function traverseFunction(member, cls, file, symbol) {//member:function cls:parent function // console.log('traverse function',member) member._value = getValue(member.id) var method = cls._flow_id ? member._value + '_' + cls._flow_id : member._value r.FlowOne[member._value] = method//todo 处理不同类同名方法 member._flow_id = method member._flow_from = cls._flow_id member._file = file if (r.FlowFilter[member._flow_id] === false) return; r.FlowNode[member._flow_id] = member; r.FDPNode[member._flow_id] = { id: member._flow_id, w: member._value.length * gD3fontSize / 1.6 + gD3fontSize * 3, text: `${member._value}()` } r.Flow += ` ${member._flow_id}([${member._value}])\nclick ${member._flow_id} "javascript:void(onFlowClick('${member._flow_id}','${file}'))"\n` if (cls._flow_id) { r.FlowLink += `${cls._flow_id} --o ${member._flow_id}\n` r.FDPLinks.push({ source: cls._flow_id, target: member._flow_id, value: 2 }) } if (r.showCall) { traverseAst(member.body, (n) => { return doBlock(n, member, file, r) }) } if (symbol) r.UML += ` ${member._value}()\n` } function doBlock(n, node, file, r) { // console.log('do block', n) if (!n) return if (n.type == "FunctionDeclaration") { traverseFunction(n, node, file) return true } else if (n.type == "ForStatement" || n.type == "WhileStatement" || n.type == "DoWhileStatement" || n.type == "ForInStatement" || n.type == "ForOfStatement") { n._file = file n._value = "for" n._flow_id = n._value + n.start + '_' + node._flow_id n._flow_from = node._flow_id r.FlowNode[n._flow_id] = n if (r.showIf) { r.Flow += ` ${n._flow_id}((${n._value}))\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: 0, text: '🔵' } } if (n.test && getRangeCode(n.test)) n._flow_condition = '|' + getRangeCode(n.test).replaceAll('|', '|').replace('{', '⌈').replace('}', '⌋').replaceAll('[', '⌈').replaceAll(']', '⌋') + '|' if (n.right && getRangeCode(n.right)) n._flow_condition = '|' + getRangeCode(n.right).replaceAll('|', '|').replace('{', '⌈').replace('}', '⌋').replaceAll('[', '⌈').replaceAll(']', '⌋') + '|' if (r.showIf && n.body) { traverseAst(n.body, (nn) => { return doBlock(nn, n, file, r) }) } if (r.showIf) return true } else if (n.type == "IfStatement") { n._file = file n._value = "if" n._flow_id = n._value + n.start + '_' + node._flow_id n._flow_from = node._flow_id r.FlowNode[n._flow_id] = n if (r.showIf) { r.Flow += ` ${n._flow_id}{${n._value}}\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: 0, text: '🔷' } } if (n.test && getRangeCode(n.test)) n._flow_condition = '|' + getRangeCode(n.test).replaceAll('|', '|').replaceAll('[', '⌈').replaceAll(']', '⌋') + '|' if (r.showIf && n.consequent) { traverseAst(n.consequent, (nn) => { return doBlock(nn, n, file, r) }) } if (r.showIf && n.alternate) { traverseAst(n.alternate, (nn) => { doBlock(nn, node, file, r) if (n.alternate.type == "BlockStatement") { return false } return true }) } if (r.showIf) return true } else if (n.type == "CallExpression") { n._file = file n._value = getValue(n) n._flow_id = n._value + '_' + node._flow_id n._flow_from = node._flow_id r.FlowNode[n._flow_id] = n if (!r.FlowFilter[n._flow_id]) return true n._flow_str = ` ${n._flow_id}([${n._value}])\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: n._value.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: n._value + '()' } r.Flow += n._flow_str return true } else if (n.type == "NewExpression") { n._file = file n._value = getValue(n) n._flow_id = n._value + '_' + node._flow_id n._flow_from = node._flow_id r.FlowNode[n._flow_id] = n if (!r.FlowFilter[n._flow_id]) return true n._flow_str = ` ${n._flow_id}[${n._value}]\nclick ${n._flow_id} "javascript:void(onFlowClick('${n._flow_id}','${file}'))"\n` r.FDPNode[n._flow_id] = { id: n._flow_id, w: n._value.length * gD3fontSize / 1.6 + gD3fontSize * 2, text: n._value } r.Flow += n._flow_str return true } } } var d3config = { estreeops: types, fontsize: 14 } function setD3Config(conf) { d3config = conf } return { getAst: getAst, traverseAst: traverseAst, analysisMermaid: analysisMermaid, analysisD3: analysisD3, setD3Config: setD3Config, types: types, loc2poi:loc2poi, setCode:setCode, } })()