import {Cell} from '@antv/x6'; import makeCode from './template/runOnline'; import simplifyDSL from './simplifyDSL'; interface DSL { cells: Cell.Properties[]; } /** * Solution * * 1. find the source node form dsl, and if it is not imove-start, * then insert a vitural imove-start at first. * * 2. transform node funciton, follows should be noted: * - import statement should be replaced with import('packge/from/network') * - export statement should be replace with return function * - each node function should be wrapped within a new function to avoid duplicate declaration global variable * * 3. assemble Logic, Context, simplyfied dsl and nodeFns map into one file * */ const INSERT_DSL_COMMENT = '// define dsl here'; const INSERT_NODE_FNS_COMMENT = '// define nodeFns here'; const importRegex = /import\s+([\s\S]*?)\s+from\s+(?:('[@\.\/\-\w]+')|("[@\.\/\-\w]+"))\s*;?/mg; const virtualSourceNode = { id: 'virtual-imove-start', shape: 'imove-start', data: { trigger: 'virtual-imove-start', configData: {}, code: 'export default async function(ctx) {\n \n}' } }; const findStartNode = (dsl: DSL): Cell.Properties => { const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); const edges = dsl.cells.filter(cell => cell.shape === 'edge'); if(nodes.length === 0) { throw new Error('Compile failed, no node is selected'); } let foundEdge = null; let startNode = nodes[0]; while(foundEdge = edges.find(edge => edge.target.cell === startNode.id)) { const newSourceId = foundEdge.source.cell; startNode = nodes.find(node => node.id === newSourceId) as Cell.Properties; } if(startNode.shape !== 'imove-start') { dsl.cells.push( virtualSourceNode, { shape: "edge", source: { cell: 'virtual-imove-start', }, target: { cell: startNode.id } } ); startNode = virtualSourceNode; } return startNode; }; const getNextNode = (curNode: Cell.Properties, dsl: DSL) => { const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); const edges = dsl.cells.filter(cell => cell.shape === 'edge'); const foundEdge = edges.find(edge => edge.source.cell === curNode.id); if(foundEdge) { return nodes.find(node => node.id === foundEdge.target.cell); } }; const compileSimplifiedDSL = (dsl: DSL): string => { const simplyfiedDSL = JSON.stringify(simplifyDSL(dsl), null, 2); return `const dsl = ${simplyfiedDSL};`; }; const compileNodeFn = (node: Cell.Properties): string => { const {data: {label, code}} = node; const newCode = code.replace(importRegex, (match: string, p1: string, p2: string, p3: string) => { const pkgName = (p2 || p3).replace(/('|")/g, ''); return `const ${p1} = (await import('https://jspm.dev/${pkgName}')).default;`; }).replace(/export\s+default/, 'return'); return `await (async function() { ${newCode} }())`; }; const compileNodeFnsMap = (dsl: DSL): string => { const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); const kvs = nodes.map(node => { const {id} = node; return `'${id}': ${compileNodeFn(node)}`; }); return `const nodeFns = {\n ${kvs.join(',\n ')}\n}`; }; const compile = (dsl: DSL, mockInput: any): string => { const startNode = findStartNode(dsl); const mockNode = getNextNode(startNode, dsl); return makeCode(mockNode, mockInput) .replace(INSERT_DSL_COMMENT, compileSimplifiedDSL(dsl)) .replace(INSERT_NODE_FNS_COMMENT, compileNodeFnsMap(dsl)) .replace('$TRIGGER$', startNode.data.trigger); }; export default compile;