別にしんどくないブログ

技術のことや読書メモを書いています

2019年のふりかえり

晦日なので2019年のふりかえりをします。

転職

ウェブリオ株式会社からサイボウズ株式会社に転職しました。
今年一番大きな変化は転職だったように感じます。
転職大成功で仕事は楽しいですし、最高のチームで働けていると日々実感できるほどチームメンバーに恵まれていて感謝しかないです。

shisama.hatenablog.com

登壇

今年は16回登壇しました。
今年は登壇回数を増やして年12回は最低でも登壇する目標をたてていたので達成できて良かったです。

speakerdeck.com

特によく閲覧していただいたスライドをふりかえりたいと思います。

off-the-main-thread with WorkerDOM

speakerdeck.com

前職でAMPの仕事をしていた関係でamp-scriptに注目していました。そのamp-script内でWorkerDOMが使われているのを知り、コードを読んで実際Reactが動くか試してみたらあっさり動いたので発表しました。

脆弱性のあるJavaScriptライブラリの使用を防ぐには

前職で脆弱性のあるライブラリを使っていないか調べるためにnpm auditを使ってCIでチェックしたりしていたのですが、他に便利なツールやサービスが無いか調べたりnpm auditとの違いを調べたりしたので発表しました。

Advanced built-in types

元々Conditional TypesやMapped Typesによる型パズルについて発表しようと思ってて、とはいえ慣れないと使えないと思っていました。参加者にとっても明日使える有益情報を話せればと思ったのでUtility Typesについて紹介しつつその型定義について発表しました。

Vue.js Performance Tips

v-kansaiさんから登壇依頼をいただいたので、登壇しました。普段Vueは使わないのですが、ReactでもあるならVueでもあるだろうと調べたらいい感じのライブラリがあったのでそれを紹介しつつパフォーマンスの計測や遅延読み込みなどについて発表しました。

SameSite Cookie

SameSite CookieChrome v80に入ることがわかっていたので、 chrome://flags/#same-site-by-default-cookies を有効にしてみたらはてなのサービスが使えなくなったので調べて発表しました。

フロントエンドエンジニアのためのセキュリティ対策 ~XSS編~

フロントエンドカンファレンス 2019で発表しました。CfPは2つ出していて「ECMAScript最新機能のキャッチアップ」か「フロントエンドエンジニアのためのセキュリティ対策」でした。主催の方からセキュリティが珍しいからセキュリティで、と伝えられたので後者が採択され発表しました。
当日は満員御礼で最も並ばれたセッションとなったそうでありがとうございます。一方で入れなかった人もいたみたいで申し訳無いことをしました。運営に携わっていた身としても心苦しいです。
内容はXSSの基本的なこととDOMPurifyなどでのサニタイズやCSP、Trusted Typesを使った対策について話しました。
カンファレンス終了後にTwitterで感想や質問いただいて大変うれしかったです。

改めて振り返るとパフォーマンスやセキュリティに偏っていたかもしれません。
来年は登壇回数を減らしたいと考えています。5分のLTは話したいことが話せないので20分ぐらいの登壇に絞っていきたいと考えています。

ブログ

38本の記事を書きました。

特に良く読んでいただいた記事を紹介します。

Node.js v12で使えるJavaScriptの新機能

shisama.hatenablog.com

Node.js v12がリリースされるタイミングでNode.js v12に関する記事を書きたいと思っていて、ちょうどMathias氏がツイートしているのに乗っかる形でJavaScriptの新機能について紹介しました。
このv12には多くの機能が追加され、特にArray.prototype.flatあたりが待望だった人が多かったようです。

ウェブリオを退職しました。「いい話」から5年経ったいま

shisama.hatenablog.com

所謂退職エントリです。前述のとおり転職したので書きました。
この記事のメインである「いい話」と現状があまりにも乖離があったのでいつかそのことについて書きたいと考えていたので書けてよかったです。
また、多くの人から「お疲れさま」と言っていただきありがとうございました。 ウェブリオのエンジニアの給料が上がったと情報が入ればその旨を更新したいと思います。

Node.js v13の主な変更点

shisama.hatenablog.com

Node.js v13がリリースされたので、リリースノートをもとに何が新しく入ったのかを紹介しました。
v13はi18nの言語フルサポート(full-icuを使ったビルド)や、ES Modulesがフラグ無しで使えるようになったりと大きな変更が入ったバージョンです。来年もNode.jsはWebの進化とともに進化しつづけると思います。最近だとWASIがサポートされたりしています。来年も楽しみです。

2019年、2020年のJavaScript

shisama.hatenablog.com

JavaScript Advent Calendar 2019 - Qiitaのトップバッターを務めさせていただきました。初日ということで今年のJavaScriptのふりかえりと来年のJavaScriptについて書きました。Edgeが軒並み使えないとなっていますが、それもchromiumベースになればChromeと同じように使えるはずなので待ちましょう。まだまだ面白いproposalがstage3以下にあるので来年も引き続きTC39のRepositoryをWatchしたいと思います。

github.com

Node.jsへのコントリビュート解説、そしてOSSへ貢献するということ

shisama.hatenablog.com

私がNode.jsへコントリビュートし始めたときに@yosuke_furukawaさんやjovi0608さんなど偉大な先人が書いてくれたブログや翻訳ドキュメントには大変お世話になりました。同じようにコントリビュートしたいと思っている人の一助になりたいと考えていました。JSConf JPの打ち上げでNode.jsへのコントリビュートの敷居の高さの話やコントリビュートする人の少なさについて話題が出たり、今年はCode + Learnが無かったので Node.js Advent Calendar 2019 - Qiita の記事にちょうどいいかもなと思い書きました。
とても長くなってしまい、もっと工夫できたかもと思っています。
この記事によりコントリビュートする人が増え、長く読まれる記事であれば大変嬉しいです。

TypeScriptの便利な型コレクションtype-festと型パズル解説~前編~

TypeScript Advent Calendar 2019 - Qiita

shisama.hatenablog.com の記事です。元々はV8によるパフォーマンス最適化を劣化させないためにTypeScriptやTSLintを使ってできることを紹介しようと思ったのですが、TSLintにはあるけどESLintにはサポートされていないルールがあったりTSLintはdeprecateになることも決まっていたので一旦保留にしました。
ESLintのルールは追加したいと思っていて、機運が高まればtypescript-eslintにPR投げようかと思っています。
代わりに何書こうと思ったときに、ふとtype-festを見に行くと便利な型が増えていて型定義は結構複雑なものでした。これを詳しく解説したらTypeScriptの型パズルに対するアレルギーが減る人もいるかもな、と思い書きました。
自分なりにできるだけ丁寧に書いた結果、とても長くなり力尽きたので前後半に分けることにしました。しかし、後編を書き出すと前編より長くなりそうだったので3部作にすることにしました。しかし、まだ出せていないので来年の最初の宿題として持ち越しになってしまいました。

OSS

今年は昨年ほどOSS活動ができなかったです。昨年はnodejs/nodeだけで30個ほどPRを出していたのですが、今年はいくつかのRepositoryに19個PRを投げて終わりそうです。
nodejs/nodeを始め、denoland/denogatsbyjs/gatsbyfirebase/firebase-toolsなどにPRを送りました。
今年はNode.jsへのコミットが減ってしまったことやフロントエンドで使えるライブラリにコミットしたかったのですが残念ながらあまりできませんでした。特にampproject/amphtmlに貢献しようと意気込んでいたけど結局何もできないまま終わってしまいました。
来年は仕事で使っているOSSやNode.jsにコミットしたいと思います。あとはAMPも追えていないのでキャッチアップしつつ何かコントリビュートできればと考えています。

個人開発

あまり作れませんでした。
今年、新規でnpmにpublishしたパッケージは1つだけでした。

www.npmjs.com

これはVuexのmutationsによるstoreの更新前後のdiffをsnapshotとして保存しておき、スナップショットテストを行うテストツールです。

あと、どういう経緯かわかっていないのですが、moongiftさんに昔作ったReactのUIライブラリが紹介していただきました。

www.moongift.jp

全然メンテしていないのでメンテします。他のも作って放置気味になっているものを整理していいきたいなと考えています。

来年は登壇回数を減らす予定なので、個人開発にも時間を割ければと考えています。

英語

英語学習は今年ついに8年ぶりにTOEICに申し込んだのですが、フロントエンドカンファレンスの資料が全くできていなかったのでやむなくTOEIC受験は諦めました。
しかし、今年は英語へのモチベーションは高まった年になりました。
前職では毎日受講できるオンライン英会話のプランが福利厚生であったのでときどきオンライン英会話のレッスンを受講しました。
現職ではチームメンバーが英語へのモチベーションが高く、チームとして英語学習に取り組んでいます。そのため、週に業務時間内にオンライン英会話など英語学習を行っています。
来年は1月にTOEIC受験することが決まっていたり、会社でTOEICの点数の伸びを競い合うイベントに参加したりしているのでやらざるを得ない状況に追い込んでいってます。
またJSConf Asiaなど海外カンファレンスやHackerNews Kansaiなどネイティブが参加するMeetupへ参加したいとも考えています。

読書

読書量については減りました。
今年読んだ本は少ないのですが、その中でも『FACTFULNESS』が面白かったです。

shisama.hatenablog.com

あと、『エンジニアのためのマネジメントキャリアパス』も面白く、ぼんやり目指そうかなと思っていたエンジニアリングマネージャーへのモチベーションが上がりました。(弊社は明示的にエンジニアリングマネージャーという役割は存在しないので近い役割を目指したいと思っています)

shisama.hatenablog.com

ビジネス書や技術書ばかり読んでいて小説やエッセイを全く読まなくなってしまったので来年は英語学習も兼ねて小説を洋書で読んだりできればいいなと考えています。

まとめ

他にも食べたラーメンの回数とかカレーの美味しかった店とかRage Against The Machineの復活やレッチリジョン・フルシアンテが復帰した話など今年のことをいっぱい書きたい気持ちもありますが、このへんで終わりにします。

今年は転職がうまくいき概ね最高の年だったので、来年はもっと良い年にしていきたいと思います。

来年もよろしくお願いいたします。

TypeScriptの便利な型コレクションtype-festと型パズル解説~前編~

TypeScript Advent Calendar 2019 - Qiita 14日目の記事です。

type-festというTypeScriptの便利な型を集めたnpmパッケージがあります。
今回はtype-festの中から特に複雑なUtilitiesの型の紹介とそれらの型パズルのような型定義について解説したいと思います。
この記事がMapped TypesやConditional Typesを使った複雑な型パズルの理解への一助になれば幸いです。

github.com

長いので前後半に分けました。後半はまた後日公開します。

前提知識

今回紹介するtype-festの型定義を読むうえで必須の知識があります。それがMapped TypesConditional Typesです。
それらを使ったUtility Typesについても知っておいた方がいいです。
まずはそれらを紹介します。もし既知の場合は「前提知識」の章は読み飛ばしてください。

Utility Types

TypeScriptはstringnumberなどの単純なプリミティブな型だけでなく、便利な型がいくつか組み込まれています。
それらをUtility Typesといいます。

www.typescriptlang.org

これらの型は大変便利です。例えば以下のようなUser型があったとします。

interface User {
  id: number;
  name: string;
  email: string;
  birthday: Date;
}

Mapped Typesを利用したUtility Types

Userからidnameだけ持つ型を再定義したいときPickという型を使うことで簡単に型定義することが可能です。

type MinimalUser = Pick<User, "id"|"name">;

このMinimalUser型は以下と同じ型になります。

type MinimalUser = {
  id: number;
  name: string;
}

PickのGenericsの第1引数から第2引数に指定したキーのみ持った型を定義することができます。

これはMapped Typesという仕組みを使っています。

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

Mapped Typesについて説明をすると長くなるので割愛しますが、以下の記事が詳しいです。

qiita.com

Conditional Typesを利用したUtility Types

Conditional Typesを使ったUtility Typesも存在します。

ExcludeはGenericsの第1引数から第2引数を除去します。

type UserKeys = keyof User;
// "id"|"name"|"email"|"birthday"

type ExcludedKeys = Exclude<UserKeys, "email" | "birthday">;

このExcludedKeysはUserKeysから"email""birthday"を取り除いた型になります。
以下と同じ型になります。

type ExcludedKeys = "id"|"name";

ではExcludeはどのように型定義されているのか見てみます。

type Exclude<T, U> = T extends U ? never : T;

三項演算子のようなもので定義されています。これはT extends Utrueの場合neverを返し、falseの場合Tを返します。
このTUに先程の値を適用すると以下のようになります。

("id"|"name"|"email"|"birthday") extends "email"|"birthday" ? never : ("id"|"name"|"email"|"birthday")

()内は順番に値を取り出すため、各値のみ考えると以下のようになります。
もう少しプログラムっぽくと以下のようになります。
※実際は動作しない擬似コードです。

// "id"|"name"|"email"|"birthday"を一つずつループする
for key of ("id"|"name"|"email"|"birthday") {
  if (key extends ("email"|"birthday")) {
    // keyが"email"または"birthday"の場合はneverを返す
    return never;
  } else {
    return key
  }
}
// idの場合
"id" extends "email"|"birthday" ? never : "id"

"id" extends "email"|"birthday"はfalseになるので"id"を返します。

// emailの場合
"email" extends "email"|"birthday" ? never : "email"

"email" extends "email"|"birthday"はtrueになるのでneverを返します。

never発生し得ない値を表す型です。そのため、Union Types(|で列挙した型)からは除去されます。

他にもUtility Typesは便利な型定義を提供しています。
以前勉強会でUtility Typesのいくつかの使い方や型定義について発表したので興味がございましたら発表資料も読んでいただけると幸いです。

speakerdeck.com

type-fest

本題のtype-festですが、これまで説明してきたTypeScript本体に組み込まれているUtility Typesにはまだ無いが実用的な便利な型コレクションライブラリになっています。
作者はおなじみのSindre Sorhus氏です。

以下のようにnpm installして使うことができます。

$ npm install type-fest

今回はtype-festが提供している型の中から特に複雑な型定義を行っているUtilitiesの型を紹介します。

Except

Exceptを使うことでObjectの型定義から指定のプロパティを除去することができます。

import {Except} from 'type-fest';
type Foo = {
  a: number;
  b: string;
  c: boolean;
};
type FooWithoutA = Except<Foo, 'a' | 'c'>;
//=> {b: string};

型定義は以下のようになっています。前述のPickExcludeが使われています。

type Except<ObjectType, KeysType extends keyof ObjectType>
  = Pick<ObjectType, Exclude<keyof ObjectType, KeysType>>;

一つずつ解説します。

Exclude<keyof ObjectType, KeysType>は値を代入すると以下のようになります。

Exclude<keyof Foo, "a"|"c">
//=> "b"

この結果を踏まえてPickに値を代入すると以下のようになります。
Pickは第1引数のObjectから第2引数で指定したキーのObjectの型を定義します。

Pick<User, "b">
//=> {b: string};

Mutable

MutableはObjectの型のreadonlyなプロパティをすべてreadonlyでは無いプロパティに変更して型を再定義します。

import {Mutable} from 'type-fest';
type Foo = {
    readonly a: number;
    readonly b: string;
};
const mutableFoo: Mutable<Foo> = {a: 1, b: '2'};
//=> {a: number; b: string;}

mutableFoo.a = 3; // 代入可能

型定義は以下のとおりです。

type Mutable<ObjectType> = {
  -readonly [KeyType in keyof ObjectType]: ObjectType[KeyType];
};

-readonly-予約語を無効にします。
-?であれば?が無効になりOptionalなプロパティではなくなります。Utility Typesの一つRequired-?を使った型定義により全てのプロパティを必須項目にする効果があります。

Merge

Mergeは型をマージします。

import {Merge} from 'type-fest';
type Foo = {
  a: number;
  b: string;
  c: Symbol;
};
type Bar = {
  b: number;
  d: boolean;
};
const foobar1: Merge<Foo, Bar> = {a: 1, b: 2, c: Symbol(1), d: true};
/**
{
  a: number;
  b: number;
  c: Symbol;
  d: boolean;
}
 */

型定義は以下のとおりです。

type Merge<FirstType, SecondType>
  = Except<FirstType, Extract<keyof FirstType, keyof SecondType>> & SecondType;

前述したExceptとUtility TypesのExtractを利用しています。
Extract<T, U>TからUを抽出することができます。

type T0 = Extract<"a" | "b" | "c", "a" | "f">;  // "a"
type T1 = Extract<string | number | (() => void), Function>;  // () => void

ではMergeの型定義について一つずつ解説します。 Extract<keyof FirstType, keyof SecondType>の部分にFoo型とBar型を入れると以下のようになります。

type T0 = Extract<keyof Foo, keyof Bar>
const duplicateKey: T0 = "b"

FooとBarの両方が持つObjectのキーの値を抽出しています。なのでT0型の変数には"b"という値のみ代入可能です。

次にExcept<FirstType, Extract<keyof FirstType, keyof SecondType>>の部分にさFoo型と先程のT0型を入れてみます。

type T1 = Except<Foo, T0>
const obj1: T1 = {
  a: 1,
  c: Symbol(1)
};

Foo型からT0型を除外したObject型がT1に入ります。 Foo型はa``b``cというプロパティを持っています。T0"b"という値のみ持つことができる型です。
keyof Foo - T0 = "a" | "c"ということになります。そのためT1にはacのプロパティのみ残ります。

Merge型と同じことをT1型を使って定義すると以下のようになります。

type FooBar = T1 & Bar;
//=> Merge<Foo, Bar>
const foobar2: FooBar = { a: 1, b: 2, c: Symbol(1), d: true }

&Intersection Typesというもので型の結合を行ってくれます。
T1{a: number; c: Symbol;}Bar{b: number; d: boolean;}の型定義がされています。この2つを組み合わせることで、Merge<Foo, Bar>と同じことができます。

今までみてきたようにMerge<A, B>はABに同じプロパティがあった場合、Bの型が優先されます。

MergeExclusive

MergeExclusive<A, B>とした場合、ABのObject型の相互のキーを排他的に持つことができます。

import { MergeExclusive } from 'type-fest';

type Foo = {
  a: number;
  b: string;
};
type Bar = {
  b: number;
  c: boolean;
};

const foobar1: MergeExclusive<Foo, Bar> = {
  a: 1,
  b: "2"
}
// OK

const foobar2: MergeExclusive<Foo, Bar> = {
  b: 1,
  c: true
}
// OK

const foobar3: MergeExclusive<Foo, Bar> = {
  a: 1,
  b: 2,
  c: true
}
// Error

MergeExclusive<Foo, Bar>の変数はFoo型とBar型のどちらの型でも適用可能ですが、どちらかの型しか適用できません。

型定義はこちらです。

type MergeExclusive<FirstType, SecondType> =
    (FirstType | SecondType) extends object ?
        (Without<FirstType, SecondType> & SecondType) | (Without<SecondType, FirstType> & FirstType) :
        FirstType | SecondType;

MergeExclusiveの前段にWithoutという型が定義されているので、そちらから先に解説します。

type Without<FirstType, SecondType> = {[KeyType in Exclude<keyof FirstType, keyof SecondType>]?: never};

FooとBarを適用したWithout<Foo, Bar>を分解すると以下のようになります。

type FooKeys = keyof Foo; // "a"|"b"
type BarKeys = keyof Bar; // "b" | "c"
type ExcludeKeys = Exclude<FooKeys, BarKeys>; // "a"
type WithoutFooBar = {[K in ExcludeKeys]?: never}; // {a?: undefined}

上記からわかるようにWithoutはFooにしかないプロパティにneverを適用しています。
Without<Bar, Foo>とすると{c?: undefined}の型になります。

では次にMergeExclusiveの中で使われているWithout<FirstType, SecondType> & SecondTypeの部分にFooとBarを適用してみると、

type WithoutFooBar = Without<Foo, Bar>; // {a?: undefined}
type MergedFooBar = WithoutFooBar & Bar; // {b: number; c: boolean;}

Without<SecondType, FirstType> & SecondTypeにFooとBarを適用すると以下のようになります。

type WithoutBarFoo = Without<Bar, Foo>; // {c?: undefined}
type MergedBarFoo = WithoutBarFoo & Foo; // {a: number; b: string;}

(Without<FirstType, SecondType> & SecondType) | (Without<SecondType, FirstType> & FirstType)を上記の結果を踏まえた形に置き換えると以下のようになります。

{b: number; c: boolean;} | {a: number; b: string;}

結果的にFooかBarがUnion Typesとして定義されることになり、どちらの型も適用できるがどちらか一方のみしか適用できないようになります。

Conditional Typesの条件(FirstType | SecondType) extends objectはFirstTypeまたはSecondTypeがobjectかどうか判定しているだけです。
FooとBarの場合は{b: number; c: boolean;}または{a: number; b: string;}のどちらかの型を適用することができます。

MergeExclusive<number, {a: number}>とした場合は以下のようになります。

type PrimitiveOrObject = MergeExclusive<number, { a: number }>; // number | {a: number}
const a: PrimitiveOrObject = 1; // OK
const obj: PrimitiveOrObject = { a: 1 }; // OK

RequireAtLeastOne

RequireAtLeastOneを使うと指定した複数のキーから少なくとも1つは必須な項目にすることができます。

import { RequireAtLeastOne } from 'type-fest';

type Foo = {
  a?: number;
  b?: string;
  c: boolean
};

const foo1: RequireAtLeastOne<Foo, "a" | "b"> = { a: 1, c: true } // OK
const foo2: RequireAtLeastOne<Foo, "a" | "b"> = { b: "2", c: true } // OK
const foo3: RequireAtLeastOne<Foo, "a" | "b"> = {c: true } // Error
const foo4: RequireAtLeastOne<Foo> = {a: 1, b: "2", c: true } // OK
const foo5: RequireAtLeastOne<Foo> = {a: 1, c: true } // OK

型定義は以下の通りです。

type RequireAtLeastOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> =
  {
     [Key in KeysType]: (
       Required<Pick<ObjectType, Key>>
     )
  }[KeysType]
  & Except<ObjectType, KeysType>;

まずは<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType>から解説します。 KeysType extends keyof ObjectType = keyof ObjectTypeにFooを適用すると以下のようになります。

KeysType extends keyof Foo = keyof Foo

KeysType extends keyof FooはFoo型が持つキー値"a"``"b"``"c"のうちのいくつかを指定することを可能にしています。
= keyof Fooの部分はもし指定が無かった場合はkeyof Fooの値を適用する。つまり"a" | "b" | "c"となります。

そのため、RequireAtLeastOne<Foo>RequireAtLeastOne<Foo, "a" | "b" | "c">と同じです。

次にRequired<Pick<ObjectType, Key>>の部分を解説します。 Utility TypesからRequiredPickが使われています。
RequiredはObject型のプロパティすべてを必須項目にします。Pickは前述の通りです。

Fooを適用し分解すると以下のようになります。

type Picked = Pick<Foo, "a">;  //  { a?: number | undefined; }
type Req = Required<Picked> // { a: number }

次に以下のKeysTypeのMapped Typesの部分を解説します。

type   {
  [Key in KeysType]: (
    Required<Pick<ObjectType, Key>>
  )
}[KeysType]

これにFooを適用して分解すると以下のようになります。

type KeysType = keyof Foo; // "a" | "b" | "c"
type RequiredFoo =
  {
    [Key in KeysType]: (
      Required<Pick<Foo, Key>>
    )
  }[KeysType]
//=> Required<Pick<Foo, "a">> | Required<Pick<Foo, "b">> | Required<Pick<Foo, "c">>
//=> {a: number} | {b: string} | {c: boolean}
const a: RequiredFoo = { a: 1, b: "2", c: true }; // OK
const a: RequiredFoo = { a: 1 }; // OK

Except<ObjectType, KeysType>の部分は前述のExceptを使ってKeysTypeの項目を除外しています。

type ExceptFoo = Except<Foo, "a"> // { b: string; c: boolean; }
type ExceptFoo = Except<Foo, "a" | "b"> // { c: boolean; }

最後にRequireAtLeastOne<Foo, "a" | "b">のときのFooと"a"|"b"をRequireAtLeastOneの型定義に適用して分解してみます。

type RequiredFoo =
  {
    [Key in KeysType]: (
      Required<Pick<Foo, Key>>
    )
  }[KeysType]
//=> {a: number} | {b: string}

type ExceptFoo = Except<Foo, "a" | "b">
//=> { c: boolean; }

type RequireAtLeastOneFoo = RequiredFoo & ExceptFoo;
//=> ({a: number} | {b: string}) & { c: boolean; }
//=> { a: number; c: boolean } | {b: string; c: boolean; }

const foo1: RequireAtLeastOne<Foo, "a" | "b"> = { a: 1, c: true } // OK
const foo2: RequireAtLeastOne<Foo, "a" | "b"> = { b: "2", c: true } // OK

まとめ

type-festは便利ですが、その型定義は複雑な型パズルになっていることがわかりました。
それらを読み解くことがMapped TypesやConditional Typesの理解を深めることができるのではないかと思いこのエントリを書きました。
もし複雑な型定義が必要になったときは参考にしてみてください。また、作った複雑な型定義は誰かが必要としているかもしれませんので、OSSとして公開したりtype-festにPRを送ったりTypeScript本体にPRを送ってみていただけると嬉しいです。

とても長くなったので、続きは後半として近日公開します。

最後までお読みいただきありがとうございました。不備や質問はTwitter - @shisama_宛かブコメでお願いいたします。

2019年買ってよかったもの

この記事は今年買ってよかったもの Advent Calendar 2019 - Adventar 20日目の記事です。

Eufy RoboVac

Ankerが出しているロボット掃除機です。
ルンバは高いので買うのに迷っていましたが、Amazonのタイムセール祭りでこちらの商品が15,840円になっていたため購入しました。

ロボット掃除機は周りからも勧められたり職場でもルンバが稼働していたりしていて気になっていました。

この商品はスマホから操作することはできませんが、時間予約が可能だったりリモコン操作が可能です。
またAnkerが出しているため、バッテリーもよく100分間掃除し続けます。

あと、思っていたより静かです。
子どもが寝ている隣の部屋で動かしても子どもが起きないのは助かっています。

1センチ程の段差は超えていくのでカーペットやマットが敷いていても問題なく掃除してくれます。

これにより掃除機をかける時間や頻度が減り我が家にとって無くてはならないものとなりました。

今年のベストバイかもしれません。

Ergohuman オフィスチェア ベーシック ハイタイプ

座面チルトを調整できるプロではなくベーシックです。

今年転職をして週3日は在宅勤務しているのですが、在宅勤務用にちょっとだけ良い椅子を買おうと思いAmazonプライムデーでゲーミングチェアを買ったのですが時期が夏ということもありお尻が蒸れて辛いものがありました。

また、座り心地も期待以下で残念でした。唯一良かったのはリクライニングが170度まで倒せてオットマン付きだったので寝れるというものでした。しかし、仕事用のため"寝れる"は重要ではありませんでした。

不満を抱いていると偶然にもErgohumanが安くなって売っているのを見かけてその場で30分ほど検討して購入を決めました。

これが大正解でした。蒸れないですし、座り心地もずっと座ってられる快適さです。
もしこれを読んでいる方でゲーミングチェアを購入検討している方は、一度ヨドバシカメラなどで座ってみてからの方がいいです。
あと革製品の場合、蒸れるので夏場はクッションを挟むなど通気できるように工夫する必要があります。

少し高い買い物でしたが、使用時間を考えるとかなり良い買い物でした。

前述のロボット掃除機に並ぶ買って良かったものです。

RAVPower 61W USB-C 急速充電器 RP-PC112

GaNを使った60W出力の充電器です。
この充電器は世界最小と豪語しているだけあって本当に小さいです。

転職してMacbookPro 15インチを会社から支給していただいたのですが、前述のとおり在宅する日とオフィスに出社する日があり持ち運びが面倒でした。
少しでも軽くしたいという思いから充電器を持ち運ばなくて済むように自宅用兼出張用に小さいのを買おうか迷っていました。
しかし、Amazonサイバーマンデーでこの商品が約4500円→約3200円に値下げして即ポチりました。

大きさや軽さについては文句ありません。後述するAnkerのGaNを使った30W充電器PD Atom1やiPhoneXRと比べてもかなり小さいことがわかるかと思います。

これにより通勤が楽になりました。なにより充電器を忘れる心配がなくなったのはQoL爆上げでした。

Patagonia マウンテンパーカ

f:id:Shisama:20191222013456j:plain

実店舗で買ってネット上では見つけられなかったので実物の写真です。

毎年買うか迷っていたマウンテンパーカですが、今年やっと買いました。

安くで買えて高機能なので大変満足です。
patagoniaかTHE NORTH FACEのどちらかって感じで考えていたので良かったです。

非常に暖かいです。また、ポケットも多いのでスマホ2台、キーケース、長財布、モバイルバッテリーぐらいだとカバンを持たなくてもいいのはすごく楽です。

この冬はこればかり着ています。

トップオープン スーツケース

スーツケースなのですが、このスーツケースの特徴としてはトップがジップになっていて開閉可能な点です。

f:id:Shisama:20191227051213p:plain

トップを開けるとポケットがあり、小物を入れておくことができます。
スーツケースは一度閉じると開けるのが手間ですが、このトップオープンはこの開閉機能があることでスーツケースからの取り出しがすごく楽になりました。
鍵をかけられるのでパスポートとかさっと取り出したいものもこのトップのポケットに入れておくことができます。

10,000円〜15,000円なので、値段も手頃でした。

MOFTノートパソコンスタンド

折りたたみができるノートパソコンスタンドです。
この製品の特徴はノートパソコンに直接貼ることができる点です。

f:id:Shisama:20191222005043j:plain

このようにノートパソコンの裏面に貼り付けます。
何度でも貼り直すことができ、貼り付けた跡も残らないので何回でも調整したり他の端末に使うことが可能です。

f:id:Shisama:20191222005127j:plain

公式によると厚さはわずか3mm、重さがわずか89gだそうです。

f:id:Shisama:20191222005449j:plain

硬いのですが、折り曲げられるようになっています。
折り曲げ線に沿って折ることでスタンドになります。

f:id:Shisama:20191222005632j:plain

スタンドの高さは2段階あり、↑は低い角度モードです。

f:id:Shisama:20191222005738j:plain

↑は高い角度モードです。高い角度の方だと人によってはタイピングしづらいかもしれません。

このMOFTのおかげで少し猫背がマシになったかもしれません。

Anker PowerPort Atom PD 1

前述のRAVPower RP-PC112を買うまではこの製品が大活躍しました。

ゴルフボールぐらいの大きさで30W出力するのは発売当初かなり衝撃的でした。充電も速いです。

プラグをたたむことができれば文句なしの製品です。

Anker PowerCore 10000 PD

Ankerのモバイルバッテリーです。
この製品の特徴は手に収まる大きさで、PD充電可能なところです。

昨年Pixel3に乗り換えたこともあり身の回りのガジェット類のUSB Type-C化が進んでいます。
そこでType-C to Type-Cのモバイルバッテリーも欲しいなと思って探したところこちらが良さそうで買いました。

Type-C差込口からのPD充電は速いです。もはやType-Aの差込口はほぼ使っていません。
モバイルバッテリーで10000mAhぐらいならこの商品は自信を持ってオススメできます。

最後に

振り返るとAmazonでばかり購入していました。
来年はAmazonのクレジットカードを作ってもいいかもしれないなと考えています。

あとガジェット類が多いですね。

今はAirpods ProやスマートウォッチやHappy Hacking Keyboard Hybridを買おうか迷っています。
どれも安くないので悩んでいます。。

昨年版はこちら shisama.hatenablog.com

最後までお読みいただきありがとうございました。不備や質問はTwitterやコメントにてお願いいたします。

Node.jsへのコントリビュート解説、そしてOSSへ貢献するということ

https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic.webpunks.co%2Fuploads%2F2016%2F08%2Fnodejs-modules-webentwicklung-webdevelopment-webpunks.jpg&f=1&nofb=1

この記事は Node.js Advent Calendar 2019 - Qiita の2日目の記事です。遅くなってしまいました。

Node.js本体へのコントリビュート解説記事です。この記事は不足している情報や更新があれば、モチベーションが続く限り更新していきたいと思っています。

JSConf JPのスタッフの打ち上げのときに日本人のNode.jsへのコミットしている人が少ないという話がでました。

Node.jsに限らずOSSへのコミット経験があるという人は私の周りには少ないです。

もちろんOSSにコミットしているから良い悪いという話ではなく、Node.jsやOSSにコミットしてみたいと相談いただくことが時々あるので僕の経験でよければ伝えたいと思いました。

私の経験からNode.jsへのコントリビュート方法の解説とOSSへの貢献を通じて得たものについて書き残しておきたいと思います。

言葉の定義として、コードやドキュメントにコミットするだけでなく、「Issueを作る」「Pull Requestを送る」をまとめてコントリビュートと呼ぶことにします。

Node.jsのコントリビュートについてはGitHubのNode.jsのリポジトリに書かれています。

github.com

また、 大津さん(@jovi0608)が2016年のCode And Learn用に翻訳を公開してくれています。

github.com

今回はこのドキュメントをもとに私の経験や補足を合わせて解説します。

また、Node.jsのコラボレーターになったときに書いた記事もご興味あればご一読ください。

shisama.hatenablog.com

目次

解説をする前に

Node.jsにはmasterブランチへのwriteの権利を持っていてコードレビューを行ったりCIを実行する人々が世界中にいます。彼らのことをコラボレーターと呼びます。

さらにコラボレーターの中から数名が仕様を決めるTechnical Steering Committee(通称TSC)がいます。

彼らだけがNode.jsの開発をしているわけではありません。定常的にコミットはしなくてもコードやドキュメントの修正を行ってくれたりIssueを作って議論してくれるコントリビューターがいるからこそNode.jsはこれまで生存してきました。

Node.jsに限らずOSSはそういった人々に支えられています。

なので、基本的にコラボレーターはコントリビューターに対して感謝の気持ちを持っています。できるだけ気持ちよく開発できるように心がけている人が多いと感じます。

なので、恐れずにチャレンジしてほしいと思います。

Node.jsへのコントリビュート解説

Code of Conductを読む

Code of Conductとは行動規範です。
OSSは様々なバックグラウンドを持った人が関わっています。Node.jsも例外ではありません。
そのためOSSではCode of Conductを守ることは最も大切なことと言っても過言ではありません。

必ず読みましょう。

node/coc.md at master · nodejs/node · GitHub

過去にCode of Conductを違反したことがきっかけでコアメンバーの除名処分が議論されたこともありました。

yosuke-furukawa.hatenablog.com

Issueを作る

Issueによるバグ報告や質問やフィードバックや機能要望はとても大切なコントリビュートの一つの形です。

Node.jsに関するIssueには2種類あると考えています。
1つ目がNode.jsを使った開発に関する質問、2つ目がNode.jsコアに関する質問やバグ報告や機能要望などです。 細分化するともっとあるかもしれませんが、大きく分けるとこの2つだと考えています。

どちらのIssueにしても直面した問題や質問に関してできるだけ詳しく書くことが解決への一番の近道だと思います。

Node.jsを使った開発に関するIssue

Node.jsを使った開発に関する質問は、nodejs/helpにIssueを作るとよいでしょう。 nodejs/nodeの方にIssueを作ったからといって怒られたりすることはないと思いますが、nodejs/helpの方に書くように促されることはあるかもしれません。

Issueテンプレートが用意されているので、Issueを作ろうとすると以下のように本文にテンプレートが最初から書かれています。

f:id:Shisama:20191203220453p:plain
https://github.com/nodejs/help/issues/new

以下のようにNode.jsのバージョン、動かしたOSのバージョン、スコープ、関係のあるパッケージ(なければ省略)を書いてください。

  • Node.js Version: 12.2.0
  • OS: macOS 10.13.6
  • Scope (install, code, runtime, meta, other?): install
  • Module (and version) (if relevant): jest

Node.jsコアに関するIssue

Node.jsコアに関する質問やバグ報告や機能要望などは、nodejs/nodeにIssueを作るとよいでしょう。
コアというのはNode.js本体のことを指します。 例えば、Node.jsでファイルシステムを操作するfsにバグがあったり、機能追加の要望を出したい、という場合です。

nodejs/nodeのNew Issueボタンを押すと以下のような選択画面が表示されます。

f:id:Shisama:20191203224941p:plain
https://github.com/nodejs/node/issues/new/choose

この画面では以下の選択肢があります。

  • Bug report: バグ報告
  • Feature request: 機能要望
  • Report a security vulnerability: 脆弱性報告。セキュリティポリシーへのリンクになっています。
  • ⁉️ Need help with Node.js?: Node.jsに関する質問。先に紹介したnodejs/helpへのリンクになっています。
  • 🌐 Found a problem with nodejs.org?: Node.js公式ウェブサイト nodejs.org に関する問題報告です。nodejs/nodejs.orgのissue作成画面へのリンクになっています。

ここでは上記のBug reportについて説明をします。
Bug reportを選択すると以下のようにIssueテンプレートが書かれた画面が表示されます。

f:id:Shisama:20191203225712p:plain
https://github.com/nodejs/node/issues/new?template=1-bug-report.md

そこに以下のようなバージョン情報とモジュール名を記載します。

  • Version: 13.1.0
  • Platform: macOS 10.14.6
  • Subsystem: fs

Subsystemについて

このSubsystemには以下の中からリポジトリ内のどこに問題があったかを書きます。

  • lib/*.js (assert, buffer, etc.)
  • build
  • doc
  • lib / src
  • test
  • tools

lib/*.jsassertfsstream...などNode.jsコアが提供しているモジュールを書いてください。

Subsystemについて難しい場合は、以下を基準にしてください。

  • コードに関すること: lib / src
  • Node.jsコアのビルドに関すること: build
  • nodejs.org/api などドキュメントに関すること: doc
  • nodejs/node内で実行するNode.jsコアのテストに関すること: test
  • nodejs/node内で使っているツールに関すること(e.g. eslint): tools

また、IssueやPull RequestのタイトルにもこのSubsystemを用います。

例えば fs: fsPromises.lchmod throws AssertionError on Ubuntuといった感じのタイトルにすると良いでしょう。

Issueの場合、必ずしもSubsystemの記載が必要とは思いません。そこで挫折してIssueを送らないのであれば、書かずに送ってしまってもいいと個人的には思います。

前述のとおり本文をしっかり書いて伝わりやすくすることが大切だと思います。

また、コードやエラーの内容を載せると伝わりやすいです。私が書いたIssueで宜しければ以下を参考にしてみてください。

github.com

Pull Requestを送る

Pull Requestを送ることはIssueを作ることに比べるとやはり敷居が少し高くなります。 コマンドによる操作やGitの操作に不慣れだと難しいかもしれません。

しかし、送ってマージされてもされなくても学びがあったり感謝されることが多いです。多くの場合、コラボレーターから感謝されるでしょう。

この章ではPull Requestを送るときのルールや気をつけること、どのようなPull Requestから始めるのがよいか説明したいと思います。

Pull Requestを送るまで

手順が多いのでおおまかな流れを先に説明しておきます。

  1. nodejs/nodeをForkしてgit cloneする
  2. nodejs/nodegit remoteで追加する
  3. nodejs/nodeのビルド環境を構築する
  4. ブランチを作成する
  5. コードやドキュメントを修正する
  6. ビルドして動作確認を行う
  7. テストやLintを実行する
  8. ForkしたリポジトリにPushする
  9. nodejs/nodeのmasterブランチに対してPull Requestを作成する

一つずつ説明します。

nodejs/nodeをForkしてgit cloneする

Node.jsのリポジトリに直接ブランチを作らずにForkしてPull Requestを送ります。
nodejs/nodeリポジトリの右上に表示されているForkボタンを押してForkしてください。

その後、Forkしたリポジトリをローカルの任意の場所にgit cloneしてください。

nodejs/nodeをgit remoteで追加する

前述でgit cloneした後にgit remoteを実行すると以下のようにoriginのみが表示されると思います。

f:id:Shisama:20191204021707p:plain
git remote

しかし、nodejs/nodeとコンフリクトが起きるかもしれないので、Pull Requestを送る前にnodejs/nodeのmasterにrebaseしましょう。そのためにupstreamとしてremoteに追加しておきましょう。

$ git remote add upstream git@github.com:nodejs/node.git

Node.js のビルド環境を構築する

Node.jsの大部分はC++JavaScriptによって書かれています。ビルドツールにはPythonが使われています。

以下に記載があります。

node/BUILDING.md at master · nodejs/node · GitHub

OSによって異なります。

macOSの場合

以下が必要になります。

  • Xcode Command Line Tools >= 10 for macOS
  • Python 2.7
    • Python 3.x系はまだ実験的に導入している段階です。2.x系のサポート終了も近く現在3.x系への移行が進められています。

Xcode Command Line Toolsを使うにはXcodeをインストールし、xcode-select --installを実行する必要があります。

ビルドの確認はgit cloneしたnodeディレクトリ内に移動して以下を実行します。

$ ./configure
$ make -j4

makeコマンドの-jオプションは並列処理するジョブを設定することができます。-j4は4並列で処理するように設定しています。並列処理することでビルド時間の短縮に繋がります。

この数値はCPUの数やCPUの数×2など諸説あったり環境によって違ったりするので、値を変更して試してみてください。

./node -vを実行し、Node.jsのバージョンが表示されれば環境構築は完了です。

macOSをお使いで途中でエラーが発生した場合は以下のコマンドを実行してみてください。

$ sudo ./tools/macos-firewall.sh

Windowsの場合

公式には色々と書かれていますが、実は一発で構築してくれるnpmパッケージが存在します。

github.com

Node.jsがインストールされていてnpmが実行できるのであれば以下のコマンドを実行してみてください。

$ npm install --global windows-build-tools

このnpmパッケージを使うことでVisual C++やPython2.7が勝手にインストールされます。既にインストールされている場合はスキップします。

ビルドの確認はgit cloneしたnodeディレクトリ内で以下を実行します。

> .\vcbuild

./node -vを実行し、Node.jsのバージョンが表示されれば環境構築は完了です。

ブランチを作成する

Pull Requestを送るためにブランチを切りましょう。

ブランチ名に関しては特にルールはありません。わかりやすい名前にしてください。

またupstreamのトラッキングが必要な場合は以下のように-tオプションを付けてブランチを作成してください。

$ git checkout -b my-branch -t upstream/master

コードやドキュメントを修正する

ディレクトリ構成

先に簡単に説明しておきます。

node
├── .github
├── benchmark
├── deps
├── doc
├── lib
├── out
├── src
├── test
└── tools
  • .github: IssueやPull Requestのテンプレートなどが置かれています。
  • benchmark: ベンチマークを測るためのコードが置かれています。パフォーマンスに影響が出そうな修正を行うときはここにもコードを追加する必要があります。
  • doc: ドキュメントが置かれています。Markdownで書かれています。https://nodejs.org/api のドキュメントもここで管理されており、doc/apiの中に全部置かれています。
  • deps: Node.jsが依存している他のOSSなどが置かれています。JSエンジンのV8やイベントループ処理をするlibuvもここに置かれています。それらのバージョンアップするときはここに取り込みます。
  • lib: JavaScriptのコードが置かれています。主にfsstreamなどのAPIの入り口はここに書かれます。最近はなるべくC++に処理を書かずにJavaScriptに処理を書いていこうという方針が取られています。機能追加をしたいときはまずはここにJavaScriptで実装してPull Requestを送ると良いでしょう。
  • out: これはビルド結果が出力されるディレクトリです。C++コンパイル後のモジュールやRelease用のnodeのバイナリが出力されたりします。
  • src: C++のコードが置かれています。主にV8の機能やlibuvの機能を使うコードが置かれています。また、JavaScriptではパフォーマンス上問題があるものもC++で書かれることがあります。nodeの起動エントリポイントはmain.ccというところにあります。
  • test: テストコードが書かれています。主にNode.jsが提供しているAPIのテストが書かれています。並列実行できるようにparallelディレクトリに書くことが多いですが、直列実行が必要な場合はsequentialディレクトリに書く必要があったりネットワークが必要であればinternetディレクトリに書く必要があったりします。基本的にlibsrcを修正するときはtestの修正も必要です。
  • tools: nodeの開発に使うツールが置かれています。eslintやテスト用のPythonスクリプトなどがここに置かれています。

主にdoclibsrctestを修正することが多いと思います。
deps内のV8やlibuvやOpenSSLのバージョンアップに関しては詳しいメンバーに任せた方がいいかと思います。

コードスタイル

Lintがあるので、基本的に修正してコミットする前はLintを実行するようにしましょう。

$ make lint

また、.editorconfigリポジトリ内に含まれているので、お使いのエディタのプラグインなどを使ってフォーマットかかるようにしてもいいと思います。

詳しくは以下にまとまっています。

node/STYLE_GUIDE.md at master · nodejs/node · GitHub

node/CPP_STYLE_GUIDE.md at master · nodejs/node · GitHub

コードやドキュメントを修正する

バグ修正や機能追加など明確に課題がありlibsrcを修正する必要がある場合は、もちろんlibsrcのコードを修正してコミットするのが良いでしょう。

libsrcを修正したときはtestにテストコードを追加してください。

しかし、明確なバグや機能追加に関しては特にないけど「Node.jsへのコミットがしてみたい」という方には以下の「テスト」「good first issue」「ドキュメント」へのコミットをオススメします。

テスト

Node.jsのテストはカバレッジが公開されています。まだ網羅されていないモジュールのテストがあればカバーするテストコードを追加するのがわかりやすいと思います。

coverage.nodejs.org

テストを書くことで、libsrcの構成についても徐々に把握していくことができると思います。
コードリーディングを通して知らないAPIを知ることもでき、テストといえど学ぶことが多いです。

id:yosuke_furukawa に勧めていただきfsのPromiseのAPIのテストのカバレッジを増やしていくところから始めました。

github.com

上記のPull Requestのように初めてのコミットではCongratulations @shisama on your first commit in Node.js core! 🎉 🎉 🎉のようにコメントがもらえることが多く、Node.jsのメンバーの人の良さが伝わります。

テストを増やしていくことはとても大切なことです。

以下のようにテストのカバレッジが低いとIssue管理されます。

github.com

good first issue

多くのOSSのIssueにはgood first issueというラベルが用意されていると思います。

nodejs/nodeのgood first issueのラベルが付いたIssue一覧: Issues · nodejs/node · GitHub

このgood first issueというのは初めてコミットする人にオススメのIssueということがわかるように付けられるラベルです。このラベルのIssueを見つけた場合はチャレンジしてみるとよいでしょう。

ドキュメント

APIのドキュメント内でtypoを見つけたり、サンプルコードの不備を見つけたときはdocにコミットしてみるのも良いと思います。

github.com

Lintを実行する

環境に合わせて以下のコマンドからLintを実行してください。markdownC++JavaScriptの静的解析が走りコードスタイルのチェックなどを行います。

macOS/Unixの場合

$ make lint

Windowsの場合

> vcbuild.bat lint

テストを実行する

環境に合わせて以下のコマンドからテストを実行してください

macOS/Unixの場合

$ ./configure && make -j4 test

Windowsの場合

> vcbuild test

さらに詳しくは以下のテストガイドラインをお読みください。

node/writing-tests.md at master · nodejs/node · GitHub

テストカバレッジを取る

テストのカバレッジを取得したい場合は以下のコマンドにてテストを実行します。

$ ./configure --coverage
$ make coverage

テストが完了すると以下のファイルをブラウザで確認してください。

カバレッジlibsrcで分かれています。

  • lib(JavaScript): coverage/index.html
  • src(C++): coverage/cxxcoverage.html

ビルドして動作確認を行う

macOS/Unixの場合

nodejs/nodeのビルドには前述のとおりmakeコマンドを用います。

$ ./configure
$ make -j4

-jオプションについても前述のとおり、お使いのCPUの数に合わせて適宜変更してください。

Windowsの場合

> .\vcbuild

ビルドが成功するとnodeディレクトリの直下から./nodeを実行してNode.jsのREPLが起動することを確認してください。

その後修正した内容に応じて動作確認を行ってください。

コミットする

修正したファイルをgit addしてgit commitするだけです。

$ git add my/changed/files
$ git commit

このときにコミットメッセージに注意してください。

コミットメッセージ

Node.jsではコミットメッセージにルールがあります。

  • 1行目はsubsystem名: 修正内容の概要を書かなければいけません。
  • 2行目は空白の改行にしてください
  • 3行目以降はコミットに関する詳細を書いてください。1行目だけで伝わる場合は3行目以降は不要です。
    • この3行目以降に関連するIssueやPull Requestなどのリンクを貼ってください
      • コミットにより閉じれるIssueがある場合はFixes: https://github.com/nodejs/node/issues/1337のようにFixes:から始めて書いてください
      • 関連する参考リンクはRefs: https://github.com/nodejs/node/pull/3615のようにRefs:から始めて書いてください

まとめると以下のように形になります。

subsystem: explain the commit in one line

The body of the commit message should be one or more paragraphs, explaining
things in more detail. Please word-wrap to keep columns to 72 characters or
less.

Fixes: https://github.com/nodejs/node/issues/1337
Refs: https://eslint.org/docs/rules/space-in-parens.html

ルールに沿わなかったからといって問題にはならないですしコラボレーターが優しく教えてくれると思います。なので、気負わずPull Requestを出してください。

Rebaseする

Pushする前にgit remote addしたupstream/mastergit rebaseしてください。

$ git fetch upstream
$ git rebase upstream/master

git rebaseすることでnodejs/nodeの最新の状態を取り込むことができます。

ForkしたリポジトリにPushする

Forkした自分のnodeリポジトリにPushする前に念の為git remoteで確認してください。

私の場合はFork先がshisama/nodeのため、以下の結果になります。

$ git remote -v

origin  git@github.com:shisama/node.git (fetch)
origin  git@github.com:shisama/node.git (push)
upstream    git@github.com:nodejs/node.git (fetch)
upstream    git@github.com:nodejs/node.git (push)

originリポジトリが自分がForkしたリポジトリになっていることを確認したら、originにPushしてください。

以下はfix-foo-barブランチをpushする例です。

$ git push origin fix-foo-bar

Pull Requestを作成

nodejs/nodeをブラウザで開くと、右上に以下のボタンが表示されていると思います。

f:id:Shisama:20191204200008p:plain

以下のボタンを押してください。

f:id:Shisama:20191204200203p:plain

すると、以下の画面が表示されます。

f:id:Shisama:20191204200327p:plain

本文には以下のテンプレート文章が入力されています。

<!--
Thank you for your pull request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests and possibly benchmarks.

Contributors guide: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
-->

##### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [ ] `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes
- [ ] tests and/or benchmarks are included
- [ ] documentation is changed or added
- [ ] commit message follows [commit guidelines](https://github.com/nodejs/node/blob/master/doc/guides/contributing/pull-requests.md#commit-message-guidelines)

<!--
Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->

<!-- -->で囲まれている部分は変更する必要はありません。

この中で重要なのはチェックリストです。

確認した事項にチェックを入れていってください。

  • [ ] make -j4 test (UNIX), or vcbuild test (Windows) passes
  • [ ] tests and/or benchmarks are included
  • [ ] documentation is changed or added
  • [ ] commit message follows commit guidelines

日本語に訳すと、

  • [ ] make -j4 test (UNIX) または vcbuild test (Windows) が通ること
  • [ ] テスト(test)やベンチマーク(benchmarks)が含まれていること
  • [ ] ドキュメントが変更、追記されていること
  • [ ] コミットメッセージが ガイドラインに従っていること

コミット内容によっては全てチェックする必要はありません。

例えば、テストを追加しただけの場合はドキュメントの変更などは無いのでdocumentation is changed or addedにはチェックを付ける必要はありません。

また、本文に追加情報が必要な場合は書いてください。

ここまできたらCreate pull requestボタンを押してPull Requestを送ってください。

コードレビュー

Pull Requestを送るとNode.jsのコラボレーターのメンバーがコードレビューしてくれます。

もし、なかなかコードレビューしてくれなければ @nodejs/collaboratorsとコメントしてコラボレーターにメンションしてください。

コードレビューで指摘があった場合は適宜修正したり議論してください。

CI

コードレビューで承認され始めるとコラボレーターがCIを実行してくれます。このCIはJenkinsで動いています。

Node.jsは様々な環境で動作しなければいけないため、数種類のOSやCPUアーキテクチャによるCIジョブが動きます。

f:id:Shisama:20191204201954p:plain

これら全てが通らない場合は何が原因かCIの結果を見て解決させましょう。

また、破壊的変更とみなされる修正の場合、代表的なnpmパッケージで正常動作するかテストが行われます。それによりnpmのエコシステムに影響が無いことを担保しようとしています。

例えばreacteslintなど100種類以上のパッケージでテストが行われます。

それがCITGMです。

github.com

CITGMでどのパッケージをテストしているかは citgm/lookup.json をご確認ください。

Landed 🎉

コラボレーターが承認してCIが通ればauthor readyというラベルが貼られます。このラベルが貼られたあと48時間の間に他のコラボレーターから反対意見などが出なければコラボレーターがmasterにマージしてくれます。

f:id:Shisama:20191204202342p:plain

これで初めてのNode.jsへのコミットが完了です🎉🎉🎉

英語について

ここまで触れてきていませんでしたが、OSSの世界ではコミュニケーションはすべて英語で行われます。

ただ、Google翻訳を使って読み書きすれば全く問題なくコミュニケーションはとれるので気負わずにコミュニケーションをとってほしいと思います。

Node.js コア以外へのコントリビュート

Node.js コア以外にもNode.jsへのコントリビュート方法はいっぱいあります。

例えば、ブログや勉強会で発信することもコントリビュートの一つだと思いますし、ドキュメントの翻訳をしたりOSSのコントリビュートのサポートをするのも立派なコントリビュートだと思います。

発信したり行動することで批判があるかもしれませんが、もっと良いこともたくさんあるので気負わずに行っていただけたら良いなと個人的には考えています。

はじめてのOSSへの貢献

ここからは私個人の話になります。技術的な話は出てきません。

この章は私がOSSへ貢献をしたことによる変化や影響に関する話です。

Node.jsにコミットする前からOSSへのコントリビュートの経験はありました。
はじめてOSSに貢献したのは、Key-ValueストレージのLevelDBをNode.jsで使うことができるようにするlevelupのハンズオンlevelmeupの翻訳でした。
このlevelmeupNodeSchoolのworkshopの一つです。
翻訳のOSSへの立派なコントリビュート方法の一つです。 この経験は @leichtgewicht@kamiyam が行っていたNodeSchool Osaka のイベント内で教えてもらいながら始めたのがきっかけでした。
そのあと、彼らの支援で関西Node学園というコミュニティを立ち上げることができ、東京Node学園祭やJSConf JPのカンファレンススタッフを経験することができました。
OSSへの貢献やコミュニティへの参加が人生に大きく影響しました。

同じようにコミュニティの輪を広げたりその人の助けになれれば良いと思うので、Node.jsや他のOSSへのコントリビュートについて質問などは可能な限り答えたいと思っています。Twitter - @shisama_宛やこのブログへのコメントなどで気兼ねなく質問してください。

また、jsconfjpのslackには日本のNode.jsの一線で活躍されている人がいるのでそこで質問するのもいいでしょう。私も相談していました。

t.co

OSSに貢献するということ

OSSに貢献するということで人との関わりが生まれます。

それは、インターネット上だけかもしれません。しかし、OSSに携わるということはまだ会ったことない人とのコミュニケーションが発生します。
そこからもしかすると新しい良い出会いがあるかもしれません。

また、同じようにOSSに貢献している人同士がインターネット上や勉強会などで偶然出会って繋がりあうこともあるかもしれません。

私自身、Node.jsにコントリビュートしなければ出会えなかった人もいたと感じます。
特にNodeSchool Osakaのメンバーや関西でのコミュニティで出会った人たちや、海外の偉大なエンジニア、JSConf JPのメンバーとの出会いは人生におけるかけがえのない財産だと感じています。

OSS貢献を通じて読者の方の人との繋がりが生まれたり人生が良い方向に向くことを願っています。この記事がその一助になれば幸いです。

長い記事でしたが、最後までお読みいただきありがとうございました。不備や質問はTwitter - @shisama_やコメント欄でお願いします。

2019年、2020年のJavaScript

この記事は JavaScript Advent Calendar 2019 - Qiita の初日の記事です。

2019年を締めくくるアドベントカレンダーの初日ということで、今年のJavaScriptを簡単に振り返りたいと思います。2020年のJavaScriptについても予習しましょう。
2019年、2020年が何を指しているかは後述します。
カテゴリが「プログラミング言語」なので、React、Vue、AngularやNode.jsなどJavaScriptを使った技術ではなく、JavaScriptの言語機能にフォーカスしたいと思います。

はじめに

※この章はJavaScriptの初歩的な内容になっています。JavaScript機能に関する内容だけ知りたい方は読み飛ばしていただいても大丈夫です。

JavaScriptには実行環境(JavaScriptエンジン)が複数あります。

上記のように実行環境は様々あり、開発している方々も違います。そのため、実行環境間で差異が発生することもあります。
そのため、JavaScriptにはTC39という標準化団体のような組織がありECMAScriptの仕様を作っています。JavaScriptはこのECMAScriptECMA-262に準拠しています。

このECMAScriptは毎年アップデートされ、ECMAScript 2019やECMAScript 2020など年がついたネーミングがされています。ES2019やES2020と省略して使われることが多いです。
ES2019は今年6月にpublishされました。

ECMAScriptの仕様に追加されるには5段階のステージングがあり、Stage 4になったものが仕様に追加されます。

それぞれのステージングに上がる条件など、詳しくはこちらをご確認ください。

ES2016以降のどの機能はどの実行環境で使用可能かを有志の方が一覧化してくれています。この一覧を見れば何の機能が何のバージョンの何のブラウザで使用可能か確認することができます。

kangax.github.io

今回はES2019で追加された機能やES2020に追加される機能について紹介します。

ES2019

Optional catch binding

try...catch - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 58 66 11.1 10

例外をキャッチするために書くtry-catchですが、従来では必ずcatch節に例外を識別するオブジェクトを指定しなければいけませんでしたが、必ずしも例外オブジェクトが必要ではありません。このOptional catch bindingにより指定しなくても良くなりました。

gist.github.com

JSON superset

JSON - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 62 66 Yes 10

JSONECMAScriptのサブセットにする提案です。

JSON文字列にはエスケープされていないU+2028(行区切り文字)やU+2029(段落区切り文字)を含むことができますが、JavaScriptでは文字列中にこれらを含むことができませんでした。以下はNode.js v8.16.2で確認しました。

gist.github.com

しかし、この仕様が追加されることでJavaScriptでもU+2028U+2029を含む文字列でもOKになりました。以下はNode.js v10.17.0で確認しました。

gist.github.com

Symbol.prototype.description

Symbol.prototype.description - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 63 70 12.1 11.0

Symbolの引数に指定した値を参照することができます。descriptionプロパティは読み取り専用です。

Object.fromEntries

Object.fromEntries() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 63 73 12.1 12.0

key, valueのペアの配列をオブジェクトに変換します。

Well-formed JSON.stringify

JSON.stringify() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 64 72 12.1 No

不正なサロゲートペアがエスケープされた文字列として出力されるようになります。

以前までは

JSON.stringify("\uD800");
// --> '"�"'

だったのが

JSON.stringify("\uD800");
// --> '"\ud800"'

になります。

String.prototype.{trimStart,trimEnd}

Array.prototype.{flat,flatMap}

Array#flat

Array.prototype.flat() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 62 69 12 11.0

ネストされた配列をフラットにします。以下MDNから引用です。

Array#flatMap

Array.prototype.flatMap() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 62 69 12 11.0

配列の値をmap関数でマッピングし、結果の配列をフラット化します。以下MDNから引用です。

ES2020

ES2020はまだ仕様が確定していません。しかし、いくつかの機能は既にStage 4となっているためES2020に含まれることが確定しています。この記事では2019/12/01時点でStage 4になっているES2020の機能を紹介します。

String.prototype.matchAll

String.prototype.matchAll() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 63 73 No 12.0

String.prototype.matchAllは指定した正規表現のキャプチャに該当するものを文字列の配列として取得できる機能です。
また、matchAllの戻り値はiteratorオブジェクトです。
そのため、値の取り出しにはfor-of構文で取り出したりnext()関数を使ったりArrayに一度変換する必要があります。 JavaScriptiteratorに関して詳細はこちらを御覧ください。

gist.github.com

import()

import - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 67 63 11.1 Experimental

import()はDynamic importと呼ばれるモジュールを動的に読み込む関数です。 非同期で実行されPromiseでラップされたモジュールが返ってきます。
使い所としては処理によって読み込むモジュールを変更したり、非同期でモジュールを読み込む場面でしょう。
Node.jsでは--experimental-modulesというフラグを付けて実行する必要がありましたが13.2からはこのフラグは不要になりました。ただしまだexperimentalな機能です。

gist.github.com

BigInt

BigInt - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 68 67 No 10.4

JavaScriptのNumber型は53bitまでしか扱えません。そのため2 ** 53 = 9007199254740992が限界値です。これ以上足しても9007199254740992になります。
9007199254740992を超える数値を扱うためにBigIntが提案されました。構文は1nのように数値のあとにnを付けます。 またBigIntはBigIntとしか演算ができません。1n + 1はTypeErrorになります。
BigIntは任意精度の整数です。任意精度な浮動小数点数についてはStage 0のdecimalとして提案されようとしています。

gist.github.com

Promise.allSettled

Promise.allSettled() - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 71 76 13 12.9

Promise.allSettledは複数のPromiseな非同期の処理を並列に実行する関数です。すべて完了した状態になったときに、Promiseの結果をオブジェクトの配列を返します。
引数にはPromiseオブジェクトの配列を渡します。 戻り値はiterableなオブジェクトです。

gist.github.com

従来からPromise.allというものがありました。これも同様に複数のPromiseを並列実行します。すべての処理が成功しresolveされれば良いのですが、どれか一つでも失敗してrejectされたときにどこまで実行されたのかはわかりません。
そこで提案されたのがPromise.allSettledです。とにかくすべて完了させて、成功/失敗かは完了後に判定できるようにオブジェクトに内包して返してあげようというものです。
このためreject(失敗)したものがどのPromiseか判定でき、必要に応じてリトライなどの処理が行えます。

globalThis

globalThis - JavaScript | MDN

Edge Edge Firefox Firefox Chrome Chrome Safari Safari f:id:Shisama:20191128111950p:plain Node.js
No 65 71 12.1 12.0

globalThisは環境を超えてグローバルオブジェクトにアクセスするためのオブジェクトです。
これまでブラウザではwindow、Node.jsではglobalがグローバルオブジェクトとして使われていました。
もちろんこれらは今まで通り各環境で使えますが、どちらでもアクセス可能なオブジェクトとしてglobalThisが追加されました。
globalThisを使うことで以下のような使い分けをする必要がなくなりました。

最後に

こう見ると2019年もJavaScriptに様々な機能が追加されました。2020年も様々な機能が追加されると思います。
現在のECMA-262のmasterブランチはES2020がターゲットになっており、https://tc39.es/ecma262/ は更新されています。
もし気になる機能でES2020として追加が確定になるかはECMA-262proposalのFinishedを見ると良いかと思います。
他にも様々な機能が提案されているので、興味があればtc39/proposalsのリポジトリを御覧ください。 また、TC39は定期的にミーティングを行っており仕様の提案について話し合っています。アジェンダミーティングノートが公開されているので興味があれば御覧ください。

以上、最後までお読みいただきありがとうございました。不備や質問はTwitter - @shisama_やコメントで受け付けています。気兼ねなくメンションください。

謝辞

このブログを公開した際、不備がありましたが以下の方々が教えてくださいました。
ありがとうございました。

W3K Web Developers Meetup #1 を開催しました。 #w3kansai

特定の言語やフレームワークなどに属さない話やWebに関する勉強会を開きたいという思いがあり、W3Kという勉強会を開催することにしました。

w3k.connpass.com

当日の様子です。

togetter.com

需要があるのかわからなかったので、最初は参加枠を15名にしていたのですが有難いことにすぐ埋まりました。そのため、参加枠を35名に増枠しての開催となりました。

キャンセルなどもありましたが、参加登録してくださっていた方の8割以上が当日来場されててドタキャンが少なく良かったと思いました。

セッション

Take rendering process to pieces for Web browser performance / @plum_shiga

レンダリングに関する話をしていただきました。
Paint処理がとにかく最強に重い!will-changeプロパティを使おうと強く主張されていて、力の入ったセッションでした。

WebUSB入門 / @aruneko99

WebUSBに関する話をしていただきました。
WebUSBはセキュリティが懸念でなかなか標準化が進んでいないイメージがあり、なかなか知見も出てきていなかったのでとても勉強になりました。

firebase を使い始めて / @koneko59

firebaseに関する話をしていただきました。

大規模データのパフォーマンス改善 / @ef_ryusx

パフォーマンス改善について話をしていただきました。
PostgreSQLのパフォーマンスを改善したことで最大約90%パフォーマンス改善したのはすごいのでぜひPostgreSQLをお使いの方はぜひ資料を御覧ください。

ビデオの字幕ってどうなってんの? / @Daikids2

web videoの字幕・キャプションを行うWebVTTについて話をしていただきました。
<track>タグやWebVTTといった馴染みのない技術を紹介されてて、見るもの聴くものが新しく勉強になりました。

Portalsを試してみた話 / @granciel_33

Portalsに関する話をしていただきました。
Google I/O '19やChrome Dev Summit '19などでも大きく紹介されていて注目度が高い技術で、まだ新しい技術なので事例や知見がまだ少なかったので大変勉強になりました。また、Portalsについてはもう少し使われ始めたら別途テーマとして勉強会を開きたいなと考えています。

最後に

初回ということで参加者が何を期待しているか、期待されていることとイベントの趣旨に大きな乖離がないか心配でしたが、概ね満足していただけたようで良かったです!これからも定期的に開催していきたいと思います。

最後までお読み頂きありがとうございました。今後ともW3Kを宜しくお願いします。

【資料公開】FRONTEND CONFERENCE 2019でXSSやセキュリティについて話します #frontkansai

2019-11-02 グランフロント大阪で行われるフロントエンドカンファレンス2019に『フロントエンドエンジニアのためのセキュリティ対策~XSS編~』というタイトルで発表します。
資料は公開しています。100ページ超えているので読むのが大変だと思います。(作るのも大変だったが・・・)
当日までに変更はあるかもしれませんが、大きくは変わらないと思います。マサカリ待ってます。

speakerdeck.com

スタッフとしても携わっているので当日はスタッフ兼登壇者ということであまり懇親会などには顔を出せないかも知れませんが、もし見かけたら気兼ねなく話しかけてください!質問やマサカリも待っています!!
発表をぜひ聞きに来てください!

2019.kfug.jp

そもそもこの話をするきっかけになったのは、Webは脆弱性と常に隣り合わせだなと日々感じることが多く、Webに関係するセキュリティ関係を調べていたところCSPやTrusted Typesなどブラウザのセキュリティ対策の機能に興味を持ち調べたためです。
CSP Lv.3はW3CのWorking Draftが出ていてTrusted TypesはChromeshipされようとしています

もし、お読み頂いている方でWebのセキュリティに興味がある方は仲良くなってください!色々教えてほしいです!

では当日お会いしましょう!お読みいただきありがとうございました。