Estensibilità del modello

L'estensibilità del modello è una funzionalità introdotta in PWA Kit v3. Il suo obiettivo è facilitare la creare di progetti PWA Kit mediante la personalizzazione di modelli. È possibile personalizzare i modelli di base esistenti, come Retail React App, o crearne di propri. I membri della community PWA Kit sono invitati a condividere i propri modelli di base con gli altri membri.

L'estensibilità del modello consente di modificare un modello selezionato senza dover duplicare ogni file al suo interno.

L'estensibilità del modello è una funzionalità facoltativa. Tuttavia, i nuovi progetti generati dopo il 15 giugno 2023 vengono configurati automaticamente per l'uso dell'estensibilità del modello.

In PWA Kit v3 un progetto può avere un modello di base e una directory degli override, entrambi configurabili nel file package.json del progetto. Per utilizzare l'estensibilità del modello è necessario dichiarare un modello di base con ccExtensibility.extends in package.json e una directory degli override con ccExtensibility.overridesDir. (I dettagli di configurazione sono trattati nella sezione successiva.)

Un modello di base è un modulo npm contenente un progetto PWA Kit completamente funzionante, preconfigurato in modo che un altro progetto PWA Kit possa eseguire l'override di alcuni dei suoi file. A volte un modello di base è definito anche "app estensibile" o "app estendibile."

La directory degli override è una directory interna al progetto in cui è possibile archiviare i file da utilizzare come override dei file corrispondenti nel modello di base.

Si supponga di avere già definito una directory degli override e di avere specificato Retail React App come modello di base. Si supponga inoltre di voler eseguire l'override del componente home page di Retail React App. In Retail React App il codice del componente home page è in app/pages/home/index.jsx nel pacchetto @salesforce/retail-react-app. Per eseguire l'override di tale file è necessario ricreare lo stesso file nella directory dichiarata tramite ccExtensibility.overridesDir in package.json. Pertanto, il percorso del nuovo file sarà <ccExtensibility.overridesDir>/app/pages/home/index.jsx.

Ogni volta che index.jsx viene importato in uno dei file del pacchetto @salesforce/retail-react-app, viene caricato il file della directory degli override anziché il file del pacchetto. Affinché avvenga l'override, solo la base del nome file deve corrispondere. Le estensioni dei file possono essere diverse.

Mentre si crea l'app, è possibile aggiungere gradualmente file dal modello di base alla directory degli override.

Maggiore è il numero di file di cui si esegue l'override, più complicato sarà tenere il passo con le modifiche nel modello di base.

Per osservare il funzionamento dell'estensibilità del modello, generare un nuovo progetto PWA Kit v3 eseguendo il comando npx @salesforce/pwa-kit-create-app@latest --outputDir <path/to/new/local/project>.

Il progetto generato utilizza il modello di base Retail React App, che offre un'app molto simile a quella presente in https://pwa-kit.mobify-storefront.com/.

Le istruzioni in questa sezione presuppongono che si desideri abilitare l'estensibilità del modello per un progetto esistente non generato con la funzionalità già abilitata.

Per definire un modello di base, aggiungere @salesforce/retail-react-app (o un modello diverso) come dipendenza npm nel file package.json del progetto.

Quindi aggiungere una chiave ccExtensibility a package.json con le chiavi e i valori seguenti:

Se si utilizza un modello di base diverso da Retail React App, non dimenticare di sostituire @salesforce/retail-react-app con l'identificatore di pacchetto dell'altro modello.

È possibile sostituire i valori di overridesDir con un nome di directory personalizzato, ma si consiglia di utilizzare overrides per coerenza tra i progetti (e il codice di esempio).

La stessa dipendenza non può trovarsi sia nel progetto principale sia nel modello di base. Le due app PWA Kit devono essere diverse affinché @salesforce/pwa-kit-dev possa distinguere tra il progetto principale e il modello di base. (Il progetto principale deve definire un modello di base e non può ereditarlo da se stesso.)

Il progetto non deve presentare conflitti di versione con le dipendenze npm del modello di base elencate nel relativo file package.json. Se il modello sottostante utilizza @chakra-ui, anche il progetto deve utilizzarlo come dipendenza e la versione deve corrispondere. Oltre a garantire la continuità operativa, questa precauzione impedisce il rigonfiamento accidentale del bundle dovuto a versioni in conflitto dello stesso pacchetto. Fa eccezione a questa regola il caso in cui siano stati aggiunti override per tutti i file del modello di base che importa una dipendenza specifica. In tal caso, la versione della dipendenza del modello di base non viene mai importata nel progetto perché sono stati eliminati tutti i file che la importano tramite gli override.

L'aggiunta di ulteriori dipendenze npm che non vengono utilizzate nel modello sottostante è permessa. 👌

Ogni progetto PWA Kit distribuito in Managed Runtime deve essere composto dai seguenti file:

  • <overridesDir>/app/main.jsx
  • <overridesDir>/app/ssr.js
  • <overridesDir>/app/routes.jsx
  • <overridesDir>/app/request-processor.js
  • config/default.js (fa eccezione il caso in cui si utilizzi la chiave mobify in package.json per archiviare i dati di configurazione del sito)

Per personalizzare i comportamenti nel progetto, è normale eseguire l'override di questi file in @salesforce/retail-react-app:

  • app/pages/home/index.jsx: la rimozione dei contenuti di marketing Salesforce predefiniti dalla home page è una delle prime operazioni per quasi tutti i progetti.
  • app/static/*: si tratta di file predefiniti che generano le icone utilizzate dai browser desktop e mobili per il branding del sito. Aggiornarli in base al brand.
  • app/constants.js
  • app/assets/svg/brand-logo.svg: di questo file viene eseguito l'override nel progetto generatore. Seguire questo esempio per eseguire l'override delle altre icone del proprio progetto.

Lo scopo di questa impostazione di tipo "filesystem come API" è quello di aiutare a individuare i file sottostanti, copiarli, quindi personalizzare il comportamento e la logica. La suddivisione del codice in file più piccoli che organizzano molti sottocomponenti facilita l'individuazione del singolo comportamento che di cui si vuole eseguire l'override.

Tuttavia, a volte occorre copiare un file nei propri override per modificare solo una parte del suo comportamento. Quando possibile, è preferibile importare le esportazioni del modello sottostante e riesportarle in modo che le modifiche della versione del modello non richiedano aggiornamenti manuali di codice nel progetto esteso.

Ad esempio, nel file routes.jsx in un progetto generato, si noti che le route predefinite vengono importate tramite import {routes as _routes} from '@salesforce/retail-react-app/app/routes' con _routes come convenzione per indicare che tali route predefinite sono gestite da una dipendenza esterna. Come il file @salesforce/retail-react-app/app/routes.jsx, l'implementazione personalizzata di routes.jsx esporta le route e aggiunge una route / (Home). La nuova route ha la precedenza su quella predefinita perché React Router trova il primo elemento di un determinato nome di percorso nella matrice di route.

Le importazioni relative rappresentano l'approccio migliore per la maggior parte dei progetti che utilizzano l'estensibilità del modello. Il comportamento di importazione per i file nel modello di base (ad esempio @salesforce/retail-react-app) ha una logica speciale che dà la precedenza al file nella directory degli override. La logica di importazione non si applica ai file nella directory degli override. Pertanto, per la maggior parte delle implementazioni si consiglia di utilizzare le importazioni relative.

Un possibile problema a cui occorre prestare attenzione utilizzando l'estensione del modello è l'importazione accidentale di due file differenti in conflitto tra loro. Un progetto PWA Kit viene creato tramite @salesforce/pwa-kit-dev, che utilizza Webpack internamente. In @salesforce/pwa-kit-dev è presente un plug-in che abilita la funzionalità "override" dell'estensibilità del modello. Il plug-in ispeziona tutte le richieste di file Webpack e pone due domande:

  1. Questa richiesta di file ha origine nel modello di base?
  2. Questo file esiste nella directory degli override?

Se la risposta a entrambe le domande è affermativa, la richiesta di file viene riscritta in fase di compilazione per puntare al file nella directory degli override.

Questa logica non entra in gioco per le richieste di file Webpack che hanno origine nella directory degli override. Pertanto, è possibile importare lo stesso file sia dalla directory degli override sia dal modello di base. Se si verifica la situazione in cui, ad esempio, @saleforce/retail-react-app/my-file e <overrides directory>/my-file vengono importati nello stesso bundle, Webpack restituisce un errore di confusione simile a can’t find <export name> from <filename>. Webpack restituisce questo errore perché il bundle non sa quale importazione sia la destinazione prevista per la sua inclusione.

È importante evitare di importare file dal modello di base quando esiste un file equivalente negli override, con un'eccezione degna di nota. Come illustrato in precedenza con l'esempio di codice nella sezione File speciali e dimostrato con constants.js, è preferibile importare il modello sottostante e riesportare tutte le relative esportazioni, con l'override o l'aggiunta del numero minimo di esportazioni necessarie.

Se occorre eseguire l'override del componente app/components/_app/index.jsx, sono disponibili numerosi componenti globali, ad esempio Header, Footer e DrawerMenu. Una volta effettuato l'override di _app/index.jsx, qualsiasi tentativo da parte del modello di base di importare questo componente reindirizzerà la richiesta di file a <ccExtensibility.overridesDir>/app/components/_app/index.jsx. Esiste tuttavia un problema. Poiché Header e Footer sono importati da app/components/_app/index.jsx, l'importazione deve essere aggiornata anche in _app/index.jsx. In caso contrario, _app/index.jsx importa Header e Footer dal modello di base.

Le importazioni ECMAScript che hanno origine in ccExtensibility.extends hanno un comportamento per così dire "magico", in quanto @salesforce/pwa-kit-dev verifica in più ubicazioni se è presente un file con tale nome, dando la precedenza a ccExtensibility.overridesDir.

Al contrario, le importazioni che hanno origine in <ccExtensibility.overridesDir>/* non hanno un comportamento di questo tipo e, di conseguenza, è necessario scegliere come destinazione i file desiderati. <ccExtensibility.overridesDir>/app/components/header/index.jsx deve quindi essere importato in modo esplicito in <ccExtensibility.overridesDir>/app/components/_app/index.jsx, come descritto di seguito:

Non effettuare questo tipo di importazioni dal pacchetto perché in tal modo viene ignorata l'intestazione nella directory degli override:

Si noti che routes.jsx è un file speciale. Nella terminologia Webpack costituisce l'"entryPoint" per l'intera applicazione. In caso di impostazioni errate in routes.jsx, l'intera fase di compilazione dell'app non andrà a buon fine poiché la suddivisione in blocchi basata su route in pwa-kit-dev non si risolve correttamente. Per questo motivo, viene incluso un esempio di come estendere correttamente le route unendo le importazioni da @salesforce/retail-react-app/app/routes.jsx predefinito e un routes.jsx locale in overridesDir.

Poiché le importazioni in questo esempio sono "in cima alla struttura", se si esegue l'override di questi file e successivamente di footer.jsx, è necessario tornare a _app/index.jsx e modificare l'importazione in modo che punti alla relativa importazione del modello.

Una limitazione nota del sistema di estensibilità del modello è data dal fatto che, in virtù dell'inserimento "magico", ad esempio retail-react-app/constants, ogni volta che il modello di base (@salesforce/retail-react-app) importa questo file, si aspetta che vengano esportati gli stessi valori.

Un file negli override che non riesce ad esportare le stesse esportazioni ECMAScript del suo equivalente nel modello di base può causare errori imprevisti come quello indicato di seguito.

L'aggiunta di un file constants.js alla directory degli override come illustrato di seguito provoca un errore:

Esempio di messaggio di errore:

Il motivo di questo errore è che CAT_MENU_DEFAULT_ROOT_CATEGORY è un'esportazione prevista in @salesforce/retail-react-app/components/_app/index.jsx e, durante l'override con la sola esportazione CUSTOM_MESSAGE sopra indicata, l'override stesso interrompe il "contratto API" sottostante (la capacità dei file di dipendere da un determinato valore, in questo caso CAT_MENU_DEFAULT_ROOT_CATEGORY) esportato da constants.js.

Il seguente approccio è quello corretto ed evita l'omissione di CAT_MENU_DEFAULT_ROOT_CATEGORY come esportazione obbligatoria e prevista. In questo modo viene mantenuta la coerenza dell'API esportata.

Questo approccio è applicabile solo in caso di modifiche additive. Nell'esempio seguente viene aggiunta un'esportazione CUSTOM_MESSAGE non presente tra le esportazioni del file retail-react-app/constants.js sottostante.

Questo approccio è valido per il seguente scenario, in cui DEFAULT_LOCALE viene esportato da retail-react-app/constants e tale valore viene modificato:

Gli hook di modello rappresentano un nuovo modo di interagire con il modello Retail React App, che storicamente era disponibile tramite il comando npx pwa-kit-create-app (ora rinominato npx @salesforce/pwa-kit-create-app).

Nell'ambito della funzionalità di estensibilità del modello, sono stati aggiunti a @salesforce/retail-react-app nuovi "hook di modello" che supportano l'aggiunta globale di un piccolo sottoinsieme di componenti, riportati nell'elenco seguente. Per impostazione predefinita, ciascuno di questi componenti restituisce null ed è intenzionalmente vuoto per evitare il rigonfiamento non necessario di un progetto di implementazione PWA Kit completato. @salesforce/retail-react-app di base non aggiunge mai alcuna funzionalità a questi componenti. Gli hook di modello restituiscono sempre null per consentire alle implementazioni del cliente di "agganciarsi" al modello in questi punti in modo da personalizzare il progetto senza dover eseguire l'override di più file del necessario.

A partire dalla versione @salesforce/retail-react-app@1.0.0 sono disponibili i seguenti hook di modello:

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

Al momento del rilascio di PWA Kit v3, un solo modello di base estensibile è disponibile pubblicamente: @salesforce/retail-react-app (altri sono previsti in futuro).

Per essere utilizzato come modello di base, un progetto PWA Kit deve essere pubblicato su npm e tutte le relative importazioni ECMAScript devono essere precedute da un valore corrispondente al nome del pacchetto. Ad esempio, @salesforce/retail-react-app viene pubblicato su npm e le relative importazioni sono precedute da @salesforce/retail-react-app. Questo prefisso è un riferimento alla directory radice del pacchetto, necessaria per il suo corretto funzionamento come modello di base.

Per utilizzare un progetto PWA Kit come modello di base estensibile, è necessario utilizzare <npm package name> per tutte le sue importazioni. Ad esempio, @salesforce/retail-react-app aggiunge ccExtensibility in package.json, che garantisce la corretta risoluzione dei riferimenti IDE locali (ad esempio @salesforce/retail-react-app/app/components/_app).