smallstonesk
4 years ago
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