Prettier 1.5 : GraphQL, CSS-in-JS et JSON
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Cette version ajoute le support du formatage GraphQL, du CSS-in-JS (y compris styled-components) et du JSON à Prettier.
C'est la version que j'attendais depuis très longtemps : celle qui n'apporte que des modifications minimes à JavaScript !
Ces 6 derniers mois, nous avons continuellement apporté des changements à divers aspects de l'impression de code JavaScript, dans l'espoir d'atteindre un jour un état stable. Aucun outil automatisé ne pourra imprimer un code parfait pour tous les cas limites possibles. L'objectif est de trouver un point d'équilibre où, lorsque les utilisateurs signalent du code imprimé de manière étrange, nous ne pouvons plus l'améliorer sans détériorer d'autres parties du code, introduire des comportements difficiles à comprendre pour les humains ou ajouter une complexité disproportionnée au codebase.
Nous n'y sommes pas encore à 100%, mais nous n'avons jamais été aussi proches !
Maintenant que les besoins de support pour JavaScript diminuent, c'est l'opportunité de prendre en charge d'autres langages utilisés par les développeurs front-end qui souhaitent les formater. Nous avons introduit TypeScript et CSS dans la précédente version et leur apportons un lot de corrections dans cette version. Nous ajoutons également le support de nouveaux langages : les requêtes GraphQL, l'intégration du CSS-in-JS et le JSON sont désormais disponibles dans prettier !
Article de blog : Ajouter une nouvelle stratégie de mise en page à Prettier par @karl
Prettier n'est pas seulement un outil utile mais aussi une technologie vraiment fascinante. @karl a passé beaucoup de temps à améliorer le support JSX et a implémenté une nouvelle primitive pour prettier : fill. Il a rédigé un article très intéressant Ajouter une nouvelle stratégie de mise en page à Prettier que je vous recommande vivement si vous souhaitez comprendre le fonctionnement interne de prettier.
GraphQL
Grâce à @stubailo, @jnwng, @tgriesser et @azz, prettier prend désormais en charge le formatage des requêtes GraphQL !
Cela fonctionne pour les fichiers .graphql et dans les templates JavaScript commençant par graphql, graphql.experimental et gql pour assurer la compatibilité avec Relay et Apollo.
ReactDOM.render(
<QueryRenderer
query={graphql`
query appQuery {
viewer {
...TodoApp_viewer
}
}
`}
// ...
/>,
mountNode
);
Notez qu'il ne prend en charge que la syntaxe open source de GraphQL, donc ne fonctionne pas avec Relay Classic, uniquement avec Relay Modern.
CSS-in-JS
Si vous utilisez styled-components ou styled-jsx, prettier reformatera désormais le CSS à l'intérieur de vos expressions de template. Merci à @pomber pour ce travail remarquable !
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
L'implémentation était assez simple mais néanmoins très utile. Merci à @josephfrazier pour cette contribution :)
{
"name": "prettier",
"version": "1.5.0",
"description": "Prettier is an opinionated JavaScript formatter",
"bin": {
"prettier": "./bin/prettier.js"
}
}
CSS
Je suis vraiment enthousiaste car nous n'avons passé que quelques jours à développer le support initial du CSS et cela a étonnamment bien fonctionné. Cette version apporte plusieurs améliorations importantes pour le CSS sans nécessiter de changements majeurs.
CSS : Chaque sélecteur est désormais imprimé sur sa propre ligne (#2047) par @yuchi
La plus grande inconnue lors du formatage CSS était la gestion des sélecteurs multiples. Notre approche initiale consistait à appliquer la règle des 80 colonnes, ne coupant la ligne que si elle dépassait cette limite. De nombreux utilisateurs ont signalé qu'ils employaient une autre stratégie : toujours effectuer un saut de ligne après une ,. Il s'avère que cette méthode est largement adoptée dans les bases de code populaires, offrant une meilleure visibilité de la structure des sélecteurs lorsqu'ils sont alignés verticalement.
// Before
.clusterPlannerDialog input[type="text"], .clusterPlannerDialog .uiTypeahead {
color: #333;
}
// After
.clusterPlannerDialog input[type="text"],
.clusterPlannerDialog .uiTypeahead {
color: #333;
}
CSS : hexadécimaux en minuscules (#2203) par @azz
Le concept de formatage de code comporte des frontières floues. Si l'aspect central concerne les espaces blancs, des éléments comme les guillemets simples/doubles ou les points-virgules y sont généralement associés. Avec Prettier pour JavaScript, nous reformatons aussi légèrement les chaînes de caractères en supprimant les \ superflus et normalisons les nombres. Pour CSS, une réflexion similaire s'impose quant aux limites du formatage. Concernant les couleurs, nous avons choisi de convertir toutes les lettres en minuscules sans aller plus loin. La conversion des rgb() en hexadécimal ou des codes hexa sur 6 caractères en version abrégée reste hors du périmètre.
// Before
.foo {
color: #AAA;
-o-color: #fabcd3;
-ms-color: #AABBCC;
}
// After
.foo {
color: #aa;
-o-color: #fabcd3;
-ms-color: #aabbcc;
}
CSS : Utiliser fill pour les valeurs CSS (#2224)
La nouvelle primitive fill s'est révélée très utile pour CSS. Pour les valeurs longues, au lieu d'insérer systématiquement un \n avant chaque élément, nous n'insérons un \n que lorsque la limite est dépassée, ce qui produit un rendu visuel bien plus harmonieux.
// Before
foo {
border-left:
1px
solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
// After
foo {
border-left: 1px solid
mix($warningBackgroundColors, $warningBorderColors, 50%);
}
CSS : Permettre le retour à la ligne pour les règles @media longues (#2219)
Il s'agit d'une autre amélioration progressive dans la prise en charge complète d'un nouveau langage. Nous intégrons désormais la capacité d'effectuer des sauts de ligne pour les règles @media trop longues.
// 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 : Imprimer @else sur la même ligne que } (#2088) par @azz
Less et SCSS évoluent vers de véritables langages de programmation :) Étape par étape, nous traitons toutes leurs constructions comme nous le ferions pour JavaScript. Cette fois, c'est le positionnement du else qui a été harmonisé.
// 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 : Implémenter prettier-ignore (#2089) par @azz
Bien que Prettier vise à formater l'intégralité du codebase, il existe des cas où l'on "sait mieux" et souhaite une échappatoire. C'est là qu'intervient le commentaire prettier-ignore. Son absence pour CSS était un oubli, désormais corrigé :)
// Before
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
// After
foo {
/* prettier-ignore */
thing: foo;
-ms-thing: foo;
}
CSS : Corriger la rupture des composes de css-modules avec les lignes longues (#2190) par @tdeekens
Pour des raisons de performance, de nombreux "packagers" n'analysent pas les fichiers pour extraire les dépendances, utilisant plutôt des expressions régulières simplistes. C'est pourquoi nous évitons de couper les appels require() longs, et cela affecte aussi CSS Modules. Si des sauts de ligne sont ajoutés dans le champ composes, il n'est plus reconnu. Nous ne le coupons donc plus, même au-delà de 80 colonnes.
// 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 : Essayer d'abord scss pour les @import avec virgule (#2225)
Nous avions opté pour un unique "parser" haut niveau pour CSS, SCSS et Less, bien qu'utilisant postcss-less et postcss-scss en interne. Un regex détermine quel parser tenter en premier, avec bascule sur l'autre en cas d'erreur de syntaxe. Malheureusement, certaines fonctionnalités ne génèrent pas d'erreur avec le premier parser (incorrect), qui ignore alors des éléments. Nous devons donc renforcer le regex pour garantir une détection fiable dès la première étape.
Heureusement, cette astuce fonctionne bien en pratique. Si nous découvrons beaucoup plus de cas particuliers, nous envisagerons probablement de faire ce qu'il convient(tm) et de les séparer en deux analyseurs syntaxiques distincts.
// Before
@import "text-shadow";
// After
@import "rounded-corners", "text-shadow";
TypeScript
La prise en charge de TypeScript est désormais solide, toutes les modifications de cette version concernent de petits cas particuliers.
TypeScript : imprimer les paramètres de type des fonctions fléchées sur la même ligne que les paramètres (#2101) par @azz
L'algorithme central de Prettier consiste à développer un groupe si tous les éléments ne tiennent pas. Cela fonctionne très bien en pratique pour la plupart du JavaScript, mais il y a un cas qu'il ne gère pas très bien : lorsque deux groupes sont côte à côte, dans ce cas : <Generics>(Arguments). Nous devons créer soigneusement des groupes pour que les arguments se développent en premier, car c'est généralement ce à quoi les gens s'attendent.
// 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 : conserver les parenthèses autour des assertions de non-null avec yield/await (#2149) par @azz
Bon gré mal gré, nous avons décidé de gérer manuellement l'ajout des parenthèses. Ainsi, lorsqu'un nouvel opérateur est introduit, nous devons nous assurer d'ajouter les parenthèses correctes lorsqu'il est imbriqué avec n'importe quelle autre combinaison d'opérateurs. Dans ce cas, nous avons oublié await à l'intérieur du ! TypeScript.
// Before
const bar = await foo(false)!;
// After
const bar = (await foo(false))!;
TypeScript : Imprimer dans les imports s'ils sont présents dans le code source (#2150) par @azz
Nous utilisons le projet typescript-eslint-parser qui traduit l'AST TypeScript en AST estree pour que Prettier puisse l'imprimer. Parfois, nous découvrons des cas particuliers qu'il ne gère pas encore. Ici, il ne permettait pas de détecter un {} vide, ce qui s'avère important pour TypeScript. Heureusement, l'équipe est très réactive et a corrigé le problème après que nous ayons implémenté une solution de contournement dans Prettier.
// Before
import from "@types/googlemaps";
// After
import {} from "@types/googlemaps";
TypeScript : Toujours diviser les interfaces sur plusieurs lignes (#2161) par @azz
Le code qui implémente les interface est partagé avec celui qui imprime les objects, qui contient une règle pour les maintenir développés s'il y a un \n à l'intérieur. Mais ce n'est pas le comportement souhaité pour les interfaces. Nous voulons toujours les développer, comme pour les classes, même si elles tiennent dans 80 colonnes.
// Before
interface FooBar { [id: string]: number; }
// After
interface FooBar {
[id: string]: number;
}
TypeScript : Corriger le point-virgule superflu dans les déclarations TypeScript ambiantes (#2167) par @azz
Les options no-semi et semi sont souvent demandées, mais l'équipe Prettier a devancé vos attentes en implémentant two-semi pour vous ! Je plaisante, c'était un bug maintenant corrigé ;)
// 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 : Grouper les paramètres de fonction dans les signatures d'appel/construction (#2169) par @azz
Ajouter un commentaire avant une méthode prenait auparavant en compte la longueur du commentaire et développait souvent la méthode de manière inattendue. Heureusement, la correction était simple : il suffisait d'encadrer la sortie dans un 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 : Mise à jour de tsep (#2183) par @azz
Ce bug était très ennuyeux si vous le rencontriez : à chaque formatage du code, il ajoutait un _ supplémentaire à la clé de l'objet !
// Before
obj = {
__: 42
___: 42
};
// After
obj = {
_: 42
__: 42
};
TypeScript : diviser lors de plusieurs extensions d'interface (#2085) par @azz
Contrairement à JavaScript, TypeScript permet d'étendre plusieurs classes simultanément. Il s'avère que cette fonctionnalité est utilisée, et Prettier gère désormais mieux son formatage.
// Before
export interface ThirdVeryLongAndBoringInterfaceName extends AVeryLongAndBoringInterfaceName, AnotherVeryLongAndBoringInterfaceName, AThirdVeryLongAndBoringInterfaceName {}
// After
export interface ThirdVeryLongAndBoringInterfaceName
extends AVeryLongAndBoringInterfaceName,
AnotherVeryLongAndBoringInterfaceName,
AThirdVeryLongAndBoringInterfaceName {}
TypeScript : prise en charge d'ObjectPattern au lieu d'ObjectExpression dans BinaryExpression (#2238) par @azz
Il s'agit d'un cas limite peu passionnant, mal géré dans la conversion TypeScript -> estree.
// Before
call(c => { bla: 1 }) || [];
// After
call(c => ({ bla: 1 })) || [];
Préserver les lignes après les directives (#2070)
En prenant en charge TypeScript, Prettier est désormais utilisé dans de nombreuses bases de code Angular, ce qui expose des cas limites précédemment mal gérés. Ici, nous ne conservions pas les lignes vides après les directives à l'intérieur des fonctions.
// 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
Cette version comporte très peu de modifications JavaScript, ce qui est formidable. Nous commençons à voir le bout du tunnel pour aboutir à un formateur de code performant. Nous n'atteindrons jamais une perfection absolue. L'objectif est que pour chaque problème signalé, aucune amélioration évidente ne soit possible sans dégrader d'autres parties du code.
Autoriser la recombinaison des lignes JSX (#1831) par @karl
L'objectif de Prettier est d'offrir un formatage cohérent : à partir d'un AST, l'impression est toujours identique. Nous avions dû faire deux compromis en conservant le format d'origine : pour JSX et les objets. Grâce à cette modification, nous ne dépendons plus de l'entrée originale pour JSX contenant du texte, ce qui permet un réagencement interne.
// 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>
);
}
Saut de ligne pour les expressions membres calculées non littérales (#2087) par @azz
L'impression des chaînes de membres est l'aspect le plus complexe de Prettier, et nous trouvons constamment des ajustements pour améliorer l'expérience.
// 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",
});
Indenter la première variable dans un scénario one-var (#2095) par @azz
Jusqu'à récemment, nous avions peu travaillé sur l'impression de multiples variables dans une déclaration unique, car l'usage courant est une déclaration par variable. Pour les déclarations uniques, nous ne voulons pas d'indentation, mais nous en avons besoin lorsqu'il y en a d'autres ensuite, sous peine d'un rendu étrange.
// 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>';
Autoriser le saut de ligne avec les imports nommés par défaut (#2096) par @azz
Ceci était une regrettable régression depuis la 1.4 : nous alignions les imports contenant un seul élément, mais la définition d'"élément unique" incluait à tort un type unique et un élément unique. C'est désormais corrigé !
// Before
import transformRouterContext, { type TransformedContextRouter } from '../../helpers/transformRouterContext';
// After
import transformRouterContext, {
type TransformedContextRouter
} from '../../helpers/transformRouterContext';
Activer allowImportExportEverywhere (#2207) par @zimme
Prettier vise à formater le code tel qu'écrit en pratique, donc nous activons les modes expérimentaux/relâchés des analyseurs syntaxiques. Babylon permet les imports dans les fonctions (hors standard), mais cela ne nous coûte rien de l'autoriser.
// Before
ParseError
// After
function f() {
import x from 'x';
}
Prise en charge des templates inline pour les appels new (#2222)
Nous enrichissons constamment les fonctionnalités pour les appels de fonctions, et devons les adapter aux appels new qui ont un type de nœud AST différent mais un traitement similaire. Cette correction refactorise les deux pour utiliser le même point d'appel, réduisant ainsi les risques d'incohérences futures.
// 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.
`);
Ne pas indenter le '+' dans une valeur d'objet (#2227)
Lorsque nous avons adopté la même heuristique pour l'assignation (a = b) et pour les objets ({a: b}), nous avons oublié de corriger l'indentation. C'est maintenant réglé.
// Before
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
// After
var abc = {
thing:
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf" +
"asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf",
}
Gérer les conditions à l'intérieur d'un ternaire (#2228)
Prettier avait déjà un cas spécial pour les expressions conditionnelles, mais cela ne s'appliquait pas lorsque la condition était la partie gauche d'un ternaire. C'est désormais le cas.
// 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
)
);
Ajout de la mise en cache pour l'impression (#2259)
Avec la version 1.0, nous avions corrigé un bogue dans l'impression qui provoquait un comportement exponentiel. Nous avions atténué le problème pour éviter les timeouts sur du code raisonnable, sans toutefois le résoudre complètement. En ajoutant une couche de cache au bon endroit, ce problème devrait désormais être réglé.
Cela devrait empêcher les timeouts lors de l'impression de l'IR de Prettier avec Prettier en mode debug.
// 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();
});
});
});
});
});
});
});
});
});
Correction de l'emplacement de la variance (#2261)
Lors de la refactorisation du code d'impression des modificateurs pour le support TypeScript, nous avons accidentellement déplacé la variance (+) avant static, ce qui n'est pas valide dans Flow. Ce problème est maintenant corrigé.
// Before
class Route {
+static param: T;
}
// After
class Route {
static +param: T;
}
Divers
Corrections diverses pour le suivi des plages et du curseur (#2266, #2248, #2250, #2136) par @CiGit et @josephfrazier
Ces deux fonctionnalités ont été introduites dans la dernière version, et nous avons découvert plusieurs problèmes lors de leur utilisation en production. Plusieurs d'entre eux ont été corrigés - si vous en rencontrez d'autres, merci de les signaler !
