Publication de Prettier 1.0
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 →
Cet article a été écrit par vjeux et édité par jlongster, initialement publié ici
Nous avons officiellement annoncé prettier il y a plus de deux mois comme solution pour résoudre le problème de perte de temps à formater votre code. Commencé comme une expérience, il a clairement trouvé un écho auprès de nombreuses personnes, cumulant environ 7000 étoiles sur GitHub et plus de 100 000 npm downloads mensuels en seulement deux mois.
Au cas où vous ne le sauriez pas, prettier est un formateur de JavaScript qui fonctionne en compilant votre code en un AST, puis en imprimant joliment cet AST. Comme les navigateurs renvoient à la ligne le texte, prettier renvoie également à la ligne le code selon une longueur de ligne donnée. Le résultat est un code élégant et parfaitement cohérent, indépendamment de son auteur. Cela résout le problème des programmeurs qui passent beaucoup de temps à déplacer manuellement du code dans leur éditeur et à débattre des styles (voir le gif).
Il n'était pas entièrement clair au départ si nous pourrions lui faire générer toujours du code valide et élégant, mais la bonne nouvelle est que ce n'est plus une question ouverte. De nombreux projets (React, Jest, immutable-js, Haul, et bien d'autres) et entreprises (Oculus, Cloudflare) ont adopté prettier pour formater leur base de code JavaScript et ont constaté les bénéfices du formatage automatique (voir ce tweet pour plus de détails !). Certains ont même exécuté prettier sur l'ensemble de leur base de code (des dizaines de milliers de lignes de code).
Au cours des trois dernières semaines, nous avons parcouru toutes les issues ouvertes, essayé de prendre autant de décisions que possible concernant la manière dont le code est formaté, et corrigé la plupart des bogues connus. Ce n'est pas parfait et nous continuerons à itérer, mais c'est le bon moment pour publier la version 1.0 !
Cela ne signifie pas que nous ne changerons plus le format, mais les changements seront minimes. Par exemple, nous envisageons d'ajuster les ternaires, mais il y aura très peu de changements de cette nature à partir de maintenant, et quand il y en aura, nous créerons une nouvelle version majeure.
Ce que cela signifie, c'est que Prettier peut être utilisé en production en toute sécurité. Nous avons corrigé une tonne de bogues et nous sommes assurés que le résultat est stable et sémantiquement correct. Prettier est prêt à être utilisé, que ce soit pour quelques fichiers dans votre projet secondaire ou pour convertir l'intégralité de votre base de code. C'est à vous de déterminer où vous en êtes dans le cheminement pour abandonner une manière particulière de formater votre code.
Voyons ce que la version 1.0 apporte à prettier !
Merci à tous les contributeurs suivants qui ont participé à cette version, et merci à Ian Storm Taylor pour le logo !
Adam Stankiewicz, Alex Rattray, Alex Stachowiak, Amjad Masad, Andrey Okonetchnikov, António Nuno Monteiro, Artem Sapegin, Benjamin Tan, Bill Mill, Brandon Mills, Brian Holt, Brian Ng, Charles Pick, ChristianHersevoort, Christopher Chedeau, Christopher Moeller, Damien Lebrun, Dan Harper, Dara Hak, David Ascher, David Hong, Davy Duperron, Eric Clemmons, Erick Romero, Esben Petersen, Filipe Fortes, Gregor Adams, Hampus,hlsson, Hawken Rives, Henry Zhu, Ilya Panasenko, James Henry, James Long, Jamie Webb, Jan Kassens, Jed Watson, Jeffrey Horn, Jeremy Morrell, Joe Fiorini, Jon LaBelle, Joseph Savona, Karl Horky, Karl O'Keeffe, Kent C. Dodds, Kevin Gibbons, Kim Joar Bekkelund, Lucas Bento, Lucas Duailibe, MarekMatejkaKCL, Mateusz Zatorski, Michael Ficarra, Michał Pierzchała, Mikael Brevik, Nathan Friedly, Patrick Camacho, Paul,arduner, Rafael Hengles, Raghuvir Kasturi, Rasmus Eneman, Riley Tomasek, Rogelio Guzman, Royce Townsend, Sean Grove, Shigeaki Okazaki, Simen,ekkhus, Simon Lydell, Sorin Muntean, Spencer Dixon, Thai Pangsakulyanont, Tom McKearney, Travis Jefferson, Umidbek Karimov, Vincent Voyer, Vu Tran, Walter Breakell, Yatharth Khatri, azu, daleroy, kalmanb, skratchdot
Options
Prettier est un formateur de code avec une philosophie d'opinion. Au lancement du projet, nous pensions que cela impliquait de ne proposer aucune configuration, à l'image de gofmt ou refmt. Mais en avançant, nous avons réalisé que cela risquait de nuire à l'adoption de Prettier : des personnes qui auraient pu en bénéficier ne l'utiliseraient pas car il ne formaterait pas leur code selon leurs préférences.
Notre interprétation actuelle d'un formateur de code "opinionated" consiste plutôt à proposer des options pour les aspects syntaxiques fondamentaux relevant du "si ça ne fait pas X, aussi bon soit-il, je ne l'utiliserai jamais". Par exemple, je (@vjeux) n'utiliserai jamais standard parce qu'il n'utilise pas les points-virgules. Cette position n'est absolument pas rationnelle, mais c'est ainsi que raisonnent beaucoup de personnes, d'où les "guerres de style".
Nous n'introduirons toujours pas d'options pour chaque type de syntaxe, mais uniquement pour les aspects les plus impactants. Nous avons identifié deux options majeures dans cette catégorie : tabulations vs espaces et points-virgules vs sans points-virgules. Nous les ajoutons donc dans Prettier !
--no-semi (#1129)
Merci à rattrayalex pour son travail considérable sur cette fonctionnalité !
// Before
console.log();
[1, 2, 3].map(x => x + 1);
// After
console.log()
;[1, 2, 3].map(x => x + 1)
--use-tabs (#1026)
Merci à rhengles pour l'implémentation et l'explication des raisons derrière cette option !
// Before
if (1) {
··console.log(); // Two spaces
}
// After
if (1) {
» console.log(); // One Tab!
}
Formatage
Le reste de cet article documente les modifications plus modestes que nous avons rassemblées pour la 1.0. Nous ne publierons généralement pas de journal des modifications sous forme d'article, mais nous pensions que cela démontre la stabilité croissante de Prettier et donne une bonne idée du type de problèmes que nous corrigeons actuellement.
Ajout d'un saut de ligne pour les fonctions fléchées sans accolades renvoyant des appels (#927)
Probablement le problème le plus signalé dans la version actuelle de Prettier : nous n'ajoutions pas de saut de ligne après les fonctions fléchées dans les appels. Maintenant, nous le faisons. À noter qu'il est encourageant de voir que les problèmes portent désormais sur des détails comme celui-ci, et non plus sur des cas où "le résultat semble complètement cassé".
// Before
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter));
// After
const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter)
);
Ajout de saut de ligne conditionnel pour les assignations et parenthèses autour des expressions binaires dans les retours (#871 & #870)
Les longues chaînes d'expressions logiques paraissaient étranges lorsque la première ligne restait sur la même ligne que la déclaration de variable ou l'instruction return. Nous préférons désormais les déplacer sur la ligne suivante et ajouter des parenthèses autour des retours pour garantir la validité.
// Before
const computedDescriptionLines = (showConfirm &&
descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// After
const computedDescriptionLines =
(showConfirm && descriptionLinesConfirming) ||
(focused && !loading && descriptionLinesFocused) ||
descriptionLines;
// Before
return !filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`);
// After
return (
!filePath.includes(coverageDirectory) &&
!filePath.endsWith(`.${SNAPSHOT_EXTENSION}`)
);
Regroupement du premier argument pour les fonctions en ligne (#947)
Première grande règle personnalisée ajoutée dans Prettier : l'impression des fonctions en tant que premier argument en ligne. Bien que moins courant aujourd'hui, certains éléments fondamentaux de JavaScript comme setTimeout utilisent ce style, d'où l'intérêt de le supporter dans Prettier.
// Before
setTimeout(
function() {
thing();
},
500
);
// After
setTimeout(function() {
thing();
}, 500);
Amélioration de la sortie JSX pour les composants stylisés (#1144)
Les littéraux de gabarit sont complexes à formater car les espaces blancs y sont significatifs. Pour éviter de modifier la sémantique des programmes, Prettier conserve actuellement leur formatage d'origine. En attendant une solution générale, nous apportons des corrections ciblées comme celle-ci pour le JSX.
// Before
<style>
{
`
color: red;
`
}
</style>
// After
<style>{`
color: red;
`}</style>
Support de la notation pointée en ligne dans les décorateurs (#1029)
MobX 3 utilise désormais des expressions membres devant être écrites en ligne. L'heuristique précédente était trop restrictive et a été corrigée.
class X {
// Before
@action.bound
setPrice(price) {
this.price = price;
}
// After
@action.bound setPrice(price) {
this.price = price;
}
}
Compactage des fonctions de déstructuration d'objet unique (#1022)
Les composants fonctionnels sans état React étant très populaires, nous compactons désormais la déstructuration du premier argument pour gagner de l'espace.
// Before
export default function StatelessFunctionalComponent(
{
searchFilters,
title,
items
}
) {
return <div />;
}
// After
export default function StatelessFunctionalComponent({
searchFilters,
title,
items,
}) {
return <div />
}
Support du curryfication (#1066)
Redux encourage fortement l'écriture de fonctions curryfiées où chaque argument est une fonction imbriquée. Plutôt que de les indenter, nous les plaçons désormais en ligne.
// Before
const mw = store =>
next =>
action => {
return next(action)
};
// After
const mw = store => next => action => {
return next(action)
};
Respect des échappements dans les chaînes JSX (#1056)
Après avoir testé différentes approches d'échappement (tout échapper, n'échapper rien, liste blanche...), nous conservons désormais le format d'échappement original des chaînes en entrée, désormais étendu aux chaînes JSX.
// Before
<a href="https://foo.bar?q1=foo&q2=bar" />
// After
<a href="https://foo.bar?q1=foo&q2=bar" />
Parenthèses
Reproduire les parenthèses depuis l'AST est à la fois une bénédiction et une malédiction. Notre position initiale était de n'imprimer que le strict minimum nécessaire pour la validité du programme. Nous ajoutons désormais des parenthèses non strictement nécessaires mais facilitant la compréhension du code.
Ajout de parenthèses pour les opérateurs binaires (#1153)
// Before
var sizeIndex = index - 1 >>> level & MASK;
// After
var sizeIndex = ((index - 1) >>> level) & MASK;
Ajout de parenthèses pour la règle no-confusing-arrow (#1182)
// Before
var x = a => 1 ? 2 : 3;
// After
var x = a => (1 ? 2 : 3);
Suppression des parenthèses dans les assignations chaînées (#967)
// Before
this.size = (this._origin = (this._capacity = 0));
// After
this.size = this._origin = this._capacity = 0;
Flow
Durant les premiers temps de prettier, nous nous sommes fortement concentrés sur une bonne prise en charge du langage JavaScript fondamental. Maintenant qu'il est dans un état satisfaisant, nous avons davantage de temps pour formater les constructions Flow de manière raffinée.
Ajout de la possibilité de rupture pour les génériques Flow (#1041)
// Before
type _ReactElement<DefaultProps, Props, Config: $Diff<Props, DefaultProps>, C: $React.Component<DefaultProps, Props, any>> = $React.Element<Config>;
// After
type _ReactElement<
DefaultProps,
Props,
Config: $Diff<Props, DefaultProps>,
C: $React.Component<DefaultProps, Props, any>
> = $React.Element<Config>;
Amélioration du formatage des intersections Flow (#1018 & #1155)
// Before
type Props =
& {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
}
& FooterProps;
// After
type Props = {
focusedChildren?: React.Children,
onClick: () => void,
overlayChildren?: React.Children,
} & FooterProps;
Ajout du support des sauts de ligne dans TupleTypeAnnotation (#1003)
// Before
export type FileMetaData = [/* id */ string, /* mtime */ number, /* visited */
| 0
| 1, /* dependencies */ Array<string>];
// After
export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0|1,
/* dependencies */ Array<string>,
];
Suppression des parenthèses pour les raccourcis Flow avec un seul argument (#972)
// Before
type T = { method: (a) => void };
// After
type T = { method: a => void };
Possibilité de rupture pour les interfaces (#1060)
// Before
export interface Environment1 extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
// After
export interface Environment1
extends GenericEnvironment<SomeType, AnotherType, YetAnotherType> {
m(): void
}
Correction du formatage des littéraux numériques de type Flow (#1132)
// Before
type Hex = {n: 1};
// After
type Hex = {n: 0x01};
Points de rupture possibles
Certains cas subsistent où prettier produit encore du code dépassant 80 colonnes alors qu'une écriture plus concise serait possible. La solution consiste à identifier minutieusement les emplacements où une rupture est envisageable tout en garantissant que cela n'affecte pas négativement les cas d'usage courants.
Autorisation de rupture après = pour les chaînes et expressions membres (#1142 & #1188)
// Before
elements[0].innerHTML = '<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered = obj.exampleOfOrderOfGetterAndSetterReordered;
// After
elements[0].innerHTML =
'<div></div><div></div><div></div><div></div><div></div><div></div>';
var testExampleOrOrderOfGetterAndSetterReordered =
obj.exampleOfOrderOfGetterAndSetterReordered;
Ajout de la possibilité de rupture pour les expressions membres de premier niveau (#1036)
// Before
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0].style.paddingRight
).toBe("1000px");
// After
expect(
findDOMNode(component.instance()).getElementsByClassName(styles.inner)[0]
.style.paddingRight
).toBe("1000px");
Divers
Intégration en ligne des expressions de classe pour les fonctions fléchées sans accolades (#1023)
Changement minimal : nous intégrons désormais en ligne les classes retournées par des fonctions fléchées.
// Before
jest.mock(
'../SearchSource',
() =>
class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
// After
jest.mock(
'../SearchSource',
() => class {
findMatchingTests(pattern) {
return {paths: []};
}
},
);
Non-respect des sauts de ligne pour les patterns de déstructuration d'objet (#981)
Pour les objets, nous appliquons un traitement spécifique : si un \n est présent dans le code source original, nous conservons le format développé. Ce comportement était initialement prévu pour les objets uniquement, mais partageant le même code que la déstructuration, il s'appliquait accidentellement à cette dernière. Ce problème a été corrigé.
// Before
const Component2 = ({
props
}) => <Text>Test</Text>;
// After
const Component1 = ({ props }) => <Text>Test</Text>;
Support linguistique
Pour que vous puissiez utiliser prettier, il doit être capable de formater votre code. Nous visons à supporter tout ce que nos analyseurs sous-jacents (babylon et flow) peuvent parser.
Ajout du support pour ForOfStatement avec le drapeau await (#1073)
async function f() {
for await (const line of readLines(\\"/path/to/file\\")) {
(line: void); // error: string ~> void
}
}
Ajout du support pour le spread de type flow (#1064)
type TypeB = { ...TypeA };
Correction sémantique
L'exigence absolue de prettier est de produire un code valide ayant le même comportement que le source original. Vous ne devriez pas craindre d'exécuter prettier sur votre codebase entière. Pour garantir cela, nous mettons en œuvre plusieurs mesures :
-
Mise en place d'un processus automatisé pour vérifier la validité du code produit
-
Utilisation sur de grandes bases de code comme Facebook, l'ensemble de CDNjs et les nombreux utilisateurs communautaires
-
Fuzzing intensif via eslump
-
Traitement prioritaire et correction immédiate de tout rapport de problème de ce type
Si vous avez la malchance d'en rencontrer un, merci de le signaler pour que nous puissions le corriger. En attendant, vous pouvez utiliser // prettier-ignore pour vous débloquer.
Gestion correcte de \r\n dans JSXText (#1170)
// Before
<div>
{" "}
Text{" "}
</div>;
// After
<div>
Text
</div>;
Correction des parenthèses dans les appels imbriqués dans new (#1169)
// Before
new factory()();
// After
new (factory())();
Ajout de parenthèses autour du type de retour des fonctions fléchées (#1152)
// Before
const f = (): string => string => {};
// After
const f = (): (string => string) => {};
Correction de l'impression des annotations de type objet exact en Flow (#1114)
// Before
type Props = {};
// After
type Props = {||};
Impression des commentaires suspendus après return (#1178)
// Before
function f() {
return;
}
// After
function f() {
return /* a */;
}
Impression des commentaires après les clés d'objet (#1131)
// Before
let a = {
"a": () => 1
};
// After
let a = {
"a" /* comment */: () => 1
};
Correction des commentaires en tête dans les SequenceExpression retournés (#1133)
// Before
function sequenceExpressionInside() {
return;
// Reason for a
a, b;
}
// After
function sequenceExpressionInside() {
return ( // Reason for a
a, b
);
}
Correction de l'impression des paramètres optionnels uniques dans les fonctions fléchées (#1002)
// Before
a = b? => c;
// After
a = (b?) => c;
Correction des exports par défaut (#998)
// Before
export { foo, bar } from "./baz";
// After
export foo, {bar} from './baz';
Impression des commentaires de ligne/mixtes sur de nouvelles lignes dans JSXEmptyExpression (#985)
// Before
<div>
{
// single line comment}
</div>;
// After
<div>
{
// single line comment
}
</div>;
Gestion des commentaires
Prettier fonctionne en imprimant l'AST, ce qui rend la gestion des commentaires délicate car ils peuvent être placés n'importe où entre les tokens. La méthode consiste à attacher chaque commentaire à un nœud AST, soit avant soit après celui-ci. Une heuristique générique permet de localiser correctement les commentaires dans la plupart des cas, mais de nombreux cas particuliers nécessitent une gestion manuelle.
Prettier ne pourra jamais imprimer parfaitement les commentaires dans tous les endroits farfelus où vous pourriez les placer, mais nous visons un résultat raisonnable dans la majorité des situations. Si vous observez un comportement étrange lié aux commentaires, merci de créer des issues pour que nous puissions trouver une solution.
Ajout du support breakParent pour willBreak (#674)
// Before
runtimeAgent.getProperties(objectId, false, false, false, ( // ownProperties // accessorPropertiesOnly // generatePreview
error,
properties,
internalProperties
) => {
return 1;
});
// After
runtimeAgent.getProperties(
objectId,
false, // ownProperties
false, // accessorPropertiesOnly
false, // generatePreview
(error, properties, internalProperties) => {
return 1
},
);
Correction des lignes vides supplémentaires avec les commentaires dans les switch case (#936)
// Before
switch (foo) {
case "bar":
doThing()
// no default
}
// After
switch (foo) {
case "bar":
doThing()
// no default
}
Correction des commentaires dans les déclarations d'import (#1030)
// Before
import {
FN1,
FN2,
// FN3,
FN4
} from // FN4,
// FN5
"./module";
// After
import {
FN1,
FN2,
// FN3,
FN4,
// FN4,
// FN5
} from './module';
Correction des commentaires sur le dernier argument des fonctions (#1176 & #905)
// Before
function f(
a: number
)// some comment here
: number {
return a + 1;
}
// After
function f(
a: number
// some comment here
): number {
return a + 1;
}
Suppression des restrictions sur les commentaires dans les unions Flow (#1040)
// Before
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
| // The upload timed out
B
| // Failed somewhere on the line
C;
// After
type UploadState<E, EM, D> =
// The upload hasn't begun yet
| A
// The upload timed out
| B
// Failed somewhere on the line
| C;
Correction des commentaires dans les ObjectPattern (#1045)
// Before
export default (
{
foo,
bar
// comment
// comment 2
}: {
foo?: Object,
bar?: Object
}
) => {};
// After
export default (
{
foo,
bar
}: {
// comment
foo?: Object,
// comment 2
bar?: Object,
},
) => {}
Correction du tri des commentaires par emplacement (#1038)
// Before
let {
// comment
a = b
} = c;
// After
let {
a = b // comment
} = c;
Correction de l'emplacement des commentaires dans les expressions binaires (#1043)
// Before
a = Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange// Comment
-
offset;
// After
a =
// Comment
Math.random() * (yRange * (1 - minVerticalFraction)) +
minVerticalFraction * yRange -
offset;
Correction des commentaires dans les méthodes de classe (#1074)
// Before
class x {
focus() // do nothing
{
// do nothing
}
}
// After
class x {
focus() {
// do nothing
// do nothing
}
}
Support de "// prettier-ignore" dans les blocs de commentaires (#1125)
// Before
module.exports = {
// Some comment
// prettier-ignore
m: matrix(1, 0, 0, 0, 1, 0, 0, 0, 1)
};
// After
module.exports = {
// Some comment
// prettier-ignore
m: matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
)
};
Stabilisation des commentaires dans les VariableDeclarator (#1130)
// Before
let obj = [ // Comment
'val'
];
// After
let obj = [
// Comment
'val'
];
Correction de la détection des commentaires avant une virgule (#1127)
// Before
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
// After
const foo = {
a: 'a' /* comment for this line */,
/* Section B */
b: 'b',
};
Correction du dernier commentaire dans un test conditionnel if (#1042)
// Before
if (isIdentifierStart(code) || code === 92) {
/* '\' */
}
// After
if (isIdentifierStart(code) || code === 92 /* '\' */) {}
