Extensibilidad de la plantilla

La extensibilidad de la plantilla es una característica introducida en PWA Kit v3. El objetivo de esta característica es permitirle crear proyectos de PWA Kit con mayor facilidad mediante la personalización de plantillas. Puede personalizar las plantillas base existentes, como la Retail React App, o puede crear las suyas. Alentamos a los miembros de la comunidad de PWA Kit a compartir sus plantillas base entre sí.

La extensibilidad de la plantilla le ayuda a modificar una plantilla elegida sin tener que duplicar todos los archivos que contiene.

La extensibilidad de la plantilla es una característica opcional. Sin embargo, los proyectos nuevos que se generen después del 15 de junio de 2023 se configurarán de manera automática para utilizar la extensibilidad de la plantilla.

En PWA Kit v3, un proyecto puede tener una plantilla base y un directorio de reemplazos, los cuales se pueden configurar en el archivo package.json del proyecto. Para utilizar la extensibilidad de la plantilla, debe indicar una plantilla base con ccExtensibility.extends en package.json y debe indicar un directorio de reemplazos con ccExtensibility.overridesDir. (Los detalles de configuración se tratan en la siguiente sección).

Una plantilla base es un módulo npm que contiene un proyecto de PWA Kit completamente funcional que ha sido preconfigurado para que otro proyecto de PWA Kit pueda reemplazar algunos de sus archivos. A veces, la plantilla base también se denomina “aplicación extensible” o “aplicación expandible”."

El directorio de reemplazos es un directorio dentro de su proyecto en el que puede almacenar archivos que reemplazarán a los archivos correspondientes en la plantilla base.

Supongamos que ya definió un directorio de reemplazos y definió la Retail React App como su plantilla base. Digamos que quiere reemplazar el componente de la página de inicio de la Retail React App. En la Retail React App, el código para el componente de la página de inicio se encuentra en app/pages/home/index.jsx dentro del paquete @salesforce/retail-react-app. Para reemplazar ese archivo, debe volver a crear ese mismo archivo en el directorio indicado mediante ccExtensibility.overridesDir en package.json. Entonces, la ruta del nuevo archivo es <ccExtensibility.overridesDir>/app/pages/home/index.jsx.

Ahora, cada vez que se importa index.jsx a cualquiera de los archivos dentro del paquete @salesforce/retail-react-app, se carga el archivo del directorio de reemplazos en lugar del archivo del paquete. Para que se ejecute el reemplazo, solo es necesario que la base del nombre del archivo sea la misma. Las extensiones del archivo pueden ser diferentes.

A medida que crea su aplicación, puede agregar archivos de forma gradual desde la plantilla base a su directorio de reemplazos.

Cuantos más archivos reemplace, se necesitará un mayor esfuerzo para mantenerse al día con los cambios en la plantilla base.

Para ver la extensibilidad de la plantilla en acción, genere un nuevo proyecto PWA Kit v3 al ejecutar npx @salesforce/pwa-kit-create-app@latest --outputDir <path/to/new/local/project>.

El proyecto generado utiliza la plantilla base de la Retail React App, que le brinda una aplicación que se ve prácticamente igual a la que se encuentra en https://pwa-kit.mobify-storefront.com/.

Las instrucciones de esta sección asumen que quiere habilitar la extensibilidad de la plantilla para un proyecto existente que no se generó con la característica ya habilitada.

Para definir una plantilla base, agregue @salesforce/retail-react-app (o una plantilla diferente) como dependencia npm en el archivo package.json de su proyecto.

Luego agregue una clave ccExtensibility a package.json con las siguientes claves y valores:

Si está utilizando una plantilla base que no es la Retail React App, no se olvide de reemplazar @salesforce/retail-react-app con el identificador de paquete de la otra plantilla.

Puede reemplazar los valores de overridesDir con un nombre de directorio personalizado, pero recomendamos usar overrides para mantener la coherencia entre los proyectos (y nuestro código de ejemplo).

La misma dependencia no puede estar tanto en el proyecto principal como en la plantilla base. Las dos aplicaciones de PWA Kit deben ser distintas para @salesforce/pwa-kit-dev para poder diferenciar el proyecto principal de la plantilla base. (El proyecto principal debe definir una plantilla base y no la puede heredar de sí mismo).

Su proyecto no debe tener conflictos con las versiones de las dependencias npm de la plantilla base que se enumeran en el archivo package.json. Si la plantilla subyacente utiliza @chakra-ui, su proyecto también debe tenerla como dependencia y debe ser la misma versión. Esta medida evita que su paquete se llene por accidente (sin mencionar el mal funcionamiento) debido a versiones en conflicto del mismo paquete. Hay una excepción a esta regla: Cuando haya agregado reemplazos para todos los archivos en la plantilla base que importa una dependencia determinada. En ese caso, la versión de la dependencia de la plantilla base nunca se importa al proyecto porque ha eliminado todos los archivos que la importan mediante reemplazos.

Agregar dependencias npm adicionales que no se usan en la plantilla subyacente está bien. 👌

Cada proyecto de PWA Kit implementado en Managed Runtime debe tener los siguientes archivos:

  • <overridesDir>/app/main.jsx
  • <overridesDir>/app/ssr.js
  • <overridesDir>/app/routes.jsx
  • <overridesDir>/app/request-processor.js
  • config/default.js (excepción: si está utilizando la clave mobify en package.json para almacenar los datos de configuración de su sitio)

Para personalizar comportamientos en el proyecto, es habitual reemplazar estos archivos en @salesforce/retail-react-app:

  • app/pages/home/index.jsx: Eliminar el contenido de marketing predeterminado de Salesforce de la página de inicio es el primer paso en casi todos los proyectos.
  • app/static/*: Estos archivos son predeterminados que contienen íconos utilizados por los navegadores de escritorio y móviles para la marca de su sitio. Actualícelos para que coincidan con su marca.
  • app/constants.js
  • app/assets/svg/brand-logo.svg: Este archivo se reemplaza en el proyecto del generador. Siga el siguiente ejemplo para reemplazar otros íconos en su proyecto.

La intención de esta configuración de “sistema de archivos como API” es ayudarle a encontrar los archivos subyacentes, copiarlos y luego personalizar el comportamiento y la lógica. Dividir el código en archivos más pequeños que organizan muchos subcomponentes hace que sea más fácil centrarse únicamente en el comportamiento que desea reemplazar.

Dicho esto, a veces es necesario copiar un archivo en los reemplazos para cambiar solo una parte del comportamiento de ese archivo. Siempre que sea posible, es mejor importar las exportaciones de la plantilla subyacente y volver a exportarlas para que los cambios en la versión de la plantilla no requieran actualizaciones manuales del código en su proyecto extendido.

Por ejemplo, en el archivo routes.jsx en un proyecto generado, se observa que las rutas predeterminadas se importan a través de import {routes as _routes} from '@salesforce/retail-react-app/app/routes' con la intención de que _routes sea un convenio para indicar que estas rutas predeterminadas son administradas por una dependencia externa. Al igual que el archivo @salesforce/retail-react-app/app/routes.jsx, nuestra implementación personalizada de routes.jsx exporta rutas; agrega una ruta / (Inicio). La nueva ruta tiene prioridad sobre la ruta predeterminada porque el enrutador React encuentra el primer artículo de un nombre de ruta determinado en la matriz de rutas.

Las importaciones relativas son el mejor enfoque para la mayoría de los proyectos que utilizan la extensibilidad de la plantilla. El comportamiento de importación de los archivos en su plantilla base (por ejemplo, @salesforce/retail-react-app) tiene una lógica especial que le da prioridad al archivo en su directorio de reemplazos. Esta lógica de importación no se aplica a los archivos en su directorio de reemplazos. Por lo tanto, recomendamos utilizar importaciones relativas para la mayoría de las implementaciones.

Un problema a tener en cuenta con la extensibilidad de la plantilla es la importación accidental de dos archivos diferentes en conflicto. Un proyecto de PWA Kit se crea a través de @salesforce/pwa-kit-dev, que utiliza webpack de manera interna. Hay un complemento en @salesforce/pwa-kit-dev que habilita la funcionalidad de “reemplazo” de la extensibilidad de la plantilla. Este complemento inspecciona todas las solicitudes de archivos del webpack y formula dos preguntas:

  1. ¿Esta solicitud de archivo se origina en la plantilla base?
  2. ¿Este archivo existe en el directorio de reemplazos?

Si la respuesta a ambas preguntas es “sí”, la solicitud de archivo se reescribe en el momento de la compilación para que se dirija al archivo en el directorio de reemplazos.

Esta lógica no entra en juego para las solicitudes de archivos de webpack que se originan en el directorio de reemplazos. De este modo, es posible importar el mismo archivo tanto desde el directorio de reemplazos como desde la plantilla base. Si esta situación ocurre, por ejemplo, cuando @saleforce/retail-react-app/my-file y <overrides directory>/my-file se importan en el mismo paquete, el webpack arroja un error confuso similar a can’t find <export name> from <filename>. El webpack arroja este error porque el paquete no sabe qué importación es el destino previsto para la inclusión del paquete.

Es importante evitar la importación de archivos desde la plantilla base cuando existe un archivo equivalente en los reemplazos, con una excepción notable. Como se explicó anteriormente con el ejemplo de código en la sección Archivos especiales y se demostró con constants.js, es mejor importar la plantilla subyacente y volver a exportar todas sus exportaciones, reemplazando o adjuntando tantas exportaciones como sean necesarias.

Cuando es necesario reemplazar el componente app/components/_app/index.jsx, hay muchos componentes globales, como Header, Footer y DrawerMenu. Una vez implementado el reemplazo de _app/index.jsx, cualquier intento de la plantilla base de importar este componente redirigirá la solicitud de archivo a <ccExtensibility.overridesDir>/app/components/_app/index.jsx. ¡Pero hay un problema! Debido a que Header y Footer son importados por app/components/_app/index.jsx, ¡la importación también debe actualizarse en _app/index.jsx! De lo contrario, _app/index.jsx importa a Header y Footer desde la plantilla base.

Las importaciones de ECMAScript que se originan en ccExtensibility.extends son “mágicas” porque @salesforce/pwa-kit-dev busca en varios lugares un archivo con ese nombre, pero dándole prioridad a ccExtensibility.overridesDir.

Por otro lado, las importaciones que se originan en <ccExtensibility.overridesDir>/* no tienen un comportamiento mágico, por ende, debe indicar los archivos que desea y, por lo tanto, <ccExtensibility.overridesDir>/app/components/header/index.jsx debe importarse explícitamente en <ccExtensibility.overridesDir>/app/components/_app/index.jsx de la siguiente manera:

No importe desde el paquete de esta manera porque omite el encabezado (Header) en su directorio de reemplazos:

Tenga en cuenta que routes.jsx es un archivo especial. Forma el “punto de entrada”, según la terminología del webpack, para toda la aplicación. Si hay algo configurado de manera incorrecta en routes.jsx, toda la aplicación fallará en la fase de compilación ya que la fragmentación basada en rutas en pwa-kit-dev no se resuelve correctamente. Debido a esto, incluimos un ejemplo de cómo extender rutas correctamente al mezclar importaciones de @salesforce/retail-react-app/app/routes.jsx por defecto y routes.jsx local en overridesDir.

Dado que las importaciones aquí están en la “cima”, si reemplaza estos archivos y luego reemplaza footer.jsx, debe volver a _app/index.jsx y modificar esa importación para que se dirija a su importación de la plantilla relativa.

Una limitación conocida del sistema de extensibilidad de la plantilla es que, en virtud de incorporar mágicamente, por ejemplo retail-react-app/constants, cada vez que la plantilla base (@salesforce/retail-react-app) importa este archivo, espera que se exporten los mismos valores.

Un archivo del directorio de reemplazos que no logra realizar las mismas exportaciones de ECMAScript que su equivalente en la plantilla base puede causar errores inesperados como el que se muestra a continuación.

Agregar un archivo constants.js a su directorio de reemplazos de esta manera provoca un error:

Ejemplo de un mensaje de error:

El motivo de este error es que CAT_MENU_DEFAULT_ROOT_CATEGORY es una exportación esperada en @salesforce/retail-react-app/components/_app/index.jsx y en el proceso de reemplazarla con la única exportación CUSTOM_MESSAGE que se menciona arriba, el reemplazo rompe el “contrato API” subyacente (la capacidad de los archivos de depender de un valor determinado, en este caso CAT_MENU_DEFAULT_ROOT_CATEGORY) al ser exportado por constants.js.

El enfoque correcto sería el siguiente. Este enfoque evita la omisión de CAT_MENU_DEFAULT_ROOT_CATEGORY como exportación requerida y esperada. De esta forma mantenemos la coherencia de la API exportada.

Este enfoque solo funciona para cambios adicionales. En el siguiente ejemplo, agregamos una exportación CUSTOM_MESSAGE que no está en las exportaciones del archivo retail-react-app/constants.js subyacente.

Este enfoque funciona para el siguiente escenario, donde DEFAULT_LOCALE es exportado por retail-react-app/constants y estamos transformando ese valor:

Los enlaces de plantilla son una nueva forma de interactuar con la plantilla de la Retail React App que históricamente ha estado disponible mediante el comando npx pwa-kit-create-app (ahora con el nombre de npx @salesforce/pwa-kit-create-app).

Como parte de la característica de extensibilidad de la plantilla, se han agregado nuevos “enlaces de plantilla” a @salesforce/retail-react-app que admiten la adición global de un pequeño subconjunto de componentes, que se enumeran en la siguiente lista. Por defecto, cada uno de estos componentes devuelve null y está vacío de manera intencional para evitar que un proyecto de implementación de PWA Kit completado se llene innecesariamente. La base @salesforce/retail-react-app nunca agrega funcionalidades a estos componentes. Los enlaces de plantilla siempre devuelven null para garantizar que las implementaciones del cliente puedan “enlazarse” a la plantilla en estos lugares y así personalizar el proyecto sin tener que reemplazar más archivos de los necesarios.

A partir de @salesforce/retail-react-app@1.0.0, se encuentran disponibles los siguientes enlaces de plantilla:

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

Al momento del lanzamiento de PWA Kit v3, solo hay una plantilla base extensible disponible para el público: @salesforce/retail-react-app (se esperan más en el futuro).

Para usarlo como plantilla base, el proyecto de PWA Kit debe publicarse en npm y todas sus importaciones de ECMAScript deben tener un prefijo cuyo valor coincida con el nombre del paquete. Por ejemplo, @salesforce/retail-react-app se publica en npm y sus importaciones tienen el prefijo @salesforce/retail-react-app. Este prefijo es una referencia al directorio raíz del paquete, que es necesario para que funcione correctamente como plantilla base.

Para usar un proyecto de PWA Kit como plantilla base extensible, debe usar <npm package name> para todas las importaciones. Por ejemplo, @salesforce/retail-react-app agrega ccExtensibility en package.json, lo que garantiza que las referencias del entorno de desarrollo integrado (IDE) local (por ejemplo, @salesforce/retail-react-app/app/components/_app) se resuelvan correctamente.