Moderniser l'infrastructure CSS dans les outils de développement

Actualisation de l'architecture des outils de développement: modernisation de l'infrastructure CSS dans les outils de développement

Cet article fait partie d'une série d'articles de blog décrivant les modifications que nous apportons à l'architecture des outils de développement et leur conception. Nous expliquons l'historique du fonctionnement du CSS dans les outils de développement et la façon dont nous l'avons modernisée en vue de la migration (à terme) vers une solution standard Web permettant de charger des CSS dans des fichiers JavaScript.

Ancienne version du CSS dans les outils de développement

Les outils de développement implémentent le CSS de deux manières différentes: une pour les fichiers CSS utilisés dans l'ancienne partie des outils de développement, et une pour les composants Web modernes utilisés dans les outils de développement.

L'implémentation du CSS dans les outils de développement a été définie il y a de nombreuses années et est désormais obsolète. Les outils de développement ont continué à utiliser le modèle module.json, et la suppression de ces fichiers a demandé beaucoup d'efforts. La section resources, qui permet de charger les fichiers CSS, constitue le dernier obstacle à la suppression de ces fichiers.

Nous souhaitions prendre le temps d'explorer différentes solutions qui pourraient, à terme, se transformer en scripts de module CSS. L'objectif était d'éliminer les contraintes techniques liées à l'ancien système, mais aussi de faciliter le processus de migration vers les scripts de module CSS.

Tous les fichiers CSS qui se trouvaient dans les outils de développement étaient considérés comme des "anciens", car ils étaient chargés à l'aide d'un fichier module.json, qui est en cours de suppression. Tous les fichiers CSS doivent être listés sous resources dans un fichier module.json situé dans le même répertoire que le fichier CSS.

Exemple de fichier module.json restant:

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

Ces fichiers CSS remplissent ensuite une carte d'objets globale appelée Root.Runtime.cachedResources, qui correspond à un mappage entre un chemin et leur contenu. Pour ajouter des styles dans les outils de développement, vous devez appeler registerRequiredCSS avec le chemin d'accès exact du fichier que vous souhaitez charger.

Exemple d'appel registerRequiredCSS:

constructor() {
  …
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  …
}

Le contenu du fichier CSS est ainsi récupéré et inséré en tant qu'élément <style> dans la page à l'aide de la fonction appendStyle :

Fonction appendStyle qui ajoute du code CSS à l'aide d'un élément de style intégré:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

Lorsque nous avons introduit les composants Web modernes (utilisant des éléments personnalisés), nous avons initialement décidé d'utiliser le code CSS via les balises <style> intégrées dans les fichiers de composants eux-mêmes. Ce phénomène s'est traduit par des défis spécifiques:

  • Manque de prise en charge de la mise en surbrillance de la syntaxe. Les plug-ins qui proposent la coloration syntaxique pour le CSS intégré ne sont généralement pas aussi performants que les fonctionnalités de coloration syntaxique et de saisie semi-automatique pour CSS écrites dans des fichiers .css.
  • Augmentez les performances. Le langage CSS intégré nécessitait également la nécessité de deux passes pour le linting: une pour les fichiers CSS et une pour le CSS intégré. Nous pourrions éliminer ce problème de performances si tous les CSS étaient écrits dans des fichiers CSS autonomes.
  • Défi de la minimisation. La taille du code CSS intégré n'a pas pu être réduite, de sorte qu'aucune taille de CSS n'a été réduite. La taille du fichier du build des outils de développement a également été augmentée par le doublon du CSS introduit par plusieurs instances du même composant Web.

L'objectif de mon projet de stage était de trouver une solution pour l'infrastructure CSS compatible avec l'ancienne infrastructure et les nouveaux composants Web utilisés dans les outils de développement.

Rechercher des solutions potentielles

Le problème peut être divisé en deux parties:

  • Déterminer comment le système de compilation gère les fichiers CSS
  • Comprendre comment les fichiers CSS sont importés et utilisés par les outils de développement.

Nous avons examiné différentes solutions potentielles pour chaque partie, détaillées ci-dessous.

Importer des fichiers CSS

L'objectif de l'importation et de l'utilisation de CSS dans les fichiers TypeScript était de respecter le plus possible les normes Web, d'assurer la cohérence dans l'ensemble des outils de développement et d'éviter les doublons de CSS dans notre code HTML. Nous voulions également pouvoir choisir une solution qui nous permettrait d'adopter les nouvelles normes des plates-formes Web, telles que les scripts de module CSS.

C'est pourquoi les instructions @import et les balises ne semblaient pas adaptées aux outils de développement. Elles ne seraient pas uniformes avec les importations dans le reste des outils de développement et donneraient lieu à un Flash of Unstyled Content (FOUC). La migration vers les scripts de module CSS serait plus difficile, car les importations devraient être explicitement ajoutées et traitées différemment qu'avec les balises <link>.

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

Solutions potentielles utilisant @import ou <link>.

Au lieu de cela, nous avons trouvé un moyen d'importer le fichier CSS en tant qu'objet CSSStyleSheet afin de l'ajouter à Shadow Dom (DevTools utilise Shadow DOM depuis quelques années maintenant) à l'aide de sa propriété adoptedStyleSheets.

Options de bundler

Nous avions besoin d'un moyen de convertir les fichiers CSS en un objet CSSStyleSheet afin de pouvoir le manipuler facilement dans le fichier TypeScript. Nous avons considéré à la fois Rollup et webpack comme des bundlers potentiels pour effectuer cette transformation à notre place. Les outils de développement utilisent déjà la propriété de consolidation dans leur build de production, mais l'ajout de l'un ou l'autre de ces bundles au build de production peut entraîner des problèmes de performances potentiels avec notre système de compilation actuel. Notre intégration au système de compilation GN de Chromium rend le regroupement plus difficile. Par conséquent, les bundlers ont tendance à ne pas s'intégrer bien au système de compilation Chromium actuel.

À la place, nous avons exploré la possibilité d'utiliser le système de compilation GN actuel pour effectuer cette transformation à notre place.

Nouvelle infrastructure d'utilisation de CSS dans les outils de développement

La nouvelle solution consiste à utiliser adoptedStyleSheets pour ajouter des styles à un Shadow DOM particulier tout en utilisant le système de compilation GN pour générer des objets CSSStyleSheet pouvant être adoptés par un document ou un ShadowRoot.

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  …
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

L'utilisation de adoptedStyleSheets présente plusieurs avantages, dont les suivants:

  • Elle est en train de devenir une norme Web moderne
  • Empêche le CSS en double
  • N'applique des styles qu'à un Shadow DOM et cela évite les problèmes causés par des noms de classe ou des sélecteurs d'ID en double dans les fichiers CSS.
  • Faciles à migrer vers les futures normes Web, telles que les scripts de module CSS et les assertions d'importation

La seule mise en garde à ce sujet était que les instructions import nécessitaient l'importation du fichier .css.js. Pour permettre au GN de générer un fichier CSS lors de la compilation, nous avons écrit le script generate_css_js_files.js. Le système de compilation traite désormais chaque fichier CSS et le transforme en fichier JavaScript qui exporte par défaut un objet CSSStyleSheet. C'est très pratique, car nous pouvons importer le fichier CSS et l'adopter facilement. De plus, nous pouvons désormais réduire facilement la taille du build de production, ce qui permet d'économiser la taille du fichier:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

Exemple généré iconButton.css.js à partir du script.

Migrer l'ancien code à l'aide de règles ESLint

Bien que la migration manuelle des composants Web puisse être effectuée facilement, le processus de migration des anciennes utilisations de registerRequiredCSS était plus complexe. Les deux principales fonctions ayant enregistré d'anciens styles étaient registerRequiredCSS et createShadowRootWithCoreStyles. La procédure de migration de ces appels étant assez mécanique, nous avons décidé d'utiliser des règles ESLint pour appliquer les corrections et migrer automatiquement le code ancien. Les outils de développement utilisent déjà un certain nombre de règles personnalisées spécifiques au codebase des outils de développement. Cela s'est avéré utile, car ESLint analyse déjà le code dans un arbre de syntaxe abstraite(abbr. AST), et nous pourrions interroger les nœuds d'appel spécifiques qui étaient des appels vers l'enregistrement du CSS.

Le plus gros problème que nous avons rencontré lors de la rédaction des règles ESLint de migration a été de capturer les cas extrêmes. Nous voulions nous assurer d'avoir le juste équilibre entre savoir quels cas extrêmes méritent d'être capturés et lesquels doivent être migrés manuellement. Nous voulions également nous assurer de pouvoir indiquer à un utilisateur qu'un fichier .css.js importé n'est pas généré automatiquement par le système de compilation, car cela permet d'éviter toute erreur de fichier introuvable lors de l'exécution.

L'un des inconvénients de l'utilisation de règles ESLint pour la migration était que nous ne pouvions pas modifier le fichier de compilation GN requis dans le système. Ces modifications devaient être effectuées manuellement par l'utilisateur dans chaque répertoire. Bien que cela nécessitait plus de travail, c'était un bon moyen de vérifier que chaque fichier .css.js importé est bien généré par le système de compilation.

Globalement, l'utilisation de règles ESLint pour cette migration nous a été très utile, car nous avons pu migrer rapidement l'ancien code vers la nouvelle infrastructure. Grâce à l'AST disponible facilement, nous avons pu gérer plusieurs cas particuliers dans la règle et les corriger automatiquement et de manière fiable à l'aide de l'API Fixer d'ESLint.

Quelles sont les prochaines étapes ?

Jusqu'à présent, tous les composants Web des outils pour les développeurs Chromium ont été migrés pour utiliser la nouvelle infrastructure CSS au lieu des styles intégrés. La plupart des anciennes utilisations de registerRequiredCSS ont également été migrées vers le nouveau système. Il ne vous reste plus qu'à supprimer autant de fichiers module.json que possible, puis à migrer cette infrastructure actuelle pour implémenter les scripts de module CSS à l'avenir.

Télécharger les canaux de prévisualisation

Nous vous conseillons d'utiliser Chrome Canary, Dev ou Beta comme navigateur de développement par défaut. Ces versions preview vous permettent d'accéder aux dernières fonctionnalités des outils de développement, de tester des API de pointe de plates-formes Web et de détecter les problèmes sur votre site avant qu'ils ne le fassent.

Contacter l'équipe des outils pour les développeurs Chrome

Utilisez les options suivantes pour discuter des nouvelles fonctionnalités et des modifications dans l'article, ou de toute autre question concernant les outils de développement.

  • Envoyez-nous une suggestion ou des commentaires via crbug.com.
  • Signalez un problème dans les outils de développement via Plus d'options   More > Aide > Signaler un problème dans les outils de développement dans les Outils de développement.
  • Envoyez un tweet à @ChromeDevTools.
  • Dites-nous en plus sur les nouveautés concernant les vidéos YouTube dans les outils de développement ou sur les vidéos YouTube de nos conseils relatifs aux outils de développement.