Merge branch 'release/2.0.0-alpha2'

This commit is contained in:
purocean
2019-08-02 09:12:52 +08:00
41 changed files with 1525 additions and 1531 deletions
-1
View File
@@ -1,4 +1,3 @@
.vscode
nohup.out
*.log
config.json
+17 -6
View File
@@ -16,6 +16,8 @@
6. 在文档中运行 PHP Python Node.js bash 代码块
## Yank-Note 2.0 开发计划
[V2 项目看板](https://github.com/purocean/yn/projects/2)
+ [x] 项目更名为 **Yank-Note**
+ [x] 前端界面可以拖动调整尺寸
+ [x] 重构前端项目结构,便于拓展:引入 `vuex`,使用 `async await` 代替可怕的回调,规范文件接口。
@@ -37,9 +39,14 @@
# yarn run dist-win # or npm run dist-win # Windows 用户
# 安装后端
cd ../background
cd app
yarn # or npm i
node main.js # 运行
yarn run serve # 运行后端
# 运行 electron
cd app
yarn # Windows 上安装 node-pty 需要费一番功夫,参考 https://github.com/microsoft/node-pty#windows
yarn run start
```
+ 访问 `http://localhost:3000`
+ 新增文件:`双击目录`
@@ -112,10 +119,6 @@
+ 表格解析增强,支持表格标题多行文本,列表等特性 [markdown-it-multimd-table](https://github.com/RedBug312/markdown-it-multimd-table)
+ 内置终端 [XTERM.JS](https://xtermjs.org/) [node-pty](https://github.com/Microsoft/node-pty)
## 开发贡献
+ 前端: `cd backend; node main.js`
+ 后端: `cd frontend; yarn run serve`
## 捐赠
如果我的工作对您有帮助,请我喝杯咖啡吧 ^_^。**附言可以留下您的 github 用户名或网站连接**。
@@ -133,6 +136,14 @@
## 更新日志
[最新发布](https://github.com/purocean/yn/releases)
### [v2.0.0-alpha2](https://github.com/purocean/yn/releases/tag/v2.0.0-alpha2) 2019-08-02
1. 默认仓库数据和配置改为在 `<home>/yank-note` 下保存
2. 调整在 Windows 端的仓库配置,不论是否在 WSL 环境下都使用 windows 风格的路径
1. 前端重构文件接口
2. 尝试引入 Electron
3. 后端重构改用 TypeScript
4. 修复若干 v2.0.0-alpha1 的问题
### [v2.0.0-alpha1](https://github.com/purocean/yn/releases/tag/v2.0.0-alpha1) 2019-08-01
1. 目录树和集成终端增加拖动调整尺寸功能
1. 重构前端代码便于拓展
+1
View File
@@ -1,2 +1,3 @@
node_modules
dist
vs
View File
+32
View File
@@ -0,0 +1,32 @@
{
"scripts": {
"build": "tsc",
"serve": "ts-node src/server/main.ts",
"start": "npm run build && electron ./dist/app.js"
},
"dependencies": {
"fs-extra": "^6.0.1",
"koa": "^2.4.1",
"koa-body": "^2.5.0",
"koa-static": "^4.0.2",
"mime": "^2.2.0",
"natural-orderby": "^1.0.2",
"node-plantuml": "^0.6.2",
"node-pty": "^0.8.1",
"opn": "^5.3.0",
"request": "^2.88.0",
"safe-buffer": "^5.1.2",
"socket.io": "^2.2.0"
},
"devDependencies": {
"@types/fs-extra": "^8.0.0",
"@types/koa": "^2.0.49",
"@types/koa-static": "^4.0.1",
"@types/mime": "^2.0.1",
"@types/node": "^12.6.1",
"@types/request": "^2.48.2",
"electron": "^6.0.0",
"ts-node": "^8.3.0",
"typescript": "^3.5.3"
}
}
+35 -2
View File
@@ -1,4 +1,6 @@
import { app, BrowserWindow } from 'electron'
import server from './server/main'
import { dialog } from 'electron'
let win: BrowserWindow | null = null
@@ -13,12 +15,43 @@ const createWindow = () => {
// 加载index.html文件
// win.loadFile('../../static/index.html')
// win.loadURL('http://g.cn')
win.loadURL('http://localhost:8066')
// 打开后端服务器
server(3044)
// 加载页面
setTimeout(() => {
win.loadURL('http://localhost:8066')
win.maximize()
}, 1000)
// 打开开发者工具
win.webContents.openDevTools()
win.on('close', e => {
const contents = win.webContents
if (contents) {
contents.executeJavaScript('window.documentSaved', true).then(val => {
if (val) {
win.destroy()
} else {
dialog.showMessageBox(win, {
type: 'question',
buttons: ['取消', '放弃保存并退出'],
title: '提示',
message: '有文档未保存,是否要退出?'
}).then(choice => {
if (choice.response === 1) {
win.destroy()
}
})
}
})
e.preventDefault()
}
})
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () => {
// 取消引用 window 对象,如果你的应用支持多窗口的话,
+39
View File
@@ -0,0 +1,39 @@
import * as fs from 'fs'
import { CONFIG_FILE } from './constant'
const configFile = CONFIG_FILE
const writeJson = (data: any) => {
fs.writeFileSync(configFile, JSON.stringify(data, null, 4), 'utf8')
}
const readJson = () => {
try {
const data = fs.readFileSync(configFile)
return JSON.parse(data.toString())
} catch (error) {
return null
}
}
const set = (key: string, value: any) => {
const config = readJson() || {}
config[key] = value
writeJson(config)
}
const get = (key: string, defaultVal: any = null) => {
const config = readJson() || {}
if (config[key] === undefined) {
set(key, defaultVal) // 写入默认值到配置文件
return defaultVal
}
return config[key]
}
export default {
set,
get
}
+12
View File
@@ -0,0 +1,12 @@
import * as path from 'path'
import * as os from 'os'
import { isWsl, toWslPath, getWinHomePath } from './wsl'
const homedir = isWsl ? toWslPath(getWinHomePath()) : os.homedir()
export const HOME_DIR = homedir
export const USER_DIR = path.join(homedir, 'yank-note')
export const TRASH_DIR = path.join(USER_DIR, 'trash')
export const MAIN_REPO_DIR = path.join(USER_DIR, 'main')
export const CONFIG_FILE = path.join(USER_DIR, 'config.json')
export const STATIC_DIR = path.join(__dirname, '../../static')
+34
View File
@@ -0,0 +1,34 @@
import * as os from 'os'
import * as fs from 'fs'
import { spawn } from 'child_process'
const convert = async (html: string, type: string) => {
try {
const path = os.tmpdir() + `/yn_convert_${new Date().getTime()}.${type}`
return new Promise((resolve, reject) => {
const process = spawn('pandoc', ['-f', 'html', '-o', path])
process.on('close', () => {
try {
const data = fs.readFileSync(path)
fs.unlinkSync(path)
resolve(data)
} catch (error) {
reject(error)
}
})
process.on('error', error => {
reject(error)
})
process.stdin.write(html)
process.stdin.end()
})
} catch (e) {
return e.message
}
}
export default convert
+227
View File
@@ -0,0 +1,227 @@
import * as fs from 'fs'
import * as xfs from 'fs-extra'
import * as path from 'path'
import * as crypto from 'crypto'
import * as NaturalOrderby from 'natural-orderby'
import * as wsl from './wsl'
import repository from './repository'
const opn = require('opn')
const isWsl = wsl.isWsl
const ignorePath = /node_modules/
const mkdirPSync = (location: string) => {
let normalizedPath = path.normalize(location)
let parsedPathObj = path.parse(normalizedPath)
let curDir = parsedPathObj.root
let folders = parsedPathObj.dir.split(path.sep)
folders.push(parsedPathObj.base)
for(let part of folders) {
if (part.indexOf(':') > -1) { // 修复 Windows 下面路径错误
continue
}
curDir = path.join(curDir, part)
if (!fs.existsSync(curDir)) {
fs.mkdirSync(curDir)
}
}
}
const rmRecursiveSync = (location: string) => {
if (fs.statSync(location).isDirectory()) {
let _ = fs.readdirSync(location)
_.forEach(childItemName => {
rmRecursiveSync(path.join(location, childItemName))
})
fs.rmdirSync(location)
} else {
fs.unlinkSync(location)
}
}
const resolvePath = (p: string, repo = 'main') => {
p = p.replace(/\.\./g, '')
const basePath = repository.getPath(repo)
if (!basePath) {
throw new Error(`仓库 ${repo} 不存在`)
}
return path.join(basePath, p)
}
const travels = (location: string, repo: string, basePath: string = null): any => {
if (!basePath) {
basePath = resolvePath('', repo)
}
if (!fs.statSync(location).isDirectory()) {
return []
}
const list = fs.readdirSync(location).filter(x => !x.startsWith('.') && !ignorePath.test(x))
const dirs = NaturalOrderby.orderBy(list.filter(x => fs.statSync(path.join(location, x)).isDirectory()))
const files = NaturalOrderby.orderBy(list.filter(x => !fs.statSync(path.join(location, x)).isDirectory()))
return dirs.map(x => {
const p = path.join(location, x)
const xpath = p.replace(basePath, '')
return {
name: x,
path: xpath,
type: 'dir',
repo: repo,
children: travels(p, repo, basePath)
}
}).concat(files.map(x => {
const p = path.join(location, x)
const xpath = p.replace(basePath, '')
return {
name: x,
path: xpath,
type: 'file',
repo: repo,
} as any
}))
}
const read = (repo: string, p: string) => {
return fs.readFileSync(resolvePath(p, repo))
}
const write = (repo: string, p: string, content: any) => {
p = resolvePath(p, repo)
mkdirPSync(path.dirname(p))
fs.writeFileSync(p, content)
return crypto.createHash('md5').update(content).digest('hex')
}
const rm = (repo: string, p: string) => {
if (resolvePath(p) !== resolvePath('', repo)) {
const newPath = path.join(repository.getTrashPath(repo), p.replace(/\.\./g, '')) + '.' + (new Date).getTime()
mkdirPSync(path.dirname(newPath))
try {
fs.renameSync(resolvePath(p, repo), newPath)
} catch (error) {
xfs.moveSync(resolvePath(p, repo), newPath)
}
}
}
const mv = (repo: string, oldPath: string, newPath: string) => {
oldPath = resolvePath(oldPath, repo)
newPath = resolvePath(newPath, repo)
if (oldPath !== newPath) {
mkdirPSync(path.dirname(newPath))
try {
fs.renameSync(oldPath, newPath)
} catch (error) {
xfs.moveSync(oldPath, newPath)
}
}
}
const exists = (repo: string, p: string) => {
return fs.existsSync(resolvePath(p, repo))
}
const hash = (repo: string, p: string) => {
const content = read(repo, p)
return crypto.createHash('md5').update(content).digest('hex')
}
const checkHash = (repo: string, p: string, oldHash: string) => {
return oldHash === hash(repo, p)
}
const upload = (repo: string, file: any, path: string) => {
write(repo, path, fs.readFileSync(file.path))
}
const tree = (repo: string) => {
return [{
name: '/',
type: 'dir',
path: '/',
repo: repo,
children: travels(resolvePath('', repo), repo)
}]
}
const open = (repo: string, p: string) => {
let path = resolvePath(p, repo)
if (isWsl) {
path = wsl.toWinPath(path)
}
opn(path)
}
const search = (repo: string, str: string) => {
str = str.trim()
if (!str) {
return []
}
const files = [] as any
const basePath = resolvePath('', repo)
const match = (p: string, str: string) => {
return p.endsWith('.md') && !p.endsWith('.c.md') && new RegExp(str, 'i').test(fs.readFileSync(p, 'utf-8'))
}
const travelFiles = (location: string) => {
if (!fs.statSync(location).isDirectory()) {
return
}
const list = fs.readdirSync(location).filter(x => !x.startsWith('.') && !ignorePath.test(x))
list.forEach(x => {
const p = path.join(location, x)
if (fs.statSync(p).isDirectory()) {
travelFiles(p)
} else if (fs.statSync(p).isFile()) {
if (match(p, str)) {
files.push({
repo,
name: x,
path: p.replace(basePath, ''),
type: 'file',
})
}
}
})
}
travelFiles(resolvePath('', repo))
return files
}
export default {
read,
write,
rm,
mv,
exists,
hash,
checkHash,
upload,
tree,
open,
search,
}
+16
View File
@@ -0,0 +1,16 @@
import * as fs from 'fs'
import { USER_DIR, TRASH_DIR, MAIN_REPO_DIR } from './constant'
const init = () => {
if (!fs.existsSync(USER_DIR)) {
fs.mkdirSync(USER_DIR)
}
if (!fs.existsSync(MAIN_REPO_DIR)) {
fs.mkdirSync(MAIN_REPO_DIR)
}
if (!fs.existsSync(TRASH_DIR)) {
fs.mkdirSync(TRASH_DIR)
}
}
export default init
+230
View File
@@ -0,0 +1,230 @@
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as Koa from 'koa'
import * as bodyParser from 'koa-body'
import * as xStatic from 'koa-static'
import * as mime from 'mime'
import * as request from 'request'
import * as pty from 'node-pty'
import { STATIC_DIR, HOME_DIR } from './constant'
import init from './init'
import file from './file'
import dataRepository from './repository'
import run from './run'
import convert from './convert'
import plantuml from './plantuml'
const result = (status = 'ok', message = '操作成功', data: any = null) => {
return { status, message, data }
}
const fileContent = async (ctx: any, next: any) => {
if (ctx.path === '/api/file') {
if (ctx.method === 'GET') {
ctx.body = result('ok', '获取成功', {
content: file.read(ctx.query.repo, ctx.query.path).toString(),
hash: file.hash(ctx.query.repo, ctx.query.path)
})
} else if (ctx.method === 'POST') {
const oldHash = ctx.request.body.old_hash
if (!oldHash) {
throw new Error('未传递文件hash')
} else if (oldHash === 'new' && file.exists(ctx.request.body.repo, ctx.request.body.path)) {
throw new Error('文件已经存在')
} else if (oldHash !== 'new' && !file.checkHash(ctx.request.body.repo, ctx.request.body.path, oldHash)) {
throw new Error('磁盘文件已经更新,请刷新文件')
}
const hash = file.write(ctx.request.body.repo, ctx.request.body.path, ctx.request.body.content)
ctx.body = result('ok', '保存成功', hash)
} else if (ctx.method === 'DELETE') {
file.rm(ctx.query.repo, ctx.query.path)
ctx.body = result()
} else if (ctx.method === 'PATCH') {
if (file.exists(ctx.request.body.repo, ctx.request.body.newPath)) {
throw new Error('文件已经存在')
}
file.mv(ctx.request.body.repo, ctx.request.body.oldPath, ctx.request.body.newPath)
ctx.body = result()
}
} else if (ctx.path === '/api/tree') {
ctx.body = result('ok', '获取成功', file.tree(ctx.query.repo))
} else {
await next()
}
}
const attachment = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/attachment')) {
if (ctx.method === 'POST') {
const path = ctx.request.body.fields.path
const repo = ctx.request.body.fields.repo
file.upload(repo, ctx.request.body.files.attachment, path)
ctx.body = result('ok', '上传成功', path)
} else if (ctx.method === 'GET') {
ctx.type = mime.getType(ctx.query.path)
ctx.body = file.read(ctx.query.repo, ctx.query.path)
}
} else {
await next()
}
}
const open = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/open')) {
if (ctx.method === 'GET') {
file.open(ctx.query.repo, ctx.query.path)
ctx.body = result()
}
} else {
await next()
}
}
const searchFile = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/search')) {
const search = ctx.query.search
const repo = ctx.query.repo
ctx.body = result('ok', '操作成功', file.search(repo, search))
} else {
await next()
}
}
const repository = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/repositories')) {
ctx.body = result('ok', '获取成功', dataRepository.list())
} else {
await next()
}
}
const plantumlGen = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/plantuml/png')) {
ctx.set('cache-control', 'max-age=86400') // 一天过期
ctx.type = 'image/png'
ctx.body = plantuml.generate(ctx.query.data)
} else {
await next()
}
}
const runCode = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/run')) {
const rst = run.runCode(ctx.request.body.language, ctx.request.body.code)
ctx.body = result('ok', '运行成功', rst)
} else {
await next()
}
}
const convertFile = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/convert/')) {
const html = ctx.request.body.html
const type = ctx.request.body.type
ctx.type = mime.getType(`file.${type}`)
ctx.body = await convert(html, type)
} else {
await next()
}
}
const proxy = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/proxy')) {
const url = ctx.query.url
ctx.body = request(url)
} else {
await next()
}
}
const readme = async (ctx: any, next: any) => {
if (ctx.path.startsWith('/api/readme')) {
if (ctx.query.path) {
ctx.type = mime.getType(ctx.query.path)
ctx.body = fs.readFileSync(path.join(STATIC_DIR, 'readme', ctx.query.path))
} else {
ctx.body = result('ok', '获取成功', {
content: fs.readFileSync(path.join(STATIC_DIR, 'readme/README.md')).toString()
})
}
} else {
await next()
}
}
const wrapper = async (ctx: any, next: any, fun: any) => {
try {
await fun(ctx, next)
} catch (error) {
ctx.body = result('error', error.message)
}
}
const server = (port = 3000) => {
init()
const app = new Koa()
app.use(xStatic(STATIC_DIR))
app.use(async (ctx: any, next: any) => {
if (ctx.path.startsWith('/static')) {
ctx.response.redirect(ctx.path.replace(/^\/static/, ''))
} else {
await next()
}
})
app.use(bodyParser({
multipart: true,
formLimit: '20mb',
jsonLimit: '20mb',
textLimit: '20mb'
}))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, fileContent))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, attachment))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, open))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, plantumlGen))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, runCode))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, convertFile))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, searchFile))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, repository))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, proxy))
app.use(async (ctx: any, next: any) => await wrapper(ctx, next, readme))
const server = require('http').createServer(app.callback())
const io = require('socket.io')(server, {path: '/ws'})
io.on('connection', (socket: any) => {
const ptyProcess = pty.spawn(os.platform() === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: HOME_DIR,
env: process.env
})
ptyProcess.on('data', (data: any) => socket.emit('output', data))
ptyProcess.on('exit', () => socket.disconnect())
socket.on('input', (data: any) => ptyProcess.write(data))
socket.on('resize', (size: any) => ptyProcess.resize(size[0], size[1]))
socket.on('disconnect', () => ptyProcess.kill())
})
server.listen(port)
console.log(`访问地址:http://localhost:${port}`)
}
if (require.main === module) {
const args = process.argv.slice(2)
server(Number(args[0]) || 3000)
}
export default server
+18
View File
@@ -0,0 +1,18 @@
import config from './config'
import * as request from 'request'
const plantuml = require('node-plantuml')
const generate = (data: any) => {
const api = config.get('plantuml-api', 'local')
if (api === 'local') {
const gen = plantuml.generate(data, {format: 'png'});
return gen.out
} else {
return request(api.replace('{data}', encodeURIComponent(data)))
}
}
export default {
generate
}
+38
View File
@@ -0,0 +1,38 @@
import * as path from 'path'
import config from './config'
import { MAIN_REPO_DIR, TRASH_DIR } from './constant'
import { isWsl, toWslPath, toWinPath } from './wsl'
const configKey = 'repositories'
// windows 上不管是不是 wsl 环境都使用 windows 风格的路径配置
const defaultVal = { main: isWsl ? toWinPath(MAIN_REPO_DIR) : MAIN_REPO_DIR }
const list = () => {
return config.get(configKey, defaultVal)
}
const getPath = (name: string) => {
let p = config.get(configKey, defaultVal)[name] || ''
p = p.trim()
if (!p) {
return null
}
if (isWsl) {
return /^[a-zA-Z]:/.test(p) ? toWslPath(p) : path.join(MAIN_REPO_DIR, p)
}
return path.isAbsolute(p) ? p : path.join(MAIN_REPO_DIR, p)
}
const getTrashPath = (name: string) => {
return path.join(TRASH_DIR, name)
}
export default {
list,
getPath,
getTrashPath,
}
+11 -8
View File
@@ -1,11 +1,12 @@
const fs = require('fs')
const os = require('os');
const path = require('path')
const isWsl = require('is-wsl')
const execFileSync = require("child_process").execFileSync
const wsl = require('./wsl')
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import { execFileSync } from 'child_process'
import * as wsl from './wsl'
const runCode = (language, code) => {
const isWsl = wsl.isWsl
const runCode = (language: string, code: string) => {
try {
switch (language) {
case 'bat':
@@ -38,4 +39,6 @@ const runCode = (language, code) => {
}
}
module.exports = { runCode }
export default {
runCode
}
+39
View File
@@ -0,0 +1,39 @@
import * as os from 'os'
import * as fs from 'fs'
import { execFileSync } from 'child_process'
export const getIsWsl = () => {
if (process.platform !== 'linux') {
return false;
}
if (os.release().toLowerCase().includes('microsoft')) {
return true;
}
try {
if (fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) {
return true;
}
} catch (_) {
return false;
}
}
export const isWsl = getIsWsl()
export const toWslPath = (path: string) => {
return execFileSync('wslpath', ['-u', `${path.replace(/\\/g, '/')}`]).toString().trim()
}
export const toWinPath = (path: string) => {
return execFileSync('wslpath', ['-w', path]).toString().trim()
}
export const getWinTempPath = () => {
return execFileSync('cmd.exe', ['/c', 'echo %temp%']).toString().trim()
}
export const getWinHomePath = () => {
return execFileSync('cmd.exe', ['/c', 'echo %HOMEDRIVE%%HOMEPATH%']).toString().trim()
}
@@ -1,2 +1,3 @@
*
!.gitignore
!readme
+16
View File
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"noImplicitAny": true,
"outDir": "./dist",
"sourceMap": true
},
"typeRoots": [
"node_modules/@types",
"global.d.ts"
],
"include": [
"src/**/*"
]
}
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
node_modules
-37
View File
@@ -1,37 +0,0 @@
const fs = require('fs')
const path = require('path')
const configFile = path.join(__dirname, '../', 'config.json')
const writeJson = data => {
fs.writeFileSync(configFile, JSON.stringify(data, null, 4), 'utf8');
}
const readJson = () => {
try {
const data = fs.readFileSync(configFile)
return JSON.parse(data.toString())
} catch (error) {
return null
}
}
const set = (key, value) => {
const config = readJson() || {}
config[key] = value
writeJson(config)
}
exports.set = set
exports.get = (key, defaultVal = null) => {
const config = readJson() || {}
if (config[key] === undefined) {
set(key, defaultVal) // 写入默认值到配置文件
return defaultVal
}
return config[key]
}
-34
View File
@@ -1,34 +0,0 @@
const os = require('os')
const fs = require('fs')
const spawn = require("child_process").spawn
const convert = async (html, type) => {
try {
const path = os.tmpdir() + `/yn_convert_${new Date().getTime()}.${type}`
return new Promise((resolve, reject) => {
const process = spawn('pandoc', ['-f', 'html', '-o', path])
process.on('close', (code) => {
try {
const data = fs.readFileSync(path)
fs.unlinkSync(path)
resolve(data)
} catch (error) {
reject(error)
}
})
process.on('error', error => {
reject(error)
})
process.stdin.write(html)
process.stdin.end()
})
} catch (e) {
return e.message
}
}
module.exports = convert
-213
View File
@@ -1,213 +0,0 @@
const fs = require('fs')
const xfs = require('fs-extra')
const path = require('path')
const opn = require('opn')
const isWsl = require('is-wsl')
const crypto = require('crypto')
const NaturalOrderby = require('natural-orderby')
const wsl = require('./wsl')
const repository = require('./repository')
const ignorePath = /node_modules/
const mkdirPSync = location => {
let normalizedPath = path.normalize(location)
let parsedPathObj = path.parse(normalizedPath)
let curDir = parsedPathObj.root
let folders = parsedPathObj.dir.split(path.sep)
folders.push(parsedPathObj.base)
for(let part of folders) {
if (part.indexOf(':') > -1) { // 修复 Windows 下面路径错误
continue
}
curDir = path.join(curDir, part)
if (!fs.existsSync(curDir)) {
fs.mkdirSync(curDir)
}
}
}
const rmRecursiveSync = location => {
if (fs.statSync(location).isDirectory()) {
let _ = fs.readdirSync(location)
_.forEach(childItemName => {
rmRecursiveSync(path.join(location, childItemName))
})
fs.rmdirSync(location)
} else {
fs.unlinkSync(location)
}
}
const resolvePath = (p, repo = 'main') => {
p = p.replace(/\.\./g, '')
const basePath = repository.getPath(repo)
if (!basePath) {
throw new Error(`仓库 ${repo} 不存在`)
}
return path.join(basePath, p)
}
const travels = (location, repo, basePath = null) => {
if (!basePath) {
basePath = resolvePath('', repo)
}
if (!fs.statSync(location).isDirectory()) {
return []
}
const list = fs.readdirSync(location).filter(x => !x.startsWith('.') && !ignorePath.test(x))
const dirs = NaturalOrderby.orderBy(list.filter(x => fs.statSync(path.join(location, x)).isDirectory()))
const files = NaturalOrderby.orderBy(list.filter(x => !fs.statSync(path.join(location, x)).isDirectory()))
return dirs.map(x => {
const p = path.join(location, x)
const xpath = p.replace(basePath, '')
return {
name: x,
path: xpath,
type: 'dir',
repo: repo,
children: travels(p, repo, basePath)
}
}).concat(files.map(x => {
const p = path.join(location, x)
const xpath = p.replace(basePath, '')
return {
name: x,
path: xpath,
type: 'file',
repo: repo,
}
}))
}
exports.read = (repo, p) => {
return fs.readFileSync(resolvePath(p, repo))
}
exports.write = (repo, p, content) => {
p = resolvePath(p, repo)
mkdirPSync(path.dirname(p))
fs.writeFileSync(p, content)
return crypto.createHash('md5').update(content).digest('hex')
}
exports.rm = (repo, p) => {
if (resolvePath(p) !== resolvePath('', repo)) {
const newPath = path.join(repository.getTrashPath(repo), p.replace(/\.\./g, '')) + '.' + (new Date).getTime()
mkdirPSync(path.dirname(newPath))
try {
fs.renameSync(resolvePath(p, repo), newPath)
} catch (error) {
xfs.moveSync(resolvePath(p, repo), newPath)
}
}
}
exports.mv = (repo, oldPath, newPath) => {
oldPath = resolvePath(oldPath, repo)
newPath = resolvePath(newPath, repo)
if (oldPath !== newPath) {
mkdirPSync(path.dirname(newPath))
try {
fs.renameSync(oldPath, newPath)
} catch (error) {
xfs.moveSync(oldPath, newPath)
}
}
}
exports.exists = (repo, p) => {
return fs.existsSync(resolvePath(p, repo))
}
exports.hash = (repo, p) => {
const content = exports.read(repo, p)
return crypto.createHash('md5').update(content).digest('hex')
}
exports.checkHash = (repo, p, oldHash) => {
return oldHash === exports.hash(repo, p)
}
exports.upload = (repo, file, path) => {
exports.write(repo, path, fs.readFileSync(file.path))
}
exports.tree = repo => {
return [{
name: '/',
type: 'dir',
path: '/',
repo: repo,
children: travels(resolvePath('', repo), repo)
}]
}
exports.open = (repo, p) => {
let path = resolvePath(p, repo)
if (isWsl) {
path = wsl.toWinPath(path)
}
opn(path)
}
exports.search = (repo, str) => {
str = str.trim()
if (!str) {
return []
}
const files = []
const basePath = resolvePath('', repo)
const match = (p, str) => {
return p.endsWith('.md') && !p.endsWith('.c.md') && new RegExp(str, 'i').test(fs.readFileSync(p, 'utf-8'))
}
const travelFiles = location => {
if (!fs.statSync(location).isDirectory()) {
return
}
const list = fs.readdirSync(location).filter(x => !x.startsWith('.') && !ignorePath.test(x))
list.forEach(x => {
const p = path.join(location, x)
if (fs.statSync(p).isDirectory()) {
travelFiles(p)
} else if (fs.statSync(p).isFile()) {
if (match(p, str)) {
files.push({
repo,
name: x,
path: p.replace(basePath, ''),
type: 'file',
})
}
}
})
}
travelFiles(resolvePath('', repo))
return files
}
-214
View File
@@ -1,214 +0,0 @@
const fs = require('fs')
const os = require('os')
const path = require('path')
const Koa = require('koa')
const bodyParser = require('koa-body')
const static = require('koa-static')
const mime = require('mime')
const request = require('request')
const pty = require('node-pty')
const file = require('./file')
const dataRepository = require('./repository')
const run = require('./run')
const convert = require('./convert')
const plantuml = require('./plantuml')
const result = (status = 'ok', message = '操作成功', data = null) => {
return { status, message, data }
}
const fileContent = async (ctx, next) => {
if (ctx.path === '/api/file') {
if (ctx.method === 'GET') {
ctx.body = result('ok', '获取成功', {
content: file.read(ctx.query.repo, ctx.query.path).toString(),
hash: file.hash(ctx.query.repo, ctx.query.path)
})
} else if (ctx.method === 'POST') {
const oldHash = ctx.request.body.old_hash
if (!oldHash) {
throw new Error('未传递文件hash')
} else if (oldHash === 'new' && file.exists(ctx.request.body.repo, ctx.request.body.path)) {
throw new Error('文件已经存在')
} else if (oldHash !== 'new' && !file.checkHash(ctx.request.body.repo, ctx.request.body.path, oldHash)) {
throw new Error('磁盘文件已经更新,请刷新文件')
}
const hash = file.write(ctx.request.body.repo, ctx.request.body.path, ctx.request.body.content)
ctx.body = result('ok', '保存成功', hash)
} else if (ctx.method === 'DELETE') {
file.rm(ctx.query.repo, ctx.query.path)
ctx.body = result()
} else if (ctx.method === 'PATCH') {
if (file.exists(ctx.request.body.repo, ctx.request.body.newPath)) {
throw new Error('文件已经存在')
}
file.mv(ctx.request.body.repo, ctx.request.body.oldPath, ctx.request.body.newPath)
ctx.body = result()
}
} else if (ctx.path === '/api/tree') {
ctx.body = result('ok', '获取成功', file.tree(ctx.query.repo))
} else {
await next()
}
}
const attachment = async (ctx, next) => {
if (ctx.path.startsWith('/api/attachment')) {
if (ctx.method === 'POST') {
const path = ctx.request.body.fields.path
const repo = ctx.request.body.fields.repo
file.upload(repo, ctx.request.body.files.attachment, path)
ctx.body = result('ok', '上传成功', path)
} else if (ctx.method === 'GET') {
ctx.type = mime.getType(ctx.query.path)
ctx.body = file.read(ctx.query.repo, ctx.query.path)
}
} else {
await next()
}
}
const open = async (ctx, next) => {
if (ctx.path.startsWith('/api/open')) {
if (ctx.method === 'GET') {
file.open(ctx.query.repo, ctx.query.path)
ctx.body = result()
}
} else {
await next()
}
}
const searchFile = async (ctx, next) => {
if (ctx.path.startsWith('/api/search')) {
const search = ctx.query.search
const repo = ctx.query.repo
ctx.body = result('ok', '操作成功', file.search(repo, search))
} else {
await next()
}
}
const repository = async (ctx, next) => {
if (ctx.path.startsWith('/api/repositories')) {
ctx.body = result('ok', '获取成功', dataRepository.list())
} else {
await next()
}
}
const plantumlGen = async (ctx, next) => {
if (ctx.path.startsWith('/api/plantuml/png')) {
ctx.set('cache-control', 'max-age=86400') // 一天过期
ctx.type = 'image/png'
ctx.body = plantuml.generate(ctx.query.data)
} else {
await next()
}
}
const runCode = async (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 {
await next()
}
}
const convertFile = async (ctx, next) => {
if (ctx.path.startsWith('/api/convert/')) {
const html = ctx.request.body.html
const type = ctx.request.body.type
ctx.type = mime.getType(`file.${type}`)
ctx.body = await convert(html, type)
} else {
await next()
}
}
const proxy = async (ctx, next) => {
if (ctx.path.startsWith('/api/proxy')) {
const url = ctx.query.url
ctx.body = request(url)
} else {
await next()
}
}
const readme = async (ctx, next) => {
if (ctx.path.startsWith('/api/readme')) {
if (ctx.query.path) {
ctx.type = mime.getType(ctx.query.path)
ctx.body = fs.readFileSync(path.join(__dirname, '../', ctx.query.path))
} else {
ctx.body = result('ok', '获取成功', {
content: fs.readFileSync(path.join(__dirname, '../README.md')).toString()
})
}
} else {
await next()
}
}
const app = new Koa()
app.use(static(
path.join(__dirname, '../static')
))
app.use(bodyParser({
multipart: true,
formLimit: '20mb',
jsonLimit: '20mb',
textLimit: '20mb'
}))
const wrapper = async (ctx, next, fun) => {
try {
await fun(ctx, next)
} catch (error) {
ctx.body = result('error', error.message)
}
}
app.use(async (ctx, next) => await wrapper(ctx, next, fileContent))
app.use(async (ctx, next) => await wrapper(ctx, next, attachment))
app.use(async (ctx, next) => await wrapper(ctx, next, open))
app.use(async (ctx, next) => await wrapper(ctx, next, plantumlGen))
app.use(async (ctx, next) => await wrapper(ctx, next, runCode))
app.use(async (ctx, next) => await wrapper(ctx, next, convertFile))
app.use(async (ctx, next) => await wrapper(ctx, next, searchFile))
app.use(async (ctx, next) => await wrapper(ctx, next, repository))
app.use(async (ctx, next) => await wrapper(ctx, next, proxy))
app.use(async (ctx, next) => await wrapper(ctx, next, readme))
const port = 3000
const server = require('http').createServer(app.callback())
const io = require('socket.io')(server, {path: '/ws'})
io.on('connection', socket => {
const ptyProcess = pty.spawn(os.platform() === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-color',
cols: 80,
rows: 24,
cwd: path.join(__dirname, '../'),
env: process.env
})
ptyProcess.on('data', data => socket.emit('output', data))
ptyProcess.on('exit', () => socket.disconnect())
socket.on('input', data => ptyProcess.write(data))
socket.on('resize', size => ptyProcess.resize(size[0], size[1]))
socket.on('disconnect', () => ptyProcess.destroy())
})
server.listen(port)
console.log(`访问地址:http://localhost:${port}`)
-16
View File
@@ -1,16 +0,0 @@
{
"dependencies": {
"fs-extra": "^6.0.1",
"koa": "^2.4.1",
"koa-body": "^2.5.0",
"koa-static": "^4.0.2",
"mime": "^2.2.0",
"natural-orderby": "^1.0.2",
"node-plantuml": "^0.6.2",
"node-pty": "^0.8.1",
"opn": "^5.3.0",
"request": "^2.88.0",
"safe-buffer": "^5.1.2",
"socket.io": "^2.2.0"
}
}
-14
View File
@@ -1,14 +0,0 @@
const plantuml = require('node-plantuml')
const config = require('./config')
const request = require('request')
exports.generate = data => {
const api = config.get('plantuml-api', 'local')
if (api === 'local') {
const gen = plantuml.generate(data, {format: 'png'});
return gen.out
} else {
return request(api.replace('{data}', encodeURIComponent(data)))
}
}
-27
View File
@@ -1,27 +0,0 @@
const path = require('path')
const config = require('./config')
const configKey = 'repositories'
const defaultVal = {"main": "./data"}
exports.list = () => {
return config.get(configKey, defaultVal)
}
exports.getPath = name => {
let p = config.get(configKey, defaultVal)[name]
if (!p) {
return null
}
if (path.isAbsolute(p)) {
return p
}
return path.join(__dirname, '../', p)
}
exports.getTrashPath = name => {
return path.join(__dirname, '/../trash/', name)
}
-13
View File
@@ -1,13 +0,0 @@
const cp = require('child_process');
exports.toWslPath = path => {
return cp.execFileSync('wslpath', ['-u', `${path.replace(/\\/g, '/')}`]).toString().trim()
}
exports.toWinPath = path => {
return cp.execFileSync('wslpath', ['-w', path]).toString().trim()
}
exports.getWinTempPath = () => {
return cp.execFileSync('cmd.exe', ['/c', 'echo %temp%']).toString().trim()
}
-881
View File
@@ -1,881 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@^1.2.2:
version "1.3.4"
resolved "http://registry.npm.taobao.org/accepts/download/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f"
dependencies:
mime-types "~2.1.16"
negotiator "0.6.1"
accepts@~1.3.4:
version "1.3.5"
resolved "http://registry.npm.taobao.org/accepts/download/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
dependencies:
mime-types "~2.1.18"
negotiator "0.6.1"
after@0.8.2:
version "0.8.2"
resolved "http://registry.npm.taobao.org/after/download/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
ajv@^5.3.0:
version "5.5.2"
resolved "http://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
any-promise@^1.0.0, any-promise@^1.1.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "http://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
asn1@~0.2.3:
version "0.2.4"
resolved "http://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
async-limiter@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
asynckit@^0.4.0:
version "0.4.0"
resolved "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
aws4@^1.8.0:
version "1.8.0"
resolved "http://registry.npm.taobao.org/aws4/download/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
backo2@1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "http://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
base64id@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/base64id/download/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "http://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
dependencies:
tweetnacl "^0.14.3"
better-assert@~1.0.0:
version "1.0.2"
resolved "http://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
dependencies:
callsite "1.0.0"
blob@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
bytes@3.0.0:
version "3.0.0"
resolved "http://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
callsite@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
caseless@~0.12.0:
version "0.12.0"
resolved "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
co-body@^5.1.1:
version "5.1.1"
resolved "http://registry.npm.taobao.org/co-body/download/co-body-5.1.1.tgz#d97781d1e3344ba4a820fd1806bddf8341505236"
dependencies:
inflation "^2.0.0"
qs "^6.4.0"
raw-body "^2.2.0"
type-is "^1.6.14"
co@^4.6.0:
version "4.6.0"
resolved "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
combined-stream@1.0.6, combined-stream@~1.0.6:
version "1.0.6"
resolved "http://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
delayed-stream "~1.0.0"
commander@^2.8.1:
version "2.16.0"
resolved "http://registry.npm.taobao.org/commander/download/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50"
component-bind@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
component-emitter@1.2.1:
version "1.2.1"
resolved "http://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
component-inherit@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
content-disposition@~0.5.0:
version "0.5.2"
resolved "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
content-type@^1.0.0:
version "1.0.4"
resolved "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
cookie@0.3.1:
version "0.3.1"
resolved "http://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
cookies@~0.7.0:
version "0.7.1"
resolved "http://registry.npm.taobao.org/cookies/download/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b"
dependencies:
depd "~1.1.1"
keygrip "~1.0.2"
core-util-is@1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
dashdash@^1.12.0:
version "1.14.1"
resolved "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
assert-plus "^1.0.0"
debug@*, debug@~3.1.0:
version "3.1.0"
resolved "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debug@^2.6.3, debug@^2.6.8:
version "2.6.9"
resolved "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
debug@~4.1.0:
version "4.1.1"
resolved "http://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
dependencies:
ms "^2.1.1"
deep-equal@~1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/deep-equal/download/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
delegates@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/delegates/download/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
depd@1.1.1:
version "1.1.1"
resolved "http://registry.npm.taobao.org/depd/download/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
depd@^1.1.0, depd@~1.1.1:
version "1.1.2"
resolved "http://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
destroy@^1.0.3:
version "1.0.4"
resolved "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ee-first@1.1.1:
version "1.1.1"
resolved "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
engine.io-client@~3.3.1:
version "3.3.2"
resolved "http://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.3.2.tgz#04e068798d75beda14375a264bb3d742d7bc33aa"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
debug "~3.1.0"
engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
parseqs "0.0.5"
parseuri "0.0.5"
ws "~6.1.0"
xmlhttprequest-ssl "~1.5.4"
yeast "0.1.2"
engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
version "2.1.3"
resolved "http://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6"
dependencies:
after "0.8.2"
arraybuffer.slice "~0.0.7"
base64-arraybuffer "0.1.5"
blob "0.0.5"
has-binary2 "~1.0.2"
engine.io@~3.3.1:
version "3.3.2"
resolved "http://registry.npm.taobao.org/engine.io/download/engine.io-3.3.2.tgz#18cbc8b6f36e9461c5c0f81df2b830de16058a59"
dependencies:
accepts "~1.3.4"
base64id "1.0.0"
cookie "0.3.1"
debug "~3.1.0"
engine.io-parser "~2.1.0"
ws "~6.1.0"
error-inject@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/error-inject/download/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37"
escape-html@~1.0.1:
version "1.0.3"
resolved "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
extend@~3.0.2:
version "3.0.2"
resolved "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
extsprintf@1.3.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
extsprintf@^1.2.0:
version "1.4.0"
resolved "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
forever-agent@~0.6.1:
version "0.6.1"
resolved "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@~2.3.2:
version "2.3.2"
resolved "http://registry.npm.taobao.org/form-data/download/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
asynckit "^0.4.0"
combined-stream "1.0.6"
mime-types "^2.1.12"
formidable@^1.1.1:
version "1.1.1"
resolved "http://registry.npm.taobao.org/formidable/download/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9"
fresh@^0.5.2:
version "0.5.2"
resolved "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
fs-extra@^6.0.1:
version "6.0.1"
resolved "http://registry.npm.taobao.org/fs-extra/download/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^4.0.0"
universalify "^0.1.0"
getpass@^0.1.1:
version "0.1.7"
resolved "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
dependencies:
assert-plus "^1.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
version "4.1.11"
resolved "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
har-schema@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
har-validator@~5.1.0:
version "5.1.0"
resolved "http://registry.npm.taobao.org/har-validator/download/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
dependencies:
ajv "^5.3.0"
har-schema "^2.0.0"
has-binary2@~1.0.2:
version "1.0.3"
resolved "http://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
dependencies:
isarray "2.0.1"
has-cors@1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
http-assert@^1.1.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/http-assert/download/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a"
dependencies:
deep-equal "~1.0.1"
http-errors "~1.6.1"
http-errors@1.6.2, http-errors@^1.2.8, http-errors@^1.6.1, http-errors@~1.6.1, http-errors@~1.6.2:
version "1.6.2"
resolved "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
dependencies:
depd "1.1.1"
inherits "2.0.3"
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-signature@~1.2.0:
version "1.2.0"
resolved "http://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
iconv-lite@0.4.19:
version "0.4.19"
resolved "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
indexof@0.0.1:
version "0.0.1"
resolved "http://registry.npm.taobao.org/indexof/download/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
inflation@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/inflation/download/inflation-2.0.0.tgz#8b417e47c28f925a45133d914ca1fd389107f30f"
inherits@2.0.3:
version "2.0.3"
resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-generator-function@^1.0.3:
version "1.0.7"
resolved "http://registry.npm.taobao.org/is-generator-function/download/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
is-wsl@^1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
isarray@2.0.1:
version "2.0.1"
resolved "http://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
isstream@~0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
jsbn@~0.1.0:
version "0.1.1"
resolved "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "http://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
json-schema@0.2.3:
version "0.2.3"
resolved "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
jsonfile@^4.0.0:
version "4.0.0"
resolved "http://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
optionalDependencies:
graceful-fs "^4.1.6"
jsprim@^1.2.2:
version "1.4.1"
resolved "http://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
keygrip@~1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/keygrip/download/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
koa-body@^2.5.0:
version "2.5.0"
resolved "http://registry.npm.taobao.org/koa-body/download/koa-body-2.5.0.tgz#84e8fcd8d5229a8cc1cb98a926e939069e716915"
dependencies:
co-body "^5.1.1"
formidable "^1.1.1"
koa-compose@^3.0.0:
version "3.2.1"
resolved "http://registry.npm.taobao.org/koa-compose/download/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7"
dependencies:
any-promise "^1.1.0"
koa-compose@^4.0.0:
version "4.0.0"
resolved "http://registry.npm.taobao.org/koa-compose/download/koa-compose-4.0.0.tgz#2800a513d9c361ef0d63852b038e4f6f2d5a773c"
koa-convert@^1.2.0:
version "1.2.0"
resolved "http://registry.npm.taobao.org/koa-convert/download/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0"
dependencies:
co "^4.6.0"
koa-compose "^3.0.0"
koa-is-json@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/koa-is-json/download/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14"
koa-send@^4.1.0:
version "4.1.3"
resolved "http://registry.npm.taobao.org/koa-send/download/koa-send-4.1.3.tgz#0822207bbf5253a414c8f1765ebc29fa41353cb6"
dependencies:
debug "^2.6.3"
http-errors "^1.6.1"
mz "^2.6.0"
resolve-path "^1.4.0"
koa-static@^4.0.2:
version "4.0.2"
resolved "http://registry.npm.taobao.org/koa-static/download/koa-static-4.0.2.tgz#6cda92d88d771dcaad9f0d825cd94a631c861a1a"
dependencies:
debug "^2.6.8"
koa-send "^4.1.0"
koa@^2.4.1:
version "2.4.1"
resolved "http://registry.npm.taobao.org/koa/download/koa-2.4.1.tgz#d449cfb970a7e9da571f699eda40bb9e32eb1484"
dependencies:
accepts "^1.2.2"
content-disposition "~0.5.0"
content-type "^1.0.0"
cookies "~0.7.0"
debug "*"
delegates "^1.0.0"
depd "^1.1.0"
destroy "^1.0.3"
error-inject "~1.0.0"
escape-html "~1.0.1"
fresh "^0.5.2"
http-assert "^1.1.0"
http-errors "^1.2.8"
is-generator-function "^1.0.3"
koa-compose "^4.0.0"
koa-convert "^1.2.0"
koa-is-json "^1.0.0"
mime-types "^2.0.7"
on-finished "^2.1.0"
only "0.0.2"
parseurl "^1.3.0"
statuses "^1.2.0"
type-is "^1.5.5"
vary "^1.0.0"
media-typer@0.3.0:
version "0.3.0"
resolved "http://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
mime-db@~1.30.0:
version "1.30.0"
resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
mime-db@~1.35.0:
version "1.35.0"
resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
mime-db@~1.37.0:
version "1.37.0"
resolved "http://registry.npm.taobao.org/mime-db/download/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
mime-types@^2.0.7, mime-types@~2.1.15, mime-types@~2.1.16:
version "2.1.17"
resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
dependencies:
mime-db "~1.30.0"
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.19"
resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0"
dependencies:
mime-db "~1.35.0"
mime-types@~2.1.18:
version "2.1.21"
resolved "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
dependencies:
mime-db "~1.37.0"
mime@^2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/mime/download/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b"
ms@2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
ms@^2.1.1:
version "2.1.1"
resolved "http://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
mz@^2.6.0:
version "2.7.0"
resolved "http://registry.npm.taobao.org/mz/download/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
dependencies:
any-promise "^1.0.0"
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@2.12.1:
version "2.12.1"
resolved "http://registry.npm.taobao.org/nan/download/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
natural-orderby@^1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/natural-orderby/download/natural-orderby-1.0.2.tgz#287a4778217fbf6af0d704129d55ae90450f7a88"
negotiator@0.6.1:
version "0.6.1"
resolved "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
node-nailgun-client@^0.1.0:
version "0.1.0"
resolved "http://registry.npm.taobao.org/node-nailgun-client/download/node-nailgun-client-0.1.0.tgz#b6424db0603781a193f367385e2d615ca6df20b2"
dependencies:
commander "^2.8.1"
node-nailgun-server@^0.1.3:
version "0.1.3"
resolved "http://registry.npm.taobao.org/node-nailgun-server/download/node-nailgun-server-0.1.3.tgz#a650918e48615c8d7c4d2393db67316e5ad012f1"
dependencies:
commander "^2.8.1"
node-plantuml@^0.6.2:
version "0.6.2"
resolved "http://registry.npm.taobao.org/node-plantuml/download/node-plantuml-0.6.2.tgz#cf159fe5bc028a54d5181e2410309266d1fde617"
dependencies:
commander "^2.8.1"
node-nailgun-client "^0.1.0"
node-nailgun-server "^0.1.3"
plantuml-encoder "^1.2.5"
node-pty@^0.8.1:
version "0.8.1"
resolved "http://registry.npm.taobao.org/node-pty/download/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f"
dependencies:
nan "2.12.1"
oauth-sign@~0.9.0:
version "0.9.0"
resolved "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
object-assign@^4.0.1:
version "4.1.1"
resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
object-component@0.0.3:
version "0.0.3"
resolved "http://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
on-finished@^2.1.0:
version "2.3.0"
resolved "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
dependencies:
ee-first "1.1.1"
only@0.0.2:
version "0.0.2"
resolved "http://registry.npm.taobao.org/only/download/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
opn@^5.3.0:
version "5.3.0"
resolved "http://registry.npm.taobao.org/opn/download/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
dependencies:
is-wsl "^1.1.0"
pako@1.0.3:
version "1.0.3"
resolved "http://registry.npm.taobao.org/pako/download/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf"
parseqs@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
dependencies:
better-assert "~1.0.0"
parseuri@0.0.5:
version "0.0.5"
resolved "http://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
dependencies:
better-assert "~1.0.0"
parseurl@^1.3.0:
version "1.3.2"
resolved "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
path-is-absolute@1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
performance-now@^2.1.0:
version "2.1.0"
resolved "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
plantuml-encoder@^1.2.5:
version "1.2.5"
resolved "http://registry.npm.taobao.org/plantuml-encoder/download/plantuml-encoder-1.2.5.tgz#6b8e5b9e1a1dbd88b3fd9fb46f734eec7d44b548"
dependencies:
pako "1.0.3"
utf8-bytes "0.0.1"
psl@^1.1.24:
version "1.1.29"
resolved "http://registry.npm.taobao.org/psl/download/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
punycode@^1.4.1:
version "1.4.1"
resolved "http://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@^6.4.0:
version "6.5.1"
resolved "http://registry.npm.taobao.org/qs/download/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
qs@~6.5.2:
version "6.5.2"
resolved "http://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
raw-body@^2.2.0:
version "2.3.2"
resolved "http://registry.npm.taobao.org/raw-body/download/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
dependencies:
bytes "3.0.0"
http-errors "1.6.2"
iconv-lite "0.4.19"
unpipe "1.0.0"
request@^2.88.0:
version "2.88.0"
resolved "http://registry.npm.taobao.org/request/download/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.0"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.4.3"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
resolve-path@^1.4.0:
version "1.4.0"
resolved "http://registry.npm.taobao.org/resolve-path/download/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7"
dependencies:
http-errors "~1.6.2"
path-is-absolute "1.0.1"
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.1.2"
resolved "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
setprototypeof@1.0.3:
version "1.0.3"
resolved "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
socket.io-adapter@~1.1.0:
version "1.1.1"
resolved "http://registry.npm.taobao.org/socket.io-adapter/download/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b"
socket.io-client@2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.2.0.tgz#84e73ee3c43d5020ccc1a258faeeb9aec2723af7"
dependencies:
backo2 "1.0.2"
base64-arraybuffer "0.1.5"
component-bind "1.0.0"
component-emitter "1.2.1"
debug "~3.1.0"
engine.io-client "~3.3.1"
has-binary2 "~1.0.2"
has-cors "1.1.0"
indexof "0.0.1"
object-component "0.0.3"
parseqs "0.0.5"
parseuri "0.0.5"
socket.io-parser "~3.3.0"
to-array "0.1.4"
socket.io-parser@~3.3.0:
version "3.3.0"
resolved "http://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f"
dependencies:
component-emitter "1.2.1"
debug "~3.1.0"
isarray "2.0.1"
socket.io@^2.2.0:
version "2.2.0"
resolved "http://registry.npm.taobao.org/socket.io/download/socket.io-2.2.0.tgz#f0f633161ef6712c972b307598ecd08c9b1b4d5b"
dependencies:
debug "~4.1.0"
engine.io "~3.3.1"
has-binary2 "~1.0.2"
socket.io-adapter "~1.1.0"
socket.io-client "2.2.0"
socket.io-parser "~3.3.0"
sshpk@^1.7.0:
version "1.14.2"
resolved "http://registry.npm.taobao.org/sshpk/download/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
dashdash "^1.12.0"
getpass "^0.1.1"
safer-buffer "^2.0.2"
optionalDependencies:
bcrypt-pbkdf "^1.0.0"
ecc-jsbn "~0.1.1"
jsbn "~0.1.0"
tweetnacl "~0.14.0"
"statuses@>= 1.3.1 < 2", statuses@^1.2.0:
version "1.4.0"
resolved "http://registry.npm.taobao.org/statuses/download/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
thenify-all@^1.0.0:
version "1.6.0"
resolved "http://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
dependencies:
thenify ">= 3.1.0 < 4"
"thenify@>= 3.1.0 < 4":
version "3.3.0"
resolved "http://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
dependencies:
any-promise "^1.0.0"
to-array@0.1.4:
version "0.1.4"
resolved "http://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "http://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "http://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
type-is@^1.5.5, type-is@^1.6.14:
version "1.6.15"
resolved "http://registry.npm.taobao.org/type-is/download/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
dependencies:
media-typer "0.3.0"
mime-types "~2.1.15"
universalify@^0.1.0:
version "0.1.2"
resolved "http://registry.npm.taobao.org/universalify/download/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
unpipe@1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
utf8-bytes@0.0.1:
version "0.0.1"
resolved "http://registry.npm.taobao.org/utf8-bytes/download/utf8-bytes-0.0.1.tgz#116b025448c9b500081cdfbf1f4d6c6c37d8837d"
uuid@^3.3.2:
version "3.3.2"
resolved "http://registry.npm.taobao.org/uuid/download/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
vary@^1.0.0:
version "1.1.2"
resolved "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
verror@1.10.0:
version "1.10.0"
resolved "http://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
ws@~6.1.0:
version "6.1.3"
resolved "http://registry.npm.taobao.org/ws/download/ws-6.1.3.tgz#d2d2e5f0e3c700ef2de89080ebc0ac6e1bf3a72d"
dependencies:
async-limiter "~1.0.0"
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "http://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
yeast@0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
-12
View File
@@ -1,12 +0,0 @@
{
"scripts": {
"build": "tsc",
"start": "npm run build && electron ./dist/app.js"
},
"devDependencies": {
"@types/node": "^12.6.1",
"electron": "^5.0.6",
"ts-node": "^8.3.0",
"typescript": "^3.5.3"
}
}
-12
View File
@@ -1,12 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"noImplicitAny": true,
"outDir": "./dist",
"sourceMap": true
},
"include": [
"src/**/*"
]
}
+2 -2
View File
@@ -6,8 +6,8 @@
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"dist": "npm run build && cp -r dist/* ../static/",
"dist-win": "npm run build && xcopy dist\\* ..\\static\\ /s /e /y"
"dist": "npm run build && cp -r ../*.* ../app/static/readme/ && cp -r dist/* ../app/static/",
"dist-win": "npm run build && xcopy ..\\*.* ..\\app\\static\\readme\\ /s /e /y && xcopy dist\\* ..\\app\\static\\ /s /e /y"
},
"dependencies": {
"core-js": "^2.6.5",
+9 -3
View File
@@ -6,6 +6,8 @@
import dayjs from 'dayjs'
import TurndownService from 'turndown'
const isElectron = !!(window && window.process && window.process.versions && window.process.versions['electron'])
const keys = {}
export default {
@@ -19,8 +21,8 @@ export default {
}
},
mounted () {
if (!(window).require) {
let loaderScript = document.createElement('script')
if (!(isElectron && window.AMDLoader && window.AMDLoader.global && window.AMDLoader.global.require) || !(window).require) {
const loaderScript = document.createElement('script')
loaderScript.type = 'text/javascript'
loaderScript.src = 'vs/loader.js'
loaderScript.addEventListener('load', this.onGotAmdLoader)
@@ -50,7 +52,11 @@ export default {
}
},
onGotAmdLoader () {
window.require(['vs/editor/editor.main'], () => {
const xrequire = isElectron ? window.AMDLoader.global.require : window.require
if (isElectron) {
xrequire.config({ paths: { 'vs': 'static/vs' } })
}
xrequire(['vs/editor/editor.main'], () => {
this.initMonaco()
})
},
+15 -3
View File
@@ -7,15 +7,21 @@
<script>
import { mapState } from 'vuex'
const isElectron = !!(window && window.process && window.process.versions && window.process.versions['electron'])
export default {
name: 'title-bar',
mounted () {
window.onbeforeunload = () => {
return !this.saved || null
if (!isElectron) {
window.onbeforeunload = () => {
return !this.saved || null
}
}
},
beforeDestroy () {
window.onbeforeunload = null
if (!isElectron) {
window.onbeforeunload = null
}
},
computed: {
...mapState('app', ['currentFile', 'savedAt', 'previousContent', 'currentContent']),
@@ -55,6 +61,12 @@ export default {
document.title = val
},
},
saved: {
immediate: true,
handler (val) {
window.documentSaved = val
},
},
}
}
</script>
+1 -1
View File
@@ -62,7 +62,7 @@ const write = async ({ repo, path }, content, oldHash) => {
body: JSON.stringify({ repo, path, content, old_hash: oldHash })
})
return { hash: result.data.hash }
return { hash: result.data }
}
const move = async ({ repo, path }, newPath) => {
+5 -2
View File
@@ -2,11 +2,14 @@ module.exports = {
devServer: {
port: 8066,
proxy: {
'/static': {
target: 'http://localhost:3044'
},
'/api': {
target: 'http://localhost:3000'
target: 'http://localhost:3044'
},
'/ws': {
target: 'http://localhost:3000',
target: 'http://localhost:3044',
ws: true
}
}
-10
View File
@@ -1,10 +0,0 @@
{
"systemParams": "linux-x64-64",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}
-2
View File
@@ -1,2 +0,0 @@
*
!.gitignore
-4
View File
@@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1