smallstonesk
4 years ago
10 changed files with 283 additions and 0 deletions
@ -0,0 +1 @@ |
|||
node_modules |
@ -0,0 +1 @@ |
|||
## iMove-cli |
@ -0,0 +1,13 @@ |
|||
#!/usr/bin/env node
|
|||
const path = require('path'); |
|||
const program = require('commander'); |
|||
const pkg = require(path.join(__dirname, './package.json')); |
|||
|
|||
program |
|||
.version(pkg.version) |
|||
.option('-d, --dev', '本地在线开发') |
|||
.parse(process.argv); |
|||
|
|||
if(program.dev) { |
|||
require('./src/cmd/dev')(); |
|||
} |
@ -0,0 +1,29 @@ |
|||
{ |
|||
"name": "@imove/cli", |
|||
"version": "0.0.1", |
|||
"description": "imove client", |
|||
"main": "index.js", |
|||
"bin": { |
|||
"imove-cli": "index.js" |
|||
}, |
|||
"keywords": [ |
|||
"imove", |
|||
"compile" |
|||
], |
|||
"scripts": { |
|||
"test": "echo \"Error: no test specified\" && exit 1" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/suanmei/iMove.git" |
|||
}, |
|||
"author": "smallstonesk", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"body-parser": "^1.19.0", |
|||
"commander": "^6.0.0", |
|||
"cors": "^2.8.5", |
|||
"express": "^4.17.1", |
|||
"fs-extra": "^9.0.1" |
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
const path = require('path'); |
|||
const fs = require('fs-extra'); |
|||
|
|||
const writeEntryFile = async (basePath, fileIds) => { |
|||
const imports = []; |
|||
const funcMaps = []; |
|||
fileIds.forEach((id, idx) => { |
|||
const funcName = `fn_${idx}`; |
|||
imports.push(`import ${funcName} from './${id}';`); |
|||
funcMaps.push(`'${id}': ${funcName}`); |
|||
}); |
|||
const fileContent = [ |
|||
imports.join('\n'), |
|||
`const nodeFns = {\n ${funcMaps.join(',\n ')}\n};`, |
|||
`export default nodeFns;` |
|||
].join('\n'); |
|||
const entryFilePath = path.join(basePath, 'index.js'); |
|||
await fs.writeFile(entryFilePath, fileContent, {encoding: 'utf8', flag:'w'}); |
|||
}; |
|||
|
|||
const writeNodeCodes = async (basePath, dsl) => { |
|||
const fileIds = []; |
|||
const {cells = []} = dsl; |
|||
const nodes = cells.filter(cell => cell.shape !== 'edge'); |
|||
for(const {id, data: {code}} of nodes) { |
|||
fileIds.push(id); |
|||
const filePath = path.join(basePath, id + '.js'); |
|||
await fs.writeFile(filePath, code, {encoding: 'utf8', flag:'w'}); |
|||
} |
|||
return fileIds; |
|||
}; |
|||
|
|||
const setup = async (dsl, logicBasePath) => { |
|||
const nodeCodesPath = path.join(logicBasePath, 'nodeFns'); |
|||
await fs.ensureDir(nodeCodesPath); |
|||
const fileIds = await writeNodeCodes(nodeCodesPath, dsl); |
|||
await writeEntryFile(nodeCodesPath, fileIds); |
|||
}; |
|||
|
|||
module.exports = setup; |
@ -0,0 +1,38 @@ |
|||
const path = require('path'); |
|||
const fs = require('fs-extra'); |
|||
const simplifyDSL = require('./simplifyDSL'); |
|||
const extractCodes = require('./extractCodes'); |
|||
const {createServer} = require('../../utils/server'); |
|||
|
|||
const TPL_PATH = path.join(__dirname, './template'); |
|||
const LOGIC_BASE_PATH = path.join(process.cwd(), './src/logic'); |
|||
|
|||
const setup = () => { |
|||
const app = createServer(); |
|||
app.post('/api/save', async (req, res) => { |
|||
|
|||
// check dsl whether existed
|
|||
if (!req.body || !req.body.dsl) { |
|||
res.status(500).json({isCompiled: false}).end(); |
|||
return; |
|||
} |
|||
|
|||
// compile
|
|||
try { |
|||
const {dsl} = req.body; |
|||
await simplifyDSL(dsl, LOGIC_BASE_PATH); |
|||
await extractCodes(dsl, LOGIC_BASE_PATH); |
|||
await fs.copy(TPL_PATH, LOGIC_BASE_PATH); |
|||
res.status(200).json({isCompiled: true}).end(); |
|||
console.log('compile successfully!'); |
|||
} catch(err) { |
|||
res.status(500).json({isCompiled: false}).end(); |
|||
console.log('compile failed! the error is:', err.message); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
module.exports = function() { |
|||
fs.ensureDirSync(LOGIC_BASE_PATH); |
|||
setup(); |
|||
}; |
@ -0,0 +1,29 @@ |
|||
const path = require('path'); |
|||
const fs = require('fs-extra'); |
|||
|
|||
const extractObj = (obj = {}, keys = []) => { |
|||
const ret = {}; |
|||
keys.forEach(key => ret[key] = obj[key]); |
|||
return ret; |
|||
}; |
|||
|
|||
const simpily = (dsl) => { |
|||
const {cells = []} = dsl; |
|||
return { |
|||
cells: cells.map(cell => { |
|||
if(cell.shape === 'edge') { |
|||
return extractObj(cell, ['id', 'shape', 'source', 'target']); |
|||
} else { |
|||
return extractObj(cell, ['id', 'shape', 'data']); |
|||
} |
|||
}) |
|||
}; |
|||
}; |
|||
|
|||
const setup = async (dsl, logicBasePath) => { |
|||
const simpilifiedDSL = simpily(dsl); |
|||
const dslFilePath = path.join(logicBasePath, 'dsl.json'); |
|||
await fs.writeFile(dslFilePath, JSON.stringify(simpilifiedDSL, null, 2)); |
|||
}; |
|||
|
|||
module.exports = setup; |
@ -0,0 +1,6 @@ |
|||
import Logic from './logic'; |
|||
import dsl from './dsl.json'; |
|||
|
|||
const logic = new Logic({dsl}); |
|||
|
|||
export default logic; |
@ -0,0 +1,103 @@ |
|||
import nodeFns from './nodeFns'; |
|||
|
|||
class Context { |
|||
|
|||
constructor(opts) { |
|||
this.curNode = null; |
|||
} |
|||
|
|||
transitionToNode(node) { |
|||
this.curNode = node; |
|||
} |
|||
|
|||
getConfig() { |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
const SHAPES = { |
|||
START: 'imove-start', |
|||
BRANCH: 'imove-branch', |
|||
BEHAVIOR: 'imove-behavior' |
|||
} |
|||
|
|||
class Logic { |
|||
|
|||
constructor(opts = {}) { |
|||
this.ctx = null; |
|||
this.dsl = opts.dsl; |
|||
} |
|||
|
|||
get cells() { |
|||
return this.dsl.cells; |
|||
} |
|||
|
|||
get nodes() { |
|||
return this.cells.filter(cell => cell.shape !== 'edge'); |
|||
} |
|||
|
|||
get startNodes() { |
|||
return this.cells.filter(cell => cell.shape === SHAPES.START); |
|||
} |
|||
|
|||
get edges() { |
|||
return this.cells.filter(cell => cell.shape === 'edge'); |
|||
} |
|||
|
|||
_getStartNode(trigger) { |
|||
for(const cell of this.startNodes) { |
|||
if(cell.data.trigger === trigger) { |
|||
return cell; |
|||
} |
|||
} |
|||
} |
|||
|
|||
_getNextNode(curNode, lastRet) { |
|||
for(const edge of this.edges) { |
|||
let isMatched = edge.source.cell === curNode.id; |
|||
// NOTE: if it is a imove-branch node, each port's condition should be tested whether it is matched
|
|||
if(curNode.shape === SHAPES.BRANCH) { |
|||
let matchedPort = ''; |
|||
const {ports} = curNode.data; |
|||
for(const key in ports) { |
|||
const {condition} = ports[key]; |
|||
const ret = new Function('ctx', 'return ' + condition)(this.ctx); |
|||
if(ret === lastRet) { |
|||
matchedPort = key; |
|||
break; |
|||
} |
|||
} |
|||
isMatched = isMatched && edge.source.port === matchedPort; |
|||
} |
|||
if(isMatched) { |
|||
const nextNodeId = edge.target.cell; |
|||
return this.nodes.find(item => item.id === nextNodeId); |
|||
} |
|||
} |
|||
} |
|||
|
|||
_initCtx(data) { |
|||
|
|||
} |
|||
|
|||
_prepareCtx() { |
|||
|
|||
} |
|||
|
|||
async invoke(trigger, data) { |
|||
let curNode = this._getStartNode(trigger); |
|||
if(!curNode) { |
|||
return Promise.reject(`Invoke failed, because no logic-start named ${trigger} found!`); |
|||
} |
|||
this._initCtx(data); |
|||
while(curNode) { |
|||
this._prepareCtx(); |
|||
const fn = nodeFns[curNode.id]; |
|||
const ret = await fn(this.ctx); |
|||
curNode = this._getNextNode(curNode, ret); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = Logic; |
@ -0,0 +1,23 @@ |
|||
const http = require('http'); |
|||
const cors = require('cors'); |
|||
const express = require('express'); |
|||
const bodyParser = require('body-parser'); |
|||
|
|||
const createServer = (port = 3456) => { |
|||
const app = express(); |
|||
app.use(bodyParser.json()); |
|||
app.use(bodyParser.urlencoded({extended: false})); |
|||
app.use((req, res, next) => { |
|||
res.header("Access-Control-Allow-Origin", "*"); |
|||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); |
|||
next(); |
|||
}); |
|||
app.listen(port, () => { |
|||
console.log(`server starts successfully at ${port}!`); |
|||
}); |
|||
return app; |
|||
}; |
|||
|
|||
module.exports = { |
|||
createServer |
|||
}; |
Loading…
Reference in new issue