9 月の OSS 活動 (Prettier とか Babel とか)
以前投稿した7 月と 8 月と 9 月前半の OSS 活動の中で、9 月前半までの記録を含めてしまったが、それ以降もそれなりに活動をしていたので改めて 9 月の OSS 活動として紹介したいと思う。
9 月は Prettier のメンテナーとしての活動を続ける傍らで Babel のパーサーの改善も行っていた。Prettier は JavaScript をパースするときにデフォルトでは @babel/parser
を使用するので、Babel のパーサーの改善は間接的に Prettier を改善することにもつながる。実際、自分が今月行った改善のおかげでいままで Prettier がフォーマットできなかったコードが一部フォーマットできるようになるので、Babel がリリースされたらテストケースを追加しようと思う。
prettier/prettier
2.1.2 をリリースした。これは 2.1 で発生したリグレッションの修正のみを含むパッチリリースである。
Prettier のようなコードフォーマッターでは、何が破壊的変更なのかという判断が非常に難しいので、厳密に semver を守れている自信はないのだけど、できるだけ semver に従ったバージョニングをしていきたいと思っている。そのため、2.1 で発生したリグレッションの修正以外のコミットはリリースに含めたくなかった。しかしすでにmaster
ブランチにそういったコミットが入っていたので、手動でpatch-release
ブランチにcherry-pick
してそこからリリースした。ローカルの Git で行う作業が多いリリースは非常に緊張する。
I've published @PrettierCode 2.1.2. This version includes some bug fixes. Please see changelog for detail.https://t.co/XoCPFUW6qm
— Sosuke Suzuki (@__sosukesuzuki) September 16, 2020
2.1.2 に含まれる以下の変更もほとんど自分が行った。
GraphQL: Fix formatting for directives in fields(9116)
GraphQL でフィールドに対するディレクティブのインデントが崩れるというバグの修正だが、正直自分は GraphQL 1回も書いたことないので直してみてから有識者に聞いたところ挙動に問題はなさそうとのことだったのでマージした。
Fix line breaks for CSS in JS
styled-components などの CSS in JS で改行が一部の改行が壊れるというバグの修正。
// Input
styled.div`
${(props) => getSize(props.$size.xs)}
${(props) => getSize(props.$size.sm, "sm")}
${(props) => getSize(props.$size.md, "md")}
`;
// Output
styled.div`
${(props) => getSize(props.$size.xs)}
${(props) => getSize(props.$size.sm, "sm")}
${(props) => getSize(props.$size.md, "md")}
`;
YAML: Fix printing doubles a blank line before a comment
これは YAML のコメントと空行を組み合わせると奇妙なフォーマットが起こるというバグで、今でもよくわかっていない。YAML のシンタックスは自分には難しく、そしてほとんど書いたことがないので実は未だによくわかっていない。なので他のメンテナーに一部引き継いでもらった。
未だに不安定な気はするものの、リグレッションは修正できたのでその状態でリリースを行った。
まだリリースされていはいないいくつかの変更もここで紹介する。
- Use better fonts in Playground。Playgroundを等幅フォントに変更した。これによってスペースの数などを目視で確認するのが圧倒的に楽になった。
- Release script: Commit and push after updating dependents count。https://prettier.io には Prettier に依存しているパッケージの数が掲載されており、リリーススクリプト内でリリースと同時に更新している。しかし、実際には変更だけしてコミット・プッシュがされていなかったのでするように修正した。
- Refactor: Use set instead of array。これは
Refactor
とタイトルに書いてあるが、実際にはリファクタリングというよりパフォーマンス改善にあたる。Set のような使い方をされていた配列を、Set と WeakMap を使って書き換えた。
sosukesuzuki/prettier-regression-testing
https://zenn.dev/sosukesuzuki/articles/753d7ec2d65e154599c3 に書いたが、GitHub Actions を使って Prettier にバグがないかを確認するツールを開発した。個人で開発したものだが、OSS 活動のために作った OSS なのでここに書いておく。
babel/babel
先月はなぜか Babel パーサーへの意欲が高まってしまい、10 個ほど Pull Request を作成した。すべてシンタックスエラーの改善だが、一部は Prettier の挙動に影響を及ぼす。
Babel は最近になって(7.9 から) errorRecovery
機能が導入されたこともあり、シンタックスエラーがあまりヒューマンフレンドリーではない傾向にある。そういった挙動を修正するのは比較的簡単だがあまり手がつけられていないようなので、もし Babel へのコントリビューションに興味がある人がいたらそこから挑戦していくと良いかもしれない。
メンテナーとしての責任や義務感がないので、全体的に楽しくやることができた。そして JavaScript や TypeScript について多少詳しくなれたような気がする。
Throw a syntax error for a declare function with a body
現在の Babel は次のような変換を行う。
// Input
declare function foo() {};
// Output
function foo() {}
この入力は TypeScript のインバリッドなコードだが、このような変換がされるのはバグである。Babel のパーサーは ambient context の関数だろうが、ボディがあれば FunctionDeclaration
としてパースするためこのようなバグが起こる。本来であれば TSDeclareFunction
としてパースするべきで、例えば typescript-eslint/typescript-estree
はそのようにパースする。また、このコードはインバリッドであるため、errorRecovery
可能なシンタックスエラーを吐いてあげると人間に親切である。なので、そのように修正した。
Throw a syntax error for a parameter properties in not constructor
現在の @babel/parser
は次のようなエラーをスローする。
// Input
class C {
not_constructor(readonly foo: string) {}
}
// Error
Unexpected token, expected "," (2:27)
この入力は constrcutor 以外に対してパラメータープロパティを使っているためインバリッドな TypeScript のコードだが、このエラーはあまりにも不親切である。そしてこのエラーは errorRecovery
不能なために、例えば次のようなコードを Prettier に入力するとエラーになる。
class C {
// o が足りない
constructr(readonly foo: string) {}
}
本来 Prettier は書きかけのコードであってもある程度フォーマットができてほしいので、これは修正する必要がある。なので、errorRecovery
可能なシンタックスエラーを吐きつつ、パースは成功するように修正した。
Throw a syntax error for a constructor with type parameters
現在の @babel/parser
は次のようなコードに対してエラーをスローしない。
// Input
class C {
constructor<T>(foo: T) {}
}
しかしこのコードは constructor に型引数があるので TS 的にはインバリッドである。なのでシンタックスエラーをスローするように修正した。
Do not throw an error for optional binding pattern params in function declaration
現在の @babel/parser
は次のようなコードに対してシンタックスエラーをスローする。
// Input
export declare function ohai({ foo }?: Args): string;
// Error
A binding pattern parameter cannot be optional in an implementation signature. (1:29)
implementation signature
ではないのにこのエラーが出るのはバグで、実際 TypeScript コンパイラーではこのエラーは出ない。なので修正した。
Throw an error for a declare class field that have an initializer
現在の @babel/parser
は次のようなコードに対してシンタックスエラーをスローしない。
// Input
class A {
declare bar: string = "test";
}
本来 TypeScript では declare
なフィールドに初期値をもたせることはできないはずなので、シンタックスエラーを吐くべきである。なので修正した。
Add missing tests for TypeScript syntax errors
これは足りていなかったテストケースを追加した。よく使われている OSS でも意外とこういうことがある。
Improve syntax error for class fields in ambient context
「初期値を持つ declare
なクラスフィールドにシンタックスエラーをスローしない」、というのは修正したが、ambient context なクラスフィールドの定義方法は他にも存在し、それらに対してもシンタックスエラーをスローするように修正した。
// Input
declare module m {
class C {
field = "field";
}
}
そして、エラーメッセージを TypeScript コンパイラと統一した。
[ts] Throw a syntax error for index signature with declare
現在の @babel/parser
は次のようなコードに対してシンタックスエラーをスローしない。
// Input
class C {
[key: string]: string;
}
TypeScript では declare
な index signature というのは定義できないはずなので、シンタックスエラーをスローするように修正した。
Throw a recoverable error for missing initializer in const declaration
現在の @babel/parser
は次のようなコードに対して errorRecovery
不能なエラーをスローする。
// Input
const a;
// Error
Unexpected token (1:9)
これは人間にとってわかりにくいので、errorRecovery
可能なエラーをスローし、パース自体は成功するように修正した。
Throw a syntax error for empty type parameter/argument
現在の @babel/parser
は次のようなコードに対してシンタックスエラーをスローしない。
// Input
let a: Foo<>;
TypeScript コンパイラは空の型引数に対してシンタックスエラーをスローするので、それに合わせる形で修正した。