200 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Contribution guide
 | |
| :v: Thanks for your contributions :v:
 | |
| 
 | |
| ## Issues
 | |
| Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues .
 | |
| Before creating a new issue, please search existing issues to avoid duplication.
 | |
| If you find the existing issue, please add your reaction or comment to the issue.
 | |
| 
 | |
| ## Localization (l10n)
 | |
| Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
 | |
| 
 | |
| 
 | |
| 
 | |
| ## Internationalization (i18n)
 | |
| Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
 | |
| 
 | |
| ## Documentation
 | |
| * Documents for contributors are located in `/docs`.
 | |
| * Documents for instance admins are located in `/docs`.
 | |
| * Documents for end users are located in `src/docs`.
 | |
| 
 | |
| ## Test
 | |
| * Test codes are located in `/test`.
 | |
| 
 | |
| ## Continuous integration
 | |
| Misskey uses CircleCI for automated test.
 | |
| Configuration files are located in `/.circleci`.
 | |
| 
 | |
| ## Glossary
 | |
| ### AP
 | |
| Stands for _**A**ctivity**P**ub_.
 | |
| 
 | |
| ### MFM
 | |
| Stands for _**M**isskey **F**lavored **M**arkdown_.
 | |
| 
 | |
| ### Mk
 | |
| Stands for _**M**iss**k**ey_.
 | |
| 
 | |
| ### SW
 | |
| Stands for _**S**ervice**W**orker_.
 | |
| 
 | |
| ### Nyaize
 | |
| Convert な(na) to にゃ(nya)
 | |
| 
 | |
| #### Denyaize
 | |
| Revert Nyaize
 | |
| 
 | |
| ## Code style
 | |
| ### セミコロンを省略しない
 | |
| ASI Hazardを避けるためでもある
 | |
| 
 | |
| ### 中括弧を省略しない
 | |
| Bad:
 | |
| ``` ts
 | |
| if (foo)
 | |
| 	bar;
 | |
| else
 | |
| 	baz;
 | |
| ```
 | |
| 
 | |
| Good:
 | |
| ``` ts
 | |
| if (foo) {
 | |
| 	bar;
 | |
| } else {
 | |
| 	baz;
 | |
| }
 | |
| ```
 | |
| 
 | |
| ただし**`if`が一行**の時だけは省略しても良い
 | |
| Good:
 | |
| ``` ts
 | |
| if (foo) bar;
 | |
| ```
 | |
| 
 | |
| ### `export default`を使わない
 | |
| インテリセンスと相性が悪かったりするため
 | |
| 
 | |
| 参考:
 | |
| * https://gfx.hatenablog.com/entry/2017/11/24/135343
 | |
| * https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
 | |
| 
 | |
| Bad:
 | |
| ``` ts
 | |
| export default function(foo: string): string {
 | |
| ```
 | |
| 
 | |
| Good:
 | |
| ``` ts
 | |
| export function something(foo: string): string {
 | |
| ```
 | |
| 
 | |
| ## Directory structure
 | |
| ```
 | |
| src ... Source code
 | |
| 	@types ... Type definitions
 | |
| 	prelude ... Independence utils for coding JavaScript without side effects
 | |
| 	misc ... Independence utils for Misskey without side effects
 | |
| 	service ... Common functions with side effects
 | |
| 	queue ... Job queues and Jobs
 | |
| 	server ... Web Server
 | |
| 	client ... Client
 | |
| 	mfm ... MFM
 | |
| 
 | |
| test ... Test code
 | |
| 
 | |
| ```
 | |
| 
 | |
| ## Notes
 | |
| ### placeholder
 | |
| SQLをクエリビルダで組み立てる際、使用するプレースホルダは重複してはならない
 | |
| 例えば
 | |
| ``` ts
 | |
| query.andWhere(new Brackets(qb => {
 | |
| 	for (const type of ps.fileType) {
 | |
| 		qb.orWhere(`:type = ANY(note.attachedFileTypes)`, { type: type });
 | |
| 	}
 | |
| }));
 | |
| ```
 | |
| と書くと、ループ中で`type`というプレースホルダが複数回使われてしまいおかしくなる
 | |
| だから次のようにする必要がある
 | |
| ```ts
 | |
| query.andWhere(new Brackets(qb => {
 | |
| 	for (const type of ps.fileType) {
 | |
| 		const i = ps.fileType.indexOf(type);
 | |
| 		qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
 | |
| 	}
 | |
| }));
 | |
| ```
 | |
| 
 | |
| ### `null` in SQL
 | |
| SQLを発行する際、パラメータが`null`になる可能性のある場合はSQL文を出し分けなければならない
 | |
| 例えば
 | |
| ``` ts
 | |
| query.where('file.folderId = :folderId', { folderId: ps.folderId });
 | |
| ```
 | |
| という処理で、`ps.folderId`が`null`だと結果的に`file.folderId = null`のようなクエリが発行されてしまい、これは正しいSQLではないので期待した結果が得られない
 | |
| だから次のようにする必要がある
 | |
| ``` ts
 | |
| if (ps.folderId) {
 | |
| 	query.where('file.folderId = :folderId', { folderId: ps.folderId });
 | |
| } else {
 | |
| 	query.where('file.folderId IS NULL');
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### `[]` in SQL
 | |
| SQLを発行する際、`IN`のパラメータが`[]`(空の配列)になる可能性のある場合はSQL文を出し分けなければならない
 | |
| 例えば
 | |
| ``` ts
 | |
| const users = await Users.find({
 | |
| 	id: In(userIds)
 | |
| });
 | |
| ```
 | |
| という処理で、`userIds`が`[]`だと結果的に`user.id IN ()`のようなクエリが発行されてしまい、これは正しいSQLではないので期待した結果が得られない
 | |
| だから次のようにする必要がある
 | |
| ``` ts
 | |
| const users = userIds.length > 0 ? await Users.find({
 | |
| 	id: In(userIds)
 | |
| }) : [];
 | |
| ```
 | |
| 
 | |
| ### 配列のインデックス in SQL
 | |
| SQLでは配列のインデックスは**1始まり**。
 | |
| `[a, b, c]`の `a`にアクセスしたいなら`[0]`ではなく`[1]`と書く
 | |
| 
 | |
| ### `undefined`にご用心
 | |
| MongoDBの時とは違い、findOneでレコードを取得する時に対象レコードが存在しない場合 **`undefined`** が返ってくるので注意。
 | |
| MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
 | |
| 
 | |
| ### 簡素な`undefined`チェック
 | |
| データベースからレコードを取得するときに、プログラムの流れ的に(ほぼ)絶対`undefined`にはならない場合でも、`undefined`チェックしないとTypeScriptに怒られます。
 | |
| でもいちいち複数行を費やして、発生するはずのない`undefined`をチェックするのも面倒なので、`ensure`というユーティリティ関数を用意しています。
 | |
| 例えば、
 | |
| ``` ts
 | |
| const user = await Users.findOne(userId);
 | |
| // この時点で user の型は User | undefined
 | |
| if (user == null) {
 | |
| 	throw 'missing user';
 | |
| }
 | |
| // この時点で user の型は User
 | |
| ```
 | |
| という処理を`ensure`を使うと
 | |
| ``` ts
 | |
| const user = await Users.findOne(userId).then(ensure);
 | |
| // この時点で user の型は User
 | |
| ```
 | |
| という風に書けます。
 | |
| もちろん`ensure`内部でエラーを握りつぶすようなことはしておらず、万が一`undefined`だった場合はPromiseがRejectされ後続の処理は実行されません。
 | |
| ``` ts
 | |
| const user = await Users.findOne(userId).then(ensure);
 | |
| // 万が一 Users.findOne の結果が undefined だったら、ensure でエラーが発生するので
 | |
| // この行に到達することは無い
 | |
| // なので、.then(ensure) は
 | |
| // if (user == null) {
 | |
| //	throw 'missing user';
 | |
| // }
 | |
| // の糖衣構文のような扱いです
 | |
| ```
 | 
