ドキュメントをMarkdownで書くように

This commit is contained in:
syuilo 2018-07-15 18:28:08 +09:00
parent caac246486
commit ee68c0ccfb
32 changed files with 165 additions and 327 deletions

View file

@ -9,6 +9,7 @@ import * as ts from 'gulp-typescript';
const sourcemaps = require('gulp-sourcemaps');
import tslint from 'gulp-tslint';
const cssnano = require('gulp-cssnano');
const stylus = require('gulp-stylus');
import * as uglifyComposer from 'gulp-uglify/composer';
import pug = require('gulp-pug');
import * as rimraf from 'rimraf';
@ -38,8 +39,6 @@ if (isDebug) {
const constants = require('./src/const.json');
require('./src/client/docs/gulpfile.ts');
gulp.task('build', [
'build:ts',
'build:copy',
@ -201,3 +200,10 @@ gulp.task('build:client:pug', [
}))
.pipe(gulp.dest('./built/client/app/'))
);
gulp.task('doc', () =>
gulp.src('./src/docs/**/*.styl')
.pipe(stylus())
.pipe((cssnano as any)())
.pipe(gulp.dest('./built/docs/assets/'))
);

128
package-lock.json generated
View file

@ -620,6 +620,11 @@
"@types/mime": "*"
}
},
"@types/showdown": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-1.7.5.tgz",
"integrity": "sha512-uUSUP6XtyTclRzTH0NLkEIiEowxYXOWDeulpngrPltEceOmsGdhfrl8xr3D4QfJA7FuUUyHwFQuWWURLFg3hgg=="
},
"@types/single-line-log": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/single-line-log/-/single-line-log-1.1.0.tgz",
@ -15107,6 +15112,129 @@
"jsonify": "~0.0.0"
}
},
"showdown": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz",
"integrity": "sha1-kepO47elRIqspoIKTifmkMatdxw=",
"requires": {
"yargs": "^10.0.3"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"camelcase": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
},
"cliui": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
}
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"requires": {
"locate-path": "^2.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
}
},
"os-locale": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
"integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
"requires": {
"execa": "^0.7.0",
"lcid": "^1.0.0",
"mem": "^1.1.0"
}
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"yargs": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz",
"integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==",
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.1.1",
"find-up": "^2.1.0",
"get-caller-file": "^1.0.1",
"os-locale": "^2.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^8.1.0"
}
},
"yargs-parser": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz",
"integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==",
"requires": {
"camelcase": "^4.1.0"
}
}
}
},
"shvl": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/shvl/-/shvl-1.3.1.tgz",

View file

@ -74,6 +74,7 @@
"@types/request-promise-native": "1.0.15",
"@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.27",
"@types/showdown": "1.7.5",
"@types/single-line-log": "1.1.0",
"@types/speakeasy": "2.0.2",
"@types/tmp": "0.0.33",
@ -179,6 +180,7 @@
"s-age": "1.1.2",
"sass-loader": "7.0.3",
"seedrandom": "2.4.3",
"showdown": "1.8.6",
"single-line-log": "1.1.2",
"speakeasy": "2.0.0",
"style-loader": "0.21.0",

View file

@ -1,3 +0,0 @@
h1 About Misskey
p Misskey is a mini blog SNS.

View file

@ -1,3 +0,0 @@
h1 Misskeyについて
p MisskeyはミニブログSNSです。

View file

@ -1,103 +0,0 @@
h1 Misskey API
p MisskeyはWeb APIを公開しており、様々な操作をプログラム上から行うことができます。
p APIを自分のアカウントから利用する場合(自分のアカウントのみ操作したい場合)と、アプリケーションから利用する場合(不特定のアカウントを操作したい場合)とで利用手順が異なりますので、それぞれのケースについて説明します。
section
h2 自分の所有するアカウントからAPIにアクセスする場合
p 「設定 > API」で、APIにアクセスするのに必要なAPIキーを取得してください。
p APIにアクセスする際には、リクエストにAPIキーを「i」というパラメータ名で含めます。
div.ui.info.warn: p %fa:exclamation-triangle%アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。
p APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
section
h2 アプリケーションからAPIにアクセスする場合
p
| 直接ユーザーのAPIキーをアプリケーションが扱うのは危険なので、
| アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のトークン(アクセストークン)をMisskeyに発行してもらい、
| そのトークンをリクエストのパラメータに含める必要があります。
div.ui.info: p %fa:info-circle%アクセストークンは、ユーザーが自分のアカウントにあなたのアプリケーションがアクセスすることを許可した場合のみ発行されます
p それでは、アクセストークンを取得するまでの流れを説明します。
section
h3 1.アプリケーションを登録する
p まず、あなたのアプリケーションやWebサービス(以後、あなたのアプリと呼びます)をMisskeyに登録します。
p
a(href=common.config.dev_url, target="_blank") デベロッパーセンター
| にアクセスし、「アプリ > アプリ作成」に進みます。
| フォームに必要事項を記入し、アプリを作成してください。フォームの記入欄の説明は以下の通りです:
table
thead
tr
th 名前
th 説明
tbody
tr
td アプリケーション名
td あなたのアプリの名称。
tr
td アプリの概要
td あなたのアプリの簡単な説明や紹介。
tr
td コールバックURL
td ユーザーが後述する認証フォームで認証を終えた際にリダイレクトするURLを設定できます。あなたのアプリがWebサービスである場合に有用です。
tr
td 権限
td あなたのアプリが要求する権限。ここで要求した機能だけがAPIからアクセスできます。
p 登録が済むとあなたのアプリのシークレットキーが入手できます。このシークレットキーは後で使用します。
div.ui.info.warn: p %fa:exclamation-triangle%アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。
section
h3 2.ユーザーに認証させる
p あなたのアプリを使ってもらうには、ユーザーにアカウントへのアクセスの許可をもらう必要があります。
p
| 認証セッションを開始するには、#{common.config.api_url}/auth/session/generate へパラメータに appSecret としてシークレットキーを含めたリクエストを送信します。
| リクエスト形式はJSONで、メソッドはPOSTです。
| レスポンスとして認証セッションのトークンや認証フォームのURLが取得できるので、認証フォームのURLをブラウザで表示し、ユーザーにフォームを提示してください。
p
| あなたのアプリがコールバックURLを設定している場合、
| ユーザーがあなたのアプリの連携を許可すると設定しているコールバックURLに token という名前でセッションのトークンが含まれたクエリを付けてリダイレクトします。
p
| あなたのアプリがコールバックURLを設定していない場合、ユーザーがあなたのアプリの連携を許可したことを(何らかの方法で(たとえばボタンを押させるなど))確認出来るようにしてください。
section
h3 3.ユーザーのアクセストークンを取得する
p ユーザーが連携を許可したら、#{common.config.api_url}/auth/session/userkey へ次のパラメータを含むリクエストを送信します:
table
thead
tr
th 名前
th 型
th 説明
tbody
tr
td appSecret
td string
td あなたのアプリのシークレットキー
tr
td token
td string
td セッションのトークン
p 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます!
p アクセストークンが取得できたら、「ユーザーのアクセストークン+あなたのアプリのシークレットキーをsha256したもの」を「i」というパラメータでリクエストに含めると、APIにアクセスすることができます。
p 「i」パラメータの生成方法を擬似コードで表すと次のようになります:
pre: code
| const i = sha256(accessToken + secretKey);
p APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
section
h2 Misskey APIの利用
p APIはすべてリクエストのパラメータ・レスポンスともにJSON形式です。また、すべてのエンドポイントはPOSTメソッドのみ受け付けます。
p APIリファレンスもご確認ください。
section
h3 レートリミット
p Misskey APIにはレートリミットがあり、短時間のうちに多数のリクエストを送信すると、一定時間APIを利用することができなくなることがあります。

View file

@ -1,9 +0,0 @@
h1 フォロー
p ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。
p ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。
section
h2 ストーキング
p ユーザーをフォローしている状態では、さらに「ストーキング」モードをオンにすることができます。ストーキングを行うと、タイムラインにそのユーザーの全ての投稿が表示されるようになります。つまり、他のユーザーに対する返信も含まれることになります。
p ストーキングするには、ユーザーページの「ストークする」をクリックします。ストーキングをやめるには、もう一度クリックします。
p ストーキングしていることは相手に通知されません。

View file

@ -1,18 +0,0 @@
/**
* Gulp tasks
*/
import * as gulp from 'gulp';
const stylus = require('gulp-stylus');
const cssnano = require('gulp-cssnano');
gulp.task('doc', [
'doc:styles'
]);
gulp.task('doc:styles', () =>
gulp.src('./src/client/docs/**/*.styl')
.pipe(stylus())
.pipe((cssnano as any)())
.pipe(gulp.dest('./built/client/docs/assets/'))
);

View file

@ -1,3 +0,0 @@
h1 Misskey Docs
p Welcome to docs of Misskey.

View file

@ -1,3 +0,0 @@
h1 Misskey ドキュメント
p Misskeyのドキュメントへようこそ

View file

@ -1,17 +0,0 @@
h1 License
div!= common.license
details
summary Libraries
section
h2 Libraries
each dependency, name in common.dependencies
details
summary= name
section
h3= name
pre= dependency.licenseText

View file

@ -1,17 +0,0 @@
h1 ライセンス
div!= common.license
details
summary サードパーティ
section
h2 サードパーティ
each dependency, name in common.dependencies
details
summary= name
section
h3= name
pre= dependency.licenseText

View file

@ -1,13 +0,0 @@
h1 ミュート
p ユーザーページから、そのユーザーをミュートすることができます。
p ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
ul
li タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRenote)
li そのユーザーからの通知
li メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
p ミュートを行ったことは相手に通知されず、ミュートされていることを知ることもできません。
p 設定>ミュート から、自分がミュートしているユーザー一覧を確認することができます。

View file

@ -1,120 +0,0 @@
h1 検索
p 投稿を検索することができます。
p
| キーワードを半角スペースで区切ると、and検索になります。
| 例えば、「git コミット」と検索すると、「gitで編集したファイルの特定の行だけコミットする方法がわからない」などがマッチします。
section
h2 キーワードの除外
p キーワードの前に「-」(ハイフン)をプリフィクスすると、そのキーワードを含まない投稿に限定します。
p 例えば、「gitというキーワードを含むが、コミットというキーワードは含まない投稿」を検索したい場合、クエリは以下のようになります:
code git -コミット
section
h2 完全一致
p テキストを「"""」で囲むと、そのテキストと完全に一致する投稿を検索します。
p 例えば、「"""にゃーん"""」と検索すると、「にゃーん」という投稿のみがヒットし、「にゃーん…」という投稿はヒットしません。
section
h2 タグ
p キーワードの前に「#」(シャープ)をプリフィクスすると、そのキーワードと一致するタグを持つ投稿に限定します。
section
h2 オプション
p
| オプションを使用して、より高度な検索を行えます。
| オプションを指定するには、「オプション名:値」という形式でクエリに含めます。
p 利用可能なオプション一覧です:
table
thead
tr
th 名前
th 説明
tbody
tr
td user
td
| 指定されたユーザー名のユーザーの投稿に限定します。
| 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。
br
| 例えば、
code user:himawari,sakurako
| と検索すると「@himawariまたは@sakurakoの投稿」だけに限定します。
| (つまりユーザーのホワイトリストです)
tr
td exclude_user
td
| 指定されたユーザー名のユーザーの投稿を除外します。
| 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。
br
| 例えば、
code exclude_user:akari,chinatsu
| と検索すると「@akariまたは@chinatsu以外の投稿」に限定します。
| (つまりユーザーのブラックリストです)
tr
td follow
td
| true ... フォローしているユーザーに限定。
br
| false ... フォローしていないユーザーに限定。
br
| null ... 特に限定しない(デフォルト)
tr
td mute
td
| mute_all ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteを除外する(デフォルト)
br
| mute_related ... ミュートしているユーザーの投稿に対する返信やRenoteだけ除外する
br
| mute_direct ... ミュートしているユーザーの投稿だけ除外する
br
| disabled ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteも含める
br
| direct_only ... ミュートしているユーザーの投稿だけに限定
br
| related_only ... ミュートしているユーザーの投稿に対する返信やRenoteだけに限定
br
| all_only ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteに限定
tr
td reply
td
| true ... 返信に限定。
br
| false ... 返信でない投稿に限定。
br
| null ... 特に限定しない(デフォルト)
tr
td renote
td
| true ... Renoteに限定。
br
| false ... Renoteでない投稿に限定。
br
| null ... 特に限定しない(デフォルト)
tr
td media
td
| true ... メディアが添付されている投稿に限定。
br
| false ... メディアが添付されていない投稿に限定。
br
| null ... 特に限定しない(デフォルト)
tr
td poll
td
| true ... 投票が添付されている投稿に限定。
br
| false ... 投票が添付されていない投稿に限定。
br
| null ... 特に限定しない(デフォルト)
tr
td until
td 上限の日時。(YYYY-MM-DD)
tr
td since
td 下限の日時。(YYYY-MM-DD)
p 例えば、「@syuiloの2017年11月1日から2017年12月31日までの『Misskey』というテキストを含む返信ではない投稿」を検索したい場合、クエリは以下のようになります:
code user:syuilo since:2017-11-01 until:2017-12-31 reply:false Misskey

View file

@ -1,3 +0,0 @@
h1 利用規約
p 公序良俗に反する行為はおやめください。

3
src/docs/about.en.md Normal file
View file

@ -0,0 +1,3 @@
# About Misskey
Misskey is a mini blog SNS.

3
src/docs/about.ja.md Normal file
View file

@ -0,0 +1,3 @@
# Misskeyについて
MisskeyはミニブログSNSです。

4
src/docs/article.pug Normal file
View file

@ -0,0 +1,4 @@
extends ./layout.pug
block main
!= html

View file

@ -1,4 +1,4 @@
@import "../style"
@import "../client/style"
@import "./ui"
body

View file

@ -4,6 +4,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as showdown from 'showdown';
import ms = require('ms');
import * as Router from 'koa-router';
import * as send from 'koa-send';
@ -16,8 +17,6 @@ import { fa } from '../../misc/fa';
import { licenseHtml } from '../../misc/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 };
@ -26,23 +25,23 @@ async function genVars(lang: string): Promise<{ [key: string]: any }> {
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');
const entities = glob.sync('./src/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');
const docs = glob.sync('./src/docs/**/*.md');
vars['docs'] = {};
docs.forEach(x => {
const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.pug$/);
const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.md$/);
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['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^# (.+?)\r?\n/)[1];
});
vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
@ -162,7 +161,7 @@ const router = new Router();
router.get('/assets/*', async ctx => {
await send(ctx, ctx.params[0], {
root: docs + '/assets/',
root: `${__dirname}/../../docs/assets/`,
maxage: ms('7 days'),
immutable: true
});
@ -183,20 +182,20 @@ router.get('/*/api/endpoints/*', async ctx => {
// @ts-ignore
params: sortParams(Object.entries(ep.params).map(([k, v]) => parseParamDefinition(k, v))),
paramDefs: extractParamDefRef(Object.entries(ep.params).map(([k, v]) => v)),
res: ep.res.props ? sortParams(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v))) : null,
res: ep.res && ep.res.props ? sortParams(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v))) : null,
resDefs: null//extractPropDefRef(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v)))
};
await ctx.render('../../../../src/client/docs/api/endpoints/view', Object.assign(await genVars(lang), vars));
await ctx.render('../../../../src/docs/api/endpoints/view', Object.assign(await genVars(lang), vars));
});
router.get('/*/api/entities/*', async ctx => {
const lang = ctx.params[0];
const entity = ctx.params[1];
const x = yaml.safeLoad(fs.readFileSync(path.resolve('./src/client/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
const x = yaml.safeLoad(fs.readFileSync(path.resolve('./src/docs/api/entities/' + entity + '.yaml'), 'utf-8')) as any;
await ctx.render('../../../../src/client/docs/api/entities/view', Object.assign(await genVars(lang), {
await ctx.render('../../../../src/docs/api/entities/view', Object.assign(await genVars(lang), {
name: x.name,
desc: x.desc,
props: sortParams(Object.entries(x.props).map(([k, v]) => parsePropDefinition(k, v))),
@ -208,7 +207,12 @@ router.get('/*/*', async ctx => {
const lang = ctx.params[0];
const doc = ctx.params[1];
await ctx.render('../../../../src/client/docs/' + doc + '.' + lang, await genVars(lang));
const conv = new showdown.Converter();
const md = fs.readFileSync(`${__dirname}/../../../src/docs/${doc}.${lang}.md`, 'utf8');
await ctx.render('../../../../src/docs/article', Object.assign({
html: conv.makeHtml(md)
}, await genVars(lang)));
});
export default router;