Aller au contenu principal

7.7.0 est disponible : Récupération d'erreurs et TypeScript 3.7

· 11 min 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 →

Aujourd'hui, nous publions Babel 7.7.0 !

Cette version inclut de nouvelles fonctionnalités de parseur comme l'attente de haut niveau (await x(), Stage 3) et les déclarations d'énumération Flow (enum) (proposition Flow). Désormais, @babel/parser peut récupérer après certaines erreurs de syntaxe !

Nous avons également ajouté la prise en charge de TypeScript 3.7 : Babel peut analyser et transformer les champs privés de classe avec annotations de type, les annotations de champs publics de classe définies avec le mot-clé declare, les signatures de fonctions d'assertion de type et les littéraux de gabarit dans les déclarations enum.

Babel comprend désormais trois nouveaux fichiers de configuration : babel.config.json, babel.config.cjs et .babelrc.cjs, qui se comportent comme les fichiers babel.config.js et .babelrc.js.

Enfin, Babel 7.7.0 utilise 20 % de mémoire en moins que la version 7.6.0.

Vous pouvez consulter l'intégralité du changelog sur GitHub.


Un grand merci à Alejandro Sánchez, Chris Garrett, 彭驰, Daniel Arthur Gallagher, ExE-Boss, Eugene Myunster, Georgii Dolzhykov, Gerald, Linus Unnebäck, Martin Forsgren, Matthew Whitworth, Micah Zoltu, Mohammad Ahmadi et Samuel Kwok pour leurs premières contributions (PRs) !

Cette version a également été rendue possible grâce à la collaboration avec d'autres projets open source : merci à Devon Govett (Parcel) pour l'implémentation de la prise en charge des fichiers babel.config.json, et à George Zahariev (Flow) pour l'ajout des déclarations Flow enum à @babel/parser !

Un remerciement spécial à Bloomberg pour avoir organisé un Hackaton Open Source encourageant ses ingénieurs à contribuer à la communauté ! En particulier Robin Ricard et Jaideep Bhoosreddy qui travaillent activement à automatiser les tests des transformations Babel contre la suite Test262.

Si vous ou votre entreprise souhaitez soutenir Babel et l'évolution de JavaScript mais ne savez pas comment procéder, vous pouvez nous faire un don sur OpenCollective et, mieux encore, travailler directement avec nous sur l'implémentation des nouvelles propositions ECMAScript ! En tant que projet porté par des bénévoles, nous comptons sur le soutien de la communauté pour financer nos efforts visant à prendre en charge la large gamme d'utilisateurs JavaScript et à assumer la propriété du code. Contactez Henry à l'adresse henry@babeljs.io si vous souhaitez en discuter !

Analyse syntaxique de await de niveau supérieur (#10449)

La proposition top-level await permet d'utiliser await sur des promesses dans des modules comme s'ils étaient encapsulés dans une grande fonction asynchrone. Cela est utile par exemple pour charger conditionnellement une dépendance ou effectuer l'initialisation d'une application :

JavaScript
// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);

// Resource initialization
const connection = await dbConnector();

@babel/parser prenait déjà en charge l'utilisation de await en dehors des fonctions asynchrones via l'option allowAwaitOutsideFunction depuis la version 7.0.0.

La version 7.7.0 introduit un nouveau plugin d'analyse syntaxique topLevelAwait avec des différences clés :

  • Il autorise await uniquement au niveau supérieur des modules (pas dans les scripts), comme le stipule la proposition. Ceci est nécessaire car les systèmes de modules synchrones (comme CommonJS) ne peuvent gérer des dépendances asynchrones.

  • Il permet de détecter le bon sourceType quand sourceType: "unambiguous" est utilisé. Notez que await étant un identifiant valide dans les scripts, de nombreuses constructions apparemment non ambiguës restent équivoques : Babel les analysera comme des scripts. Par exemple, await -1 pourrait être soit une expression await attendant -1, soit une soustraction entre await et 1.

Si vous utilisez @babel/parser directement, activez le plugin topLevelAwait :

JavaScript
parser.parse(inputCode, {
plugins: ["topLevelAwait"]
});

Nous avons également créé le package @babel/plugin-syntax-top-level-await à ajouter à votre configuration Babel :


module.exports = {
plugins: [
"@babel/plugin-syntax-top-level-await"
]
}

Notez que l'usage de await de niveau supérieur nécessite le support de votre module bundler. Babel n'effectue aucune transformation : avec Rollup, activez experimentalTopLevelAwait ; webpack 5 supporte experiments.topLevelAwait.

Dès cette version, @babel/preset-env activera automatiquement @babel/plugin-syntax-top-level-await si le caller le supporte. Remarque : babel-loader et rollup-plugin-babel ne signalent pas encore ce support à Babel, mais nous collaborons avec leurs mainteneurs.

Récupération d'erreurs dans l'analyse syntaxique (#10363)

Comme beaucoup d'analyseurs JavaScript, @babel/parser lance une erreur dès qu'une syntaxe invalide est rencontrée. Ce comportement convient à Babel car transformer un programme JavaScript nécessite une entrée valide.

Vu la popularité de Babel, de nombreux outils (notamment babel-eslint et Prettier) utilisent @babel/parser. Pour eux, un analyseur abandonnant à la première erreur est sous-optimal.

Prenons ce code invalide à cause d'une propriété __proto__ dupliquée :

JavaScript
let a = {
__proto__: x,
__proto__: y
}

let a = 2;

Le workflow actuel avec ESLint et Prettier est :

  1. Prettier ne peut formater le fichier

  2. ESLint signale une erreur Redefinition of __proto__ property

  3. Vous supprimez la seconde propriété __proto__

  4. Prettier ne peut formater le fichier

  5. ESLint signale l'erreur Identifier 'a' has already been declared

  6. Vous supprimez le second mot-clé let

  7. Prettier formate le fichier

Ne serait-il pas préférable d'avoir ceci ?

  1. Prettier formate le fichier

  2. ESLint signale deux erreurs : Redefinition of __proto__ property et Identifier 'a' has already been declared

  3. Vous supprimez la seconde propriété __proto__ et le second mot-clé let

Dans cette version, nous ajoutons une nouvelle option à @babel/parser : errorRecovery. Lorsqu'elle est activée, l'AST résultant contiendra une propriété errors listant toutes les erreurs dont @babel/parser a pu récupérer :

JavaScript
const input = `
let a = {
__proto__: x,
__proto__: y
}

let a = 2;
`;

parser.parse(input); // Throws "Redefinition of __proto__ property"

const ast = parser.parse(input, { errorRecovery: true });
ast.errors == [
SyntaxError: "Redefinition of __proto__ property",
SyntaxError: "Identifier 'a' has already been declared",
];

@babel/parser peut encore générer des exceptions car toutes les erreurs ne sont pas actuellement récupérables. Nous continuerons d'améliorer ces cas !

Nouvelles extensions de fichiers de configuration (#10501, #10599)

Babel 6 ne supportait qu'un seul fichier de configuration : .babelrc, dont le contenu devait être spécifié en JSON.

Babel 7 a modifié la signification des .babelrc et introduit deux nouveaux fichiers de configuration : babel.config.js et .babelrc.js (vous pouvez lire la différence entre eux dans la documentation). Nous avons ajouté des fichiers de configuration en JavaScript pour permettre une logique personnalisée lors de l'activation/désactivation de plugins/options.

Cependant, un avantage majeur des fichiers JSON est leur meilleure capacité de mise en cache. Un même fichier JavaScript peut produire des valeurs différentes lors de deux exécutions, tandis qu'un fichier JSON garantit toujours d'évaluer au même objet. De plus, les configurations JSON sont facilement sérialisables, contrairement aux valeurs JavaScript comme les fonctions ou objets avec données implicites.

Notez que Babel met également en cache les transformations avec des configurations JavaScript, mais le fichier de configuration doit être évalué (pour vérifier la validité du cache) et le cache doit être configuré manuellement.

Pour ces raisons, Babel 7.7.0 introduit le support d'un nouveau fichier de configuration : babel.config.json, dont le comportement est identique à babel.config.js.

Nous avons également ajouté le support de deux autres fichiers : babel.config.cjs et .babelrc.cjs, à utiliser avec l'option "type": "module" de Node.js dans package.json (car Babel ne supporte pas les modules ECMAScript dans les fichiers de configuration). Hormis cette différence de "type": "module", ils se comportent exactement comme babel.config.js et .babelrc.js.

TypeScript 3.7 (#10543, #10545)

TypeScript 3.7 RC inclut le support du chaînage optionnel, de l'opérateur de coalescence des nuls, des fonctions d'assertion, des déclarations de champs de classe typés uniquement et bien d'autres fonctionnalités liées aux types.

Le chaînage optionnel (a?.b) et la coalescence des nuls (a ?? b) sont supportés dans Babel depuis la 7.0.0 via @babel/plugin-proposal-optional-chaining et @babel/plugin-proposal-nullish-coalescing-operator.

Dans Babel 7.7.0, vous pouvez désormais utiliser les fonctions d'assertion et declare dans les champs de classe :

function assertString(x): assert x is string {
if (typeof x !== "string") throw new Error("It must be a string!");
}

class Developer extends Person {
declare usingBabel: boolean;
}

Pour éviter les changements cassants, nous avons introduit le support de declare dans les champs de classe derrière un drapeau : "allowDeclareFields", supporté par @babel/plugin-transform-typescript et @babel/preset-typescript. Ce comportement deviendra probablement la valeur par défaut, il est donc recommandé de migrer votre configuration :

{
"presets": [
["@babel/preset-typescript", {
"allowDeclareFields": true
}]
]
}

Utiliser l'opérateur spread dans le JSX compilé (#10572)

Lors de l'utilisation des propriétés étendues (spread) dans les éléments JSX, Babel injecte par défaut une fonction utilitaire :

JSX
<a x {...y} />

// 🡇 🡇 🡇

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

React.createElement("a", _extends({
x: true
}, y));

En 2016, avec l'amélioration du support natif d'ES6, nous avons ajouté l'option useBuiltIns à @babel/plugin-transform-react-jsx permettant à la sortie compilée d'utiliser directement Object.assign et de supprimer le code excédentaire :

JSX
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", Object.assign({
x: true
}, y));

Cependant, grâce à la prise en charge native de la propagation d'objets, cela nous permet de produire un code encore plus optimisé :

JSX
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", { x: true, ...y });

Vous pouvez l'activer en utilisant l'option useSpread avec soit @babel/preset-react soit @babel/plugin-transform-react-jsx :

{
presets: [
["@babel/react", { useSpread: true }]
]
}

Améliorations de l'utilisation mémoire (#10480)

Depuis le début, nous avons fait des efforts (#433, #3475, #7028, etc.) pour améliorer les performances. Babel 7.7.0 utilise désormais 20 % de mémoire en moins et transforme les fichiers volumineux 8 % plus vite par rapport à la version 7.6.0.

Pour parvenir à ces résultats, nous avons optimisé différentes opérations effectuées durant le cycle de vie des objets NodePath (utilisés pour encapsuler chaque nœud AST) :

  1. Nous évitons désormais d'initialiser certaines propriétés d'objet rarement utilisées jusqu'à ce qu'elles soient nécessaires, ce qui nous permet d'éviter une allocation Object.create(null) pour presque chaque nœud AST.

  2. Nous avons réduit la charge de suivi pour chaque visite de nœud en remplaçant quelques propriétés peu communes par des accesseurs (getters), afin que @babel/traverse puisse ignorer leur mise à jour.

  3. Nous avons optimisé l'utilisation mémoire en compressant plusieurs propriétés booléennes utilisées pour représenter l'état d'un parcours de nœud (c.-à-d. ignoré, arrêté ou supprimé) dans un tableau de bits.

Toutes ces améliorations se traduisent par la différence suivante en termes de performances de transformation et d'utilisation mémoire :

PerformanceMemory usage

Vous pouvez également consulter les données brutes des graphiques ci-dessus. Si vous souhaitez en savoir plus sur ce sujet, vous pouvez lire le document détaillé de Jùnliàng à propos des changements qu'il a apportés pour obtenir ces améliorations !