mirror of
https://github.com/purocean/yn.git
synced 2026-10-16 07:26:07 +08:00
增加运行代码功能
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
>vue + monaco-editor + markdown-it + nodejs + Koa
|
||||
|
||||
## 目标
|
||||
1. 界面字大不费眼 :)
|
||||
1. markdown 撰写
|
||||
2. 图片文件保存在本地,导出 markdown 文件可简单处理离线工作
|
||||
3. 支持一些流程图表绘制
|
||||
3. 支持一些流程图表绘制 startuml mermaidt
|
||||
3. 支持加密解密,用来保存账号等隐私文件,文件可单独设置密码
|
||||
4. 不需要更多花哨功能,简单够用就行
|
||||
5. 尽量少依赖三方库,也不花心思维护,杂凑在一起的功能,恰好工作即可 ^_^
|
||||
6. 在文档中运行 PHP Python Node.js 代码块
|
||||
|
||||
## 直接使用
|
||||
+ `cd backend; yarn; node main.js`
|
||||
@@ -16,6 +18,8 @@
|
||||
+ 删除文件/目录:`shift + 右键文件/目录`
|
||||
+ 重命名文件/目录:`ctrl + 右键文件/目录`
|
||||
+ 文件加密:以 `.c.md` 结尾的文件视为加密文件
|
||||
+ startuml 图形需要安装 Java
|
||||
+ 运行代码块第一行需要包含以 `--run--` 字符串,示例见下面截图,需要安装 PHP Python Node.js
|
||||
|
||||
## 开发
|
||||
+ 后端: `cd backend; node main.js`
|
||||
@@ -33,13 +37,16 @@
|
||||
+ [x] 图片粘贴预览
|
||||
+ [x] 未保存切换文件自动保存
|
||||
+ [x] 密码输入优化
|
||||
+ [x] nodejs静态文件
|
||||
+ [x] 静态文件
|
||||
+ [x] 运行 python php nodejs 块
|
||||
+ [ ] 运行代码支持图表生成
|
||||
+ [ ] 增加 rss 抓取
|
||||
+ [ ] 认证
|
||||
+ [ ] 全文查找 Ctrl + p
|
||||
+ [ ] 微信结合
|
||||
+ [ ] git备份
|
||||
+ [ ] git 备份
|
||||
+ [ ] 标签
|
||||
+ [ ] 移动端展示
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -6,6 +6,7 @@ const mime = require('mime')
|
||||
const plantuml = require('node-plantuml')
|
||||
|
||||
const file = require('./file')
|
||||
const run = require('./run')
|
||||
|
||||
const result = (status = 'ok', message = '操作成功', data = null) => {
|
||||
return { status, message, data }
|
||||
@@ -58,6 +59,15 @@ const plantumlGen = (ctx, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
const runCode = (ctx, next) => {
|
||||
if (ctx.path.startsWith('/api/run')) {
|
||||
const rst = run.runCode(ctx.request.body.language, ctx.request.body.code)
|
||||
ctx.body = result('ok', '运行成功', rst)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
const app = new Koa()
|
||||
|
||||
app.use(static(
|
||||
@@ -68,6 +78,7 @@ app.use(bodyParser({multipart: true}))
|
||||
app.use(async (ctx, next) => fileContent(ctx, next))
|
||||
app.use(async (ctx, next) => attachment(ctx, next))
|
||||
app.use(async (ctx, next) => plantumlGen(ctx, next))
|
||||
app.use(async (ctx, next) => runCode(ctx, next))
|
||||
|
||||
const port = 3000
|
||||
app.listen(port)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const execFileSync = require("child_process").execFileSync;
|
||||
|
||||
const runCode = (language, code) => {
|
||||
try {
|
||||
switch (language) {
|
||||
case 'php':
|
||||
return execFileSync('php', ['-r', code]).toString()
|
||||
case 'python':
|
||||
return execFileSync('python', ['-c', code]).toString()
|
||||
case 'py':
|
||||
return execFileSync('python', ['-c', code]).toString()
|
||||
case 'js':
|
||||
return execFileSync('node', ['-e', code]).toString()
|
||||
default:
|
||||
return `不支持 ${language} 语言`
|
||||
}
|
||||
} catch (e) {
|
||||
return e.message
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runCode }
|
||||
@@ -12,7 +12,7 @@ module.exports = {
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000'
|
||||
target: 'http://localhost:3001'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
const languages = ['php', 'python', 'js']
|
||||
|
||||
const cachePrefix = 'run_code_result_'
|
||||
|
||||
const RunPlugin = (md) => {
|
||||
const temp = md.renderer.rules.fence.bind(md.renderer.rules)
|
||||
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
|
||||
const token = tokens[idx]
|
||||
if (!languages.includes(token.info)) {
|
||||
return temp(tokens, idx, options, env, slf)
|
||||
}
|
||||
|
||||
const code = token.content.trim()
|
||||
const firstLine = code.split(/\n/)[0].trim()
|
||||
if (!firstLine.includes('--run--')) {
|
||||
return temp(tokens, idx, options, env, slf)
|
||||
}
|
||||
|
||||
const hash = CryptoJS.MD5(code).toString()
|
||||
|
||||
const click = `
|
||||
var code = '${encodeURIComponent(code).replace(/'/g, '\\\'')}'
|
||||
code = decodeURIComponent(code)
|
||||
var resultDiv = this.parentElement.firstChild
|
||||
resultDiv.innerHTML = '运行中……'
|
||||
|
||||
fetch('/api/run', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({language: '${token.info}', code: code})
|
||||
}).then(res => {
|
||||
res.json().then(result => {
|
||||
if (result.status === 'ok') {
|
||||
localStorage.setItem('${cachePrefix}${hash}', result.data)
|
||||
resultDiv.innerHTML = result.data
|
||||
} else {
|
||||
resultDiv.innerHTML = result.message
|
||||
}
|
||||
}).catch(e => {
|
||||
resultDiv.innerHTML = e.message
|
||||
})
|
||||
}).catch(e => {
|
||||
resultDiv.innerHTML = e.message
|
||||
})
|
||||
`
|
||||
const result = localStorage[`${cachePrefix}${hash}`] || ''
|
||||
|
||||
const resultDiv = `<div id="run-result-${hash}" style="position: relative;border-top: dashed 1px #888; padding: .5em 0; margin-top: 1.5em;"><div style="padding: .5em 0">${result}</div><button style="position: absolute; top: -.7em; height: 0; width: 0; border-left: .7em #b7b3b3 solid; border-top: .6em #dddddd00 solid; border-bottom: .6em #dddddd00 solid; border-right: 0; background: rgba(0, 0, 0, 0); cursor: pointer; outline: none" onclick="${click}"></button></div>`
|
||||
|
||||
const codeContent = temp(tokens, idx, options, env, slf).trim().replace(/<\/pre>$/, '')
|
||||
|
||||
return `${codeContent}${resultDiv}</pre>`
|
||||
}
|
||||
}
|
||||
|
||||
RunPlugin.clearCache = () => {
|
||||
Object.keys(localStorage).forEach(key => {
|
||||
if (key.startsWith(cachePrefix)) {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default RunPlugin
|
||||
@@ -9,6 +9,7 @@ import _ from 'lodash'
|
||||
import Markdown from 'markdown-it'
|
||||
import TaskLists from 'markdown-it-task-lists'
|
||||
import Plantuml from 'markdown-it-plantuml'
|
||||
import RunPlugin from './RunPlugin'
|
||||
|
||||
import Highlight from 'highlight.js'
|
||||
import MermaidPlugin from './MermaidPlugin'
|
||||
@@ -36,7 +37,7 @@ export default {
|
||||
generateSource: umlCode => {
|
||||
return '/api/plantuml/svg?data=' + encodeURIComponent(umlCode)
|
||||
}
|
||||
})
|
||||
}).use(RunPlugin)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
import Editor from '../components/Editor'
|
||||
import XView from '../components/View'
|
||||
import Tree from '../components/Tree'
|
||||
import RunPlugin from '../components/RunPlugin'
|
||||
import File from '../file'
|
||||
|
||||
export default {
|
||||
@@ -44,6 +45,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
RunPlugin.clearCache()
|
||||
this.restartTimer()
|
||||
},
|
||||
beforeDestroy () {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
Reference in New Issue
Block a user