Aller au contenu principal

Justification

Traduction Bêta Non Officielle

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 →

Prettier est un formateur de code à méthodologie rigoureuse. Ce document explique certains de ses choix fondamentaux.

Les priorités de Prettier

Exactitude

La première exigence de Prettier est de produire un code valide ayant exactement le même comportement qu'avant le formatage. Signalez tout cas où Prettier ne respecte pas ces règles d'exactitude - c'est un bogue à corriger !

Chaînes de caractères

Guillemets doubles ou simples ? Prettier choisit celui qui minimise le nombre de caractères d'échappement. "It's gettin' better!" plutôt que 'It\'s gettin\' better!'. En cas d'égalité ou d'absence de guillemets dans la chaîne, Prettier utilise par défaut les doubles guillemets (modifiable via l'option singleQuote).

JSX dispose de sa propre option pour les guillemets : jsxSingleQuote. JSX puise ses origines dans HTML, où les guillemets doubles dominent pour les attributs. Les outils de développement navigateur suivent cette convention en affichant toujours le HTML avec des guillemets doubles, même si le code source utilise des simples. Une option distincte permet d'utiliser des guillemets simples pour le JS et des doubles pour le "HTML" (JSX).

Prettier conserve le mode d'échappement de vos chaînes. Par exemple, "🙂" ne sera pas formaté en "\uD83D\uDE42" et vice versa.

Lignes vides

Les lignes vides s'avèrent difficiles à générer automatiquement. L'approche de Prettier est de préserver les lignes vides telles qu'elles apparaissent dans le code source original. Deux règles supplémentaires s'appliquent :

  • Prettier fusionne les lignes vides multiples en une seule ligne vide.

  • Les lignes vides en début et fin de blocs (et de fichiers entiers) sont supprimées (les fichiers se terminent toujours par un saut de ligne unique).

Objets multi-lignes

Par défaut, l'algorithme d'impression de Prettier produit les expressions sur une seule ligne si possible. Les objets étant utilisés dans de nombreux contextes JavaScript, leur formatage multi-lignes améliore parfois la lisibilité. Voir par exemple les listes d'objets, les configurations imbriquées, les feuilles de style et les méthodes clés. Ne pouvant établir une règle universelle, Prettier conserve par défaut les objets multi-lignes s'il existe un saut de ligne entre { et la première clé dans le code source. Ainsi, les objets longs sur une ligne sont automatiquement étendus, tandis que les objets multi-lignes courts ne sont jamais compressés.

Ce comportement conditionnel peut être désactivé via l'option objectWrap.

Astuce : Pour convertir un objet multi-lignes en une seule ligne :

const user = {
name: "John Doe",
age: 30,
};

…supprimez simplement le saut de ligne après { :

const user = {  name: "John Doe",
age: 30
};

…puis exécutez Prettier :

const user = { name: "John Doe", age: 30 };

Pour revenir au format multi-lignes, ajoutez un saut de ligne après { :

const user = {
name: "John Doe", age: 30 };

…et exécutez Prettier :

const user = {
name: "John Doe",
age: 30,
};
Note sur la réversibilité du formatage

Le formatage semi-manuel des littéraux objets est en réalité une solution de contournement, pas une fonctionnalité. Il a été implémenté uniquement parce qu'à l'époque aucune bonne heuristique n'avait été trouvée et qu'un correctif urgent était nécessaire. Cependant, en tant que stratégie générale, Prettier évite le formatage non réversible comme celui-ci. L'équipe recherche donc toujours des heuristiques qui permettraient soit de supprimer complètement ce comportement, soit au moins de réduire le nombre de situations où il s'applique.

Que signifie réversible ? Une fois qu'un littéral objet devient multiligne, Prettier ne le réunifiera pas en une seule ligne. Si dans du code formaté par Prettier, nous ajoutons une propriété à un littéral objet, exécutons Prettier, puis changeons d'avis, supprimons la propriété ajoutée et exécutons à nouveau Prettier, nous pourrions obtenir un formatage différent de l'original. Ce changement inutile pourrait même être inclus dans un commit, ce qui est exactement le genre de situation que Prettier a été conçu pour prévenir.

Décorateurs

Tout comme pour les objets, les décorateurs sont utilisés dans de nombreux contextes différents. Parfois il est logique de les écrire au-dessus de la ligne qu'ils décorent, parfois il est préférable de les mettre sur la même ligne. Nous n'avons pas trouvé de règle universellement applicable, donc Prettier conserve le positionnement que vous avez utilisé (s'ils tiennent sur la ligne). Ce n'est pas idéal, mais c'est une solution pragmatique à un problème complexe.

@Component({
selector: "hero-button",
template: `<button>{{ label }}</button>`,
})
class HeroButtonComponent {
// These decorators were written inline and fit on the line so they stay
// inline.
@Output() change = new EventEmitter();
@Input() label: string;

// These were written multiline, so they stay multiline.
@readonly
@nonenumerable
NODE_TYPE: 2;
}

Il existe une exception : les classes. Nous estimons qu'il n'est jamais pertinent d'écrire les décorateurs sur la même ligne, ils sont donc toujours déplacés sur leur propre ligne.

// Before running Prettier:
@observer class OrderLine {
@observable price: number = 0;
}
// After running Prettier:
@observer
class OrderLine {
@observable price: number = 0;
}

Note : Prettier 1.14.x et antérieur tentait de déplacer automatiquement vos décorateurs. Si vous avez utilisé une ancienne version sur votre code, vous devrez peut-être manuellement regrouper certains décorateurs par endroits pour éviter des incohérences :

@observer
class OrderLine {
@observable price: number = 0;
@observable
amount: number = 0;
}

Littéraux de modèle (template literals)

Les littéraux de modèle peuvent contenir des interpolations. Décider s'il est approprié d'insérer un saut de ligne dans une interpolation dépend malheureusement du contenu sémantique du modèle - par exemple, introduire un saut de ligne au milieu d'une phrase en langage naturel est généralement indésirable. Comme Prettier ne dispose pas d'assez d'informations pour prendre cette décision seul, il utilise une heuristique similaire à celle des objets : il ne scindera une expression d'interpolation sur plusieurs lignes que s'il y avait déjà un saut de ligne dans cette interpolation.

Cela signifie qu'un littéral comme le suivant ne sera pas divisé sur plusieurs lignes, même s'il dépasse la largeur d'impression :

`this is a long message which contains an interpolation: ${format(data)} <- like this`;

Si vous souhaitez que Prettier divise une interpolation, vous devez vous assurer qu'il y a un saut de ligne quelque part dans le ${...}. Sinon, il gardera tout sur une seule ligne, quelle que soit sa longueur.

L'équipe préférerait ne pas dépendre du formatage original de cette manière, mais c'est la meilleure heuristique disponible actuellement.

Points-virgules

Cette section concerne l'utilisation de l'option noSemi.

Considérez ce fragment de code :

if (shouldAddLines) {
[-1, 1].forEach(delta => addLine(delta * 20))
}

Bien que le code ci-dessus fonctionne parfaitement sans points-virgules, Prettier le transforme en réalité en :

if (shouldAddLines) {
;[-1, 1].forEach(delta => addLine(delta * 20))
}

Ceci pour vous éviter des erreurs. Imaginez que Prettier n'insère pas ce point-virgule et que vous ajoutiez cette ligne :

 if (shouldAddLines) {
+ console.log('Do we even get here??')
[-1, 1].forEach(delta => addLine(delta * 20))
}

Oups ! Le code signifie en réalité :

if (shouldAddLines) {
console.log('Do we even get here??')[-1, 1].forEach(delta => addLine(delta * 20))
}

Avec un point-virgule devant ce [, ces problèmes n'arrivent jamais. Cela rend la ligne indépendante des autres, vous permettant de déplacer et d'ajouter des lignes sans vous soucier des règles ASI (Automatic Semicolon Insertion).

Cette pratique est également courante dans standard qui utilise un style sans point-virgule.

Notez que si votre programme contient actuellement un bogue lié aux points-virgules, Prettier ne le corrigera pas automatiquement. Rappelez-vous : Prettier reformate uniquement le code, il ne modifie pas son comportement. Prenons cet exemple de code bogué, où le développeur a oublié de placer un point-virgule avant le ( :

console.log('Running a background task')
(async () => {
await doBackgroundWork()
})()

Si vous passez ce code à Prettier, il ne modifiera pas son comportement. À la place, il le reformatera de manière à montrer comment ce code se comportera réellement lors de son exécution.

console.log("Running a background task")(async () => {
await doBackgroundWork();
})();

Largeur d'impression

L'option printWidth est davantage une ligne directrice qu'une règle stricte pour Prettier. Ce n'est pas une limite de longueur maximale absolue, mais plutôt une indication de la longueur approximative souhaitée pour les lignes. Prettier produira des lignes plus courtes et plus longues, tout en s'efforçant globalement de respecter la largeur spécifiée.

Certains cas particuliers existent, comme les chaînes très longues, les expressions régulières, les commentaires ou les noms de variables, qui ne peuvent pas être divisés sur plusieurs lignes (sans transformations que Prettier ne réalise pas). De même, un code imbriqué sur 50 niveaux produira principalement des lignes d'indentation :)

En dehors de cela, quelques cas spécifiques dépassent intentionnellement la largeur d'impression.

Imports

Prettier peut diviser les longues déclarations import sur plusieurs lignes :

import {
CollectionDashboard,
DashboardPlaceholder,
} from "../components/collections/collection-dashboard/main";

L'exemple suivant excède la largeur d'impression, mais Prettier l'imprime malgré tout sur une seule ligne :

import { CollectionDashboard } from "../components/collections/collection-dashboard/main";

Cela peut surprendre, mais cela répond à une demande courante de conserver les import avec un seul élément sur une ligne. Idem pour les appels require.

Fonctions de test

Une autre demande fréquente est de conserver les descriptions de tests longues sur une seule ligne, même si cela excède la limite. Dans ces cas, diviser les arguments sur de nouvelles lignes n'aide guère.

describe("NodeRegistry", () => {
it("makes no request if there are no nodes to prefetch, even if the cache is stale", async () => {
// The above line exceeds the print width but stayed on one line anyway.
});
});

Prettier traite spécialement les fonctions communes de frameworks de tests comme describe, it et test.

JSX

Prettier formate différemment le JSX comparé au JavaScript standard :

function greet(user) {
return user
? `Welcome back, ${user.name}!`
: "Greetings, traveler! Sign up today!";
}

function Greet({ user }) {
return (
<div>
{user ? (
<p>Welcome back, {user.name}!</p>
) : (
<p>Greetings, traveler! Sign up today!</p>
)}
</div>
);
}

Pour deux raisons.

Premièrement, beaucoup de développeurs entourent déjà leur JSX de parenthèses, notamment dans les return. Prettier suit cette convention.

Deuxièmement, ce formatage facilite l'édition du JSX. Il est facile d'oublier un point-virgule. Contrairement au JS standard, un point-virgule résiduel en JSX peut apparaître comme texte visible sur votre page.

<div>
<p>Greetings, traveler! Sign up today!</p>; {/* <-- Oops! */}
</div>

Commentaires

Concernant le contenu des commentaires, Prettier peut peu faire. Les commentaires peuvent contenir du texte, du code désactivé ou des diagrammes ASCII. Leur nature libre empêche tout formatage automatique cohérent. Seules les annotations JSDoc (commentaires en bloc avec chaque ligne commençant par *) voient leur indentation ajustée.

La position des commentaires pose un défi complexe. Prettier tente de les conserver à leur emplacement d'origine, mais la flexibilité de placement rend cette tâche ardue.

Privilégiez les commentaires sur leurs propres lignes plutôt qu'en fin de ligne. Préférez // eslint-disable-next-line à // eslint-disable-line.

Notez que les "commentaires magiques" comme eslint-disable-next-line ou $FlowFixMe peuvent nécessiter un déplacement manuel lorsque Prettier divise une expression sur plusieurs lignes.

Imaginez ce code :

// eslint-disable-next-line no-eval
const result = safeToEval ? eval(input) : fallback(input);

Puis vous ajoutez une condition :

// eslint-disable-next-line no-eval
const result = safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Prettier produira :

// eslint-disable-next-line no-eval
const result =
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Le commentaire eslint-disable-next-line n'est alors plus actif. Dans ce cas, déplacez manuellement le commentaire :

const result =
// eslint-disable-next-line no-eval
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Si possible, privilégiez les commentaires qui agissent sur des plages de lignes (par ex. eslint-disable et eslint-enable) ou au niveau des instructions (par ex. /* istanbul ignore next */), ils sont encore plus sûrs. Il est possible d'interdire les commentaires eslint-disable-line et eslint-disable-next-line en utilisant eslint-plugin-eslint-comments.

Clause de non-responsabilité concernant la syntaxe non standard

Prettier est souvent capable de reconnaître et de formater une syntaxe non standard telle que des propositions à un stade précoce d'ECMAScript et des extensions de syntaxe Markdown non définies par une spécification quelconque. Le support pour une telle syntaxe est considéré comme étant fait de notre mieux et expérimental. Des incompatibilités peuvent être introduites dans toute version et ne doivent pas être considérées comme des changements cassants.

Clause de non-responsabilité concernant les fichiers générés automatiquement

Certains fichiers, comme package.json ou composer.lock, sont générés automatiquement et régulièrement mis à jour par le gestionnaire de paquets. Si Prettier utilisait les mêmes règles de formatage JSON que pour les autres fichiers, cela créerait régulièrement des conflits avec ces autres outils. Pour éviter cet inconvénient, Prettier utilisera sur ces fichiers un formateur basé sur JSON.stringify à la place. Vous pourriez remarquer des différences, comme la suppression des espaces verticaux, mais ce comportement est intentionnel.

Ce dont Prettier ne se préoccupe pas

Prettier se contente d'imprimer le code. Il ne le transforme pas. Ceci afin de limiter la portée de Prettier. Concentrons-nous sur l'impression et faisons-le vraiment bien !

Voici quelques exemples de choses qui ne relèvent pas du domaine de Prettier :

  • Transformer les chaînes entre guillemets simples ou doubles en littéraux de gabarit ou vice versa.

  • Utiliser + pour diviser les littéraux de chaînes longs en parties tenant dans la largeur d'impression.

  • Ajouter/supprimer {} et return là où ils sont facultatifs.

  • Transformer ?: en instructions if-else.

  • Trier/déplacer les imports, les clés d'objet, les membres de classe, les clés JSX, les propriétés CSS ou quoi que ce soit d'autre. En plus d'être une transformation plutôt qu'une simple impression (comme mentionné ci-dessus), le tri est potentiellement dangereux à cause des effets de bord (pour les imports, par exemple) et rend difficile la vérification de l'objectif le plus important : la correction.