Aller au contenu principal

Prettier 1.4 : Prise en charge de TypeScript et CSS

· 17 minutes de lecture
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 →

Cette version introduit le support des langages TypeScript, CSS, Less et SCSS dans Prettier !

prettier-revolution-conf

Prise en charge de TypeScript

C'est la fonctionnalité la plus demandée pour prettier. Avec la version 1.4.0, vous pouvez désormais utiliser prettier pour formater vos fichiers .ts et .tsx !

Prettier fonctionne en utilisant ces projets pour générer une représentation AST du code avant de l'imprimer. Babylon (l'analyseur qui alimente Babel) et Flow produisent tous deux un AST suivant approximativement le format estree pour les parties JavaScript, avec des nœuds spécifiques pour les éléments propres à Flow.

TypeScript, comme Flow, introduit des nœuds spéciaux pour sa syntaxe spécifique. Malheureusement, il ne respecte pas le format estree pour le reste du langage JavaScript. Cela place prettier dans une situation délicate car nous devrions essentiellement le forker complètement pour prendre en charge TypeScript.

Cette incompatibilité d'AST n'est pas un nouveau problème, comme l'a rencontré un autre projet : ESLint. Avec un AST différent, aucune règle ESLint ne fonctionne. Heureusement pour nous, @JamesHenry et @soda0289 ont créé typescript-eslint-parser, qui convertit un AST TypeScript en format estree - exactement ce dont prettier a besoin !

Après l'intégration de ce projet dans prettier, @azz, @despairblue et @Pajn ont implémenté tous les nœuds spécifiques à TypeScript et validé les 13k tests de la suite TypeScript. Ce fut un énorme travail qui est enfin prêt à l'emploi :)

Nous avons testé prettier sur les plus gros projets TypeScript de GitHub pour garantir un formatage correct. Nous n'avons pas encore optimisé tous les aspects du formatage, alors si vous remarquez quelque chose d'étrange, ouvrez une issue !

Prise en charge de CSS, Less et SCSS

Si TypeScript était la fonctionnalité open source la plus demandée, CSS était la priorité numéro un des ingénieurs Facebook. Une fois habitué à formater son code dans un langage, on veut le faire partout !

Le CSS s'est avéré être un langage bien plus petit que JavaScript, et son support n'a pris que quelques jours. Nous utilisons postcss par @ai comme analyseur sous-jacent, capable de traiter CSS, Less et SCSS. Nous dépendons aussi de postcss-values-parser, postcss-selector-parser par @ben-eb et postcss-media-query-parser par @dryoma.

Malheureusement, postcss ne prend pas encore en charge Sass ni Stylus. Nous serions ravis de les ajouter si quelqu'un se charge de leur implémentation.

Notez que prettier se limite actuellement au formatage du code : il ne respecte pas encore d'options comme singleQuote et n'effectue pas de normalisation des couleurs ou des nombres comme pour JavaScript.

Intégration avec les éditeurs

La première phase du projet visait à produire un code correct et bien formaté. Maintenant que cet objectif est atteint, nous pouvons améliorer les intégrations. Nous venons d'ajouter deux fonctionnalités majeures : le maintien de la position du curseur et le formatage partiel d'un fichier.

Notez que nous venons tout juste d'ajouter cette prise en charge dans Prettier lui-même, et aucune intégration d'éditeur ne l'utilise encore. De plus, nous ne l'avons pas vraiment testée en pratique, nous allons donc probablement devoir corriger certains problèmes !

Ajout de l'option cursorOffset pour le déplacement du curseur (#1637) par @josephfrazier

Actuellement, nous laissons les éditeurs déterminer la position du curseur, ce qu'ils font assez correctement. Mais comme nous imprimons le code, nous pouvons fournir la position exacte !

Ajout des options --range-start/end pour formater uniquement des parties de l'entrée (#1609) par @josephfrazier

Cette fonctionnalité est très fréquemment demandée. Actuellement, Prettier ne formate que le fichier entier. Désormais, il est possible de formater une plage spécifique.

Cela fonctionne en remontant dans l'AST pour trouver l'instruction la plus proche. Ainsi, vous n'avez pas besoin de sélectionner exactement la plage valide. Il suffit de sélectionner approximativement la zone du code à reformater, et cela fonctionnera !

Ajout de l'option filepath pour permettre l'inférence du type de fichier (#1835) par @mitermayer

Comme nous formatons maintenant CSS et TypeScript, il n'est pas pratique de devoir spécifier le parseur pour chaque fichier. Vous pouvez désormais passer le chemin du fichier sur lequel vous travaillez, et Prettier lira l'extension et déterminera le bon parseur à utiliser.

Principales fonctionnalités

Retour à la ligne du contenu texte dans JSX (#1120, #1671, #1827, #1829) par @karl

Le principal problème restant avec Prettier lors de l'impression de JSX concernait le traitement du texte. Le comportement précédent ajoutait un vilain {" "} et laissait les lignes trop longues intactes. Désormais, nous traitons chaque mot comme un jeton et pouvons assurer un flux correct.

C'est un travail remarquable de @karl car non seulement il a implémenté la fonctionnalité, mais il a aussi introduit une nouvelle primitive dans Prettier pour imprimer une séquence d'éléments et revenir à la ligne dès qu'un élément atteint la marge.

// 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>

Suppression des parenthèses pour JSX dans les fonctions fléchées (#1733) par @xixixao

Les développeurs de composants fonctionnels vont apprécier ce changement. Nous ne mettons plus de parenthèses pour les fonctions fléchées qui retournent du 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>;

Amélioration de l'impression des littéraux de gabarit (#1664, #1714) par @josephfrazier

L'impression des littéraux de gabarit a toujours posé beaucoup de difficultés à Prettier. Avec la version 1.3.0, nous avons considérablement amélioré la situation, et avec cette version, je pense que nous gérons toutes les situations courantes de manière satisfaisante.

Pour contourner les problèmes, nous avions ajouté un utilitaire qui supprimait les lignes vides de la sortie, mais cela produisait parfois des résultats vraiment étranges ; cela a maintenant disparu. Une autre amélioration : au lieu d'indenter au niveau du ${, nous indentons désormais au début de la ligne contenant ${.

Faites-nous savoir si vous rencontrez toujours des problèmes avec le rendu des littéraux de gabarit après cette version !

// 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;
`

Utiliser les mêmes règles de rupture pour les assignations et les valeurs d'objet (#1721)

Nous disposons d'une logique fine pour déterminer comment interrompre les expressions après une assignation (ex: a = ...). Nous appliquons désormais la même logique aux valeurs d'objet. Cela devrait améliorer la présentation des logiques booléennes multi-lignes ou des conditionnels volumineux. C'est aussi un bon exemple d'harmonisation de notre formateur.

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

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

Indenter les conditions à l'intérieur de !() (#1731)

Nous recevions régulièrement des retours sur le rendu précédent, classé dans la catégorie "probablement difficile à corriger, à vérifier plus tard". Finalement, la solution s'est avérée très simple : la voilà !

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

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

Corrections de formatage

Placer les corps de boucle sur la même ligne quand possible (#1498)

Nous le faisions déjà pour les instructions if, nous devons être cohérents et l'appliquer aussi aux boucles.

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

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

Corriger les lignes vides avec les unions Flow (#1511) par @existentialism

Pas besoin d'indenter deux fois ;)

// 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 }
>;

Ne pas mettre de parenthèses pour un argument unique avec commentaire de fin de ligne (#1518)

Notre détection des fonctions fléchées sans parenthèses vérifait uniquement la présence de commentaires, mais nous devons uniquement considérer les commentaires en ligne comme (/* comment */ num), pas ceux de fin de ligne.

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

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

Ne pas indenter les ternaires imbriqués (#1822)

Cela évite une indentation apparente de 4 caractères au lieu de deux. L'inconvénient est qu'une condition multi-ligne ne sera pas alignée correctement, mais c'est un compromis acceptable. Les ternaires imbriqués ont généralement des conditions courtes.

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

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

Aligner les conditionnels chaînés dans les attributs JSX (#1519)

Aucune ambiguïté ne peut survenir après ces blocs, donc l'indentation supplémentaire est inutile.

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

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

Déséchapper les caractères inutilement échappés dans les chaînes (#1575) par @josephfrazier

Nous optimisons déjà les chaînes de plusieurs manières - cette petite amélioration supprime les \ superflus.

// Before
a = 'hol\a';

// After
a = 'hola';

Corriger le rendu des objets vides dans les expressions booléennes (#1590) par @dmitrika

Nous alignions les objets dans les expressions booléennes car il paraît étrange d'avoir un { seul sur sa ligne, mais cela produisait un rendu étrange pour les objets vides. Ils restent désormais sur leur propre ligne s'ils sont vides.

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

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

Supprimer les parenthèses des SequenceExpressions dans les ForStatements (#1597) par @k15a

Les assignations multiples dans les boucles for sont courantes - nous n'ajoutons plus de parenthèses.

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

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

Ne pas aligner les flèches quand l'argument a un commentaire en tête (#1660)

Les commentaires de bloc dans les fonctions fléchées ne perturbent plus le formatage !

// 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 =>

Correction des derniers commentaires dans les imports (#1677)

Encore un endroit nécessitant une logique spéciale pour les commentaires !

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

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

Gestion des commentaires dans les chaînes de membres (#1686, #1691)

Nous avions déjà traité certains placements et ajoutions constamment de nouveaux emplacements potentiels. Désormais, nous adoptons une approche plus générale. Ces problèmes ne devraient plus se reproduire à l'avenir.

// 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);

Utilisation de expandLast pour les fonctions fléchées imbriquées (#1720)

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

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

Placement des commentaires JSX à l'intérieur des parenthèses (#1712)

Cela affecte principalement les ingénieurs de Facebook où nous ajoutons automatiquement $FlowFixMe lors des mises à jour de Flow. Désormais, ces commentaires restent intacts.

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

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

Forçage de \n pour les déclarations multiples de variables (#1723)

Très souvent demandé : auparavant, nous ne forçions le saut de ligne que si la ligne dépassait 80 colonnes. Maintenant, nous le faisons systématiquement lorsqu'au moins une déclaration comporte une assignation.

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

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

Mise en ligne de | null et | void (#1734)

La version développée des unions Flow est lisible avec plusieurs objets mais paraît étrange pour la nullabilité. Nous mettons désormais en ligne | null et | void.

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

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

Saut de ligne sur implements plutôt que extends (#1730)

Nous ne cassons plus sur extends. Les classes avec des extensions longues apparaîtront moins étranges.

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

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

Mise en ligne des imports uniques (#1729)

Tout comme pour les appels require longs, nous ne forçons plus de saut de ligne dans les import lorsqu'un seul élément est importé.

// Before
import somethingSuperLongsomethingSuperLong
from "somethingSuperLongsomethingSuperLongsomethingSuperLong";

// After
import somethingSuperLongsomethingSuperLong from "somethingSuperLongsomethingSuperLongsomethingSuperLong";

Ajout de la possibilité de casser pour SequenceExpression (#1749)

Saviez-vous qu'en l'absence d'instructions, vous pourriez utiliser () au lieu de {} et , au lieu de ; ? Certains exploitent ceci dans les retours de fonctions fléchées. Bien que non recommandé, c'est facile à supporter dans Prettier ¯\_(ツ)_/¯

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

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

Suppression du saut de ligne forcé dans les corps de boucles vides (#1815)

Les boucles au corps vide n'affichent plus leurs {} sur deux lignes distinctes.

// Before
while (true) {
}

// After
while (true) {}

Préservation des lignes vides entre les cas d'un switch commentés (#1708)

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

// After
switch (true) {
case true:

// Good luck getting here
case false:
}

Correction sémantique

Suppression de ast-types (#1743, #1744, #1745, #1746, #1747)

Nous avions l'habitude de localiser les commentaires en parcourant l'AST via les définitions d'ast-types. Cela causait parfois des problèmes lorsqu'un champ n'était pas déclaré : nous ne trouvions pas le nœud concerné, ce qui entraînait soit un placement incorrect des commentaires soit une erreur. Il s'avère que nous n'avons pas besoin de maintenir cette correspondance et pouvons simplement parcourir les objets - si un élément possède un champ type, c'est un nœud.

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

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

Préserver les espaces Unicode inhabituels (#1658, #1165) par @karl et @yamafaktory

Si vous ajoutiez des caractères invisibles dans du texte JSX, nous les remplacions par des espaces standards. Bien que l'utilité de cette pratique soit discutable, nous les conservons désormais tels quels !

Empêcher l'échappement des commentaires de fin dans les littéraux de gabarit (#1580, #1713, #1598, #1713) par @josephfrazier et @k15a

Nous utilisions un code complexe (et peu fiable) pour gérer les commentaires dans les littéraux de gabarit. Une solution élégante avait été implémentée pour les expressions JSX {} : créer une limite avant le } final et, si des commentaires restent non imprimés, les écrire en bloc avant d'ajouter un \n et le }. Cette logique est désormais appliquée aux littéraux de gabarit :)

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

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

Parenthéser correctement await (#1513, #1595, #1593) par @bakkot et @existentialism

Plutôt qu'une méthode automatisée, nous spécifions manuellement toutes les combinaisons de nœuds nécessitant des parenthèses. Cette approche laisse probablement subsister des cas marginaux complexes. Pour await, nous avons considérablement renforcé la gestion en ajoutant les parenthèses nécessaires et en supprimant celles superflues.

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

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

Préserver les informations getter/setter pour les ObjectTypeProperty de flow (#1585) par @josephfrazier

Un autre cas marginal que nous n'avions pas correctement traité !

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

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

Ajouter des parenthèses pour les types à argument unique avec génériques (#1814)

Encore une subtilité de parenthésage que nous avions négligée !

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

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

Retour au mode non strict dans babylon (#1587, #1608) par @josephfrazier

Nous voulons que Prettier puisse analyser tout JavaScript existant. Pour babylon, nous devions choisir entre le mode strict ou non. Le mode strict étant majoritaire, nous l'avions privilégié. Cependant, face à des littéraux octaux comme 0775 (invalides en mode strict), nous tentons désormais une seconde analyse en mode non strict. Nous acceptons également return hors fonction, valide dans les fichiers Node.

// Before
SyntaxError

// After
return 0775;

Interface en ligne de commande

Autoriser l'utilisation conjointe de --write et --list-different (#1633)

Cette combinaison est utile pour créer un hook de commit indiquant à l'utilisateur ce qui a réellement changé via une seule commande.

Ignorer node_modules lors de l'exécution de Prettier en CLI (#1683) par @thymikee

Il est très facile d'exécuter accidentellement Prettier sur node_modules/, ce que vous ne souhaitez presque jamais faire. Nous désactivons donc ce comportement par défaut et ajoutons une option --with-node-modules pour les cas particuliers.

Parcourir les fichiers cachés avec les globs (#1844) par @jhgg

Nous avons activé le parcours des fichiers pointés (dotfiles) dans la bibliothèque de globs utilisée. Ainsi, * inclura désormais .eslintrc.