SCAST MCP Server

by davidkingzyb
Verified
var ESTREEJS=(function(){ const option={ ecmaVersion: 2022, locations:true, ranges:true, sourceType:'module', } var Code=''; var types={ "Program":true, "BlockStatement":true, "ExpressionStatement":true, "Identifier":true, "Literal":false, "TemplateElement":false, "RegExpLiteral":false, "FunctionDeclaration":true, "FunctionExpression":true, "ArrowFunctionExpression":true, "CallExpression":true, "ReturnStatement":false, "YieldExpression":false, "NewExpression":true, "VariableDeclaration":true, "VariableDeclarator":true, "MemberExpression":true, "ChainExpression":false, "ArrayExpression":true, "ObjectExpression":true, "SequenceExpression":false, "ArrayPattern":true, "ObjectPattern":true, "IfStatement":true, "SwitchStatement":true, "SwitchCase":true, "ConditionalExpression":true, "ThrowStatement":false, "TryStatement":true, "CatchClause":true, "WhileStatement":true, "DoWhileStatement":true, "ForStatement":true, "ForInStatement":true, "ForOfStatement":true, "ContinueStatement":false, "BreakStatement":false, "ClassDeclaration":true, "ClassBody":true, "ThisExpression":false, "Property":true, "PropertyDefinition":true, "MethodDefinition":true, "AwaitExpression":true, "SpreadElement":false, "AssignmentExpression":false, "LogicalExpression":false, "UnaryExpression":false, "TemplateLiteral":false, "BinaryExpression":false, "AssignmentPattern":false, "UpdateExpression":false, "ImportDeclaration":false, "ImportSpecifier":false, "ImportDefaultSpecifier":false, "ImportNamespaceSpecifier":false, "ExportNamedDeclaration":false, "ExportSpecifier":false, "ExportAllDeclaration":false, "ExportDefaultDeclaration":false, } var d3config={estreeops:types,fontsize:14} function setD3Config(conf){ d3config=conf } function setCode(code){ Code = code; } function getAst(code){ var EStree=acorn.parse(code, option) return EStree } function traverseAst(node,callback){ // console.log('traverseAst js', node); var isreturn=callback(node) if(isreturn===true)return Object.keys(node).forEach((key) => { const item = node[key] if (Array.isArray(item)&&key!='children'&&key!='range') { item.forEach((sub) => { sub!==null&&sub.type && traverseAst(sub, callback) }) } item && item.type && traverseAst(item, callback) }) } function getTokenizer(code){ const tokens=[...acorn.tokenizer(code,option)] } function pruneJsonTree(json) { if (json && typeof json === 'object') { if (Array.isArray(json)) { return json.map(item => pruneJsonTree(item)).filter(item => item !== undefined); } else { const newObj = {}; for (const key in json) { if (json.hasOwnProperty(key)) { const value = json[key]; if (typeof value === 'object' || Array.isArray(value)) { const prunedValue = pruneJsonTree(value); if (prunedValue !== undefined) { newObj[key] = prunedValue; } } else if (types[json.type]) { newObj[key] = value; } } } return Object.keys(newObj).length > 0 ? newObj : undefined; } } else { return json; } } function loc2poi(node){ return {line:node.loc.start.line,start:node.loc.start.column} } function getRangeCode(node){ if(node==null)return 'xx' 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 if(node.callee.type=="FunctionExpression")node.children.push(node.callee)//for self execute function 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) } if(n.type=="NewExpression"){ r.FlowVarNew[node._name]=getValue(n) } }) return true } function traverseClass(node,file){ node._file=file node._value=getValue(node.id) node._flow_id=node._value if(r.FlowFilter[node._value]===false)return; r.FlowNode[node._flow_id]=node; if(r.FlowOne[node._flow_id]){ r.FlowOne[node._flow_id].unshift(node._value) }else{ 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]===false)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+'()'} if(!r.FlowFilter[n._flow_id])return true 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 console.log('p var ',member._value,getValue(n)) r.FlowVarNew[member._value]=getValue(n) if(r.FlowFilter[n._flow_id]===false)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}]`} if(!r.FlowFilter[n._flow_id])return true r.Flow+=n._flow_str return true } } function traverseObject(n,node,file){ for(let prop of n.properties){ if(!prop.value)continue 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 if(r.FlowOne[member._value]){ r.FlowOne[member._value].unshift(method) }else{ r.FlowOne[member._value]=[method] } 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 } if(r.showIf)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 n._flow_callee=n.callee.name||n.callee.object?.name; if(n._flow_callee&&r.FlowNode[n._flow_id]){ n._flow_id=n._flow_callee+n._flow_id } r.FlowNode[n._flow_id]=n if(r.FlowFilter[n._flow_id]===false)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+'()'} if(!r.FlowFilter[n._flow_id])return true//for first click not render detail 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]===false)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} if(!r.FlowFilter[n._flow_id])return true//for first click not render detail r.Flow+=n._flow_str return true }else if(n.type=="VariableDeclarator"){ traverseAst(n,(nn)=>{ if(nn.type=="NewExpression"){ console.log('block var',n,nn) r.FlowVarNew[getValue(n)]=getValue(nn) } }) } } } return { getAst:getAst, traverseAst:traverseAst, analysisMermaid:analysisMermaid, analysisD3:analysisD3, setD3Config:setD3Config, types:types, loc2poi:loc2poi, setCode:setCode } })()