diff --git a/gulpfile.ts b/gulpfile.ts
index 0bc18dd7c4..6807b6d571 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -48,7 +48,7 @@ if (isDebug) {
const constants = require('./src/const.json');
-require('./src/web/docs/api/endpoints/gulpfile.ts');
+require('./src/web/docs/api/gulpfile.ts');
gulp.task('build', [
'build:js',
@@ -61,7 +61,7 @@ gulp.task('build', [
gulp.task('rebuild', ['clean', 'build']);
gulp.task('build:doc', [
- 'doc:endpoints',
+ 'doc:api',
'doc:styles'
]);
diff --git a/src/web/docs/api/endpoints/posts/create.yaml b/src/web/docs/api/endpoints/posts/create.yaml
index 498a99159c..5e2307dab4 100644
--- a/src/web/docs/api/endpoints/posts/create.yaml
+++ b/src/web/docs/api/endpoints/posts/create.yaml
@@ -10,7 +10,7 @@ params:
optional: true
desc:
ja: "投稿の本文"
- en: "Text of a post"
+ en: "The text of your post"
- name: "media_ids"
type: "id(DriveFile)[]"
optional: true
@@ -22,19 +22,19 @@ params:
optional: true
desc:
ja: "返信する投稿"
- en: "A post you want to reply"
+ en: "The post you want to reply"
- name: "repost_id"
type: "id(Post)"
optional: true
desc:
ja: "引用する投稿"
- en: "A post you want to quote"
+ en: "The post you want to quote"
- name: "poll"
type: "object"
optional: true
desc:
ja: "投票"
- en: "A poll"
+ en: "The poll"
defName: "poll"
def:
- name: "choices"
diff --git a/src/web/docs/api/endpoints/style.styl b/src/web/docs/api/endpoints/style.styl
index ab74e100b5..07fb7ec2a3 100644
--- a/src/web/docs/api/endpoints/style.styl
+++ b/src/web/docs/api/endpoints/style.styl
@@ -1,4 +1,4 @@
-@import "../../style"
+@import "../style"
#url
padding 8px 12px
@@ -6,13 +6,3 @@
color #fff
background #222e40
border-radius 4px
-
-table
- .name
- font-weight bold
-
- .name
- .type
- .optional
- font-family Consolas, 'Courier New', Courier, Monaco, monospace
-
diff --git a/src/web/docs/api/endpoints/view.pug b/src/web/docs/api/endpoints/view.pug
index 841ca8b3f9..cebef9fa5b 100644
--- a/src/web/docs/api/endpoints/view.pug
+++ b/src/web/docs/api/endpoints/view.pug
@@ -1,63 +1,30 @@
-doctype html
+extends ../../layout.pug
+include ../mixins
-mixin i18n(xs)
- each text, lang in xs
- span(class=`i18n ${lang}`)= text
+block title
+ | #{endpoint} | Misskey API
-mixin table(params)
- table
- thead: tr
- th Name
- th Type
- th Optional
- th Description
- tbody
- each param in params
- tr
- td.name= param.name
- td.type
- if param.kind == 'id'
- | #{param.type} (
- a(href=`/docs/api/entities/${param.entity}`)= param.entity
- | ID)
- else if param.kind == 'entity'
- | #{param.type} (
- a(href=`/docs/api/entities/${param.entity}`)= param.entity
- | )
- else if param.kind == 'object'
- | #{param.type} (
- a(href=`#${param.defName}`)= param.defName
- | )
- else
- = param.type
- td.optional= param.optional.toString()
- td.desc: +i18n(param.desc)
+block meta
+ link(rel="stylesheet" href="/assets/docs/api/endpoints/style.css")
-html
- head
- meta(charset="UTF-8")
- title #{endpoint} | Misskey API
- link(rel="stylesheet" href="/assets/docs/api/endpoints/style.css")
+block main
+ h1= endpoint
- body
- main
- h1= endpoint
+ p#url= url
- p#url= url
+ p#desc: +i18n(desc)
- p#desc: +i18n(desc)
+ section
+ h2 Params
+ +propTable(params)
- section
- h2 Params
- +table(params)
+ if paramDefs
+ each paramDef in paramDefs
+ section(id= paramDef.name)
+ h3= paramDef.name
+ +propTable(paramDef.params)
- if paramDefs
- each paramDef in paramDefs
- section(id= paramDef.name)
- h3= paramDef.name
- +table(paramDef.params)
-
- section
- h2 Response
- +table(res)
+ section
+ h2 Response
+ +propTable(res)
diff --git a/src/web/docs/api/entities/post.yaml b/src/web/docs/api/entities/post.yaml
new file mode 100644
index 0000000000..551f3b7c3e
--- /dev/null
+++ b/src/web/docs/api/entities/post.yaml
@@ -0,0 +1,124 @@
+name: "Post"
+
+desc:
+ ja: "投稿。"
+ en: "A post."
+
+props:
+ - name: "id"
+ type: "id"
+ optional: false
+ desc:
+ ja: "投稿ID"
+ en: "The ID of this post"
+ - name: "created_at"
+ type: "date"
+ optional: false
+ desc:
+ ja: "投稿日時"
+ en: "The posted date of this post"
+ - name: "text"
+ type: "string"
+ optional: true
+ desc:
+ ja: "投稿の本文"
+ en: "The text of this post"
+ - name: "media_ids"
+ type: "id(DriveFile)[]"
+ optional: true
+ desc:
+ ja: "添付されているメディアのID"
+ en: "The IDs of the attached media"
+ - name: "media"
+ type: "entity(DriveFile)[]"
+ optional: true
+ desc:
+ ja: "添付されているメディア"
+ en: "The attached media"
+ - name: "user_id"
+ type: "id(User)"
+ optional: false
+ desc:
+ ja: "投稿者ID"
+ en: "The ID of author of this post"
+ - name: "user"
+ type: "entity(User)"
+ optional: true
+ desc:
+ ja: "投稿者"
+ en: "The author of this post"
+ - name: "my_reaction"
+ type: "string"
+ optional: true
+ desc:
+ ja: "この投稿に対する自分のリアクション"
+ en: "The your reaction of this post"
+ - name: "reaction_counts"
+ type: "object"
+ optional: false
+ desc:
+ ja: "リアクションをキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
+ - name: "reply_id"
+ type: "id(Post)"
+ optional: true
+ desc:
+ ja: "返信した投稿のID"
+ en: "The ID of the replyed post"
+ - name: "reply"
+ type: "entity(Post)"
+ optional: true
+ desc:
+ ja: "返信した投稿"
+ en: "The replyed post"
+ - name: "repost_id"
+ type: "id(Post)"
+ optional: true
+ desc:
+ ja: "引用した投稿のID"
+ en: "The ID of the quoted post"
+ - name: "repost"
+ type: "entity(Post)"
+ optional: true
+ desc:
+ ja: "引用した投稿"
+ en: "The quoted post"
+ - name: "poll"
+ type: "object"
+ optional: true
+ desc:
+ ja: "投票"
+ en: "The poll"
+ defName: "poll"
+ def:
+ - name: "choices"
+ type: "object[]"
+ optional: false
+ desc:
+ ja: "投票の選択肢"
+ en: "The choices of this poll"
+ defName: "choice"
+ def:
+ - name: "id"
+ type: "number"
+ optional: false
+ desc:
+ ja: "選択肢ID"
+ en: "The ID of this choice"
+ - name: "is_voted"
+ type: "boolean"
+ optional: true
+ desc:
+ ja: "自分がこの選択肢に投票したかどうか"
+ en: "Whether you voted to this choice"
+ - name: "text"
+ type: "string"
+ optional: false
+ desc:
+ ja: "選択肢本文"
+ en: "The text of this choice"
+ - name: "votes"
+ type: "number"
+ optional: false
+ desc:
+ ja: "この選択肢に投票された数"
+ en: "The number voted for this choice"
diff --git a/src/web/docs/api/entities/style.styl b/src/web/docs/api/entities/style.styl
new file mode 100644
index 0000000000..bddf0f53ab
--- /dev/null
+++ b/src/web/docs/api/entities/style.styl
@@ -0,0 +1 @@
+@import "../style"
diff --git a/src/web/docs/api/entities/view.pug b/src/web/docs/api/entities/view.pug
new file mode 100644
index 0000000000..f210582f1a
--- /dev/null
+++ b/src/web/docs/api/entities/view.pug
@@ -0,0 +1,23 @@
+extends ../../layout.pug
+include ../mixins
+
+block title
+ | #{name} | Misskey API
+
+block meta
+ link(rel="stylesheet" href="/assets/docs/api/entities/style.css")
+
+block main
+ h1= name
+
+ p#desc: +i18n(desc)
+
+ section
+ h2 Properties
+ +propTable(props)
+
+ if propDefs
+ each propDef in propDefs
+ section(id= propDef.name)
+ h3= propDef.name
+ +propTable(propDef.params)
diff --git a/src/web/docs/api/endpoints/gulpfile.ts b/src/web/docs/api/gulpfile.ts
similarity index 50%
rename from src/web/docs/api/endpoints/gulpfile.ts
rename to src/web/docs/api/gulpfile.ts
index e375447c55..05567b6233 100644
--- a/src/web/docs/api/endpoints/gulpfile.ts
+++ b/src/web/docs/api/gulpfile.ts
@@ -10,12 +10,15 @@ import * as pug from 'pug';
import * as yaml from 'js-yaml';
import * as mkdirp from 'mkdirp';
-import config from './../../../../conf';
+import config from './../../../conf';
+
+const kebab = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
const parseParam = param => {
- const id = param.type.match(/^id\((.+?)\)/);
+ 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';
@@ -36,30 +39,53 @@ const parseParam = param => {
if (isObject) {
param.kind = 'object';
}
+ if (isDate) {
+ param.kind = 'date';
+ param.type = 'string';
+ if (isArray) {
+ param.type += '[]';
+ }
+ }
return param;
};
+const sortParams = params => {
+ params.sort((a, b) => {
+ if (a.name < b.name)
+ return -1;
+ if (a.name > b.name)
+ return 1;
+ return 0;
+ });
+ return params;
+};
+
const extractDefs = params => {
- const defs = [];
+ let defs = [];
params.forEach(param => {
if (param.def) {
defs.push({
name: param.defName,
- params: param.def.map(p => parseParam(p))
+ params: sortParams(param.def.map(p => parseParam(p)))
});
const childDefs = extractDefs(param.def);
- defs.concat(childDefs);
+ defs = defs.concat(childDefs);
}
});
return defs;
};
-gulp.task('doc:endpoints', () => {
+gulp.task('doc:api', [
+ 'doc:api:endpoints',
+ 'doc:api:entities'
+]);
+
+gulp.task('doc:api:endpoints', () => {
glob('./src/web/docs/api/endpoints/**/*.yaml', (globErr, files) => {
if (globErr) {
console.error(globErr);
@@ -72,10 +98,11 @@ gulp.task('doc:endpoints', () => {
endpoint: ep.endpoint,
url: `${config.api_url}/${ep.endpoint}`,
desc: ep.desc,
- params: ep.params.map(p => parseParam(p)),
+ params: sortParams(ep.params.map(p => parseParam(p))),
paramDefs: extractDefs(ep.params),
- res: ep.res.map(p => parseParam(p)),
- resDefs: extractDefs(ep.res)
+ res: sortParams(ep.res.map(p => parseParam(p))),
+ resDefs: extractDefs(ep.res),
+ kebab
};
pug.renderFile('./src/web/docs/api/endpoints/view.pug', vars, (renderErr, html) => {
if (renderErr) {
@@ -94,3 +121,36 @@ gulp.task('doc:endpoints', () => {
});
});
});
+
+gulp.task('doc:api:entities', () => {
+ glob('./src/web/docs/api/entities/**/*.yaml', (globErr, files) => {
+ if (globErr) {
+ console.error(globErr);
+ return;
+ }
+ files.forEach(file => {
+ const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8'));
+ const vars = {
+ name: entity.name,
+ desc: entity.desc,
+ props: sortParams(entity.props.map(p => parseParam(p))),
+ propDefs: extractDefs(entity.props),
+ kebab
+ };
+ pug.renderFile('./src/web/docs/api/entities/view.pug', vars, (renderErr, html) => {
+ if (renderErr) {
+ console.error(renderErr);
+ return;
+ }
+ const htmlPath = `./built/web/docs/api/entities/${kebab(entity.name)}.html`;
+ mkdirp(path.dirname(htmlPath), (mkdirErr) => {
+ if (mkdirErr) {
+ console.error(mkdirErr);
+ return;
+ }
+ fs.writeFileSync(htmlPath, html, 'utf-8');
+ });
+ });
+ });
+ });
+});
diff --git a/src/web/docs/api/mixins.pug b/src/web/docs/api/mixins.pug
new file mode 100644
index 0000000000..b302c78263
--- /dev/null
+++ b/src/web/docs/api/mixins.pug
@@ -0,0 +1,33 @@
+mixin propTable(props)
+ table.props
+ thead: tr
+ th Name
+ th Type
+ th Optional
+ th Description
+ tbody
+ each prop in props
+ tr
+ td.name= prop.name
+ td.type
+ i= prop.type
+ if prop.kind == 'id'
+ if prop.entity
+ | (
+ a(href=`/docs/api/entities/${kebab(prop.entity)}`)= prop.entity
+ | ID)
+ else
+ | (ID)
+ else if prop.kind == 'entity'
+ | (
+ a(href=`/docs/api/entities/${kebab(prop.entity)}`)= prop.entity
+ | )
+ else if prop.kind == 'object'
+ if prop.def
+ | (
+ a(href=`#${prop.defName}`)= prop.defName
+ | )
+ else if prop.kind == 'date'
+ | (Date)
+ td.optional= prop.optional.toString()
+ td.desc: +i18n(prop.desc)
diff --git a/src/web/docs/api/style.styl b/src/web/docs/api/style.styl
new file mode 100644
index 0000000000..3675a4da6f
--- /dev/null
+++ b/src/web/docs/api/style.styl
@@ -0,0 +1,11 @@
+@import "../style"
+
+table.props
+ .name
+ font-weight bold
+
+ .name
+ .type
+ .optional
+ font-family Consolas, 'Courier New', Courier, Monaco, monospace
+
diff --git a/src/web/docs/layout.pug b/src/web/docs/layout.pug
new file mode 100644
index 0000000000..68ca9eb62d
--- /dev/null
+++ b/src/web/docs/layout.pug
@@ -0,0 +1,16 @@
+doctype html
+
+mixin i18n(xs)
+ each text, lang in xs
+ span(class=`i18n ${lang}`)!= text
+
+html
+ head
+ meta(charset="UTF-8")
+ title
+ block title
+ block meta
+
+ body
+ main
+ block main