EnglishBac à sable

Résolution des composants

Une balise capitalisée sans liaison dans la portée est un nom que le compilateur peut résoudre. Indiquez-lui une fois où se trouve Button, et tous les templates peuvent utiliser <Button> sans import.

// Aucun import dans ce fichier.
export function Toolbar() {
  return (
    <Stack gap="3">
      <Button>Enregistrer</Button>
    </Stack>
  );
}

Ce n'est pas de l'auto-import au sens de l'éditeur : rien n'est écrit dans votre source. Le compilateur injecte l'import à la compilation, donc la sortie est exactement ce que vous auriez écrit à la main, et le bundler voit un import statique ordinaire.

Fluixi le faisait déjà pour Show, For, Portal et les composants du routeur. resolve est le même mécanisme, ouvert.

Configuration

Les règles vivent sur le plugin, car l'origine d'un composant est la même partout dans un projet :

// vite.config.ts
import { fluixi } from '@fluixi/vite-plugin';

export default defineConfig({
  plugins: [
    fluixi({
      resolve: [
        { components: { Button: '@ui/button', Stack: '@ui/layout' } },
      ],
    }),
  ],
});

Une bibliothèque de composants fournit sa propre carte, l'adoption tient donc en une ligne :

import { uiComponents } from '@fluixi-ui/resolver';

fluixi({ resolve: [uiComponents()] });

Quand la balise et l'export diffèrent

resolve: [
  { components: { Btn: { module: '@ui/button', export: 'Button' } } },
  // `export: 'default'` pour une organisation un fichier par composant
  { components: { Card: { module: './Card.js', export: 'default' } } },
];

Motifs

Une seule règle peut couvrir toute une famille. $1 et les suivants sont substitués depuis la correspondance :

resolve: [{ match: /^Icon(.+)$/, module: '@/icons/$1' }];

<IconTrash> se résout vers @/icons/Trash. Attention à la limite décrite plus bas : les motifs ne peuvent pas être déclarés pour TypeScript.

Un import l'emporte toujours

Écrire l'import vous-même masque la règle. La résolution ne complète que les noms sans liaison dans la portée : il n'y a donc jamais de conflit à arbitrer, et sortir un composant de la configuration reste sans danger.

Le dire à TypeScript

L'import n'existe qu'après compilation : sans aide, TypeScript signale Cannot find name 'Button' pour chaque balise résolue — le code s'exécute, l'éditeur est inutilisable.

Le plugin génère les déclarations pour vous. Au démarrage du serveur de développement et à la compilation, il écrit fluixi.d.ts à la racine du projet :

// GENERATED by @fluixi/compiler — do not edit.
import * as __ui_button from '@ui/button';

declare global {
  export import Button = __ui_button.Button;
}

Versionnez ce fichier. C'est lui qui permet à un clone neuf de passer le typage avant que quiconque ait lancé le serveur de développement, et à un job CI qui vérifie les types sans compiler de fonctionner.

Il doit être couvert par include dans tsconfig.json, ce qui, pour l'emplacement par défaut, signifie le nommer à côté de src :

{ "include": ["src", "fluixi.d.ts"] }

Les projets créés par l'outil de scaffolding ont déjà cette ligne. Si le fichier atterrit là où TypeScript ne le lira pas, le plugin le signale pendant la compilation : un fichier ignoré et un fichier absent produisent des erreurs identiques, autant savoir lequel vous avez.

Placez-le ailleurs, ou désactivez-le, avec globals :

fluixi({ resolve: [...], globals: 'types/fluixi.d.ts' });
fluixi({ resolve: [...], globals: false });

Ce que le fichier généré laisse de côté

  • Les noms que lib.dom revendique déjà. Un const Text ambiant ne peut pas l'emporter sur le Text du DOM : ceux-là sont écartés et listés dans l'en-tête, importez-les explicitement.
  • Les composants de paquets dont vous ne dépendez pas. Les déclarer produit un import irrésoluble qui, sous skipLibCheck, échoue en silence et dégrade le composant en any — pire que l'erreur qu'il remplace.
  • Les règles à motif. Un motif correspond à une infinité de noms : il n'y a pas de liste à émettre. Ces balises compilent, mais demandent une déclaration écrite à la main pour l'éditeur.

Dans l'éditeur

@fluixi/ts-plugin résout les mêmes noms : survol, aller à la définition et renommage fonctionnent sur une balise résolue comme sur une balise importée — y compris dans les templates html ``, où la balise n'est que du texte pour TypeScript.

Comme les déclarations utilisent un alias (export import Button = …) plutôt qu'une constante typée, le survol conserve la vraie signature et sa JSDoc, et l'aller à la définition atterrit sur la source du composant plutôt que sur le fichier généré.