From 118db9b267c451a164ceaabfdb7ca16d4834e9e2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 6 Jul 2018 20:27:48 +0900
Subject: [PATCH] wip

---
 src/client/docs/api/entities/note.yaml |   4 +-
 src/client/docs/api/entities/view.pug  |   2 +-
 src/client/docs/layout.pug             |  14 +--
 src/client/docs/vars.ts                |  64 -----------
 src/server/web/docs.ts                 | 143 +++++++++++++++++++++----
 5 files changed, 135 insertions(+), 92 deletions(-)
 delete mode 100644 src/client/docs/vars.ts

diff --git a/src/client/docs/api/entities/note.yaml b/src/client/docs/api/entities/note.yaml
index 6fd26543bb..c508dab3db 100644
--- a/src/client/docs/api/entities/note.yaml
+++ b/src/client/docs/api/entities/note.yaml
@@ -27,8 +27,8 @@ props:
     type: "string"
     optional: true
     desc:
-      ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)"
-      en: "The text of this note (in Markdown like format if local)"
+      ja: "投稿の本文"
+      en: "The text of this note"
   - name: "mediaIds"
     type: "id(DriveFile)[]"
     optional: true
diff --git a/src/client/docs/api/entities/view.pug b/src/client/docs/api/entities/view.pug
index ac938151a7..a930f71eb6 100644
--- a/src/client/docs/api/entities/view.pug
+++ b/src/client/docs/api/entities/view.pug
@@ -10,7 +10,7 @@ block main
 	p#desc= desc[lang] || desc['ja']
 
 	section
-		h2 %i18n:docs.api.entities.properties%
+		h2= i18n('docs.api.entities.properties')
 		+propTable(props)
 
 		if propDefs
diff --git a/src/client/docs/layout.pug b/src/client/docs/layout.pug
index 1d9ebcb4cd..4186d2d365 100644
--- a/src/client/docs/layout.pug
+++ b/src/client/docs/layout.pug
@@ -10,24 +10,24 @@ html(lang= lang)
 		block meta
 
 		//- FontAwesome style
-		style #{common.facss}
+		style #{facss}
 
 	body
 		nav
 			ul
-				each doc in common.docs
+				each doc in docs
 					li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja']
 			section
 				h2 API
 				ul
 					li Entities
 						ul
-							each entity in common.entities
-								li: a(href=`/docs/${lang}/api/entities/${common.kebab(entity)}`)= entity
+							each entity in entities
+								li: a(href=`/docs/${lang}/api/entities/${kebab(entity)}`)= entity
 					li Endpoints
 						ul
-							each endpoint in common.endpoints
-								li: a(href=`/docs/${lang}/api/endpoints/${common.kebab(endpoint)}`)= endpoint
+							each endpoint in endpoints
+								li: a(href=`/docs/${lang}/api/endpoints/${kebab(endpoint)}`)= endpoint
 		main
 			article
 				block main
@@ -38,4 +38,4 @@ html(lang= lang)
 				p
 					| %i18n:docs.edit-this-page-on-github%
 					a(href=src target="_blank") %i18n:docs.edit-this-page-on-github-link%
-				small= common.copyright
+				small= copyright
diff --git a/src/client/docs/vars.ts b/src/client/docs/vars.ts
deleted file mode 100644
index 93082767e3..0000000000
--- a/src/client/docs/vars.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import * as fs from 'fs';
-import * as util from 'util';
-import * as glob from 'glob';
-import * as yaml from 'js-yaml';
-import * as licenseChecker from 'license-checker';
-import * as tmp from 'tmp';
-
-import { fa } from '../../build/fa';
-import config from '../../config';
-import { licenseHtml } from '../../build/license';
-const constants = require('../../const.json');
-
-export default async function(): Promise<{ [key: string]: any }> {
-	const vars = {} as { [key: string]: any };
-
-	const endpoints = glob.sync('./src/client/docs/api/endpoints/**/*.yaml');
-	vars['endpoints'] = endpoints.map(ep => {
-		const _ep = yaml.safeLoad(fs.readFileSync(ep, 'utf-8')) as any;
-		return _ep.endpoint;
-	});
-
-	const entities = glob.sync('./src/client/docs/api/entities/**/*.yaml');
-	vars['entities'] = entities.map(x => {
-		const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')) as any;
-		return _x.name;
-	});
-
-	const docs = glob.sync('./src/client/docs/**/*.*.pug');
-	vars['docs'] = {};
-	docs.forEach(x => {
-		const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.pug$/);
-		if (vars['docs'][name] == null) {
-			vars['docs'][name] = {
-				name,
-				title: {}
-			};
-		}
-		vars['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^h1 (.+?)\r?\n/)[1];
-	});
-
-	vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
-
-	vars['config'] = config;
-
-	vars['copyright'] = constants.copyright;
-
-	vars['facss'] = fa.dom.css();
-
-	vars['license'] = licenseHtml;
-
-	const tmpObj = tmp.fileSync();
-	fs.writeFileSync(tmpObj.name, JSON.stringify({
-		licenseText: ''
-	}), 'utf-8');
-	const dependencies = await util.promisify(licenseChecker.init).bind(licenseChecker)({
-		start: __dirname + '/../../../',
-		customPath: tmpObj.name
-	});
-	tmpObj.removeCallback();
-
-	vars['dependencies'] = dependencies;
-
-	return vars;
-}
diff --git a/src/server/web/docs.ts b/src/server/web/docs.ts
index b1916abc13..c7430e0733 100644
--- a/src/server/web/docs.ts
+++ b/src/server/web/docs.ts
@@ -2,17 +2,66 @@
  * Docs
  */
 
+import * as fs from 'fs';
+import * as path from 'path';
 import ms = require('ms');
 import * as Router from 'koa-router';
 import * as send from 'koa-send';
 import { Context } from 'cafy';
+import * as glob from 'glob';
+import * as yaml from 'js-yaml';
 import ObjectContext from 'cafy/built/types/object';
 import config from '../../config';
-import generateVars from '../../client/docs/vars';
 import I18n from '../../build/i18n';
+import { fa } from '../../build/fa';
+import { licenseHtml } from '../../build/license';
+const constants = require('../../const.json');
 
 const docs = `${__dirname}/../../client/docs/`;
 
+async function genVars(lang: string): Promise<{ [key: string]: any }> {
+	const vars = {} as { [key: string]: any };
+
+	vars['lang'] = lang;
+
+	const endpoints = glob.sync('./built/server/api/endpoints/**/*.js');
+	vars['endpoints'] = endpoints.map(ep => require('../../../' + ep)).filter(x => x.meta).map(x => x.meta.name);
+
+	const entities = glob.sync('./src/client/docs/api/entities/**/*.yaml');
+	vars['entities'] = entities.map(x => {
+		const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')) as any;
+		return _x.name;
+	});
+
+	const docs = glob.sync('./src/client/docs/**/*.*.pug');
+	vars['docs'] = {};
+	docs.forEach(x => {
+		const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.pug$/);
+		if (vars['docs'][name] == null) {
+			vars['docs'][name] = {
+				name,
+				title: {}
+			};
+		}
+		vars['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^h1 (.+?)\r?\n/)[1];
+	});
+
+	vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
+
+	vars['config'] = config;
+
+	vars['copyright'] = constants.copyright;
+
+	vars['facss'] = fa.dom.css();
+
+	vars['license'] = licenseHtml;
+
+	const i18n = new I18n(lang);
+	vars['i18n'] = (key: string) => i18n.get(null, key);
+
+	return vars;
+}
+
 // WIP type
 const parseEPDefParam = (key: string, param: Context) => {
 	return Object.assign({
@@ -21,6 +70,46 @@ const parseEPDefParam = (key: string, param: Context) => {
 	}, param.data);
 };
 
+const parseParam = (param: any) => {
+	const id = param.type.match(/^id\((.+?)\)|^id/);
+	const entity = param.type.match(/^entity\((.+?)\)/);
+	const isObject = /^object/.test(param.type);
+	const isDate = /^date/.test(param.type);
+	const isArray = /\[\]$/.test(param.type);
+	if (id) {
+		param.kind = 'id';
+		param.type = 'string';
+		param.entity = id[1];
+		if (isArray) {
+			param.type += '[]';
+		}
+	}
+	if (entity) {
+		param.kind = 'entity';
+		param.type = 'object';
+		param.entity = entity[1];
+		if (isArray) {
+			param.type += '[]';
+		}
+	}
+	if (isObject) {
+		param.kind = 'object';
+	}
+	if (isDate) {
+		param.kind = 'date';
+		param.type = 'string';
+		if (isArray) {
+			param.type += '[]';
+		}
+	}
+
+	if (param.optional) {
+		param.type += '?';
+	}
+
+	return param;
+};
+
 const sortParams = (params: Array<{name: string}>) => {
 	params.sort((a, b) => {
 		if (a.name < b.name)
@@ -33,7 +122,7 @@ const sortParams = (params: Array<{name: string}>) => {
 };
 
 // WIP type
-const extractDefs = (params: Context[]) => {
+const extractEPDefs = (params: Context[]) => {
 	let defs: any[] = [];
 
 	params.forEach(param => {
@@ -44,7 +133,26 @@ const extractDefs = (params: Context[]) => {
 				params: sortParams(Object.keys(props).map(k => parseEPDefParam(k, props[k])))
 			});
 
-			const childDefs = extractDefs(Object.keys(props).map(k => props[k]));
+			const childDefs = extractEPDefs(Object.keys(props).map(k => props[k]));
+
+			defs = defs.concat(childDefs);
+		}
+	});
+
+	return sortParams(defs);
+};
+
+const extractDefs = (params: any[]) => {
+	let defs: any[] = [];
+
+	params.forEach(param => {
+		if (param.def) {
+			defs.push({
+				name: param.defName,
+				params: sortParams(param.def.map((p: any) => parseParam(p)))
+			});
+
+			const childDefs = extractDefs(param.def);
 
 			defs = defs.concat(childDefs);
 		}
@@ -68,6 +176,7 @@ router.get('/*/api/endpoints/*', async ctx => {
 	const ep = require('../../../built/server/api/endpoints/' + ctx.params[1]).meta;
 
 	const vars = {
+		title: ep.name,
 		endpoint: ep.name,
 		url: {
 			host: config.api_url,
@@ -76,26 +185,24 @@ router.get('/*/api/endpoints/*', async ctx => {
 		desc: ep.desc,
 		// @ts-ignore
 		params: sortParams(Object.keys(ep.params).map(k => parseEPDefParam(k, ep.params[k]))),
-		paramDefs: extractDefs(Object.keys(ep.params).map(k => ep.params[k])),
+		paramDefs: extractEPDefs(Object.keys(ep.params).map(k => ep.params[k])),
 	};
 
-	const commonVars = await generateVars();
+	await ctx.render('../../../../src/client/docs/api/endpoints/view', Object.assign(await genVars(lang), vars));
+});
 
-	const i18n = new I18n(lang);
+router.get('/*/api/entities/*', async ctx => {
+	const lang = ctx.params[0];
+	const entity = ctx.params[1];
 
-	await ctx.render('../../../../src/client/docs/api/endpoints/view', Object.assign({}, vars, {
-		lang,
-		title: ep.name,
-		kebab: (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(),
-		i18n: (key: string) => i18n.get(null, key),
-		common: commonVars
+	const x = yaml.safeLoad(fs.readFileSync(path.resolve('./src/client/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
+
+	await ctx.render('../../../../src/client/docs/api/entities/view', Object.assign(await genVars(lang), {
+		name: x.name,
+		desc: x.desc,
+		props: sortParams(x.props.map((p: any) => parseParam(p))),
+		propDefs: extractDefs(x.props)
 	}));
 });
 
-router.get('*', async ctx => {
-	await send(ctx, `${ctx.params[0]}.html`, {
-		root: docs
-	});
-});
-
 export default router;