11 changed files with 221 additions and 50 deletions
@ -0,0 +1,107 @@ |
|||||
|
import {Cell} from '@antv/x6'; |
||||
|
import tpl 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 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): string => { |
||||
|
const startNode = findStartNode(dsl); |
||||
|
return tpl |
||||
|
.replace(INSERT_DSL_COMMENT, compileSimplifiedDSL(dsl)) |
||||
|
.replace(INSERT_NODE_FNS_COMMENT, compileNodeFnsMap(dsl)) |
||||
|
.replace('$TRIGGER$', startNode.data.trigger); |
||||
|
}; |
||||
|
|
||||
|
export default compile; |
@ -0,0 +1,34 @@ |
|||||
|
import {Cell} from '@antv/x6'; |
||||
|
import addPlugins from './addPlugins'; |
||||
|
import simplifyDSL from './simplifyDSL'; |
||||
|
import extractNodeFns from './extractNodeFns'; |
||||
|
import logicTpl from './template/logic'; |
||||
|
import indexTpl from './template/index'; |
||||
|
import contextTpl from './template/context'; |
||||
|
|
||||
|
interface DSL { |
||||
|
cells: Cell.Properties[]; |
||||
|
} |
||||
|
|
||||
|
interface IOutput { |
||||
|
'nodeFns': { |
||||
|
[fileName: string]: string |
||||
|
}; |
||||
|
'context.js': string; |
||||
|
'dsl.json': string; |
||||
|
'index.js': string; |
||||
|
'logic.js': string; |
||||
|
} |
||||
|
|
||||
|
const compile = (dsl: DSL, plugins = []): IOutput => { |
||||
|
const output: IOutput = { |
||||
|
'nodeFns': extractNodeFns(dsl), |
||||
|
"context.js": contextTpl, |
||||
|
'dsl.json': JSON.stringify(simplifyDSL(dsl), null, 2), |
||||
|
'index.js': addPlugins(indexTpl, plugins), |
||||
|
'logic.js': logicTpl |
||||
|
}; |
||||
|
return output; |
||||
|
}; |
||||
|
|
||||
|
export default compile; |
@ -0,0 +1,31 @@ |
|||||
|
import logic from './logic'; |
||||
|
import context from './context'; |
||||
|
|
||||
|
export default ` |
||||
|
(async function run() { |
||||
|
// Context
|
||||
|
${context.replace(/export\s+default/, '')} |
||||
|
|
||||
|
// Logic
|
||||
|
${logic |
||||
|
.split('\n') |
||||
|
.filter(line => !line.match(/import nodeFns/) && !line.match(/import Context/)) |
||||
|
.join('\n') |
||||
|
.replace(/export\s+default/, '') |
||||
|
.replace( |
||||
|
`import EventEmitter from 'eventemitter3';`, |
||||
|
`const EventEmitter = (await import('https://jspm.dev/eventemitter3')).default;` |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
// DSL
|
||||
|
// define dsl here
|
||||
|
|
||||
|
// nodeFns map
|
||||
|
// define nodeFns here
|
||||
|
|
||||
|
// instantiation and invoke
|
||||
|
const logic = new Logic({ dsl }); |
||||
|
logic.invoke('$TRIGGER$'); |
||||
|
})(); |
||||
|
`;
|
Loading…
Reference in new issue