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