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