docs-server
Advanced tools
Comparing version 0.3.0 to 1.0.0
@@ -0,1 +1,11 @@ | ||
<a name="0.3.0"></a> | ||
# [0.3.0](https://github.com/lbwa/docs-server/compare/v0.2.0...v0.3.0) (2018-07-25) | ||
### Features | ||
* **Application:** expose application.server and application.gen ([08a0c45](https://github.com/lbwa/docs-server/commit/08a0c45)) | ||
<a name="0.2.0"></a> | ||
@@ -2,0 +12,0 @@ # [0.2.0](https://github.com/lbwa/docs-server/compare/v0.1.2...v0.2.0) (2018-07-24) |
@@ -12,11 +12,13 @@ "use strict"; | ||
const { stringify } = require('../utils/index'); | ||
module.exports = (ctx, next) => __awaiter(this, void 0, void 0, function* () { | ||
ctx.status = 404; | ||
ctx.body = stringify({ | ||
errno: 1, | ||
message: '[Error]: Invalid request' | ||
module.exports = function (status = 404) { | ||
return (ctx, next) => __awaiter(this, void 0, void 0, function* () { | ||
ctx.status = status; | ||
ctx.body = stringify({ | ||
errno: 1, | ||
message: '[Error]: Invalid request' | ||
}); | ||
ctx.set({ | ||
'Content-Type': 'application/json; charset=utf-8' | ||
}); | ||
}); | ||
ctx.set({ | ||
'Content-Type': 'application/json; charset=utf-8' | ||
}); | ||
}); | ||
}; |
@@ -20,3 +20,3 @@ "use strict"; | ||
} | ||
activate({ cwd, catalogOutput }) { | ||
activate({ cwd, dest }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -30,6 +30,6 @@ let headMeta; | ||
} | ||
fs.writeFile(catalogOutput, headMeta, (err) => { | ||
fs.writeFile(dest, headMeta, (err) => { | ||
err | ||
? console.error(err) | ||
: console.log(`\nš generate menu successfully in ${catalogOutput} ! \n`); | ||
: console.log(`\nš generate menu successfully in ${dest} ! \n`); | ||
}); | ||
@@ -36,0 +36,0 @@ return this; |
@@ -18,7 +18,16 @@ "use strict"; | ||
class Application { | ||
constructor({ cwd = resolve('/'), catalogOutput = resolve('/menu.json'), port = '8800' } = {}) { | ||
constructor({ cwd = resolve('/'), dest = resolve('/menu.json'), port = '8800', extra = [] } = {}) { | ||
if (!(this instanceof Application)) { | ||
return new Application({ | ||
cwd, | ||
dest, | ||
port, | ||
extra | ||
}); | ||
} | ||
this.options = { | ||
cwd, | ||
catalogOutput, | ||
port | ||
dest, | ||
port, | ||
extra | ||
}; | ||
@@ -30,15 +39,20 @@ this.activate(); | ||
const options = this.options; | ||
this.server = this.activateServer(); | ||
this.gen = yield this.activateGenerator(options.cwd, options.catalogOutput); | ||
this.genPromise = this.activateGenerator(options.cwd, options.dest); | ||
this.gen = yield this.genPromise; | ||
this.server = this.activateServer(this.gen.contentList, options.extra, options.dest); | ||
}); | ||
} | ||
activateGenerator(cwd, catalogOutput) { | ||
activateGenerator(cwd, dest) { | ||
return gen.activate({ | ||
cwd, | ||
catalogOutput | ||
dest | ||
}); | ||
} | ||
activateServer() { | ||
activateServer(contentList, extra, dest) { | ||
const port = this.options.port; | ||
const server = new Server(); | ||
const server = new Server({ | ||
contentList, | ||
extra, | ||
dest | ||
}); | ||
return server.listen(port, () => { | ||
@@ -48,6 +62,3 @@ console.log(`\nServer is listening on http://localhost:${port}\n`); | ||
} | ||
handleError(err) { | ||
console.error(err); | ||
} | ||
} | ||
module.exports = Application; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Router = require("koa-router"); | ||
const config = require('../config/index'); | ||
const logger = require('../utils/logger'); | ||
const home = require('../controllers/home'); | ||
const docs = require('../controllers/docs'); | ||
const error = require('../controllers/error'); | ||
const createErrorHandle = require('../controllers/error'); | ||
const createExtraRoutes = require('./extra'); | ||
const createMenuRoute = require('./menu'); | ||
const router = new Router(); | ||
const routes = config.routes; | ||
const docsPath = config.docsPath; | ||
router | ||
.get('/', home); | ||
for (let route of routes) { | ||
const format = /^\//.test(route.path) ? route.path : `/${route.path}`; | ||
router | ||
.get(format, route.callback); | ||
function createRoutes(contentList, extra, dest) { | ||
const docsRoutes = Object.keys(contentList); | ||
router.get('/', home); | ||
for (let route of docsRoutes) { | ||
const formatRoute = /^\//.test(route) ? route : `/${route}`; | ||
router.get(formatRoute, docs); | ||
logger.info(`[Server]`, `Generate route: ${route}`); | ||
} | ||
createMenuRoute(extra, dest, router); | ||
createExtraRoutes(extra, router); | ||
return router; | ||
} | ||
router | ||
.get(`/${docsPath}/:id`, docs) | ||
.get('*', error); | ||
module.exports = router; | ||
module.exports = function createRouter(contentList, extra, dest) { | ||
createRoutes(contentList, extra, dest) | ||
.get('*', createErrorHandle(404)) | ||
.all('*', createErrorHandle(405)); | ||
return router; | ||
}; |
@@ -13,3 +13,3 @@ "use strict"; | ||
const compress = require("koa-compress"); | ||
const router = require('./routers/index'); | ||
const createRouter = require('./routers/index'); | ||
const { stringify } = require('./utils/index'); | ||
@@ -61,3 +61,3 @@ const isTest = process.env.NODE_ENV === 'test'; | ||
class Server extends Koa { | ||
constructor({ customHeaders = {}, threshold = 1 } = {}) { | ||
constructor({ customHeaders = {}, threshold = 1, contentList = {}, extra = [], dest = './menu.json' } = {}) { | ||
super(); | ||
@@ -67,3 +67,3 @@ this.setIOMiddleware(); | ||
this.setGzip(threshold); | ||
this.setRouter(); | ||
this.setRouter(contentList, extra, dest); | ||
} | ||
@@ -81,3 +81,4 @@ setIOMiddleware() { | ||
} | ||
setRouter() { | ||
setRouter(contentList, extra, dest) { | ||
const router = createRouter(contentList, extra, dest); | ||
this.use(router.routes()); | ||
@@ -84,0 +85,0 @@ this.use(router.allowedMethods()); |
{ | ||
"name": "docs-server", | ||
"version": "0.3.0", | ||
"version": "1.0.0", | ||
"description": "A server which is used to build one of microservices for docs system", | ||
@@ -23,3 +23,3 @@ "main": "dist/index.js", | ||
"clean": "rm -vrf dist/*", | ||
"test": "yarn ts && cross-env NODE_ENV=test npx mocha test/http.spec.js", | ||
"test": "yarn ts && cross-env NODE_ENV=test npx mocha test/app.spec.js", | ||
"changelog": "npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0", | ||
@@ -49,2 +49,3 @@ "release": "sh scripts/release.sh" | ||
"mocha": "^5.2.0", | ||
"supertest": "^3.1.0", | ||
"typescript": "^2.9.2" | ||
@@ -51,0 +52,0 @@ }, |
@@ -7,79 +7,64 @@ # Docs-server [![npm](https://img.shields.io/npm/v/docs-server.svg)](https://www.npmjs.com/package/docs-server) [![CircleCI](https://circleci.com/gh/lbwa/docs-server.svg?style=svg)](https://circleci.com/gh/lbwa/docs-server) [![node](https://img.shields.io/node/v/docs-server.svg)](https://www.npmjs.com/package/docs-server) | ||
- Perform markdown searching and generate correct dynamic routes according to root path. | ||
- Perform automatic markdown searching and generate correct dynamic routes according to the root path of your project. | ||
- Support for specifying additional static routes. | ||
- Support multiple-level documentation routes. | ||
## Notice | ||
- Support for specifying additional static resources routes. | ||
Only support second-level directory temporarily. | ||
## Usage | ||
Your project structure should be like this: | ||
- Simple running | ||
```markdown | ||
āāā doc [custom directory name] | ||
| āāā a.md | ||
| āāā b.md | ||
| āāā c.md | ||
| āāā d.md | ||
| | ||
āāā docs-server.config.js [define your custom config] | ||
| | ||
āāā something.json [additional static route] | ||
| | ||
... | ||
āāā package.json | ||
```js | ||
const DocsServer = require('docs-server') | ||
// It should be running at http://localhost:8800 by default | ||
const app = new DocsServer() | ||
``` | ||
## Usage | ||
or | ||
1. You should specify your documents directory. | ||
```js | ||
const DocsServer = require('docs-server')() | ||
``` | ||
- ( Optional ) You can specify your custom configuration. | ||
```js | ||
// docs-server.config.js | ||
const send = require('koa-send') | ||
const resolve = require('path').resolve | ||
const DocsServer = require('docs-server') | ||
module.exports = { | ||
// documents directory (required) | ||
// based on root path | ||
docsPath: 'doc', | ||
// Notice: all options is optional | ||
const app = new DocsServer({ | ||
// should be nodejs current working directory | ||
cwd: resolve('./'), | ||
// extra static file route (optional) | ||
routes: [ | ||
// the output path of catalog files (based on current working directory) | ||
dest: resolve('./menu.json'), | ||
// your server running port | ||
port: '8800', | ||
// extra static resource routes | ||
extra: [ | ||
{ | ||
path: 'menu', | ||
callback: async (ctx, next) => { | ||
await send(ctx, './menu.json', { | ||
root: resolve(__dirname, './') | ||
}) | ||
route: '/test', // eg. http://locahost:8800/test | ||
middleware: async (ctx, next) => { | ||
// do something | ||
} | ||
}, | ||
{ | ||
path: 'something', | ||
callback: async (ctx, next) => { | ||
await send(ctx, './something.json', { | ||
root: resolve(__dirname, './') | ||
}) | ||
} | ||
} | ||
] | ||
} | ||
}) | ||
``` | ||
2. Import module and run it | ||
- Test you building | ||
```js | ||
const DocsServer = require('docs-server') | ||
// It should be running at http://localhost:8800 by default | ||
const app = new DocsServer() | ||
```powershell | ||
# test your server | ||
curl -v http://localhost:8800 # response from / | ||
curl -v http://localhost:8800/doc/sample # response from /doc/sample | ||
``` | ||
3. ( Optional ) You can specify output path of catalog file ( menu.json ) and server port. | ||
## CHANGELOG | ||
```js | ||
const app = new DocsServer({ | ||
catalogOutput: path.resolve(__dirname, './') | ||
port: '3000' | ||
}) | ||
``` | ||
[CHANGELOG](./CHANGELOG.md) |
@@ -0,1 +1,3 @@ | ||
import Koa = require('koa') | ||
export interface resHeaders { | ||
@@ -32,10 +34,11 @@ [key: string]: string | ||
cwd: string | ||
catalogOutput: string | ||
dest: string | ||
} | ||
export interface appOptions { | ||
export interface appOptions { | ||
cwd?: string | ||
catalogOutput?: string | ||
dest?: string | ||
port?: number | string | ||
directory?: string | ||
extra?: extraRoute[] | ||
} | ||
@@ -46,2 +49,10 @@ | ||
threshold?: number | ||
contentList?: contentList | ||
extra?: extraRoute[] | ||
dest?: string | ||
} | ||
export interface extraRoute { | ||
route?: string | ||
middleware?: (ctx: Koa.Context, next: Function) => {} | ||
} |
@@ -0,0 +0,0 @@ import Koa = require('koa') |
import Koa = require('koa') | ||
const { stringify } = require('../utils/index') | ||
module.exports = async (ctx: Koa.Context, next: Function) => { | ||
ctx.status = 404 | ||
ctx.body = stringify({ | ||
errno: 1, | ||
message: '[Error]: Invalid request' | ||
}) | ||
ctx.set({ | ||
'Content-Type': 'application/json; charset=utf-8' | ||
}) | ||
module.exports = function (status: number = 404) { | ||
return async (ctx: Koa.Context, next: Function) => { | ||
ctx.status = status | ||
ctx.body = stringify({ | ||
errno: 1, | ||
message: '[Error]: Invalid request' | ||
}) | ||
ctx.set({ | ||
'Content-Type': 'application/json; charset=utf-8' | ||
}) | ||
} | ||
} |
@@ -18,4 +18,11 @@ import { meta, post, initialContent, contentList, targetPath } from './config/types' | ||
// extract function named `activate` for getting a Promise object | ||
async activate ({ cwd, catalogOutput }: targetPath) { | ||
/** | ||
* activate Generator | ||
* extract function named `activate` for getting a Promise object | ||
* | ||
* @param {targetPath} { cwd, dest }working path and output path | ||
* @returns generator instance | ||
* @memberof Gen | ||
*/ | ||
async activate ({ cwd, dest }: targetPath) { | ||
let headMeta: string | ||
@@ -29,6 +36,6 @@ | ||
fs.writeFile(catalogOutput, headMeta, (err: object) => { | ||
fs.writeFile(dest, headMeta, (err: object) => { | ||
err | ||
? console.error(err) | ||
: console.log(`\nš generate menu successfully in ${catalogOutput} ! \n`) | ||
: console.log(`\nš generate menu successfully in ${dest} ! \n`) | ||
}) | ||
@@ -39,2 +46,9 @@ | ||
/** | ||
*activate scanner | ||
* | ||
* @param {string} path working path | ||
* @returns {Promise<catalog>} | ||
* @memberof Gen | ||
*/ | ||
async parser (path: string) { | ||
@@ -104,2 +118,9 @@ let catalog: post[] = [] | ||
/** | ||
*scan all markdown files in project | ||
* | ||
* @param {string} cwd the beginning of working path | ||
* @returns {Promise<singleDocDate>[]} | ||
* @memberof Gen | ||
*/ | ||
async scanner (cwd: string) { | ||
@@ -130,2 +151,9 @@ let docsPath: string[] = [] | ||
/** | ||
*generate all path | ||
* | ||
* @param {string} cwd working path | ||
* @returns {Promise<string[]>} a Array instance including all path | ||
* @memberof Gen | ||
*/ | ||
getDocsPath (cwd: string): Promise<string[]> { | ||
@@ -143,2 +171,9 @@ return new Promise((resolve, reject) => { | ||
/** | ||
*read specific file by async | ||
* | ||
* @param {string} target target file | ||
* @returns Promise<err | file data> | ||
* @memberof Gen | ||
*/ | ||
readFile (target: string) { | ||
@@ -145,0 +180,0 @@ // `target` just like 'do/sample/.../sample.md' |
@@ -1,2 +0,2 @@ | ||
import { appOptions } from './config/types' | ||
import { appOptions, extraRoute } from './config/types' | ||
import Gen from './generator' | ||
@@ -9,2 +9,7 @@ import http = require('http') | ||
/** | ||
* | ||
* @param {string} dir path | ||
* @returns {string} path based on current working directory of nodejs process | ||
*/ | ||
function resolve (dir: string): string { | ||
@@ -14,5 +19,13 @@ return join(process.cwd(), dir) | ||
/** | ||
* @class Application | ||
* @param {String} cwd current working directory(should be a absolute path) | ||
* @param {String} dest the output path of menu.json(should be a absolute path) | ||
* @param {String} port server port | ||
* @param {extraRoute[]} extra extra static resources routes | ||
*/ | ||
class Application { | ||
options: appOptions | ||
server: http.Server // http.Server -> server.close | ||
server: http.Server // http.Server -> to expose `server.close` function | ||
genPromise: Promise<Gen> | ||
gen: Gen | ||
@@ -23,13 +36,24 @@ | ||
cwd = resolve('/'), | ||
catalogOutput = resolve('/menu.json'), | ||
port = '8800' | ||
dest = resolve('/menu.json'), | ||
port = '8800', | ||
extra = [] | ||
}: appOptions = {} | ||
) { | ||
if (!(this instanceof Application)) { | ||
return new Application({ | ||
cwd, | ||
dest, | ||
port, | ||
extra | ||
}) | ||
} | ||
this.options = { | ||
cwd, | ||
catalogOutput, | ||
port | ||
dest, | ||
port, | ||
extra | ||
} | ||
this.activate() | ||
// this.server has a value, this.gen is undefined on this position | ||
} | ||
@@ -40,21 +64,36 @@ | ||
// To expose this.server when Application instantiation | ||
// Make sure this.activateServer() before this.activateGenerator() | ||
// Router's docs controller sync with gen.contentList | ||
this.server = this.activateServer() | ||
/** | ||
* 1. this.activeGenerator will be invoked immediately | ||
* 2. async function will be restore execution (enter microtask queue) | ||
* until Application complete instantiation which means current event loop | ||
* completed. | ||
* 3. this.gen is valid until microtask complete | ||
* 2. this.gen must be pending status promise when instantiation completed | ||
*/ | ||
this.gen = await this.activateGenerator(options.cwd, options.catalogOutput) | ||
this.genPromise = this.activateGenerator(options.cwd, options.dest) | ||
/** | ||
* 1. async function wouldn't restore execution (enter microtask queue) | ||
* until Application complete instantiation which means all mission | ||
* in the current (macro-)task queue has been completed | ||
* 2. this.gen is invalid until Application complete this own initialization | ||
*/ | ||
this.gen = await this.genPromise | ||
// Doesn't be invoked until generator complete mission | ||
this.server = this.activateServer( | ||
this.gen.contentList, | ||
options.extra, | ||
options.dest | ||
) | ||
} | ||
activateGenerator (cwd: string, catalogOutput: string) { | ||
/** | ||
*activate generator | ||
* | ||
* @param {string} cwd project root path(current working directory) | ||
* @param {string} dest the output path of menu.json | ||
* @returns {Gen} Generator instance | ||
* @memberof Application | ||
*/ | ||
activateGenerator (cwd: string, dest: string) { | ||
return gen.activate({ | ||
cwd, | ||
catalogOutput | ||
dest | ||
}) | ||
@@ -64,5 +103,17 @@ } | ||
// ! Notice: Make sure `this` value equal to Application instance | ||
activateServer () { | ||
/** | ||
*build local server | ||
* | ||
* @param {Gen['contentList']} contentList content storage | ||
* @param {extraRoute[]} extra extra static resources routes | ||
* @returns {http.Server} http.Server instance | ||
* @memberof Application | ||
*/ | ||
activateServer (contentList: Gen['contentList'], extra: extraRoute[], dest: string):http.Server { | ||
const port = this.options.port | ||
const server = new Server() | ||
const server = new Server({ | ||
contentList, | ||
extra, | ||
dest | ||
}) | ||
return server.listen(port, () => { | ||
@@ -72,8 +123,4 @@ console.log(`\nServer is listening on http://localhost:${port}\n`) | ||
} | ||
handleError (err: Error) { | ||
console.error(err) | ||
} | ||
} | ||
module.exports = Application |
import Router = require('koa-router') | ||
import { contentList, extraRoute } from '../config/types' | ||
const config = require('../config/index') | ||
const logger = require('../utils/logger') | ||
const home = require('../controllers/home') | ||
const docs = require('../controllers/docs') | ||
const error = require('../controllers/error') | ||
const createErrorHandle = require('../controllers/error') | ||
const createExtraRoutes = require('./extra') | ||
const createMenuRoute = require('./menu') | ||
const router = new Router() | ||
const routes = config.routes | ||
const docsPath = config.docsPath | ||
/** | ||
* create all routes based on configuration | ||
* | ||
* @param {contentList} contentList content storage | ||
* @param {extraRoute[]} extra extra static resources routes | ||
* @returns a koa router instance | ||
*/ | ||
function createRoutes ( | ||
contentList: contentList, | ||
extra: extraRoute[], | ||
dest: string | ||
) { | ||
const docsRoutes = Object.keys(contentList) | ||
router | ||
.get('/', home) | ||
router.get('/', home) | ||
for (let route of routes) { | ||
const format = /^\//.test(route.path) ? route.path : `/${route.path}` | ||
router | ||
.get(format, route.callback) | ||
for (let route of docsRoutes) { | ||
const formatRoute = /^\//.test(route) ? route : `/${route}` | ||
router.get(formatRoute, docs) | ||
logger.info(`[Server]`, `Generate route: ${route}`) | ||
} | ||
// Make sure createMenuRoute before createExtraRoutes | ||
createMenuRoute(extra, dest, router) | ||
createExtraRoutes(extra, router) | ||
return router | ||
} | ||
router | ||
// only file name | ||
.get(`/${docsPath}/:id`, docs) | ||
.get('*', error) | ||
/** | ||
* create a koa router instance | ||
* | ||
* @param {contentList} contentList content storage | ||
* @param {extraRoute[]} extra extra static resources routes | ||
* @returns a koa router instance | ||
*/ | ||
module.exports = function createRouter ( | ||
contentList: contentList, | ||
extra: extraRoute[], | ||
dest: string | ||
) { | ||
createRoutes(contentList, extra, dest) | ||
.get('*', createErrorHandle(404)) | ||
.all('*', createErrorHandle(405)) | ||
module.exports = router | ||
return router | ||
} |
@@ -5,3 +5,3 @@ import Koa = require('koa') | ||
const router = require('./routers/index') | ||
const createRouter = require('./routers/index') | ||
const { stringify } = require('./utils/index') | ||
@@ -52,5 +52,17 @@ const isTest = process.env.NODE_ENV === 'test' | ||
/** | ||
*create a server instance based on Koa | ||
* | ||
* @class Server | ||
* @extends {Koa} | ||
*/ | ||
class Server extends Koa { | ||
// Once params is empty object, customHeaders and threshold will be set default value | ||
constructor ({ customHeaders={}, threshold=1 }: server = {}) { | ||
constructor ({ | ||
customHeaders={}, | ||
threshold=1, | ||
contentList = {}, | ||
extra = [], | ||
dest = './menu.json' | ||
}: server = {}) { | ||
super() | ||
@@ -60,3 +72,3 @@ this.setIOMiddleware() | ||
this.setGzip(threshold) | ||
this.setRouter() | ||
this.setRouter(contentList, extra, dest) | ||
} | ||
@@ -78,3 +90,8 @@ | ||
setRouter () { | ||
setRouter ( | ||
contentList: server['contentList'], | ||
extra: server['extra'], | ||
dest: server['dest'] | ||
) { | ||
const router = createRouter(contentList, extra, dest) | ||
this.use(router.routes()) | ||
@@ -81,0 +98,0 @@ this.use(router.allowedMethods()) |
@@ -0,0 +0,0 @@ const stringify = JSON.stringify.bind(JSON) |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
41272
33
1098
0
2
14
70