Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
qizhang.yang committed Aug 6, 2018
0 parents commit 7c19ad7
Show file tree
Hide file tree
Showing 18 changed files with 2,584 additions and 0 deletions.
161 changes: 161 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# enjoy doc

通过本地文件,网络地址等快速生成文档

## 目的

公司的项目中有很多开发相关的文档,之前习惯放gitlab的wiki,效率和体验都不是很好,于是做了这个小工具,目的就是为了方便的组织和查看本地文档。

## 功能

- 使用markdown格式
- 支持自定义目录
- 支持*绝对路径**文件内容**网络地址*三种文件形式

目前的问题:

- 不支持页内hash导航

## 安装

本地安装

```bash
npm install enjoy-doc
```

也可以全局

```bash
npm install enjoy-doc -g
```

## 使用

定义一个导出文档文件的模块

```javascript
/**
* docs.js
*/

const path = require('path');

const base = '../js/mlrn/';
const getFilePath = p => path.join(__dirname, base, p);

module.exports = {
title: 'MED-RN文档',
nav: [
{
text: '首页',
file: '# MED-RN文档',
route: '',
},
{
text: '展示组件',
children: [
{
text: 'Header (头部)',
file: getFilePath('ui/Header/index.md'),
route: 'ui-Header',
},
{
text: 'FooterButton (底部按钮)',
file: getFilePath('ui/FooterButton/index.md'),
route: 'ui-FooterButton',
},
{
text: 'Divider (页面分隔线)',
file: getFilePath('ui/Divider/index.md'),
route: 'ui-Divider',
},
{
text: 'EmptyView (空白展示)',
file: getFilePath('ui/EmptyView/index.md'),
route: 'ui-EmptyView',
},
{
text: 'Image (图片)',
file: getFilePath('ui/Image/index.md'),
route: 'ui-Image',
},
{
text: 'InlineImage (文本图片)',
file: getFilePath('ui/InlineImage/index.md'),
route: 'ui-InlineImage',
},
{
text: 'AnimationImageView (帧动画播放)',
file: getFilePath('ui/AnimationImageView/index.md'),
route: 'ui-AnimationImageView',
},
],
},
{
text: '表单组件',
children: [
{
text: 'InputItem (文本输入)',
file: getFilePath('ui/InputItem/index.md'),
route: 'ui-InputItem',
},
{
text: 'Radio (单选框)',
file: getFilePath('ui/Radio/index.md'),
route: 'ui-Radio',
},
{
text: 'SearchInputWithClear (搜索框)',
file: getFilePath('ui/SearchInputWithClear/index.md'),
route: 'ui-SearchInputWithClear',
},
{
text: 'SidePickerView (侧边选择器)',
file: getFilePath('ui/SidePickerView/index.md'),
route: 'ui-SidePickerView',
},
],
},
// ...
],
};

```

添加一个命令

```json
"doc": "enjoy-doc ./docs.js --port=4000"
```

然后使用命令打开

```bash
npm run doc
```

### 命令参数

```bash
enjoy-doc file --port=4000
```

- `file` 配置文件
- `--port` 运行的端口

## 配置说明

- `title`: 标题
- `nav`: 导航路径

nav配置:

- `text`: 标题,必填
- `file`: 文件,支持传入*绝对路径**文件内容**网络地址*三种
- `route`: 路由的唯一名称,可选,如果不传,则不可选中
- `children`: 子导航配置,同nav

## TODO

- 支持文档内的hash导航
135 changes: 135 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* app.js
*/

const express = require('express');
const app = express();
const http = require('http');
const https = require('https');
const server = http.Server(app);
const io = require('socket.io')(server);
const path = require('path');
const fs = require('fs');
const opn = require('opn');

const docConfig = require(path.resolve(process.argv[2]));

const args = (function () {
const restArgv = process.argv.splice(3);
return restArgv.reduce((prev, item) => {
const pairs = item.split('=');
prev[pairs[0].replace(/-+/g, '')] = pairs[1] || 1;
return prev;
}, {});
})();

const routeMap = (function () {
const map = new Map();
function find(children) {
if (!children) return;

children.forEach((item) => {
if (item.route !== undefined) {
map.set(item.route || '__empty', item);
}
if (item.children) {
find(item.children);
}
});
}

find(docConfig.nav);
return map;
})();

function covertMDFile(filePath, fileData) {
return fileData.replace(/[^!]?\[(.(?!\\\]))*]\(([^)]*)\)/, function () {
const str = arguments[0];
const match = arguments[2];
if (/http(s)?/.test(match)) {
return str;
}
const linkPath = path.join(filePath, '..', match);
let route = '';
routeMap.forEach((item) => {
if (item.file === linkPath) {
route = item.route;
}
});

// use hash
return route ? str.replace(match, '#' + route) : str;
});
}

app.locals = {
version: require('./package.json').version,
}

app.use(express.static(path.join(__dirname, './public')));
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, './views'));

app.get('/', (req, res) => {
res.render('index', { config: docConfig, port: PORT });
});

app.get('/doc', (req, res, next) => {
const config = routeMap.get(req.query.route || '__empty');
if (!config) {
return res.sendStatus(404);
}

// 来自远程
if (/http(s)?:/gi.test(config.file)) {
const { URL } = require('url');
let client = http;
if (config.file.startsWith('https')) {
client = https;
}
client.get(new URL(config.file), (_req) => {
_req.pipe(res)
}).on('error', (e) => {
next(e);
});
return;
}

if (!fs.existsSync(config.file)) {
return res.send(config.file);
}

fs.readFile(config.file, (err, data) => {
if (err) {
return next(err);
}
res.send(covertMDFile(config.file, data.toString()));
});
});

app.get('/imgproxy', (req, res, next) => {
const { src, route } = req.query;
const config = routeMap.get(req.query.route);
const imgPath = path.join(config.file, '..', src);
res.sendFile(imgPath);
});

app.use(function errorHandler(err, req, res, next) {
console.log(err);
res.sendStatus(500);
});

const PORT = args.port || 3000;
server.listen(PORT, () => {
console.log(`Server run port ${PORT}`);
setTimeout(() => {
io.clients((error, clients) => {
if (clients.length === 0) {
opn(`http://localhost:${PORT}`);
}
});
}, 2000);
});

io.on('connection', socket => {
});
29 changes: 29 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node

const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');

const argv = process.argv;

let childProcess;

function run() {
if (childProcess) {
childProcess.kill();
}
childProcess = spawn('node', [path.resolve(__dirname, './app')].concat(argv.slice(2)), { stdio: 'inherit' });

childProcess.on('error', () => {
console.log('Error occurred, restart...');
run();
});
}

fs.watchFile(process.argv[2], (curr, prev) => {
console.log('Reload docs...');
run();
});

run();

Loading

0 comments on commit 7c19ad7

Please sign in to comment.