Extensibilité des modèles

L’extensibilité des modèles est une fonctionnalité introduite dans PWA Kit v3. Cette fonctionnalité doit vous permettre de créer plus facilement des projets PWA Kit en personnalisant des modèles. Vous pouvez personnaliser les modèles de base existants, tels que l’application Retail React App, ou vous pouvez créer le vôtre. Nous encourageons les membres de la communauté PWA Kit à partager leurs modèles de base entre eux.

L’extensibilité des modèles vous aide à modifier un modèle choisi sans avoir à dupliquer tous les fichiers qu’il contient.

C’est une fonctionnalité facultative. Les nouveaux projets générés après le 15 juin 2023 sont toutefois automatiquement configurés afin d’utiliser l’extensibilité des modèles.

Dans PWA Kit v3, un projet peut avoir un modèle de base et un répertoire overrides, et ils peuvent tous deux être configurés dans le fichier package.json du projet. Pour utiliser l’extensibilité des modèles, vous devez déclarer un modèle de base avec ccExtensibility.extends dans package.json, ainsi qu’un répertoire overrides avec ccExtensibility.overridesDir. (Les détails de configuration sont traités dans la section suivante.)

Un modèle de base est un module npm qui contient un projet PWA Kit entièrement fonctionnel et préconfiguré de sorte qu’un autre projet PWA Kit puisse remplacer certains de ses fichiers. Parfois, un modèle de base est également appelé « application extensible »."

Le répertoire overrides est un répertoire de votre projet dans lequel vous pouvez stocker des fichiers qui remplaceront les fichiers correspondants dans le modèle de base.

Nous partirons de l’hypothèse que vous avez déjà défini un répertoire overrrides et défini l’application Retail React App comme modèle de base. Supposons que vous souhaitiez maintenant remplacer le composant de page d’accueil de l’application Retail React App. Dans l’application Retail React App, le code du composant de page d’accueil se trouve dans app/pages/home/index.jsx dans le paquet @salesforce/retail-react-app. Pour remplacer ce fichier, vous devez le recréer dans le répertoire déclaré via ccExtensibility.overridesDir dans package.json. Le chemin du nouveau fichier est donc <ccExtensibility.overridesDir>/app/pages/home/index.jsx.

Désormais, chaque fois que index.jsx est importé dans l’un des fichiers du paquet @salesforce/retail-react-app, le fichier du répertoire overrides est chargé à la place du fichier du paquet. Il suffit que la base du nom de fichier soit la même pour que le remplacement ait lieu. Les extensions de fichiers peuvent être différentes.

Vous pouvez ajouter progressivement des fichiers du modèle de base à votre répertoire overrides au fil de la création de votre application.

Plus vous remplacez de fichiers, plus l’effort nécessaire pour suivre les modifications apportées au modèle de base augmente.

Pour voir l’extensibilité des modèles à l’œuvre, générez un nouveau projet PWA Kit v3 en exécutant npx @salesforce/pwa-kit-create-app@latest --outputDir <path/to/new/local/project>.

Le projet généré utilise le modèle de base Retail React App. Vous obtenez donc une application qui ressemble beaucoup à celle que vous trouverez à l’adresse https://pwa-kit.mobify-storefront.com/.

Les instructions de cette section supposent que vous souhaitez activer l’extensibilité des modèles pour un projet existant, mais pour lequel la fonctionnalité n’était pas déjà activée lors de sa création.

Pour définir un modèle de base, ajoutez @salesforce/retail-react-app (ou un autre modèle) en tant que dépendance npm dans le fichier package.json de votre projet.

Ajoutez ensuite une clé ccExtensibility à package.json avec les clés et valeurs suivantes :

Si vous utilisez un modèle de base autre que Retail React App, n’oubliez pas de remplacer @salesforce/retail-react-app par l’identifiant du paquet de l’autre modèle.

Vous pouvez remplacer les valeurs pour overridesDir par un nom de répertoire personnalisé, mais nous vous recommandons d’utiliser overrides par souci de cohérence entre les projets (et avec notre exemple de code).

Une même dépendance ne peut pas se trouver à la fois dans le projet principal et dans le modèle de base. Les deux applications PWA Kit doivent être différentes pour que @salesforce/pwa-kit-dev puisse faire la différence entre le projet principal et le modèle de base. (Le projet principal doit définir un modèle de base et ne peut pas hériter de lui-même.)

Votre projet ne doit pas avoir de conflits de versions avec les dépendances npm du modèle de base répertoriées dans son fichier package.json. Si le modèle sous-jacent utilise @chakra-ui, votre projet doit également l’avoir comme dépendance et il doit s’agir de la même version. Cette précaution évite un gonflement accidentel de votre paquet (sans parler des ruptures fonctionnelles) qu’entraîneraient des versions conflictuelles du même paquet. Il y a une exception à cette règle : lorsque vous avez ajouté des remplacements pour tous les fichiers du modèle de base qui importent une dépendance donnée. Dans ce cas, la version de la dépendance du modèle de base n’est jamais importée dans votre projet car vous avez éliminé tous les fichiers qui l’importent via des remplacements.

L’ajout de dépendances npm supplémentaires qui ne sont pas utilisées dans le modèle sous-jacent est tout à fait possible.

Chaque projet PWA Kit déployé sur Managed Runtime doit contenir les fichiers suivants :

  • <overridesDir>/app/main.jsx
  • <overridesDir>/app/ssr.js
  • <overridesDir>/app/routes.jsx
  • <overridesDir>/app/request-processor.js
  • config/default.js (exception : si vous utilisez la clé mobify dans package.json pour stocker les données de configuration de votre site)

Pour personnaliser les comportements dans le projet, il est courant de remplacer ces fichiers dans @salesforce/retail-react-app :

  • app/pages/home/index.jsx : la suppression du contenu marketing Salesforce par défaut de la page d’accueil est la première étape de presque tous les projets.
  • app/static/* : ces fichiers sont des fichiers par défaut qui fournissent les icônes utilisées par les navigateurs sur ordinateur et sur mobile pour personnaliser votre site. Mettez-les à jour pour qu’ils correspondent à votre marque.
  • app/constants.js
  • app/assets/svg/brand-logo.svg : ce fichier est remplacé dans le projet générateur. Suivez cet exemple pour remplacer d’autres icônes de votre projet.

Cette configuration de « système de fichiers en tant qu’API » a pour but de vous aider à trouver les fichiers sous-jacents, à les copier, puis à en personnaliser le comportement et la logique. En décomposant le code en fichiers plus petits qui orchestrent de nombreux sous-composants, il est plus facile de cibler uniquement le comportement que vous souhaitez remplacer.

Cela dit, vous devez parfois copier un fichier dans vos remplacements pour modifier uniquement une partie du comportement de ce fichier. Dans la mesure du possible, il est préférable d’importer les exportations de modèles sous-jacents puis de les réexporter, afin que les modifications apportées à la version du modèle ne nécessitent pas de mises à jour manuelles du code dans votre projet étendu.

Par exemple, dans le fichier routes.jsx d’un projet généré, notez que les routes par défaut sont importées via import {routes as _routes} from '@salesforce/retail-react-app/app/routes'. _routes sert de convention pour indiquer que ces routes par défaut sont gérées par une dépendance externe. Tout comme le fichier @salesforce/retail-react-app/app/routes.jsx, notre implémentation personnalisée de routes.jsx exporte les routes ; il ajoute une route / (Accueil). La nouvelle route est prioritaire sur la route par défaut car React Router trouve le premier élément d’un chemin donné dans le tableau routes.

Les importations relatives constituent la meilleure approche pour la plupart des projets qui utilisent l’extensibilité des modèles. Le comportement d’importation des fichiers de votre modèle de base (par exemple, @salesforce/retail-react-app) a une logique spéciale qui donne la priorité au fichier situé dans votre répertoire overrides. Cette logique d’importation ne s’applique pas aux fichiers de votre répertoire overrides. Par conséquent, nous recommandons d’utiliser des importations relatives pour la plupart des implémentations.

Un risque de l’extensibilité des modèles est l’importation accidentelle de deux fichiers différents en conflit. Un projet PWA Kit est généré via @salesforce/pwa-kit-dev, qui utilise webpack en interne. Il existe un plug-in dans @salesforce/pwa-kit-dev qui permet la fonctionnalité « override » de l’extensibilité des modèles. Ce plug-in inspecte toutes les requests de fichiers Webpack et pose deux questions :

  1. Cette request de fichier provient-elle du modèle de base ?
  2. Ce fichier existe-t-il dans le répertoire overrides ?

Si la réponse aux deux questions est « oui », la request de fichier est réécrite au moment du build pour pointer vers le fichier situé dans le répertoire overrides.

Cette logique n’entre pas en jeu pour les requests de fichiers Webpack provenant du répertoire overrides. Il est en effet possible d’importer le même fichier depuis le répertoire overrides et depuis le modèle de base. Si cette situation se produit avec, par exemple, @saleforce/retail-react-app/my-file et <overrides directory>/my-file importés dans le même paquet, webpack lève une erreur déroutante du type can’t find <export name> from <filename>. Webpack génère cette erreur car le paquet ne sait pas quelle importation est la cible prévue pour l’inclusion du paquet.

Il est important d’éviter d’importer des fichiers à partir du modèle de base lorsqu’un fichier équivalent existe dans overrides, à une exception notable près. Comme indiqué précédemment dans l’exemple de code de la section Fichiers spéciaux et comme illustré avec constants.js, il est préférable d’importer le modèle sous-jacent et de réexporter toutes ses exportations, en remplaçant ou en ajoutant aussi peu d’exportations que nécessaire.

Si vous devez remplacer le composant app/components/_app/index.jsx, il existe de nombreux composants globaux, tels que Header, Footer et DrawerMenu. Une fois le remplacement de _app/index.jsx en place, à chaque fois que le modèle de base cherchera à importer ce composant, la request de fichier sera redirigée vers <ccExtensibility.overridesDir>/app/components/_app/index.jsx. Mais il y a un piège ! Étant donné que Header et Footer sont importés par app/components/_app/index.jsx, l’importation doit également être mise à jour dans _app/index.jsx ! Sinon, _app/index.jsx importe Header et Footer depuis le modèle de base.

Les importations ECMAScript provenant de ccExtensibility.extends sont « magiques » dans la mesure où @salesforce/pwa-kit-dev recherche à plusieurs endroits un fichier de ce nom (ccExtensibility.overridesDir étant prioritaire).

D’un autre côté, les importations provenant de <ccExtensibility.overridesDir>/* n’ont pas de comportement magique, vous devez donc cibler les fichiers souhaités, et <ccExtensibility.overridesDir>/app/components/header/index.jsx doit donc être importé explicitement dans <ccExtensibility.overridesDir>/app/components/_app/index.jsx comme ceci :

N’importez pas à partir du paquet comme illustré, car cela contourne l’en-tête dans votre répertoire overrides :

Notez que routes.jsx est un fichier spécial. Il constitue le « point d’entrée », dans la terminologie webpack, pour l’ensemble de l’application. Une erreur de configuration dans routes.jsx entraînera l’échec de l’ensemble de votre application lors de la phase de compilation, car le découpage basé sur le routage dans pwa-kit-dev ne pourra pas être résolu correctement. C’est pour cette raison que nous incluons un exemple d’extension correcte des routes (en mélangeant les importations du @salesforce/retail-react-app/app/routes.jsx par défaut et d’un routes.jsx local dans overridesDir).

Étant donné que les importations se trouvent ici « en haut de l’arborescence », si vous remplacez ces fichiers puis remplacez footer.jsx, vous devez revenir à _app/index.jsx pour modifier cette importation afin qu’elle pointe vers l’importation relative de votre modèle.

Il existe une limitation connue du système d’extensibilité des modèles : d’après l’extraction magique, par exemple de retail-react-app/constants, chaque fois que le modèle de base (@salesforce/retail-react-app) importe ce fichier, il s’attend à ce que les mêmes valeurs soient exportées.

Un fichier de remplacement qui ne parvient pas à exporter les mêmes exportations ECMAScript que son équivalent dans le modèle de base peut provoquer des erreurs inattendues comme celle ci-dessous.

L’ajout d’un fichier constants.js à votre répertoire overrides comme ceci provoque une erreur :

Exemple de message d’erreur :

Voici la raison de cette erreur : CAT_MENU_DEFAULT_ROOT_CATEGORY est une exportation attendue dans @salesforce/retail-react-app/components/_app/index.jsx. En la remplaçant par la seule exportation CUSTOM_MESSAGE ci-dessus, votre remplacement rompt le « contrat de l’API » sous-jacent (la capacité des fichiers à dépendre d’une valeur donnée, dans ce cas CAT_MENU_DEFAULT_ROOT_CATEGORY), car elle est exportée par constants.js

L’approche correcte serait la suivante. Cette approche évite d’omettreCAT_MENU_DEFAULT_ROOT_CATEGORY en tant qu’exportation requise et attendue. Nous maintenons ainsi la cohérence de l’API exportée.

Cette approche ne fonctionne que pour les changements additifs. Dans l’exemple suivant, nous ajoutons une exportation CUSTOM_MESSAGE qui ne figure pas dans les exportations du fichier retail-react-app/constants.js sous-jacent.

Cette approche fonctionne pour le scénario suivant, dans lequel DEFAULT_LOCALE est exporté par retail-react-app/constants, et nous modifions cette valeur :

Les hooks de modèle offrent une nouvelle façon d’interagir avec le modèle Retail React App, historiquement disponible via la commande npx pwa-kit-create-app (maintenant renommée npx @salesforce/pwa-kit-create-app).

Dans le cadre de la fonctionnalité d’extensibilité des modèles, de nouveaux « hooks de modèle » ont été ajoutés à @salesforce/retail-react-app. Ils prennent en charge l’ajout global d’un petit sous-ensemble de composants, répertoriés dans la liste suivante. Par défaut, chacun de ces composants renvoie null et est intentionnellement vide afin d’éviter d’ajouter une surcharge inutile à un projet d’implémentation de PWA Kit terminé. La base @salesforce/retail-react-app n’ajoute jamais aucune fonctionnalité à ces composants. Les hooks de modèle renvoient toujours null afin de garantir que les implémentations client puissent « s’accrocher » au modèle à ces emplacements et personnaliser le projet sans avoir à remplacer plus de fichiers que nécessaire.

À partir de @salesforce/retail-react-app@1.0.0, les hooks de modèles suivants sont disponibles :

  • app/components/_app/partials/above-header.jsx
  • app/pages/product-list/partials/above-page-header.jsx

Au moment de la sortie de PWA Kit v3, un seul modèle de base extensible est disponible au grand public : @salesforce/retail-react-app (d’autres sont attendus par la suite).

Pour servir de modèle de base, un projet PWA Kit doit être publié sur npm et toutes ses importations ECMAScript doivent être préfixées par une valeur correspondant au nom du paquet. Par exemple, @salesforce/retail-react-app est publié sur npm et ses importations sont préfixées par @salesforce/retail-react-app. Ce préfixe est une référence au répertoire racine du paquet, nécessaire à son bon fonctionnement en tant que modèle de base.

Pour vous servir d’un projet PWA Kit comme modèle de base extensible, il doit utiliser <npm package name> pour toutes ses importations. Par exemple, @salesforce/retail-react-app ajoute ccExtensibility dans package.json, ce qui garantit la résolution correcte des références à l’IDE local (par exemple, @salesforce/retail-react-app/app/components/_app).