Prettier 1.5: GraphQL、CSS-in-JS & JSONのサポート
このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →
このリリースでは、GraphQLのフォーマットサポート、CSS-in-JS(styled-componentsを含む)、およびJSONサポートがPrettierに追加されました。
これは私が長い間待ち望んでいたリリースです:JavaScriptへの変更が最小限のリリースです!
過去6ヶ月間、私たちはJavaScriptの印刷に関する様々な側面を変更し続け、いつか安定した状態に到達できることを望んでいました。全てのエッジケースで完璧なコードを印刷する自動化ツールは存在しません。目標は、ユーザーが奇妙に印刷されたコードを報告した際に、他のコードを悪化させたり、人間が理解するのが非常に難しい動作を導入したり、コードベースに不釣り合いな複雑さを加えずに改善できない、良い着地点を見つけることです。
まだ100%到達したわけではありませんが、これまで以上に近づいています!
JavaScriptのサポート需要が減少傾向にある今、フロントエンド開発者が取り組んでいてフォーマットを求めている他の言語をサポートする機会です。前回のリリースではTypeScriptとCSSを導入し、今回のリリースではそれらに対する一連の修正を行っています。さらに新しい言語のサポートも追加しました:GraphQLクエリ、CSS-in-JSの埋め込み、JSONがPrettierで利用可能になりました!
ブログ記事: Prettierに新しいレイアウト戦略を追加 by @karl
Prettierは便利なツールであるだけでなく、非常に洗練された技術でもあります。@karlはJSXサポートの改善に多くの時間を費やし、その過程でPrettierに新しいプリミティブfillを実装しました。彼が書いたPrettierに新しいレイアウト戦略を追加という非常に興味深いブログ記事は、Prettierの内部動作に興味がある方にぜひ読んでいただきたい内容です。
GraphQL
@stubailo、@jnwng、@tgriesser、@azzの貢献により、PrettierがGraphQLクエリの印刷をサポートしました!
この機能は.graphqlファイルと、RelayやApolloで動作させるためにgraphql、graphql.experimental、gqlで始まるJavaScriptテンプレート内で有効です。
ReactDOM.render(
<QueryRenderer
query={graphql`
query appQuery {
viewer {
...TodoApp_viewer
}
}
`}
// ...
/>,
mountNode
);
オープンソース版のGraphQL構文のみをサポートしているため、Relay Classicには対応せず、Relay Modernでのみ動作することに注意してください。
CSS-in-JS
styled-componentsやstyled-jsxを使用している場合、Prettierがテンプレート式内のCSSを再フォーマットするようになりました。素晴らしい作業をしてくれた@pomberに感謝します!
const EqualDivider = styled.div`
margin: 0.5rem;
padding: 1rem;
background: papayawhip;
> * {
flex: 1;
&:not(:first-child) {
${props => (props.vertical ? "margin-top" : "margin-left")}: 1rem;
}
}
`;
JSON
実装は非常に簡単でしたが、それでも非常に便利な機能です。実装してくれた@josephfrazierに感謝します :)
{
"name": "prettier",
"version": "1.5.0",
"description": "Prettier is an opinionated JavaScript formatter",
"bin": {
"prettier": "./bin/prettier.js"
}
}
CSS
初期のCSSサポートを構築するのに数日しかかけなかったにもかかわらず、驚くほどうまく機能したことに非常に興奮しています。今回のリリースではCSSにいくつかの重要な改良が加えられましたが、大きな変更を必要とするものはありません。
CSS: すべてのセレクタが独自の行に印刷されるようになりました (#2047) by @yuchi
CSSをフォーマットする際の最大の未知要素は、複数のセレクターをどのように扱うかでした。当初採用したアプローチは80カラムルールで、セレクターがその長さを超える場合にのみ改行する方法でした。しかし多くのユーザーから、別の戦略(常に , の後に改行する方法)を使用していると報告がありました。実際、多くの人気コードベースがこのアプローチを採用しており、セレクターを縦に並べることで構造が明確になり可読性が向上することがわかりました。
// Before
.clusterPlannerDialog input[type="text"], .clusterPlannerDialog .uiTypeahead {
color: #333;
}
// After
.clusterPlannerDialog input[type="text"],
.clusterPlannerDialog .uiTypeahead {
color: #333;
}
CSS: 小文字の16進数カラーコード (#2203) by @azz
コードフォーマットの概念には曖昧な境界があります。中核となるのは空白処理ですが、シングルクォートとダブルクォートの使い分けやセミコロンの有無なども通常含まれます。PrettierのJavaScript処理では、余分な \ を削除したり数値を正規化したりして文字列を軽くリフォーマットします。CSSでも同様に境界線を解釈する必要があります。カラー表現については、すべての文字を小文字に変換するだけで留めることにしました。rgb()を16進数に変換したり、6桁のHEXを3桁に短縮したりするのは対象外です。
// Before
.foo {
color: #AAA;
-o-color: #fabcd3;
-ms-color: #AABBCC;
}
// After
.foo {
color: #aa;
-o-color: #fabcd3;
-ms-color: #aabbcc;
}
CSS: CSS値にfillプリミティブを適用 (#2224)
新しいfillプリミティブがCSSで非常に有用であることが判明しました。長い値の場合、すべての要素の前に\nを入れる代わりに、制限長を超えた場合のみ\nを挿入します。これによりコードの見た目が大幅に改善されます。
// Before
foo {
border-left:
1px
solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
// After
foo {
border-left: 1px solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
CSS: 長いメディアクエリの改行を許可 (#2219)
これは新しい言語を適切にサポートする過程での小さな修正です。長い@mediaルールで改行できる機能を実装しました。
// Before
@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {
}
// After
@media all and (-webkit-min-device-pixel-ratio: 1.5),
all and (-o-min-device-pixel-ratio: 3/2),
all and (min--moz-device-pixel-ratio: 1.5),
all and (min-device-pixel-ratio: 1.5) {
}
CSS: @elseを}と同じ行に出力 (#2088) by @azz
LessやSCSSは本格的なプログラミング言語へと進化しています。少しずつ、これらの構文をJavaScriptと同じ方法で出力できるようになってきました。今回はelseの配置に関する改善です。
// Before
@if $media == phonePortrait {
$k: .15625;
}
@else if $media == tabletPortrait {
$k: .065106;
}
// After
@if $media == phonePortrait {
$k: .15625;
} @else if $media == tabletPortrait {
$k: .065106;
}
CSS: prettier-ignoreを実装 (#2089) by @azz
Prettierでコードベース全体をフォーマットするのが基本方針ですが、特定の箇所では「手動で整形したい」場合があります。そのためのエスケープハッチがprettier-ignoreコメントです。以前はCSSで機能していませんでしたが、これは見落としだったため、今回実装されました :)
// Before
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
// After
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
CSS: CSS Modulesのcomposesが長い行幅で壊れる問題を修正 (#2190) by @tdeekens
高速化のため、多くの「パッケージャー」は依存関係を抽出する際にファイルをパースせず、単純な正規表現を使用しています。これが長いrequire()呼び出しを改行しない理由であり、CSS Modulesにも影響していました。composesフィールドに改行を入れると認識されなくなるため、80カラムを超える場合でもそこで改行しないように変更しました。
// Before
.reference {
composes:
selector
from
"a/long/file/path/exceeding/the/maximum/length/forcing/a/line-wrap/file.css";
}
// After
.reference {
composes: selector from "a/long/file/path/exceeding/the/maximum/length/forcing/a/line-wrap/file.css";
}
CSS: カンマを含む@importではまずSCSSを試行 (#2225)
CSS、SCSS、Lessの処理に高レベルの「パーサー」を1つだけ使用する設計判断をしましたが、内部ではpostcss-lessとpostcss-scssを使用しています。最初に試行するパーサーを決定するために正規表現を使用し、構文エラーが発生した場合は別のパーサーにフォールバックします。残念ながら特定の機能では、最初の(不正確な)パーサーがエラーをスローせず、一部の要素をスキップしてしまうことがありました。そのため、正規表現を強化して早期検出の精度を高める必要がありました。
幸いなことに、このハックは実践でうまく機能しています。さらに多くのエッジケースが見つかった場合には、適切な方法(tm)で2つのパーサーに分割する必要が出てくるでしょう。
// Before
@import "text-shadow";
// After
@import "rounded-corners", "text-shadow";
TypeScript
TypeScriptのサポートは安定しており、今回のリリースにおける変更はすべて小さなエッジケースへの対応です。
TypeScript: アロー関数の型パラメーターをパラメーターと同じ行に出力する (#2101) by @azz
Prettierの中核アルゴリズムは、すべての要素が収まらない場合にグループを展開するというものです。これはJavaScriptのほとんどのケースで実際にうまく機能しますが、2つのグループが並列するケース(この場合: <Generics>(Arguments))は十分に処理できません。ユーザーが一般的に期待するように、引数が最初に展開されるようグループを慎重に設計する必要があります。
// Before
export const forwardS = R.curry(<
V,
T
>(prop: string, reducer: ReducerFunction<V, T>, value: V, state: {[name: string]: T}) =>
R.assoc(prop, reducer(value, state[prop]), state)
);
// After
export const forwardS = R.curry(
<V, T>(
prop: string,
reducer: ReducerFunction<V, T>,
value: V,
state: { [name: string]: T }
) => R.assoc(prop, reducer(value, state[prop]), state)
);
TypeScript: yield/awaitの非nullアサーションで括弧を維持する (#2149) by @azz
良し悪しはありますが、括弧の追加を手動で処理することに決めました。新しい演算子が導入された際には、他の演算子の組み合わせとネストされた場合に正しい括弧を追加する必要があります。今回、TypeScriptの!内でのawait処理を見落としていました。
// Before
const bar = await foo(false)!;
// After
const bar = (await foo(false))!;
TypeScript: ソース内にがある場合、importでそれを出力する (#2150) by @azz
Prettierが印刷できるよう、TypeScript ASTをestree ASTに変換するtypescript-eslint-parserプロジェクトを使用しています。時折、未対応のエッジケースが見つかります。今回は空の{}を検知する方法が提供されておらず、これがTypeScriptにとって重要であることが判明しました。幸いチームの対応は迅速で、Prettier内に回避策を実装した後、彼らが修正してくれました。
// Before
import from "@types/googlemaps";
// After
import {} from "@types/googlemaps";
TypeScript: インターフェースは常に複数行に分割する (#2161) by @azz
interfaceの実装コードはobjectを印刷するコードと共有されており、内部に\nがある場合に展開状態を維持するルールが含まれています。しかしこれはインターフェースの意図した動作ではなく、クラスと同様に80列に収まる場合でも常に展開したいのです。
// Before
interface FooBar { [id: string]: number; }
// After
interface FooBar {
[id: string]: number;
}
TypeScript: 環境TypeScript宣言での余分なセミコロンを修正 (#2167) by @azz
no-semiやsemiはよくリクエストされますが、Prettierチームは先んじてtwo-semiを実装しました!冗談です、これはバグでしたが修正されました ;)
// Before
declare module "classnames" {
export default function classnames(
...inputs: (string | number | false | object | undefined)[]
): string;;
}
// After
declare module "classnames" {
export default function classnames(
...inputs: (string | number | false | object | undefined)[]
): string;
}
TypeScript: コール/コンストラクトシグネチャ内の関数パラメーターをグループ化する (#2169) by @azz
メソッド前にコメントを追加すると、従来はコメント長を考慮して予期せずメソッドが展開されることがありました。幸い修正は簡単で、出力をgroupでラップするだけです。
// Before
interface TimerConstructor {
// Line-splitting comment
new (
interval: number,
callback: (handler: Timer) => void
): Timer;
}
interface TimerConstructor {
// Line-splitting comment
new (interval: number, callback: (handler: Timer) => void): Timer;
}
TypeScript: tsepをアップグレード (#2183) by @azz
このバグに遭遇すると非常に厄介でした:コードをフォーマットするたびに、オブジェクトキーに_が1つ追加されてしまっていたのです!
// Before
obj = {
__: 42
___: 42
};
// After
obj = {
_: 42
__: 42
};
TypeScript: 複数のインターフェース拡張で改行する (#2085) by @azz
JavaScriptとは異なり、TypeScriptでは複数のクラスを一度に継承できます。この機能が実際に利用されていることが判明したため、Prettierでは出力品質が向上しました。
// Before
export interface ThirdVeryLongAndBoringInterfaceName extends AVeryLongAndBoringInterfaceName, AnotherVeryLongAndBoringInterfaceName, AThirdVeryLongAndBoringInterfaceName {}
// After
export interface ThirdVeryLongAndBoringInterfaceName
extends AVeryLongAndBoringInterfaceName,
AnotherVeryLongAndBoringInterfaceName,
AThirdVeryLongAndBoringInterfaceName {}
TypeScript: BinaryExpression内のObjectExpressionの代わりにObjectPatternを扱う (#2238) by @azz
これは特筆すべきことではありませんが、TypeScriptからestreeへの変換で適切に処理されていないエッジケースです。
// Before
call(c => { bla: 1 }) || [];
// After
call(c => ({ bla: 1 })) || [];
ディレクティブ後の行を保持する (#2070)
TypeScriptサポートにより、Prettierは多くのAngularコードベースで使用されるようになり、これまで適切に処理されていなかったエッジケースが表面化しました。このケースでは、関数内のディレクティブ後の空行が保持されていませんでした。
// Before
export default class {
constructor($log, $uibModal) {
"ngInject";
Object.assign(this, { $log, $uibModal });
// After
export default class {
constructor($log, $uibModal) {
"ngInject";
Object.assign(this, { $log, $uibModal });
JavaScript
このリリースではJavaScript関連の変更が非常に少なく、これは喜ばしいことです。トンネルの先に光が見え始め、優れたコードフォーマッタへと近づいています。100%完璧な自動フォーマッタになることは決してありませんが、目標は報告されるすべての課題について、他の部分を後退させることなく出力方法を改善する明確な方法がない状態にすることです。
JSXの行を再結合できるようにする (#1831) by @karl
Prettierの目標は、ASTが与えられた場合に常に同じ方法で出力する一貫したコードフォーマットを提供することです。これまでJSXとオブジェクトの2箇所で妥協し元のフォーマットを参照する必要がありましたが、この変更により内部にテキストを含むJSXでは元の入力に依存しなくなりました。これによりJSX内部のテキストを再フローできるようになります。
// Before
const Abc = () => {
return (
<div>
Please state your
{" "}
<b>name</b>
{" "}
and
{" "}
<b>occupation</b>
{" "}
for the board of directors.
</div>
);
};
// After
const Abc = () => {
return (
<div>
Please state your <b>name</b> and <b>occupation</b> for the board of
directors.
</div>
);
}
非リテラルな計算済みメンバー式で改行する (#2087) by @azz
メンバーチェーンの出力はPrettierの中で最も複雑な部分であり、より良い体験を提供するための微調整を継続的に見つけています。
// Before
nock(/test/)
.matchHeader("Accept", "application/json")[httpMethodNock(method)]("/foo")
.reply(200, {
foo: "bar",
});
// After
nock(/test/)
.matchHeader("Accept", "application/json")
[httpMethodNock(method)]("/foo")
.reply(200, {
foo: "bar",
});
one-varシナリオで最初の変数をインデントする (#2095) by @azz
これまで、一般的な慣行が変数ごとに個別の宣言を行うことだったため、単一宣言内の複数変数出力のサポートはほとんど手を付けていませんでした。単一宣言ではインデントしたくありませんが、後続の変数がある場合はインデントが必要であることが判明しました。そうしないと見た目が不自然になるためです。
// Before
var templateTagsMapping = {
'%{itemIndex}': 'index',
'%{itemContentMetaTextViews}': 'views'
},
separator = '<span class="item__content__meta__separator">•</span>';
// After
var templateTagsMapping = {
'%{itemIndex}': 'index',
'%{itemContentMetaTextViews}': 'views'
},
separator = '<span class="item__content__meta__separator">•</span>';
デフォルトと名前付きインポートの両方で改行を許可する (#2096) by @azz
これはバージョン1.4からの残念な退歩で、単一要素のみを含むimportをインライン化していましたが、単一要素の定義が単一の型と単一の要素を許可していたことが判明しました。これは現在修正されています。
// Before
import transformRouterContext, { type TransformedContextRouter } from '../../helpers/transformRouterContext';
// After
import transformRouterContext, {
type TransformedContextRouter
} from '../../helpers/transformRouterContext';
allowImportExportEverywhereを有効にする (#2207) by @zimme
Prettierの目標は実際に書かれたコードをフォーマットすることなので、サポートするすべてのパーサーに対して緩やかな/実験的なモードを有効にしています。Babylonでは標準には含まれない関数内でのimportを許可していますが、これを許可するのに大きなコストはかかりません。
// Before
ParseError
// After
function f() {
import x from 'x';
}
new呼び出しに対するインラインテンプレートのサポート (#2222)
関数呼び出しの機能を追加し続ける中で、ASTノードタイプが異なるにもかかわらず実際には同じように扱いたいnew呼び出しにもバックポートする必要がありました。この修正では両者が同じ呼び出しサイトを通過するようにリファクタリングしたため、今後これ以上の不整合が潜り込むのを防げるはずです。
// Before
new Error(
formatErrorMessage`
This is a really bad error.
Which has more than one line.
`
);
// After
new Error(formatErrorMessage`
This is a really bad error.
Which has more than one line.
`);
オブジェクト値内の + をインデントしない (#2227)
代入式 (a = b) とオブジェクト ({a: b}) で同じヒューリスティックを使用するように変更した際、インデント処理の修正を忘れていました。今回この問題を修正しました。
// Before
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
// After
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
三項演算子内の条件式処理を修正 (#2228)
Prettier はすでに式が条件式の場合の特別な処理を持っていましたが、条件式が三項演算子の左側にある場合に適用されていませんでした。今回このケースも対応しました。
// Before
room = room.map((row, rowIndex) =>
row.map(
(col, colIndex) =>
rowIndex === 0 ||
colIndex === 0 ||
rowIndex === height ||
colIndex === width
? 1
: 0
)
);
// After
room = room.map((row, rowIndex) =>
row.map(
(col, colIndex) =>
rowIndex === 0 ||
colIndex === 0 ||
rowIndex === height ||
colIndex === width
? 1
: 0
)
);
プリント処理にキャッシュを追加 (#2259)
1.0 リリース時に、プリント処理のバグ修正により指数関数的な処理時間の問題が発生しました。合理的なコードがタイムアウトしないよう主要な問題は対処していましたが、完全な修正ではありませんでした。適切な箇所にキャッシュ層を追加したことで、この問題は完全に解決されました。
これにより、デバッグモードで Prettier の IR(中間表現)を Prettier でプリントする際にタイムアウトが発生しなくなりました。
// Before
...times out...
// After
someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
return someObject.someFunction().then(function () {
anotherFunction();
});
});
});
});
});
});
});
});
});
バリアンス記号の位置を修正 (#2261)
TypeScript サポート導入時に修飾子のプリント処理をリファクタリングした際、Flow で無効となる static の前にバリアンス記号 (+) が移動してしまう不具合が発生していました。この問題を修正しました。
// Before
class Route {
+static param: T;
}
// After
class Route {
static +param: T;
}
その他の変更
範囲指定とカーソル追跡に関する各種修正 (#2266, #2248, #2250, #2136) by @CiGit and @josephfrazier
これらの機能は前回リリースで導入されましたが、実際のプロダクション環境で使用した際に複数の問題が発見されました。今回多くの問題を修正しましたが、他にも問題を見つけた場合はぜひ報告してください!
