Utilisation de Rspack
Les applications frontend OpenMRS sont construites avec Rspack , en utilisant une configuration standard fournie par openmrs-esm-core via le paquet d’outillage rspack-config. Cette configuration standardise la façon dont les modules sont construits afin que chaque application de la distribution respecte les mêmes règles de fédération, de partage et de chargement.
Vous n’êtes pas obligé d’utiliser la configuration par défaut. Les applications peuvent fournir leur propre rspack.config.js. Elle reste toutefois fortement recommandée. Cette page explique comment fonctionne la configuration par défaut et quelles contraintes une configuration personnalisée doit quand même respecter.
Rspack accepte le format de configuration de Webpack. Tout ce qui est décrit sur cette page s’applique donc aussi aux applications qui utilisent encore Webpack. Les quelques applications qui n’ont pas encore migré récupèrent les mêmes valeurs par défaut via @openmrs/webpack-config. Voir Vous utilisez encore Webpack ? plus bas.
Mise en place
La configuration générée par npm create @openmrs/o3-app@latest charge la configuration standard depuis @openmrs/rspack-config, puis désactive les avertissements de taille d’asset en production. Ces avertissements utilisent le budget générique de 244 KiB de Webpack, qui n’est pas très pertinent pour les modules O3 lorsque les dépendances framework et Carbon sont partagées via l’app shell:
const config = require("@openmrs/rspack-config");
const base = config.default ?? config;
const disablePerformanceHints = (cfg) => ({
...cfg,
performance: { ...(cfg.performance ?? {}), hints: false },
});
module.exports =
typeof base === "function" ? (...args) => disablePerformanceHints(base(...args)) : disablePerformanceHints(base);Si vous écrivez une configuration à la main, module.exports = config.default ?? config reste le minimum réel. Le wrapper généré garde les builds de nouveaux modules silencieux tout en déléguant les règles de fédération, de dépendances partagées et de loaders aux valeurs par défaut décrites ci-dessous.
Les modules plus anciens peuvent encore utiliser l’export de compatibilité openmrs/default-rspack-config. Il fonctionne toujours quand le module dépend du paquet d’outillage openmrs, mais les nouveaux modules devraient dépendre directement de @openmrs/rspack-config.
Module Federation
Le frontend O3 utilise Module Federation , introduit à l’origine dans Webpack 5 et pris en charge par Rspack via la même API de plugin. Module Federation permet à des applications déployées indépendamment de partager du code à l’exécution, au lieu de tout embarquer au moment du build. C’est ce qui rend possible le modèle microfrontend d’O3. Le dépôt module-federation-examples répertorie la plupart des modèles.
O3 utilise plus précisément le modèle des conteneurs distants dynamiques. Cela signifie que les dépendances et la liste des modules disponibles sont résolues à l’exécution. L’app shell n’a donc pas besoin de connaître à l’avance les applications et bibliothèques présentes dans la distribution. Notre configuration s’inspire fortement des exemples de remotes dynamiques maintenus par l’équipe Module Federation.
Conséquence importante: un module frontend chargé par l’app shell ne doit pas déclarer de point d’entrée explicite. Tout ce qui n’est pas l’app shell lui-même est chargé comme remote dynamique.
Le type de bibliothèque var
A l’exécution, chaque module frontend est construit avec output.library.type: 'var'. Le type var expose le module comme une propriété globale dont le nom correspond au nom du paquet, avec @, / et - remplacés par des underscores:
name.replace(/[\/\-@]/g, "_");
// "@openmrs/esm-patient-chart-app" -> "_openmrs_esm_patient_chart_app"Cette transformation est importante, car l’app shell a besoin d’un identifiant JavaScript valide pour accéder au module et récupérer son API de fédération.
Le module exposé ./start
Chaque module fédéré qui expose des contributions pour l’app shell doit exposer un module nommé "./start". Ce module exposé doit généralement exporter:
- Les exports de lifecycle référencés depuis
routes.json startupApp()quand le module a besoin d’une configuration unique avant son premier lifecycleimportTranslationquand le module fournit des fichiers de traduction
Dans la configuration Rspack par défaut, cela est câblé en exposant le fichier index.ts du module sous le nom "./start". Si vous chargez vous-même du code avec importDynamic depuis @openmrs/esm-framework, vous pouvez viser un autre module exposé. Le chargeur de routes et d’extensions de l’app shell utilise toutefois ./start par défaut.
Liste de compatibilité
Si vous écrivez un rspack.config.js personnalisé, tout module destiné à fonctionner dans l’app shell doit respecter les contraintes suivantes:
output.library.typevautvar.output.library.namesuit la règle de transformation des noms OpenMRS (@openmrs/esm-foo->_openmrs_esm_foo).- Le remote expose un module
./startque l’app shell peut charger. - Le module
./startexporte chaque lifecycle de composant nommé dans leroutes.jsondu module.
Dépendances partagées
Module Federation permet de partager les dépendances afin qu’elles soient chargées une seule fois dans toute la distribution, même si chaque module frontend est déployé indépendamment.
Dans la configuration Rspack d’OpenMRS, chaque entrée de peerDependencies dans le package.json d’une application est traitée comme une bibliothèque fédérée partagée. Toutes les dépendances partagées sont configurées comme singletons. En d’autres termes, nous choisissons délibérément de ne pas prendre en charge plusieurs versions d’une même bibliothèque à l’exécution. Les raisons sont les suivantes:
- Les dépendances de base comme React, React DOM et React Router doivent être des singletons, car elles utilisent de l’état global non versionné, par exemple
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. Avoir deux copies de React dans une même page est un bug. - Forcer une seule version de
@carbon/reactpartout garde l’interface visuellement et fonctionnellement cohérente entre les modules. Le coût est qu’une mise à jour d’une dépendance de base doit se faire sur toute la distribution à la fois. Nous pourrons réexaminer ce compromis plus tard.
Toutes les dépendances partagées sont chargées dans le scope default, et le nom partagé correspond au chemin d’import. Par exemple, React est partagé sous le nom react et importé comme react.
Personnaliser le build
La configuration par défaut exporte un ensemble d’objets mutables qui sont fusionnés dans la configuration Rspack finale. Pour surcharger un comportement, importez la configuration, modifiez l’export pertinent, puis réexportez:
const config = require("@openmrs/rspack-config");
config.cssRuleConfig.rules = [myCustomRule];
module.exports = config.default ?? config;Les points de surcharge disponibles sont:
| Export | Ce dans quoi il est fusionné |
|---|---|
overrides | Configuration Rspack de haut niveau (fusion profonde ; les tableaux sont concaténés) |
additionalConfig | Configuration Rspack de haut niveau (les clés remplacent les valeurs existantes) |
scriptRuleConfig | Règle de loader pour les fichiers .js / .jsx / .ts / .tsx |
cssRuleConfig | Règle de loader pour les fichiers .css |
scssRuleConfig | Règle de loader pour les fichiers .scss |
assetRuleConfig | Règle de loader pour les ressources statiques |
watchConfig | watchOptions |
optimizationConfig | optimization |
Choisissez le point de surcharge le plus étroit possible. Si vous devez seulement indiquer au loader de traiter un chemin supplémentaire dans node_modules, modifiez scriptRuleConfig plutôt que de réassigner toute la configuration de haut niveau. Les déclarations de ces hooks se trouvent dans le code source de rspack-config si vous avez besoin d’inspecter leurs formes exactes.
Recettes courantes
esm-patient-common-lib
La configuration par défaut suppose que tout ce qui se trouve dans node_modules est déjà du JavaScript simple. Nous évitons donc de le passer dans le loader SWC. Cela garde les builds rapides et convient à toute bibliothèque publiée sur npm comme JavaScript.
L’exception est esm-patient-common-lib, une bibliothèque partagée du patient chart qui n’a pas d’étape de build et livre du TypeScript brut. Les applications hors du monorepo patient chart qui la consomment via npm doivent étendre leur rspack.config.js pour appliquer le loader à node_modules/@openmrs/esm-patient-common-lib. Les applications à l’intérieur du monorepo patient chart ne sont pas concernées, car le protocole workspace de Yarn résout la bibliothèque vers le code source et notre configuration la récupère via le chemin workspace plutôt que via node_modules.
esm-form-entry-app
Le module frontend de saisie de formulaires est une application Angular qui encapsule le moteur de formulaires Angular. Contrairement à tous les autres modules frontend, il se construit encore avec Webpack et non Rspack, car il dépend de @angular-architects/module-federation, qui n’a pas encore été porté vers Rspack. Le plugin modifie la configuration workspace Angular pour ajouter une propriété extraWebpackConfig aux cibles build et serve, pointant vers une configuration Webpack personnalisée qui:
- Configure
ModuleFederationPluginpour exposer l’application form entry comme module remote. - Déclare les dépendances partagées à partir des
peerDependenciesde l’application form entry. - Expose
src/index.tscomme module de fédération./start.
C’est acceptable en pratique, car Module Federation est interopérable entre bundlers. Un remote construit avec Webpack peut être consommé par un host construit avec Rspack, et inversement.
Vous utilisez encore Webpack ?
Un petit nombre de modules n’ont pas encore migré vers Rspack. Tant qu’ils restent sur Webpack:
- Les modules actuels devraient utiliser directement le paquet
@openmrs/webpack-config. Les modules plus anciens peuvent encore utiliser l’export de compatibilitéopenmrs/default-webpack-config. - Tout ce qui est décrit sur cette page s’applique: Module Federation, le type de bibliothèque
var, la convention./start, la politique de dépendances partagées singletons et les hooks de surcharge sont indépendants du bundler. - Lorsque vous êtes prêt à basculer, consultez Migration vers Rspack et Vitest. Le remplacement de configuration est mécanique.