メインコンテンツへスキップ

Prettier 1.4: TypeScriptとCSSのサポート

· 1分で読める
非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

このリリースで、PrettierはTypeScript、CSS、Less、SCSS言語のサポートを導入しました!

prettier-revolution-conf

TypeScriptのサポート

これはPrettierで最も要望の多かった機能です。1.4.0では、.tsおよび.tsxファイルをPrettierでフォーマットできるようになりました!

Prettierの仕組みは、プロジェクトを使用してコードのAST表現を生成し出力することです。Babelを支えるパーサーであるbabylonもFlowも、JavaScript部分についてはestree形式に準じたASTを生成し、Flow固有のノードには特別なノードを使用しています。

TypeScriptもFlowと同様に、独自の構文に対して特別なノードを導入します。しかし残念ながら、JavaScript言語の他の部分についてはestree形式に従っていません。このためPrettierでTypeScriptを出力するには、実質的に完全にフォークする必要があるという厳しい状況に陥りました。

ASTの互換性問題は新たなものではなく、ESLintも同様の問題に直面しました。ASTが異なるため、ESLintルールは一切機能しません。幸いにも、@JamesHenry@soda0289typescript-eslint-parserというプロジェクトを開発し、TypeScriptのASTをestree形式に変換してくれます。まさにPrettierに必要なものでした!

このプロジェクトをPrettierに組み込んだ後、@azz@despairblue@PajnがTypeScript固有のノードをすべて実装し、TypeScriptテストスイートの13,000件のテストを正常にパスすることを確認しました。これは大規模な取り組みであり、ついに使用可能になりました :)

GitHub上で見つかった最大級のTypeScriptプロジェクトでPrettierをテストし、正しいコードを出力することを確認しました。コードフォーマットの最適化にはまだ十分な時間をかけていませんので、不審な点があればぜひissueを登録してください!

CSS、Less、SCSSのサポート

TypeScriptはオープンソースコミュニティから最も要望の多かった機能ですが、FacebookエンジニアからはCSSが最大の要望でした。ある言語でコードをきれいに印刷することに慣れると、あらゆる場所でそれを実行したくなるものです!

CSSはJavaScriptよりもはるかに小規模な言語であることが判明し、サポートには数日しかかかりませんでした。基盤となるパーサーには@aipostcssを使用しており、CSS、Less、SCSSを解析できます。また、@ben-ebのpostcss-values-parserpostcss-selector-parser@dryomapostcss-media-query-parserにも依存しています。

残念ながら、postcssは現時点でSassやStylusを解析できません。これらを出力する作業を引き受けてくれる方がいれば、喜んでサポートしたいと考えています。

現在Prettierはコードのフォーマットのみを行っており、singleQuoteのようなオプションは反映されず、JavaScriptで行っているような色や数値の正規化も実施していません。

エディタ統合

プロジェクトの第一段階は、Prettierが正しく見栄えの良いコードを出力できるようにすることでした。形が整った今、統合機能の改善に時間を割けます。最近導入した二つの優れた機能は:カーソル位置の維持と、ファイル全体ではなく範囲を指定してフォーマットする機能です。

なお、このサポートはPrettier本体には実装されましたが、エディタ統合機能ではまだ使用されていません。また、実際に試したこともほとんどないため、まだ改善すべき点があるかもしれません!

cursorOffsetオプションを追加し、カーソル位置を変換できるように (#1637) by @josephfrazier

現在、エディタにカーソルの位置を決定させていますが、その処理はまあまあです。しかし、コードを出力する際に、正しい位置を指定できるようになりました!

入力の一部のみをフォーマットするための --range-start/end オプションを追加 (#1609) by @josephfrazier

これは非常に頻繁に要望されていた機能です。現在、Prettierはファイル全体のみをフォーマットしていましたが、これで範囲を指定してフォーマットできるようになりました。

この機能は、ASTを遡って最も近いステートメントを見つけることで動作します。この方法により、有効な範囲を正確に選択する必要がありません。フォーマットしたいコードのおおよその場所をドラッグするだけで、フォーマットが実行されます!

ファイルタイプの推論を可能にするためにfilepathオプションを追加 (#1835) by @mitermayer

現在、CSSやTypeScriptもフォーマットするようになったため、すべてのファイルでパーサーを指定するのは不便です。これで、作業しているファイルのファイルパスを渡すと、Prettierが拡張子を読み取って適切なパーサーを選択します。

主な変更点

JSX内のテキストコンテンツの折り返しを追加 (#1120, #1671, #1827, #1829) by @karl

PrettierでJSXを出力する際に残っていた最大の問題は、テキストを出力する際の扱いでした。以前のPrettierの動作は、醜い{" "}を前に追加し、行が長すぎる場合はそのまま放置していました。現在では、各単語をトークンとして扱い、正しくフローさせることが可能になりました。

これは@karlによる素晴らしい作業です。彼はこの機能を実装しただけでなく、Prettier内部に新しいプリミティブを導入し、要素のシーケンスを出力し、いずれかが端に達した時点で改行できるようにしました。

// Before
<div>
Please state your
{" "}
<b>name</b>
{" "}
and
{" "}
<b>occupation</b>
{" "}
for the board of directors.
</div>

// After
<div>
Please state your <b>name</b> and <b>occupation</b> for the board of
directors.
</div>

アロー関数内のJSXに対する括弧を削除 (#1733) by @xixixao

関数コンポーネントを書いている人々は、この変更を喜ぶでしょう。JSXを返すアロー関数に対して、括弧を付けなくなりました。

// Before
const render1 = ({ styles }) => (
<div style={styles}>
Keep the wrapping parens. Put each key on its own line.
</div>
);

// After
const render1 = ({ styles }) =>
<div style={styles}>
Keep the wrapping parens. Put each key on its own line.
</div>;

テンプレートリテラルの出力を改善 (#1664, #1714) by @josephfrazier

テンプレートリテラルの出力は、常にPrettierにとって多くの困難を引き起こしてきました。1.3.0で状況を大幅に改善し、このリリースでは、一般的な状況を適切に処理できるようになったと確信しています。

問題を回避するために、出力から空行を削除するユーティリティを追加していましたが、時々非常に奇妙な結果を招くことがありました。これはもうありません。また、別の調整として、${が開始される位置でインデントする代わりに、${を含む行が開始される位置でインデントするようにしました。

このリリース後もテンプレートリテラルの出力で問題があればお知らせください!

// Before
const Bar = styled.div`
color: ${props => (props.highlight.length > 0 ? palette([
'text',
'dark',
'tertiary'
])(props) : palette([
'text',
'dark',
'primary'
])(props))} !important;
`

// After
const Bar = styled.div`
color: ${props =>
props.highlight.length > 0
? palette(["text", "dark", "tertiary"])(props)
: palette(["text", "dark", "primary"])(props)} !important;
`

代入式とオブジェクト値で同じ改行ルールを適用 (#1721)

代入後の改行処理(例:a = ...)には多くの細かいロジックがあります。これをオブジェクト値にも適用するようになりました。複数行にわたる論理演算や大規模な条件式で有用です。これは一貫したプリント処理の好例でもあります。

// Before
const o = {
somethingThatsAReallyLongPropName: this.props.cardType ===
AwesomizerCardEnum.SEEFIRST,
};

// After
const o = {
somethingThatsAReallyLongPropName:
this.props.cardType === AwesomizerCardEnum.SEEFIRST,
};

!() 内の条件式のインデントを修正 (#1731)

従来のレンダリング方法について不満の声が絶えず、「後で対応する困難な課題リスト」に挙がっていましたが、実際は非常に簡単に修正できました。どうぞご活用ください!

// Before
const anyTestFailures = !(aggregatedResults.numFailedTests === 0 &&
aggregatedResults.numRuntimeErrorTestSuites === 0);

// After
const anyTestFailures = !(
aggregatedResults.numFailedTests === 0 &&
aggregatedResults.numRuntimeErrorTestSuites === 0
);

フォーマット修正

可能な場合ループ本体を同じ行に配置 (#1498)

if文では既に実装していたため、ループでも一貫して適用すべきでした。

// Before
for (a in b)
var c = {};

// After
for (a in b) var c = {};

Flowユニオンでの空行を修正 (#1511) by @existentialism

二重インデントは避けるべきです ;)

// Before
type Foo = Promise<

| { ok: true, bar: string, baz: SomeOtherLongType }
| { ok: false, bar: SomeOtherLongType }
>;

// After
type Foo = Promise<
{ ok: true, bar: string, baz: SomeOtherLongType } |
{ ok: false, bar: SomeOtherLongType }
>;

行末コメント付き単一引数の括弧を除去 (#1518)

アロー関数の括弧除去判定では従来「コメントの存在」をチェックしていましたが、(/* comment */ num) のようなインラインコメントのみを対象とし、行末コメントは除外するよう修正しました。

// Before
KEYPAD_NUMBERS.map((num) => ( // Buttons 0-9
<div />
));

KEYPAD_NUMBERS.map(num => ( // Buttons 0-9
<div />
));

ネストした三項演算子のインデントを停止 (#1822)

これにより2文字幅のインデントが4文字に見える問題を回避します。条件式が複数行の場合の位置揃えは犠牲になりますが、ネストした三項演算子では通常条件式が短いため適切なトレードオフと考えます。

// Before
aaaaaaaaaaaaaaa
? bbbbbbbbbbbbbbbbbb
: ccccccccccccccc
? ddddddddddddddd
: eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg;

// After
aaaaaaaaaaaaaaa
? bbbbbbbbbbbbbbbbbb
: ccccccccccccccc
? ddddddddddddddd
: eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg;

JSX属性内の連鎖条件式をインライン化 (#1519)

後続ブロックが存在しないため、インデントによる曖昧性解消は不要です。

// Before
<div
src={
!isEnabled &&
diffUpdateMessageInput != null &&
this.state.isUpdateMessageEmpty
}
/>;

// After
<div
src={
!isEnabled &&
diffUpdateMessageInput != null &&
this.state.isUpdateMessageEmpty
}
/>;

文字列内の不要なエスケープ文字を解除 (#1575) by @josephfrazier

既に文字列のクリーンアップを多様な方法で実施していますが、不要な\を除去する小さな改善を追加しました。

// Before
a = 'hol\a';

// After
a = 'hola';

空オブジェクトのブール値処理を修正 (#1590) by @dmitrika

ブール式内のオブジェクトはインライン化したいのですが、{ が単独の行にあるのは不自然に見えるからです。しかし空オブジェクトの場合、これをインライン化すると不自然な振る舞いが発生します。そのため空オブジェクトは独立行に保持するよう修正しました。

const x = firstItemWithAVeryLongNameThatKeepsGoing ||
secondItemWithALongNameAsWell || {};

// After
const x =
firstItemWithAVeryLongNameThatKeepsGoing ||
secondItemWithALongNameAsWell ||
{};

For文内SequenceExpressionsの括弧を除去 (#1597) by @k15a

forループ内で複数値を代入する一般的なパターンについて、不要な括弧を付与しないようになりました。

// Before
for ((i = 0), (len = arr.length); i < len; i++) {

// After
for (i = 0, len = arr.length; i < len; i++) {

引数に先頭コメントがあるアロー関数のインライン化を停止 (#1660)

アロー関数内にブロックコメントがある場合のフォーマット崩れを修正しました!

// Before
export const bem = block => /**
* @param {String} [element] - the BEM Element within that block; if undefined, selects the block itself.
*/
element => /**
* @param {?String} [modifier] - the BEM Modifier for the Block or Element; if undefined, selects the Block or Element unmodified.
*/
modifier =>

// After
export const bem = block =>
/**
* @param {String} [element] - the BEM Element within that block; if undefined, selects the block itself.
*/
element =>
/**
* @param {?String} [modifier] - the BEM Modifier for the Block or Element; if undefined, selects the Block or Element unmodified.
*/
modifier =>

import文の最終コメント修正 (#1677)

コメント処理の特殊ロジックが必要な別のケースです!

// Before
import {
ExecutionResult,
DocumentNode,
/* tslint:disable */
SelectionSetNode,
} /* tslint:enable */ from 'graphql';

// After
import {
DocumentNode,
/* tslint:disable */
SelectionSetNode,
/* tslint:enable */
} from 'graphql';

メンバーチェーン内のコメント処理 (#1686, #1691)

以前は特定の配置のみ対応していましたが、出現箇所が増えるにつれ、より汎用的なアプローチに切り替えました。今後は同様の問題が発生しにくくなるはずです。

// Before
const configModel = this.baseConfigurationService.getCache().consolidated // global/default values (do NOT modify)
.merge(this.cachedWorkspaceConfig);

// After
const configModel = this.baseConfigurationService
.getCache()
.consolidated // global/default values (do NOT modify)
.merge(this.cachedWorkspaceConfig);

ネストされたアロー関数でexpandLastを使用 (#1720)

// Before
f(action => next =>
next(action));

// After
f(action => next =>
next(action),
);

JSXコメントを括弧内に配置 (#1712)

Flowの新バージョン導入時に自動追加される$FlowFixMeコメントが影響を受けなくなり、Facebookエンジニアにとって特に有益です。

// Before
const aDiv = /* $FlowFixMe */
(
<div className="foo">
Foo bar
</div>
);

// After
const aDiv = (
/* $FlowFixMe */
<div className="foo">
Foo bar
</div>
);

複数変数宣言での改行強制 (#1723)

従来は80カラム超の場合のみ改行していましたが、代入のある変数が1つでも存在する場合、カラム数に関わらず改行するよう変更しました。

// Before
var numberValue1 = 1, numberValue2 = 2;

// After
var numberValue1 = 1,
numberValue2 = 2;

| null と | void のインライン化 (#1734)

Flowユニオンの展開形式は複数オブジェクト時には適していますが、null許容型では不自然に見えるため、| null| voidをインライン化します。

// Before
interface RelayProps {
articles:
| Array<
| {
__id: string,
}
| null
>
| null
}

// After
interface RelayProps {
articles: Array<{
__id: string,
} | null> | null,
}

extendsではなくimplementsで改行 (#1730)

extendsでの改行を廃止し、改行が必要なクラス定義の見た目を改善しました。

// Before
class MyContractSelectionWidget
extends React.Component<
void,
MyContractSelectionWidgetPropsType,
void
> {
method() {}
}

// After
class MyContractSelectionWidget extends React.Component<
void,
MyContractSelectionWidgetPropsType,
void
> {
method() {}
}

単一インポートのインライン化 (#1729)

長いrequire呼び出しと同様に、単一要素のimport文を改行しないよう変更しました。

// Before
import somethingSuperLongsomethingSuperLong
from "somethingSuperLongsomethingSuperLongsomethingSuperLong";

// After
import somethingSuperLongsomethingSuperLong from "somethingSuperLongsomethingSuperLongsomethingSuperLong";

SequenceExpressionの改行サポート追加 (#1749)

ご存知でしたか? 文を含まないコードでは{}の代わりに()を、;の代わりに,を使えます。アロー関数の返り値でこの手法を使うケースがあります(推奨されませんが)Prettierで簡単にサポートできるため実装しました ¯\_(ツ)_/¯

// Before
const f = (argument1, argument2, argument3) =>
(doSomethingWithArgument(argument1), doSomethingWithArgument(
argument2
), argument1);

// After
const f = (argument1, argument2, argument3) => (
doSomethingWithArgument(argument1),
doSomethingWithArgument(argument2),
argument1
);

空ループ本文での強制改行廃止 (#1815)

空のループ本文{}が2行に分割されなくなりました。

// Before
while (true) {
}

// After
while (true) {}

コメント付きswitch case間の空行維持 (#1708)

// Before
switch (true) {
case true:
// Good luck getting here
case false:
}

// After
switch (true) {
case true:

// Good luck getting here
case false:
}

正確性の確保

ast-typesの削除 (#1743, #1744, #1745, #1746, #1747)

これまでast-typesの定義を使用してASTを走査し、コメントを配置する場所を特定していました。一部のフィールドが宣言されていない場合、ノードを見つけられずにコメントが間違った場所に出力されるかエラーが発生する問題が時折ありました。このマッピングを保持する必要はなく、オブジェクトを走査し、ノードにtypeフィールドがあればそれがノードであると判断できることが判明しました。

// Before
Error: did not recognize object of type "ObjectTypeSpreadProperty"

// After
type X = {...Y/**/};
type X = {/**/...Y};

特殊なユニコード空白文字を保持 (#1658, #1165) by @karl and @yamafaktory

JSXテキスト内に不可視文字を追加していた場合、通常のスペースに置き換えていました。このようなケースが実際に必要かは不明ですが、現在は元のまま出力するようになりました!

末尾のテンプレートリテラルコメントがエスケープされないようにする (#1580, #1713, #1598, #1713) by @josephfrazier and @k15a

テンプレートリテラル内のコメント処理には複雑で不安定なコードを使用していましたが、JSXの{}式向けに優れたソリューションを導入しました。このアイデアは}の終端前に境界を設け、未出力のコメントが残っている場合に一括でフラッシュし、改行(\n)を挿入してから}を出力するものです。このロジックをテンプレートリテラルにも適用するようになりました :)

// Before
`${0} // comment`;

// After
`${
0
// comment
}`;

awaitの括弧付けを正しく行う (#1513, #1595, #1593) by @bakkot and @existentialism

括弧を自動で付与する仕組みはなく、代わりにノードの全組み合わせパターンを指定し、括弧の要否を判断しています。そのため未対応の特殊な組み合わせが残っている可能性があります。今回はawait処理を強化し、必要な箇所には括弧を追加し、不要な箇所では削除するようにしました。

// Before
(await spellcheck) && spellcheck.setChecking(false);
new A((await x));

// After
await (spellcheck && spellcheck.setChecking(false));
new A(await x);

flow ObjectTypePropertyのgetter/setter情報を保持 (#1585) by @josephfrazier

まだ正しく対応できていない別の特殊ケースです!

// Before
type T = { method: () => void };

// After
type T = { get method(): void }

ジェネリック付き単一引数型に括弧を追加 (#1814)

適切に追加されていなかった別の括弧ケースです!

// Before
type ExtractType = <A>B<C> => D

// After
type ExtractType = <A>(B<C>) => D

babylonで非strictモードにフォールバック (#1587, #1608) by @josephfrazier

PrettierがすべてのJavaScriptを解析できるようにするため、babylonパーサーではファイルがstrictモードかどうかを選択する必要があります。大半のファイルがstrictモードで解析されるため、デフォルトでstrictモードを採用しました。しかし0775のような8進数リテラルがある場合、解析自体が失敗していました。現在はstrictモードで解析に失敗した場合、非strictモードで再試行します。またNode.jsファイルで有効な、関数外でのreturnも許可するようになりました。

// Before
SyntaxError

// After
return 0775;

CLI

--write--list-different と併用可能に (#1633)

コミットフックを実装する際、変更箇所を単一コマンドでユーザーに通知する場合に両オプションを組み合わせると便利です。

CLI実行時にnode_modulesを無視 (#1683) by @thymikee

誤ってnode_modules/フォルダに対してPrettierを実行することは容易ですが、通常これは望ましくありません。デフォルトで無効化し、必要な場合は--with-node-modulesオプションを追加しました。

globでドットファイルを走査 (#1844) by @jhgg

使用中のglob解析ライブラリで.dotfilesの処理を有効化しました。これにより*を指定すると.eslintrcも対象になります。