From 67a4446a271788ab260f9d089b0e63c17be88d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Fri, 25 Oct 2024 09:25:01 +0200 Subject: [PATCH 01/16] Client transforms for reducer data (#6422) Co-authored-by: Steve Piercy --- docs/source/backend/index.md | 17 +++++++++++++++++ packages/registry/news/6422.bugfix | 1 + packages/registry/src/index.ts | 2 +- packages/registry/src/registry.test.tsx | 9 +++++++++ packages/volto/news/6422.feature | 1 + packages/volto/src/reducers/content/content.js | 12 ++++++++++++ 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 packages/registry/news/6422.bugfix create mode 100644 packages/volto/news/6422.feature diff --git a/docs/source/backend/index.md b/docs/source/backend/index.md index 8dbab251c4..4292411c35 100644 --- a/docs/source/backend/index.md +++ b/docs/source/backend/index.md @@ -34,6 +34,23 @@ Block transformers Search and indexing integration : By providing the right adapters, you can extract searchable text from blocks. +Client Reducer Content Transforms +: These transforms run in the client when the response from the backend is received. + These are useful when you need to modify on the fly the response from the backend, in case you need to make an amendment of the backend data, like a data migration of any kind. + You can register a utility that mutates the response at your convenience. + + ```ts + import { upgradeV20241023 } from './upgrades/upgradeV20241023'; + + config.registerUtility({ + name: 'upgradeV20241023', + type: 'transform', + dependencies: { reducer: 'content' }, + method: upgradeV20241023, + }); + ``` + + The `type` of the utility needs to be `transform` and the dependencies set to `{reducer: 'content'}`. ## Proxied backend routes diff --git a/packages/registry/news/6422.bugfix b/packages/registry/news/6422.bugfix new file mode 100644 index 0000000000..978f5bfa81 --- /dev/null +++ b/packages/registry/news/6422.bugfix @@ -0,0 +1 @@ +Return empty array when `getUtilities` does not match anything. @sneridagh diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 830202f797..7f229a66a8 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -474,7 +474,7 @@ class Config { .join('+'); const utilityName = `${depsString ? `|${depsString}` : ''}`; - const utilitiesKeys = Object.keys(this._data.utilities[type]).filter( + const utilitiesKeys = Object.keys(this._data.utilities[type] || {}).filter( (key) => key.startsWith(utilityName), ); const utilities = utilitiesKeys.map( diff --git a/packages/registry/src/registry.test.tsx b/packages/registry/src/registry.test.tsx index ab189a9368..90ad418249 100644 --- a/packages/registry/src/registry.test.tsx +++ b/packages/registry/src/registry.test.tsx @@ -1063,4 +1063,13 @@ describe('Utilities registry', () => { .method(), ).toEqual('this is a validator for maxLength'); }); + + it('getUtilities - registers two utilities with the same dependencies and different names', () => { + expect( + config.getUtilities({ + dependencies: { fieldType: 'string' }, + type: 'validator', + }), + ).toEqual([]); + }); }); diff --git a/packages/volto/news/6422.feature b/packages/volto/news/6422.feature new file mode 100644 index 0000000000..7eb17ffd98 --- /dev/null +++ b/packages/volto/news/6422.feature @@ -0,0 +1 @@ +Introducing "client transforms for Redux reducers". @sneridagh diff --git a/packages/volto/src/reducers/content/content.js b/packages/volto/src/reducers/content/content.js index c610b46e0a..475498d7f6 100644 --- a/packages/volto/src/reducers/content/content.js +++ b/packages/volto/src/reducers/content/content.js @@ -20,6 +20,8 @@ import { UPDATE_UPLOADED_FILES, } from '@plone/volto/constants/ActionTypes'; +import config from '@plone/volto/registry'; + const initialState = { create: { loaded: false, @@ -192,6 +194,16 @@ export default function content(state = initialState, action = {}) { }; }); } + + const transforms = config.getUtilities({ + type: 'transform', + dependencies: { reducer: 'content' }, + }); + + transforms.forEach(({ method }) => { + method(result); + }); + return action.subrequest ? { ...state, From 6b9986a175fe351c2f3e89ec28af6aaf7f3c5a31 Mon Sep 17 00:00:00 2001 From: Faakhir Zahid <110815427+Faakhir30@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:16:17 +0500 Subject: [PATCH 02/16] Used resource title instead of resource type in page title. (#6402) Co-authored-by: Jefferson Bledsoe Co-authored-by: Ichim David --- packages/volto/news/6308.internal | 1 + packages/volto/src/components/manage/Edit/Edit.jsx | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 packages/volto/news/6308.internal diff --git a/packages/volto/news/6308.internal b/packages/volto/news/6308.internal new file mode 100644 index 0000000000..c2b9271b6b --- /dev/null +++ b/packages/volto/news/6308.internal @@ -0,0 +1 @@ +Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 diff --git a/packages/volto/src/components/manage/Edit/Edit.jsx b/packages/volto/src/components/manage/Edit/Edit.jsx index 333f6cf651..93efab9a7c 100644 --- a/packages/volto/src/components/manage/Edit/Edit.jsx +++ b/packages/volto/src/components/manage/Edit/Edit.jsx @@ -346,11 +346,15 @@ class Edit extends Component { <> {this.props.content?.language && ( From 8427cfb087eab4899195134063701052777de59a Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Oct 2024 04:53:22 -0700 Subject: [PATCH 03/16] Fix broken links (#6439) --- docs/source/contributing/acceptance-tests.md | 2 +- docs/source/development/i18n.md | 8 ++++---- docs/source/upgrade-guide/index.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/contributing/acceptance-tests.md b/docs/source/contributing/acceptance-tests.md index ea9b66ec1f..c74bd9d4cb 100644 --- a/docs/source/contributing/acceptance-tests.md +++ b/docs/source/contributing/acceptance-tests.md @@ -72,7 +72,7 @@ There is a directory per spec. This directory is hot reloaded with your changes as you write the tests. ```{seealso} -[Cypress documentation](https://docs.cypress.io/guides/overview/why-cypress) +[Cypress documentation](https://docs.cypress.io/app/get-started/why-cypress) ``` diff --git a/docs/source/development/i18n.md b/docs/source/development/i18n.md index 01072108de..36bffe4dfc 100644 --- a/docs/source/development/i18n.md +++ b/docs/source/development/i18n.md @@ -17,7 +17,7 @@ This chapter describes the most common use cases for internationalization when d ## Process and file structure overview Volto uses the library [react-intl](https://www.npmjs.com/package/react-intl) to provide translations for any potential language. -Anything in the [official documentation of react-intl](https://formatjs.io/docs/react-intl/) also applies to Volto. +Anything in the [official documentation of react-intl](https://formatjs.github.io/docs/react-intl/) also applies to Volto. The workflow for creating *new* translatable text strings is as follows: @@ -79,13 +79,13 @@ function HelloWorld(props) { The identifier `hello_world` is then commonly used across all the translations. There are more features available, such as using placeholders. -See the documentation for all features in the [`FormattedMessage` component](https://formatjs.io/docs/react-intl/components#formattedmessage). +See the documentation for all features in the [`FormattedMessage` component](https://formatjs.github.io/docs/react-intl/components#formattedmessage). ### Translate attributes As `FormatMessage` is only suitable for creating text within HTML elements, it cannot be used for translating individual attributes. -But with the method [`formatMessage`](https://formatjs.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings. +But with the method [`formatMessage`](https://formatjs.github.io/docs/react-intl/api/#formatmessage), there is another way to translate primitive strings. This approach can be best explained with an example. Assume you have a component called `TeaserImage` which contains an image that has, for accessibility reasons, the `alt` attribute. @@ -97,7 +97,7 @@ To translate the `alt` attribute, you have to do the following steps: import { defineMessages, injectIntl, intlShape } from 'react-intl'; ``` -2. Define a message (or more) via [`defineMessages`](https://formatjs.io/docs/react-intl/api/#definemessagesdefinemessage): +2. Define a message (or more) via [`defineMessages`](https://formatjs.github.io/docs/react-intl/api/#definemessagesdefinemessage): ```js const messages = defineMessages({ diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index 321510c672..2371e089c4 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -743,7 +743,7 @@ This is because the overrides that `@testing-library/cypress` introduce can be r Since there are some commands that can call exports in {file}`cypress/support/commands.js`, this import may be run more than once, and then it errors. So you have to make sure that import is run only once while the tests are run. -Check the official [Cypress Migration Guide](https://docs.cypress.io/guides/references/migration-guide) for more information. +Check the official [Cypress Migration Guide](https://docs.cypress.io/app/references/migration-guide) for more information. ### New Image component @@ -1135,7 +1135,7 @@ If you have already updated your configuration to use Cypress 10 or later in a p It is possible that forcing your project to use older versions might still work with old configurations. ```{seealso} -See https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0 for more information. +See https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-version-10-0 for more information. ``` ### The complete configuration registry is passed to the add-ons and the project configuration pipeline From 885b16a21397364994625dc359e06b8983be2753 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Oct 2024 05:55:04 -0700 Subject: [PATCH 04/16] Fix broken links (#6440) --- docs/source/upgrade-guide/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index 2371e089c4..3da7fbb39a 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -1135,7 +1135,7 @@ If you have already updated your configuration to use Cypress 10 or later in a p It is possible that forcing your project to use older versions might still work with old configurations. ```{seealso} -See https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-version-10-0 for more information. +See https://docs.cypress.io/app/references/migration-guide#Migrating-to-Cypress-100 for more information. ``` ### The complete configuration registry is passed to the add-ons and the project configuration pipeline From 42daabae0a9b37e0bfb638098feadcd7a31c98d3 Mon Sep 17 00:00:00 2001 From: David Glick Date: Fri, 25 Oct 2024 07:55:08 -0700 Subject: [PATCH 05/16] URL-Management control panel: Add missing translations (#6436) --- .../volto/locales/ca/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/de/LC_MESSAGES/volto.po | 36 ++++++++++++++++--- .../volto/locales/en/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/es/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/eu/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/fi/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/fr/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/hi/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/it/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/ja/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/nl/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/pt/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/pt_BR/LC_MESSAGES/volto.po | 26 ++++++++++++++ .../volto/locales/ro/LC_MESSAGES/volto.po | 26 ++++++++++++++ packages/volto/locales/volto.pot | 28 ++++++++++++++- .../volto/locales/zh_CN/LC_MESSAGES/volto.po | 26 ++++++++++++++ packages/volto/news/6436.bugfix | 1 + .../manage/Controlpanels/Aliases.jsx | 32 +++++++++++++---- 18 files changed, 449 insertions(+), 12 deletions(-) create mode 100644 packages/volto/news/6436.bugfix diff --git a/packages/volto/locales/ca/LC_MESSAGES/volto.po b/packages/volto/locales/ca/LC_MESSAGES/volto.po index 9c127313f2..9ad9f86d79 100644 --- a/packages/volto/locales/ca/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ca/LC_MESSAGES/volto.po @@ -432,6 +432,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -491,6 +496,11 @@ msgstr "Base de cerca" msgid "Block" msgstr "Bloc" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1414,6 +1424,11 @@ msgstr "" msgid "Event view" msgstr "Esdeveniment" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1525,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Fitxers pujats: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2185,6 +2201,11 @@ msgstr "Gestiona les traduccions per a {title}" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4584,6 +4605,11 @@ msgstr "quan" msgid "event_where" msgstr "On?" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/de/LC_MESSAGES/volto.po b/packages/volto/locales/de/LC_MESSAGES/volto.po index accbee32db..e8627e28be 100644 --- a/packages/volto/locales/de/LC_MESSAGES/volto.po +++ b/packages/volto/locales/de/LC_MESSAGES/volto.po @@ -431,6 +431,11 @@ msgstr "Rolle {role} zum {entry} zugewiesen." msgid "Assignments" msgstr "Zuweisungen" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "automatisch" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -490,6 +495,11 @@ msgstr "Basis Suchfilter" msgid "Block" msgstr "Block" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "beide" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -862,12 +872,12 @@ msgstr "Arbeitskopie erstellen" #. Default: "Created after" #: components/manage/Controlpanels/Aliases msgid "Created after" -msgstr "" +msgstr "Erstellt nach" #. Default: "Created before" #: components/manage/Controlpanels/Aliases msgid "Created before" -msgstr "" +msgstr "Erstellt vor" #. Default: "Created by {creator} on {date}" #: components/manage/WorkingCopyToastsFactory/WorkingCopyToastsFactory @@ -1413,6 +1423,11 @@ msgstr "" msgid "Event view" msgstr "Termin" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "Beispiel" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1524,9 +1539,10 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Hochgeladene Dateien: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" -msgstr "" +msgstr "Filtern" #. Default: "Filter Rules:" #: components/manage/Controlpanels/Rules/Rules @@ -1536,7 +1552,7 @@ msgstr "Filterregeln:" #. Default: "Filter by path" #: components/manage/Controlpanels/Aliases msgid "Filter by prefix" -msgstr "Nach Präfix filtern" +msgstr "Nach Pfad filtern" #. Default: "Filter users by groups" #: helpers/MessageLabels/MessageLabels @@ -2184,6 +2200,11 @@ msgstr "Übersetzungen für {} verwalten" msgid "Manual" msgstr "Manuell" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "manuell" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4471,7 +4492,7 @@ msgstr "Passwort vergessen?" #. Default: "Add many alternative URLs at once by uploading a CSV file. The first column should be the path to redirect from; the second, the path to redirect to. Both paths must be Plone-site-relative, starting with a slash (/). An optional third column can contain a date and time. An optional fourth column can contain a boolean to mark as a manual redirect (default true)." #: components/manage/Controlpanels/Aliases msgid "bulkUploadUrlsHelp" -msgstr "" +msgstr "Fügen Sie mehrere alternative URLs auf einmal hinzu, indem Sie eine CSV-Datei hochladen. Die erste Spalte sollte den Pfad enthalten, von dem die Weiterleitung erfolgen soll; die zweite den Pfad, zu dem die Weiterleitung erfolgen soll. Beide Pfade müssen relativ zur Plone-Site sein und mit einem Schrägstrich (/) beginnen. Eine optionale dritte Spalte kann ein Datum und eine Uhrzeit enthalten. Eine optionale vierte Spalte kann einen Booleschen Wert enthalten, um eine manuelle Weiterleitung zu kennzeichnen (Standard: „true“)." #. Default: "Checkbox" #: config/Blocks @@ -4583,6 +4604,11 @@ msgstr "Datum" msgid "event_where" msgstr "Ort" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "/beispiel" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/en/LC_MESSAGES/volto.po b/packages/volto/locales/en/LC_MESSAGES/volto.po index 77d49db13f..1dba741cab 100644 --- a/packages/volto/locales/en/LC_MESSAGES/volto.po +++ b/packages/volto/locales/en/LC_MESSAGES/volto.po @@ -426,6 +426,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -485,6 +490,11 @@ msgstr "" msgid "Block" msgstr "" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1408,6 +1418,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1519,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2179,6 +2195,11 @@ msgstr "" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4578,6 +4599,11 @@ msgstr "" msgid "event_where" msgstr "" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/es/LC_MESSAGES/volto.po b/packages/volto/locales/es/LC_MESSAGES/volto.po index 4cc52bdb15..23630b90a1 100644 --- a/packages/volto/locales/es/LC_MESSAGES/volto.po +++ b/packages/volto/locales/es/LC_MESSAGES/volto.po @@ -433,6 +433,11 @@ msgstr "" msgid "Assignments" msgstr "Tareas" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -492,6 +497,11 @@ msgstr "Consulta base de la búsqueda" msgid "Block" msgstr "Bloque" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1415,6 +1425,11 @@ msgstr "" msgid "Event view" msgstr "Vista de evento" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1526,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Archivos subidos: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "Filtrar" @@ -2186,6 +2202,11 @@ msgstr "Administrar traducciones de {title}" msgid "Manual" msgstr "Manual" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4585,6 +4606,11 @@ msgstr "Cuándo" msgid "event_where" msgstr "Dónde" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/eu/LC_MESSAGES/volto.po b/packages/volto/locales/eu/LC_MESSAGES/volto.po index e2fed09346..095444c38d 100644 --- a/packages/volto/locales/eu/LC_MESSAGES/volto.po +++ b/packages/volto/locales/eu/LC_MESSAGES/volto.po @@ -433,6 +433,11 @@ msgstr "" msgid "Assignments" msgstr "Esleipenak" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -492,6 +497,11 @@ msgstr "Oinarrizko bilaketa kontsulta" msgid "Block" msgstr "Blokea" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1415,6 +1425,11 @@ msgstr "" msgid "Event view" msgstr "Hitzorduaren bista" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1526,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Igotako fitxategiak: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2186,6 +2202,11 @@ msgstr "Kudeatu honen itzulpenak {title}" msgid "Manual" msgstr "Eskuz" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4585,6 +4606,11 @@ msgstr "Noiz" msgid "event_where" msgstr "Non" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/fi/LC_MESSAGES/volto.po b/packages/volto/locales/fi/LC_MESSAGES/volto.po index f851233f39..2be0080512 100644 --- a/packages/volto/locales/fi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fi/LC_MESSAGES/volto.po @@ -431,6 +431,11 @@ msgstr "" msgid "Assignments" msgstr "Tehtävät" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -490,6 +495,11 @@ msgstr "" msgid "Block" msgstr "Palikka" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1413,6 +1423,11 @@ msgstr "" msgid "Event view" msgstr "Tapahtuman näkymä" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1524,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Lähetetyt tiedostot: {uploadedFiles} " #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2184,6 +2200,11 @@ msgstr "Hallitse käännöksiä: {title}" msgid "Manual" msgstr "Manuaalinen" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4583,6 +4604,11 @@ msgstr "Milloin" msgid "event_where" msgstr "Missä" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/fr/LC_MESSAGES/volto.po b/packages/volto/locales/fr/LC_MESSAGES/volto.po index 2f5d0b3005..be16dc03d7 100644 --- a/packages/volto/locales/fr/LC_MESSAGES/volto.po +++ b/packages/volto/locales/fr/LC_MESSAGES/volto.po @@ -433,6 +433,11 @@ msgstr "" msgid "Assignments" msgstr "Affectations" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -492,6 +497,11 @@ msgstr "Requête de base de la recherche" msgid "Block" msgstr "Bloc" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1415,6 +1425,11 @@ msgstr "" msgid "Event view" msgstr "Vue d'événement" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1526,6 +1541,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Fichiers téléchargés : {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2186,6 +2202,11 @@ msgstr "Gérer les traductions pour {title}" msgid "Manual" msgstr "Manuel" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4585,6 +4606,11 @@ msgstr "Quand" msgid "event_where" msgstr "Où" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/hi/LC_MESSAGES/volto.po b/packages/volto/locales/hi/LC_MESSAGES/volto.po index 06006f6f10..11afb55efa 100644 --- a/packages/volto/locales/hi/LC_MESSAGES/volto.po +++ b/packages/volto/locales/hi/LC_MESSAGES/volto.po @@ -426,6 +426,11 @@ msgstr "{entry} को {role} भूमिका का काम दें" msgid "Assignments" msgstr "असाइनमेंट" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -485,6 +490,11 @@ msgstr "मूल खोज क्वेरी" msgid "Block" msgstr "ब्लॉक" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1408,6 +1418,11 @@ msgstr "" msgid "Event view" msgstr "आयोजन दृश्य" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1519,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr ""फ़ाइलें अपलोड की गईं: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "फ़िल्टर" @@ -2179,6 +2195,11 @@ msgstr "{title} के लिए अनुवाद प्रबंधित क msgid "Manual" msgstr "मैनुअल" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4578,6 +4599,11 @@ msgstr "कब" msgid "event_where" msgstr "कहाँ" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/it/LC_MESSAGES/volto.po b/packages/volto/locales/it/LC_MESSAGES/volto.po index 794ac7f9a6..13eb9c2280 100644 --- a/packages/volto/locales/it/LC_MESSAGES/volto.po +++ b/packages/volto/locales/it/LC_MESSAGES/volto.po @@ -426,6 +426,11 @@ msgstr "Assegnare il ruolo di {role} a {entry}" msgid "Assignments" msgstr "Assegnazione" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -485,6 +490,11 @@ msgstr "Ricerca iniziale" msgid "Block" msgstr "Blocco" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1408,6 +1418,11 @@ msgstr "La data di inizio evento essere uguale o precedente al {endDateValueOrEn msgid "Event view" msgstr "Vista evento" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1519,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "File caricati: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "Filtra" @@ -2179,6 +2195,11 @@ msgstr "Gestisci le traduzioni per {title}" msgid "Manual" msgstr "Manuale" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4578,6 +4599,11 @@ msgstr "Quando" msgid "event_where" msgstr "Dove" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/ja/LC_MESSAGES/volto.po b/packages/volto/locales/ja/LC_MESSAGES/volto.po index 3d016b4985..8f69541e34 100644 --- a/packages/volto/locales/ja/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ja/LC_MESSAGES/volto.po @@ -431,6 +431,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -490,6 +495,11 @@ msgstr "" msgid "Block" msgstr "ブロック" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1413,6 +1423,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1524,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "アップロードされたファイル: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2184,6 +2200,11 @@ msgstr "{title}の翻訳を管理する" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4583,6 +4604,11 @@ msgstr "日時" msgid "event_where" msgstr "場所" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po index 7ae5fd8b4a..4357dae948 100644 --- a/packages/volto/locales/nl/LC_MESSAGES/volto.po +++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po @@ -430,6 +430,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -489,6 +494,11 @@ msgstr "" msgid "Block" msgstr "" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1412,6 +1422,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1523,6 +1538,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Geüploade bestanden: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2183,6 +2199,11 @@ msgstr "" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4582,6 +4603,11 @@ msgstr "" msgid "event_where" msgstr "" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/pt/LC_MESSAGES/volto.po b/packages/volto/locales/pt/LC_MESSAGES/volto.po index 38e7165a15..d30596d054 100644 --- a/packages/volto/locales/pt/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt/LC_MESSAGES/volto.po @@ -431,6 +431,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -490,6 +495,11 @@ msgstr "" msgid "Block" msgstr "Bloco" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1413,6 +1423,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1524,6 +1539,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Arquivos enviados: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2184,6 +2200,11 @@ msgstr "" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4583,6 +4604,11 @@ msgstr "" msgid "event_where" msgstr "" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po index a49133408d..af520c765e 100644 --- a/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po +++ b/packages/volto/locales/pt_BR/LC_MESSAGES/volto.po @@ -432,6 +432,11 @@ msgstr "Atribuir o papel de {role} à {entry}" msgid "Assignments" msgstr "Atribuições" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -491,6 +496,11 @@ msgstr "Base da consulta para busca" msgid "Block" msgstr "Bloco" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1414,6 +1424,11 @@ msgstr "A data de início do evento deve ser igual ou anterior a {endDateValueOr msgid "Event view" msgstr "Visão de Evento" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1525,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Arquivos carregados: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "Filtrar" @@ -2185,6 +2201,11 @@ msgstr "Gerenciar traduções para {title}" msgid "Manual" msgstr "Manual" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4584,6 +4605,11 @@ msgstr "Quando" msgid "event_where" msgstr "Onde" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/ro/LC_MESSAGES/volto.po b/packages/volto/locales/ro/LC_MESSAGES/volto.po index 64f8414719..6ef3d38984 100644 --- a/packages/volto/locales/ro/LC_MESSAGES/volto.po +++ b/packages/volto/locales/ro/LC_MESSAGES/volto.po @@ -426,6 +426,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -485,6 +490,11 @@ msgstr "Interogare căutare de bază" msgid "Block" msgstr "Bloc" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1408,6 +1418,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1519,6 +1534,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "Fișiere încărcate: {uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2179,6 +2195,11 @@ msgstr "Gestionați traducerile pentru {title}" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4578,6 +4599,11 @@ msgstr "Data" msgid "event_where" msgstr "Locație" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/volto.pot b/packages/volto/locales/volto.pot index 8d69039230..0490c9b7a6 100644 --- a/packages/volto/locales/volto.pot +++ b/packages/volto/locales/volto.pot @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Plone\n" -"POT-Creation-Date: 2024-10-22T18:13:27.236Z\n" +"POT-Creation-Date: 2024-10-24T16:41:21.354Z\n" "Last-Translator: Plone i18n \n" "Language-Team: Plone i18n \n" "Content-Type: text/plain; charset=utf-8\n" @@ -428,6 +428,11 @@ msgstr "" msgid "Assignments" msgstr "" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -487,6 +492,11 @@ msgstr "" msgid "Block" msgstr "" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1410,6 +1420,11 @@ msgstr "" msgid "Event view" msgstr "" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1521,6 +1536,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2181,6 +2197,11 @@ msgstr "" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4580,6 +4601,11 @@ msgstr "" msgid "event_where" msgstr "" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po index c64ddee6a5..6d98786196 100644 --- a/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po +++ b/packages/volto/locales/zh_CN/LC_MESSAGES/volto.po @@ -432,6 +432,11 @@ msgstr "" msgid "Assignments" msgstr "分配" +#. Default: "Automatically" +#: components/manage/Controlpanels/Aliases +msgid "Automatically" +msgstr "" + #. Default: "Available" #: components/manage/Controlpanels/AddonsControlpanel msgid "Available" @@ -491,6 +496,11 @@ msgstr "基本搜索查询" msgid "Block" msgstr "块" +#. Default: "Both" +#: components/manage/Controlpanels/Aliases +msgid "Both" +msgstr "" + #. Default: "Both email address and password are case sensitive, check that caps lock is not enabled." #: components/theme/Login/Login msgid "Both email address and password are case sensitive, check that caps lock is not enabled." @@ -1414,6 +1424,11 @@ msgstr "" msgid "Event view" msgstr "事件视图" +#. Default: "Example" +#: components/manage/Controlpanels/Aliases +msgid "Example" +msgstr "" + #. Default: "Exclude from navigation" #: components/manage/Contents/ContentsPropertiesModal msgid "Exclude from navigation" @@ -1525,6 +1540,7 @@ msgid "Files uploaded: {uploadedFiles}" msgstr "已上载的文件:{uploadedFiles}" #. Default: "Filter" +#: components/manage/Controlpanels/Aliases #: helpers/MessageLabels/MessageLabels msgid "Filter" msgstr "" @@ -2185,6 +2201,11 @@ msgstr "管理{title}的翻译" msgid "Manual" msgstr "" +#. Default: "Manually" +#: components/manage/Controlpanels/Aliases +msgid "Manually" +msgstr "" + #. Default: "Manually or automatically added?" #: components/manage/Controlpanels/Aliases msgid "Manually or automatically added?" @@ -4584,6 +4605,11 @@ msgstr "" msgid "event_where" msgstr "" +#. Default: "/example" +#: components/manage/Controlpanels/Aliases +msgid "examplePath" +msgstr "" + #. Default: "This website does not accept files larger than {limit}" #: helpers/MessageLabels/MessageLabels msgid "fileTooLarge" diff --git a/packages/volto/news/6436.bugfix b/packages/volto/news/6436.bugfix new file mode 100644 index 0000000000..a0e0bf3309 --- /dev/null +++ b/packages/volto/news/6436.bugfix @@ -0,0 +1 @@ +URL Management control panel: add missing translations. @davisagli diff --git a/packages/volto/src/components/manage/Controlpanels/Aliases.jsx b/packages/volto/src/components/manage/Controlpanels/Aliases.jsx index 56bd71f0e9..d607e4949b 100644 --- a/packages/volto/src/components/manage/Controlpanels/Aliases.jsx +++ b/packages/volto/src/components/manage/Controlpanels/Aliases.jsx @@ -106,6 +106,22 @@ const messages = defineMessages({ id: 'CSVFile', defaultMessage: 'CSV file', }, + Both: { + id: 'Both', + defaultMessage: 'Both', + }, + Automatically: { + id: 'Automatically', + defaultMessage: 'Automatically', + }, + Manually: { + id: 'Manually', + defaultMessage: 'Manually', + }, + examplePath: { + id: 'examplePath', + defaultMessage: '/example', + }, }); const filterChoices = [ @@ -320,7 +336,7 @@ const Aliases = (props) => { defaultMessage="Enter the absolute path where the alternative URL should exist. The path must start with '/'. Only URLs that result in a 404 not found page will result in a redirect occurring." /> ), - placeholder: '/example', + placeholder: intl.formatMessage(messages.examplePath), }, targetUrlPath: { title: intl.formatMessage(messages.targetUrlPathTitle), @@ -330,7 +346,7 @@ const Aliases = (props) => { defaultMessage="Enter the absolute path of the target. Target must exist or be an existing alternative URL path to the target." /> ), - placeholder: '/example', + placeholder: intl.formatMessage(messages.examplePath), }, }, required: ['altUrlPath', 'targetUrlPath'], @@ -359,7 +375,11 @@ const Aliases = (props) => { />

- Example: + + :
/old-home-page.asp,/front-page,2019/01/27 10:42:59 @@ -407,7 +427,7 @@ const Aliases = (props) => { > setFilterQuery(e.target.value)} /> @@ -422,7 +442,7 @@ const Aliases = (props) => { {filterChoices.map((o, i) => ( { )} From bd591ca11e4a875864aa7ecf0a73f5f2f6e8153e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Oct 2024 09:40:43 -0700 Subject: [PATCH 06/16] Rename page title from Frontend to Volto UI (#6438) --- docs/source/index.md | 16 ++++++++-------- packages/volto/news/6438.documentation | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 packages/volto/news/6438.documentation diff --git a/docs/source/index.md b/docs/source/index.md index ef65b2294f..2b47ebce16 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -1,18 +1,18 @@ --- myst: html_meta: - "description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release." - "property=og:description": "Volto is the React-based frontend for the Plone CMS. It is the default UI for the Plone 6 release." - "property=og:title": "Frontend" - "keywords": "Volto, Plone, frontend, React" + "description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6." + "property=og:description": "The Volto user interface (UI) is a React-based frontend for the Plone CMS. It is the default user interface starting with the release of Plone 6." + "property=og:title": "Volto UI" + "keywords": "Volto, Plone, frontend, user interface, React" --- (volto-index-label)= -# Frontend +# Volto UI -Volto is a React-based frontend for the [Plone CMS](https://plone.org). -It is the default frontend starting with the Plone 6 release. +The Volto user interface (UI) is a React-based frontend for the [Plone content management system](https://plone.org). +It is the default user interface starting with the release of Plone 6. Volto provides an attractive proposition: integration with the modern frontend development world, access to the huge ecosystem of React libraries and add-ons, combined with the ability to use the mature Plone CMS backend as a development platform. @@ -38,7 +38,7 @@ An integrator is someone who uses Volto to build a project. ### Users -A user of Volto is someone who edits content in a Plone content management system with Volto as the frontend. +A user of Volto is someone who edits content in a Plone content management system with Volto as the user interface. - {doc}`user-manual/index` provides information about how to manage content in a Plone site. diff --git a/packages/volto/news/6438.documentation b/packages/volto/news/6438.documentation new file mode 100644 index 0000000000..f894f6db1b --- /dev/null +++ b/packages/volto/news/6438.documentation @@ -0,0 +1 @@ +Rename page title from Frontend to Volto UI. @stevepiercy From 3ff2f2711a70288a0c5e51b6111dd938ee87947e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 26 Oct 2024 04:42:58 -0700 Subject: [PATCH 07/16] Clean up #6422 (#6443) --- docs/source/backend/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/backend/index.md b/docs/source/backend/index.md index 4292411c35..614be62855 100644 --- a/docs/source/backend/index.md +++ b/docs/source/backend/index.md @@ -34,9 +34,9 @@ Block transformers Search and indexing integration : By providing the right adapters, you can extract searchable text from blocks. -Client Reducer Content Transforms +Client reducer content transforms : These transforms run in the client when the response from the backend is received. - These are useful when you need to modify on the fly the response from the backend, in case you need to make an amendment of the backend data, like a data migration of any kind. + These are useful when you need to modify the response from the backend on-the-fly for amending the backend data, such as a data migration of any kind. You can register a utility that mutates the response at your convenience. ```ts @@ -50,7 +50,7 @@ Client Reducer Content Transforms }); ``` - The `type` of the utility needs to be `transform` and the dependencies set to `{reducer: 'content'}`. + The `type` of the utility needs to be `transform`, and the `dependencies` set to `{reducer: 'content'}`. ## Proxied backend routes From 523e750a6d813a88e8a72a1e0eddae5b66bd1fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sat, 26 Oct 2024 13:46:16 +0200 Subject: [PATCH 08/16] Add deprecation notices to the Upgrade guide (#6426) Co-authored-by: Steve Piercy Co-authored-by: David Glick --- docs/source/upgrade-guide/index.md | 136 +++++++++++++++++++++++++ packages/volto/news/6426.documentation | 1 + 2 files changed, 137 insertions(+) create mode 100644 packages/volto/news/6426.documentation diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index 3da7fbb39a..60de994f58 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -522,6 +522,142 @@ The `react/jsx-key` rule has been enabled in ESlint for catching missing `key` i You might catch some violations in your project or add-on code after running ESlint. Adding the missing `key` property whenever the violation is reported will fix it. +### Deprecation notices for Volto 18 + +#### `@plone/generator-volto` + +```{deprecated} Volto 18.0.0 +``` + +The Node.js-based Volto project boilerplate generator is deprecated from Volto 18 onwards. +After the release of Volto 18, it will be marked as deprecated, archived, and it won't receive any further updates. +Although you can still migrate your project to Volto 18 using this boilerplate, you should migrate to using [Cookieplone](https://github.com/plone/cookieplone). + +##### Alternative + +Migrate your project to use a [Cookieplone](https://github.com/plone/cookieplone) boilerplate. + +#### Volto project configurations + +```{deprecated} Volto 18.0.0 +``` + +Configuring Volto using {file}`src/config.js` at the project level is deprecated in Volto 18, and will be removed in Volto 19. + +```{seealso} +See https://github.com/plone/volto/issues/6396 for details. +``` + +##### Alternative + +You should configure your projects in a policy add-on. +You can move your project to use [Cookieplone](https://github.com/plone/cookieplone) which provides the necessary boilerplate for it. + +#### Semantic UI + +```{deprecated} Volto 18.0.0 +``` + +The Semantic UI library is not maintained anymore, and will be removed in Plone 7. +You should no longer use Semantic UI in add-ons and projects. + +```{seealso} +Related PLIPs: + +- https://github.com/plone/volto/issues/6321 +- https://github.com/plone/volto/issues/6323 +``` + +##### Alternatives + +You can use any supported component framework of your choice for implementing new components, especially in the public theme side. +If you create new widgets or components for the CMSUI—in other words, the non-public side—you should use the [`@plone/components`](https://github.com/plone/volto/tree/main/packages/components) library as an alternative. +Even though it's still in the development phase, it will be completed in the next few months, and will be supported in the future. + +#### `lodash` library + +```{deprecated} Volto 18.0.0 +``` + +`lodash` is deprecated in Volto 18, and will be removed in Plone 7. + +`lodash` has not received any updates since 2021. +It has performance issues from bloated bundles and it's not prepared for ESM. +Lots of `lodash` utility helpers can be replaced with vanilla ES. +These issues cause concern about its future maintainability. + +In place of `lodash`, Plone 7 will use both the `lodash-es` library, which is ESM ready, and modern vanilla ES alternatives whenever possible. + +##### Alternatives + +```{seealso} +The following links suggest alternatives to `lodash`. + +- https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore +- https://javascript.plainenglish.io/you-dont-need-lodash-how-i-gave-up-lodash-693c8b96a07c + +If you still need some of the utilities in `lodash` and cannot use vanilla ES, you can use `lodash-es` instead. +``` + +#### `@loadable/component` and Volto `Loadables` framework + +```{deprecated} Volto 18.0.0 +``` + +`@loadable/component` and the Volto `Loadables` framework is deprecated in Volto 18, and will be removed from Plone 7. +It's a Webpack-only library, and it does not have Vite plugin support. +Since React 18, this library is no longer necessary, as it has the initial implementation of the "concurrent mode". +React 19 will further improve it and add more features around it. + +##### Alternatives + +Use plain React 18 lazy load features and its idioms for lazy load components. + +```jsx +const myLazyComponent = lazy(()=> import('@plone/volto/components/theme/MyLazyComponent/MyLazyComponent')) + +const RandomComponent = (props) => ( + + + +) +``` + +There's no support for pre-loading or lazy loading entire libraries as in `@loadable/component`. +With the removal of barrel imports files, as described in the next deprecation notice, it is now unnecessary. + +#### Removal of barrel import files + +```{deprecated} Volto 18.0.0 +``` + +Volto previously used barrel imports, which are centralized files where other imports are re-exported, to improve the developer user experience. +With barrel imports, a developer only needs to remember to import from the re-exported place, not the full path. + +Since the barrel imports directly import all the code, a lot of imports ended up in the same main chunk of code. +It became a bad practice. +Modern bundlers, such as Vite, rely upon the import path to determine whether to bundle code together or not, reducing the bundle size. + +The barrel imports must be removed to increase the natural number of chunks that Volto divides on—especially on routes—resulting in code splitting done the right and natural way. +This forces us to rewrite all the imports everywhere—including core, projects, and add-ons—once we implement it. +The barrel imports files include the following. + +- {file}`src/components/index.js` +- {file}`src/helpers/index.js` +- {file}`src/actions/index.js` + +##### Alternative + +Implement only direct imports in code, preparing now for the upcoming change. + +```diff +-import { BodyClass } from '@plone/volto/helpers'; ++import BodyClass from '@plone/volto/helpers/BodyClass/BodyClass'; +``` + +Once this is implemented, a code modification will be provided for a smooth migration. + + (volto-upgrade-guide-17.x.x)= ## Upgrading to Volto 17.x.x diff --git a/packages/volto/news/6426.documentation b/packages/volto/news/6426.documentation new file mode 100644 index 0000000000..dc5079729a --- /dev/null +++ b/packages/volto/news/6426.documentation @@ -0,0 +1 @@ +Added deprecation notices to the upgrade guide for Volto 18. @sneridagh From 715503dcdd638cb5c6a4fd3fd55d7d82d1352613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sat, 26 Oct 2024 13:52:08 +0200 Subject: [PATCH 09/16] Replace all `yarn` appearences with `pnpm` (#6433) Co-authored-by: David Glick Co-authored-by: Steve Piercy --- docs/source/addons/i18n.md | 6 +- docs/source/addons/theme.md | 2 +- docs/source/client/quick-start.md | 4 +- .../configuration/environmentvariables.md | 38 +++++------ docs/source/configuration/internalproxy.md | 2 +- docs/source/configuration/volto-config-js.md | 4 +- .../configuration/zero-config-builds.md | 2 +- docs/source/contributing/linting.md | 11 +++- docs/source/contributing/testing.md | 5 +- docs/source/deploying/seamless-mode.md | 8 +-- docs/source/deploying/sentry.md | 4 +- docs/source/deploying/simple.md | 6 +- docs/source/development/creating-project.md | 57 ++--------------- .../development/environment-variables.md | 8 +-- docs/source/development/i18n.md | 6 +- docs/source/development/ie11compat.md | 64 ------------------- docs/source/development/index.md | 1 - docs/source/development/lazyload.md | 2 +- packages/volto/news/6433.documentation | 1 + 19 files changed, 64 insertions(+), 167 deletions(-) delete mode 100644 docs/source/development/ie11compat.md create mode 100644 packages/volto/news/6433.documentation diff --git a/docs/source/addons/i18n.md b/docs/source/addons/i18n.md index 4ae542e544..9df832da70 100644 --- a/docs/source/addons/i18n.md +++ b/docs/source/addons/i18n.md @@ -23,10 +23,10 @@ Your add-on has a `locales` folder with a `.pot` file. └── volto.po ``` -1. Run `yarn i18n` in the context of your add-on. +1. Run `pnpm i18n` in the context of your add-on. 1. Go to each `.po` file in your `locales` folder, and write the translations for each translation literal. -In the context of your project, run `yarn i18n` to merge the add-on translations with the ones of your project. +In the context of your project, run `pnpm i18n` to merge the add-on translations with the ones of your project. ## Override translations @@ -34,4 +34,4 @@ In the context of your project, run `yarn i18n` to merge the add-on translations If you have multiple add-ons installed in your project, the translations are loaded in the order your add-ons are listed in `package.json`. If two add-ons provide different translations for the same message, then the last defined add-on wins. -When running `yarn i18n` in the context of your project, the project's own locales are processed last and can override translations from any add-on. +When running `pnpm i18n` in the context of your project, the project's own locales are processed last and can override translations from any add-on. diff --git a/docs/source/addons/theme.md b/docs/source/addons/theme.md index d14d559140..99251fd370 100644 --- a/docs/source/addons/theme.md +++ b/docs/source/addons/theme.md @@ -33,7 +33,7 @@ or add a key in your `package.json` project: or via a `THEME` variable: ```shell -THEME='volto-my-theme' yarn start +THEME='volto-my-theme' pnpm start ``` 2. Create a directory `src/theme` in your add-on, then add this file `theme.config`, replacing `` with your add-on name: diff --git a/docs/source/client/quick-start.md b/docs/source/client/quick-start.md index dd3174a9b7..ad62cd181f 100644 --- a/docs/source/client/quick-start.md +++ b/docs/source/client/quick-start.md @@ -21,9 +21,11 @@ These functions can be used in other use cases like command line helpers, script To install the Javascript Plone client run the following command: ```shell -yarn add @plone/client +pnpm add @plone/client ``` +or use your package manager of choice. + ## `ploneClient` entry point The main artifact that the client provides is the `ploneClient` entry point. diff --git a/docs/source/configuration/environmentvariables.md b/docs/source/configuration/environmentvariables.md index a63c6144f1..701ce5c7c2 100644 --- a/docs/source/configuration/environmentvariables.md +++ b/docs/source/configuration/environmentvariables.md @@ -21,7 +21,7 @@ All configurable environment variables work at runtime, not only at build time. You could, for example, build your Volto application, then start it in production with the `RAZZLE_API_PATH` environment variable. ```shell -yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod +pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod ``` This brings you a lot of power since you don't have to rebuild on every configuration change. @@ -40,7 +40,7 @@ You can also generate builds on your continuous integration, then deploy them an However, if you are not able to upgrade the packages `plone.restapi` (8.12.1 or greater) and `plone.rest` (2.0.0a1 or greater) in the backend, you can adjust your web server configuration and use the `RAZZLE_LEGACY_TRAVERSE` flag. ```shell - RAZZLE_LEGACY_TRAVERSE=true yarn start:prod + RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod ``` `VOLTO_ROBOTSTXT` @@ -50,7 +50,7 @@ You can also generate builds on your continuous integration, then deploy them an ```shell VOLTO_ROBOTSTXT="User-agent: * - Disallow: /" yarn start + Disallow: /" pnpm start ``` ```{note} @@ -67,19 +67,19 @@ You can also generate builds on your continuous integration, then deploy them an It helps you identify problems with a customization that does not work as you expect. ```shell - DEBUG=volto:shadowing yarn start + DEBUG=volto:shadowing pnpm start ``` `i18n` enables the log of missing internationalization messages in the console. ```shell - DEBUG=volto:i18n yarn start + DEBUG=volto:i18n pnpm start ``` `*` enables logging everywhere it exists in Volto. ```shell - DEBUG=volto:* yarn start + DEBUG=volto:* pnpm start ``` `DEBUG_ADDONS_LOADER` @@ -107,34 +107,34 @@ You can also generate builds on your continuous integration, then deploy them an `ADDONS` can be used to temporarily add an add-on to your build for testing purposes. ```shell - yarn add volto-slate - ADDONS=volto-slate:asDefault yarn start + pnpm add @kitconcept/volto-light-theme + ADDONS=@kitconcept/volto-light-theme pnpm start ``` `ADDONS` can also be used to temporarily enable a feature or a set of customizations. ```shell # given a folder './packages/coresandbox', like in vanilla Volto - ADDONS=coresandbox:multilingualFixture yarn start + ADDONS=coresandbox:multilingualFixture pnpm start ``` If you need to specify several add-ons, separate them with a semicolon (`;`): ```shell - ADDONS="test-addon;test-addon2" yarn start + ADDONS="test-addon;test-addon2" pnpm start ``` - + You can specify profiles for installation: - + ```shell - ADDONS="test-addon:profile1;test-addon2:profile2" yarn start + ADDONS="test-addon:profile1;test-addon2:profile2" pnpm start ``` The following code snippets demonstrate how to configure add-ons. First in `package.json`: - + ```json "addons": [ "@kitconcept/volto-blocks-grid" @@ -152,8 +152,8 @@ You can also generate builds on your continuous integration, then deploy them an And finally using `ADDONS`: ```shell - yarn add volto-slate - ADDONS=volto-slate:asDefault yarn start + pnpm add volto-slate + ADDONS=volto-slate:asDefault pnpm start ``` As a result, your app will load the add-ons in the following order: @@ -165,14 +165,14 @@ You can also generate builds on your continuous integration, then deploy them an ```{important} The `ADDONS` key is a Volto specific configuration. Simply setting `ADDONS` doesn't download the JavaScript package. - This has to be covered another way, by either installing the add-on package (with `yarn add`), or loading it as a development package with `mrs-developer`. + This has to be covered another way, by either installing the add-on package (with `pnpm add`), or loading it as a development package with `mrs-developer`. ``` `BUILD_DIR` This is a runtime-only environment variable that directs the build to run Volto from a specific location, other than the default folder `build`. ```shell - yarn + pnpm install BUILD_DIR=dist node dist/server.js ``` @@ -182,7 +182,7 @@ You can also generate builds on your continuous integration, then deploy them an It can be relative to the current project or absolute. ```shell - VOLTOCONFIG=../../volto.config.js yarn start + VOLTOCONFIG=../../volto.config.js pnpm start ``` ```` diff --git a/docs/source/configuration/internalproxy.md b/docs/source/configuration/internalproxy.md index ec8612fe65..dde6c9b369 100644 --- a/docs/source/configuration/internalproxy.md +++ b/docs/source/configuration/internalproxy.md @@ -61,7 +61,7 @@ export const settings = { or use the environment variable: ```bash -RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite yarn start +RAZZLE_DEV_PROXY_API_PATH=http://localhost:8081/mysite pnpm start ``` This redefines the request path from the internal proxy of the server side Node.js process to the Plone content backend API, but leaves the frontend Volto process making all content requests to `http://localhost:3000/++api++/`. diff --git a/docs/source/configuration/volto-config-js.md b/docs/source/configuration/volto-config-js.md index a8e4c4faa7..8bb16ce951 100644 --- a/docs/source/configuration/volto-config-js.md +++ b/docs/source/configuration/volto-config-js.md @@ -70,11 +70,11 @@ This environment variable allows you to specify a custom location for {file}`vol It can be relative to the current project or absolute. ```shell -VOLTOCONFIG=../../volto.config.js yarn start +VOLTOCONFIG=../../volto.config.js pnpm start ``` ```shell -VOLTOCONFIG=$(pwd)/volto.config.js yarn start +VOLTOCONFIG=$(pwd)/volto.config.js pnpm start ``` You can also set it from the root of the monorepo: diff --git a/docs/source/configuration/zero-config-builds.md b/docs/source/configuration/zero-config-builds.md index 7b4c396075..5ac09d7d87 100644 --- a/docs/source/configuration/zero-config-builds.md +++ b/docs/source/configuration/zero-config-builds.md @@ -17,7 +17,7 @@ In the past (before Volto 13), Volto was configured in build time using several environment variables, commonly supplied via the command line, such as the following: ```shell -PORT=11001 RAZZLE_API_PATH=https://plone.org/api yarn build` +PORT=11001 RAZZLE_API_PATH=https://plone.org/api pnpm build` ``` and since Razzle is an isomorphic application, some of these values passed on build time, were diff --git a/docs/source/contributing/linting.md b/docs/source/contributing/linting.md index cd291fd556..feea6f0307 100644 --- a/docs/source/contributing/linting.md +++ b/docs/source/contributing/linting.md @@ -76,7 +76,7 @@ From here we will have access to the commands to check for errors and to fix the You can run the pnpm `eslint`, `prettier`, and `stylelint` commands from the Volto package folder: ```shell -pnpm lint +pnpm lint pnpm prettier pnpm stylelint ``` @@ -89,8 +89,13 @@ pnpm prettier:fix pnpm stylelint:fix ``` -````{note} -The same commands can be found in your Volto add-on projects, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file. +```{versionadded} Volto 18.0.0-alpha.43 +[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator. +Cookieplone uses the frontend code installed using `pnpm` instead of `yarn`. +``` + +````{deprecated} Volto 18.0.0 +The same commands can be found in your Volto legacy add-ons and projects created with `@plone/generator-volto`, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file. You will use similar commands to run the linting commands, but with `yarn` instead of `pnpm`: diff --git a/docs/source/contributing/testing.md b/docs/source/contributing/testing.md index a1244c9496..37f241151d 100644 --- a/docs/source/contributing/testing.md +++ b/docs/source/contributing/testing.md @@ -66,10 +66,9 @@ This makes it faster and easier to test code changes. In GitHub workflows or for testing add-ons, it's useful to use an alternate Jest configuration. Volto provides a way to do so using a file {file}`jest.config.js`, or pointing the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable. -Because the Volto add-ons and Volto add-ons projects still use `yarn`, you must run the test command using `yarn` instead of `pnpm`. ```shell -RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test +RAZZLE_JEST_CONFIG=my-custom-jest-config.js pnpm test ``` ```{note} @@ -85,7 +84,7 @@ Sometimes you need to enable different configurations and enable optional compon You can use the `ADDONS` environment variable to define them. ```bash -ADDONS=test-addon,test-addon2 yarn start +ADDONS=test-addon,test-addon2 pnpm start ``` See {doc}`../configuration/environmentvariables` for more information. diff --git a/docs/source/deploying/seamless-mode.md b/docs/source/deploying/seamless-mode.md index a678728a73..0173bdbaf3 100644 --- a/docs/source/deploying/seamless-mode.md +++ b/docs/source/deploying/seamless-mode.md @@ -48,13 +48,13 @@ All the environment variables that are configurable now work at runtime, not at Before Volto 13, you'd do: ```bash -RAZZLE_API_PATH=https://plone.org yarn build && yarn start:prod +RAZZLE_API_PATH=https://plone.org pnpm build && pnpm start:prod ``` From Volto 13 onwards, you can now do: ```bash -yarn build && RAZZLE_API_PATH=https://plone.org yarn start:prod +pnpm build && RAZZLE_API_PATH=https://plone.org pnpm start:prod ``` ```` @@ -130,7 +130,7 @@ server { error_log /dev/stdout; # [seamless mode] Recommended as default configuration, using seamless mode new plone.rest traversal - # yarn build && yarn start:prod + # pnpm build && pnpm start:prod location ~ /\+\+api\+\+($|/.*) { rewrite ^/\+\+api\+\+($|/.*) /VirtualHostBase/http/myservername.org/Plone/++api++/VirtualHostRoot/$1 break; proxy_pass http://backend; @@ -138,7 +138,7 @@ server { # Legacy deployment example, using RAZZLE_LEGACY_TRAVERSE Volto won't append ++api++ automatically # Recommended only if you can't upgrade to latest `plone.restapi` and `plone.rest` - # yarn build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true yarn start:prod + # pnpm build && RAZZLE_API_PATH=http://myservername.org/api RAZZLE_LEGACY_TRAVERSE=true pnpm start:prod # location ~ /api($|/.*) { # rewrite ^/api($|/.*) /VirtualHostBase/http/myservername.org/Plone/VirtualHostRoot/_vh_api$1 break; # proxy_pass http://backend; diff --git a/docs/source/deploying/sentry.md b/docs/source/deploying/sentry.md index f92ba34ecc..4266ffd4b8 100644 --- a/docs/source/deploying/sentry.md +++ b/docs/source/deploying/sentry.md @@ -82,7 +82,7 @@ SENTRY_AUTH_TOKEN=foo \ SENTRY_ORG=my_organization \ SENTRY_PROJECT=new_project \ SENTRY_RELEASE=2.0.0 \ -SENTRY_DSN=https://boo@sentry.com/1 yarn build +SENTRY_DSN=https://boo@sentry.com/1 pnpm build node build/server.js ``` @@ -265,7 +265,7 @@ SENTRY_PROJECT=new_project SENTRY_RELEASE=2.0.0 SENTRY_DSN=https://boo@sentry.com/1 SENTRY_FRONTEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"},"extras":{"logger":"javascript-frontend", "release":"1.4.1"}}' -SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} yarn build +SENTRY_BACKEND_CONFIG='{"tags":{"site":"www.test.com","app":"test_app"} pnpm build node build/server.js ``` diff --git a/docs/source/deploying/simple.md b/docs/source/deploying/simple.md index 1215cb3cd5..31b3894a3e 100644 --- a/docs/source/deploying/simple.md +++ b/docs/source/deploying/simple.md @@ -12,7 +12,7 @@ myst: Volto is a Node.js application that runs on your machine/server and listens to a port. Once you are ready to deploy it, you should build it running: ```bash -$ yarn build +$ pnpm build ``` The Volto configuration determines the external URL Volto will be served, so if you just issue this command, the build will get that values and build an static bundle with that values (PORT=3000, API_PATH=http://localhost:8080/Plone). @@ -20,13 +20,13 @@ The Volto configuration determines the external URL Volto will be served, so if In order to make Volto work on a server under an specific DNS name, you must parametrize the build like: ```bash -$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api yarn build +$ PORT=volto_node_process_port RAZZLE_API_PATH=https://mywebsite.com/api pnpm build ``` After the build, the bundle is created in `/build` folder, then in order to launch your application you can run: ```bash -$ yarn start:prod +$ pnpm start:prod ``` or ```bash diff --git a/docs/source/development/creating-project.md b/docs/source/development/creating-project.md index 5052533f81..f5ef93fefc 100644 --- a/docs/source/development/creating-project.md +++ b/docs/source/development/creating-project.md @@ -9,60 +9,15 @@ myst: # Create a Volto project without a backend -This document shows how to create a Volto project with the frontend only when you have your own existing backend, such as Plone {term}`Classic UI`, {term}`Nick`, or {term}`Guillotina`. +```{versionadded} Volto 18.0.0-alpha.43 +``` + +[Cookieplone](https://github.com/plone/cookieplone) is now the recommended way to develop Volto projects, using it as a boilerplate generator. +Even if you don't need the backend, you can create a Plone project, then use only the {file}`frontend` folder for your purposes. +As a bonus, it will contain the means for deploying your project. ```{seealso} To create a full Plone project with both frontend and backend, see {doc}`plone:install/create-project` instead. To contribute to Volto, see {doc}`../contributing/developing-core`. ``` - -For using Volto for a project—in other words, use Volto as a library—you should use Volto's project generator `@plone/generator-volto`. -It's a boilerplate project generator based on Yeoman that will provide you with the basic files and folder structure to bootstrap a Volto site. -In addition to bootstrapping stand-alone Volto projects, it can also bootstrap Volto add-ons. - -1. Open a terminal and execute: - - ```shell - npm install -g yo @plone/generator-volto - # Install the latest and stable release of Volto with the following command - yo @plone/volto - # or you can install the "canary" release, including any alpha release - yo @plone/volto --canary - # or you can install any specific released version - yo @plone/volto --volto=15.0.0 - # you can even pass a GitHub repo and specific branch - yo @plone/volto --volto=plone/volto#16.0.0 - # you can bootstrap with add-ons - yo @plone/volto --addon=volto-form-block - ``` - -2. Answer the questions when prompted, and provide the name of the new app (folder) to be created. - For the sake of this documentation, use `myvoltoproject` as the project name. - - ````{note} - You can run the generator with parameters to tailor your requirements. - - ```shell - yo @plone/volto --help - ``` - - ```{seealso} - [`@plone/generator-volto` `README.md`](https://github.com/plone/volto/blob/main/packages/generator-volto/README.md). - ``` - ```` - -3. Change your working directory to the newly created folder `myvoltoproject` (or whatever name you entered). - - ```shell - cd myvoltoproject - ``` - -4. `@plone/generator-volto` installed the dependencies for you. - Start the project. - - ```shell - yarn start - ``` - - This starts the development server, which compiles the project code, and when done, it serves the app at http://localhost:3000. diff --git a/docs/source/development/environment-variables.md b/docs/source/development/environment-variables.md index 6b8b79efc3..78dc734177 100644 --- a/docs/source/development/environment-variables.md +++ b/docs/source/development/environment-variables.md @@ -33,7 +33,7 @@ docker run -it --rm -p 55001:8080 -e SITE=Plone -e ADDONS='plone.restapi==8.20.0 ``` ```shell -RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' yarn start +RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/Plone' pnpm start ``` Let's say that you do have it in a customized site id: http://localhost:55001/myplonesite @@ -43,7 +43,7 @@ docker run -it --rm -p 55001:8080 -e SITE=myplonesite -e ADDONS='plone.restapi== ``` ```shell -RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' yarn start +RAZZLE_DEV_PROXY_API_PATH='http://localhost:55001/myplonesite' pnpm start ``` ### Debug an external site (provided you have access to it) @@ -55,7 +55,7 @@ This is an advanced feature, and needs understanding of what you are doing and w Let's say you want to debug a deployed site in production, but the build does not allow you to look deeper into the tracebacks. You could bootstrap a frontend in your machine, and point it to the production server, combining environment variables like: ``` -RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ yarn start +RAZZLE_DEV_PROXY_API_PATH=https://2021.ploneconf.org RAZZLE_PROXY_REWRITE_TARGET=https://2021.ploneconf.org/++api++ pnpm start ``` This has the drawback that could be that the proxy does not work well with the proxied SSL connection. @@ -63,7 +63,7 @@ This has the drawback that could be that the proxy does not work well with the p If you have access (via tunnel) to the port of the deployed backend is even more easier: ``` -RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone yarn start +RAZZLE_DEV_PROXY_API_PATH=http://2021.ploneconf.org:8080/Plone pnpm start ``` This will use the internal proxy to access the backend, bypassing CORS. diff --git a/docs/source/development/i18n.md b/docs/source/development/i18n.md index 36bffe4dfc..f9f2f4dd03 100644 --- a/docs/source/development/i18n.md +++ b/docs/source/development/i18n.md @@ -145,7 +145,7 @@ Volto provides an i18n extraction script to get all translatable strings from yo You can invoke this script with the following command. ```sh -yarn i18n +pnpm i18n ``` This will generate the following output: @@ -181,7 +181,7 @@ defineMessages({ }); ``` -Then run `yarn i18n`. +Then run `pnpm i18n`. You will find the translation ready to override in your `locales` directory, such as `locales/de/LC_MESSAGES/volto.po`. ```text @@ -190,7 +190,7 @@ msgid "Back" msgstr "My overridden translation" ``` -After you set the override, then run `yarn i18n` again to create the `de.json` translation files. +After you set the override, then run `pnpm i18n` again to create the `de.json` translation files. Restart Volto to see the changes applied. diff --git a/docs/source/development/ie11compat.md b/docs/source/development/ie11compat.md deleted file mode 100644 index 16735ed7ae..0000000000 --- a/docs/source/development/ie11compat.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -myst: - html_meta: - "description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling" - "property=og:description": "Legacy browser support with polyfills, babel-env, and pre- and post-transpiling" - "property=og:title": "Legacy Browser Support (IE11 compatibility)" - "keywords": "Volto, Plone, frontend, React, IE11 compatibility, polyfills, legacy browser support" ---- - -# Legacy Browser Support (IE11 compatibility) - -There are some caveats if we still want to target IE11 as supported browser. - -```{important} -This documentation is orientative. Volto does NOT support legacy or vendor deprecated browsers (as in IE11). -``` - -## Version pinning - -These package versions should be pinned to this especific versions, unless -their code or dependencies have some es6 only compatible, because their -maintainers mainly target the Node.js world. - -* "query-string": "4.1.0" -* "superagent": "3.8.2" - -## Polyfills - -Then in the project that should target it, these changes are required: - -add as a dependency `@babel/polyfill`. - - yarn add @babel/polyfill - -and in `src/client.jsx`: - -```js -import '@babel/polyfill'; -``` - -```{seealso} -See https://babeljs.io/docs/babel-polyfill for more updated information -``` - -## babel-env - -Razzle supports `@babel/preset-env`, that supports including `browserlist` in -`package.json`. So you can add this to `package.json`: - -```json - "browserslist": [ - "last 2 version", - "IE 11" - ], -``` - -This supports the query specific DSL for `browserlist` targeting the browsers -that you need to add. - -## Pre-transpiling - -Some packages in `node_modules` are ES6 only, for some older browsers, you might want to add a pre (or post) transpiling. There's a script `pre-build-transpiling.js` (Volto root folder) that might help you with it. Also this command line might help: - - ./node_modules/.bin/babel --presets="@babel/env" XXX --out-dir XXX diff --git a/docs/source/development/index.md b/docs/source/development/index.md index 90c28191f8..a1e5952999 100644 --- a/docs/source/development/index.md +++ b/docs/source/development/index.md @@ -36,5 +36,4 @@ pluggables widget how-to-restrict-blocks color-picker-widget -ie11compat ``` diff --git a/docs/source/development/lazyload.md b/docs/source/development/lazyload.md index 668a7190b8..7a04d373cf 100644 --- a/docs/source/development/lazyload.md +++ b/docs/source/development/lazyload.md @@ -36,7 +36,7 @@ You can find the complete `@loadable/component` documentation here: https://load You can check the code splitting state by using the included bundle analyzer: ```shell -yarn analyze +pnpm --filter=volto analyze ``` A browser will open with the bundle inspector. diff --git a/packages/volto/news/6433.documentation b/packages/volto/news/6433.documentation new file mode 100644 index 0000000000..82111de185 --- /dev/null +++ b/packages/volto/news/6433.documentation @@ -0,0 +1 @@ +Replace `yarn` with `pnpm` wherever necessary. @sneridagh From f576c13850e36ca4faf0ae54a6c86b21914658bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sat, 26 Oct 2024 13:52:20 +0200 Subject: [PATCH 10/16] [Next.js] Better Vercel deployment (#6441) Co-authored-by: Steve Piercy --- apps/nextjs/README.md | 95 +++++++++++++++++++++++------------ apps/nextjs/next.config.mjs | 27 ++++++---- apps/nextjs/src/app/config.ts | 21 ++++++-- 3 files changed, 97 insertions(+), 46 deletions(-) diff --git a/apps/nextjs/README.md b/apps/nextjs/README.md index c04dca61ad..4660a9939a 100644 --- a/apps/nextjs/README.md +++ b/apps/nextjs/README.md @@ -1,6 +1,7 @@ # Plone on Next.js -This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library. This is intended to serve as both a playground for the development of both packages and as demo of Plone using Next.js. +This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app router and the `@plone/client` and `@plone/components` library. +This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Next.js. > [!WARNING] > This package or app is experimental. @@ -9,7 +10,7 @@ This is a proof of concept of a [Next.js](https://nextjs.org) app, using the app ## Development -To start, from the root of the monorepo: +To start, from the root of the monorepo, issue the following commands in a shell session. ```shell pnpm install @@ -25,65 +26,93 @@ make backend-docker-start ## Deployment at Vercel - -We introduce an environment variable `API_SERVER_URL`. -You need to create this environment variable in the Vercel deployment's control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown. +For deploying your app at Vercel, you need to create the environment variable `API_SERVER_URL` in Vercel's deployment control panel, specifying the URL where your backend API server is deployed, and the route where the API is located, as shown. ```shell API_SERVER_URL=https://my-server-DNS-name.tld/api ``` +For production deployments, you will need to force the deployment URL, otherwise you will have issues with CORS. +To do so, set another environment variable for the production URL, `NEXT_PRODUCTION_URL`. +This URL needs to be scheme-less, without `http` or `https`, and consist only of the domain name: + +```shell +NEXT_PRODUCTION_URL=my-nextjs-production-DNS-name.tld +``` + ### Application rewrite configuragtion -To avoid issues with CORS and maintain the server counterpart private, our Next.js app should have a rewrite, configured as follows: +To avoid issues with CORS and maintain the server counterpart private, your Next.js app should have a rewrite, configured as follows: ```jsx const nextConfig = { // Rewrite to the backend to avoid CORS async rewrites() { - const apiServerURL = - process.env.API_SERVER_URL || - 'http://localhost:8080/Plone/%2B%2Bapi%2B%2B'; + let apiServerURL, vhmRewriteRule; + if ( + process.env.API_SERVER_URL && + (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL) + ) { + // We are in Vercel + apiServerURL = process.env.API_SERVER_URL; + vhmRewriteRule = `/VirtualHostBase/https/${ + process.env.NEXT_PRODUCTION_URL + ? // We are in the production deployment + process.env.NEXT_PRODUCTION_URL + : // We are in the preview deployment + process.env.NEXT_PUBLIC_VERCEL_URL + }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`; + } else if (process.env.API_SERVER_URL) { + // We are in development + apiServerURL = process.env.API_SERVER_URL; + vhmRewriteRule = + '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot'; + } else { + // We are in development and the API_SERVER_URL is not set, so we use a local backend + apiServerURL = 'http://localhost:8080'; + vhmRewriteRule = + '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot'; + } return [ { source: '/\\+\\+api\\+\\+/:slug*', destination: - `${apiServerURL}/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot/:slug*`, + `${apiServerURL}${vhmRewriteRule}/:slug*`, }, ]; }, }; ``` -Plone Client uses the `++api++` prefix as default, so we should create a redirect in our app pointing to the API server, but using Plone's traditional virtual host management configuration. +Plone Client uses the `++api++` prefix as default, so you should create a redirect in your app pointing to the API server, but using Plone's traditional virtual host management configuration. -Next.js rewrites are picky on the `destination` field, because its rewrite library does not support URLs with regular expression operators. -Therefore, we can't use the usual `++api++` route for the rewrite. -This will allow us to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules. -We will fallback to configure a `api` route in our reverse proxy of choice. +Next.js rewrites are picky with the `destination` field, because its rewrite library does not support URLs with regular expression operators. +Therefore, you can't use the usual `++api++` route for the rewrite. +This will allow you to infer the current server URL—even in deployed branches and pull requests—without touching the rewrite rules. +You will fallback to configure a `api` route in your reverse proxy of choice. ### Plone backend You have to deploy the Plone backend elsewhere, since Vercel is serverless oriented. -We need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section. +You need to set up the rewrite rule in Next.js's `rewrite` feature as shown in the previous section. -We will fallback to configure an `api` route in our reverse proxy of choice. +You will fallback to configure an `api` route in your reverse proxy of choice. -For example, if we use `traefik`: +For example, if you use `traefik`: ```yaml - ## VHM rewrite /api/ (Plone Next.js) - - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)" - ## We remove the incoming /api and just use the path - - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1" - - ## /api router - - traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`) - - traefik.http.routers.rt-backend-api.entrypoints=https - - traefik.http.routers.rt-backend-api.tls=true - - traefik.http.routers.rt-backend-api.service=svc-backend - - traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api +## VHM rewrite /api/ (Plone Next.js) +- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/api($$|/.*)" +## We remove the incoming /api and just use the path +- "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=$$1" + +## /api router +- traefik.http.routers.rt-backend-api.rule=Host(`my_server_DNS_name`) && PathPrefix(`/api`) +- traefik.http.routers.rt-backend-api.entrypoints=https +- traefik.http.routers.rt-backend-api.tls=true +- traefik.http.routers.rt-backend-api.service=svc-backend +- traefik.http.routers.rt-backend-api.middlewares=gzip,mw-backend-vhm-api ``` ## About this app @@ -100,16 +129,18 @@ pnpm dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +You can start editing the page by modifying `app/page.tsx`. +The page auto-updates as you edit the file. ## Learn More To learn more about Next.js, take a look at the following resources: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and its API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/). +Your feedback and contributions are welcome! ## Deploy on Vercel diff --git a/apps/nextjs/next.config.mjs b/apps/nextjs/next.config.mjs index f214d4f86b..2bc72a90f0 100644 --- a/apps/nextjs/next.config.mjs +++ b/apps/nextjs/next.config.mjs @@ -1,4 +1,4 @@ -import path from 'path'; +// import path from 'path'; /** @type {import('next').NextConfig} */ const nextConfig = { @@ -21,17 +21,26 @@ const nextConfig = { // Rewrite to the backend to avoid CORS async rewrites() { let apiServerURL, vhmRewriteRule; - if (process.env.API_SERVER_URL) { - apiServerURL = process.env.API_SERVER_URL; - vhmRewriteRule = `/VirtualHostBase/https/${process.env.NEXT_PUBLIC_VERCEL_URL}%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`; - } else if ( + if ( process.env.API_SERVER_URL && - !process.env.NEXT_PUBLIC_VERCEL_URL + (process.env.NEXT_PRODUCTION_URL || process.env.NEXT_PUBLIC_VERCEL_URL) ) { - throw new Error( - 'API_SERVER_URL set and NEXT_PUBLIC_VERCEL_URL not present.', - ); + // We are in Vercel + apiServerURL = process.env.API_SERVER_URL; + vhmRewriteRule = `/VirtualHostBase/https/${ + process.env.NEXT_PRODUCTION_URL + ? // We are in the production deployment + process.env.NEXT_PRODUCTION_URL + : // We are in the preview deployment + process.env.NEXT_PUBLIC_VERCEL_URL + }%3A443/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot`; + } else if (process.env.API_SERVER_URL) { + // We are in development + apiServerURL = process.env.API_SERVER_URL; + vhmRewriteRule = + '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot'; } else { + // We are in development and the API_SERVER_URL is not set, so we use a local backend apiServerURL = 'http://localhost:8080'; vhmRewriteRule = '/VirtualHostBase/http/localhost%3A3000/Plone/%2B%2Bapi%2B%2B/VirtualHostRoot'; diff --git a/apps/nextjs/src/app/config.ts b/apps/nextjs/src/app/config.ts index 2bbdde6a9a..8d0b440630 100644 --- a/apps/nextjs/src/app/config.ts +++ b/apps/nextjs/src/app/config.ts @@ -1,15 +1,26 @@ import config from '@plone/registry'; +import type { ConfigType } from '@plone/registry'; import { slate } from '@plone/blocks'; import { blocksConfig } from '@plone/blocks'; -const settings = { - apiPath: process.env.NEXT_PUBLIC_VERCEL_URL - ? // Vercel does not prepend the schema to the NEXT_PUBLIC_VERCEL_URL automatic env var - `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` - : 'http://localhost:3000', +const settings: Partial = { slate, }; +if (process.env.NEXT_PUBLIC_VERCEL_URL) { + // This app is at Vercel + if (process.env.NEXT_PRODUCTION_URL) { + // This app is in a production deployment, so set the apiPath to the production URL + settings.apiPath = process.env.NEXT_PRODUCTION_URL; + } else { + // This app is in a preview deployment, so set the apiPath to the Vercel URL + settings.apiPath = `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`; + } +} else { + // This app is in development, so set the apiPath to localhost + settings.apiPath = 'http://localhost:3000/'; +} + // @ts-expect-error Improve typings config.set('settings', settings); From 83e2a6ab73d25cdb77a01f97bd11d80952f88eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sun, 27 Oct 2024 15:37:06 +0100 Subject: [PATCH 11/16] `@plone/registry` as ESM module, move to TS, complete documentation (#6399) Co-authored-by: Steve Piercy Co-authored-by: David Glick --- .eslintignore | 2 + .github/workflows/acceptance.yml | 3 + .github/workflows/docs-rtd-pr-preview.yml | 1 + .github/workflows/readme-link-check.yml | 2 +- .prettierignore | 1 + apps/remix/tsconfig.json | 2 - apps/remix/vite.config.ts | 2 + docs/source/upgrade-guide/index.md | 57 ++ package.json | 3 + .../generators/app/templates/.eslintrc.js | 10 +- .../app/templates/.storybook/main.js | 4 +- packages/generator-volto/news/6399.breaking | 2 + .../registry/{.eslintrc.js => .eslintrc.cjs} | 8 +- packages/registry/.gitignore | 7 +- packages/registry/.readthedocs.yaml | 34 + packages/registry/Makefile | 90 ++ packages/registry/README.md | 196 +--- .../__tests__/addon-registry-app.test.js | 18 + .../registry/__tests__/addon-registry.test.js | 366 ++++++++ .../__tests__/create-addons-loader.test.js | 164 ++++ .../@plone/slots/components/Logo/Logo.svg | 1 + .../node_modules/@plone/slots/index.js | 1 + .../node_modules/@plone/slots/package.json | 4 + .../@plone/slots/components/Logo/Logo.svg | 1 + .../test-app/node_modules/my-addon/index.js | 1 + .../node_modules/my-addon/package.json | 4 + .../__tests__/fixtures/test-app/package.json | 3 + .../fixtures/test-app/registry.config.js | 3 + .../addons/my-volto-config-addon/package.json | 10 + .../my-volto-config-addon/src/testaddon.js} | 0 .../addons/non-volto-addon-lib/index.js | 1 + .../addons/non-volto-addon-lib/package.json | 5 + .../addons/test-addon/package.json | 10 + .../test-released-source-addon/index.js | 1 + .../src/custom-addons/volto/server.jsx | 1 + .../addons/test-addon/src/testaddon.js | 0 .../addons/test-released-dummy/index.js | 1 + .../addons/test-released-dummy/package.json | 8 + .../fixtures/test-volto-project/jsconfig.json | 11 + .../@plone/volto/src/LanguageSwitcher.jsx | 1 + .../@plone/volto/src/TSComponent.tsx | 1 + .../node_modules/@plone/volto/src/client.js | 1 + .../node_modules/@plone/volto/src/routes.jsx | 1 + .../node_modules/@plone/volto/src/server.jsx | 0 .../index.js | 1 + .../package.json | 11 + .../node_modules/test-released-addon/index.js | 1 + .../test-released-addon/package.json | 8 + .../node_modules/test-released-dummy/index.js | 2 + .../test-released-dummy/package.json | 5 + .../test-released-source-addon/package.json | 5 + .../razzle.extend.js | 0 .../src/customizations/@root/marker.js | 0 .../src/customizations/LanguageSwitcher.js | 1 + .../src/customizations/TSComponent.jsx | 1 + .../src/customizations/client.js | 1 + .../src/customizations/routes.tsx | 1 + .../test-released-source-addon/src/index.js | 0 .../test-released-unmentioned/index.js | 2 + .../test-released-unmentioned/package.json | 5 + .../test-released-via-addons-env-var/index.js | 2 + .../package.json | 4 + .../fixtures/test-volto-project/package.json | 12 + .../package.json | 4 + .../src/index.js | 1 + .../src/custom-addons/test-addon/testaddon.js | 1 + .../src/custom-addons/volto/client.js | 1 + .../src/customizations/LanguageSwitcher.js | 1 + .../src/customizations/TSComponent.jsx | 0 .../src/customizations/client.js | 1 + .../src/customizations/routes.tsx | 0 .../src/customizations/server.jsx | 1 + .../fixtures/test-volto-project/src/marker.js | 0 .../test-volto-project/volto.config.envvar.js | 3 + .../test-volto-project/volto.config.js | 3 + .../__tests__/fixtures/volto-addon1.js | 14 + .../__tests__/fixtures/volto-addon2.js | 29 + .../__tests__/fixtures/volto-addon3.js | 41 + .../docs/_static/Plone_logo_square.png | Bin 0 -> 30912 bytes packages/registry/docs/_static/favicon.ico | Bin 0 -> 5430 bytes packages/registry/docs/_static/logo.svg | 48 + packages/registry/docs/_static/print.css | 3 + .../docs/conceptual-guides/add-on-registry.md | 38 + .../conceptual-guides/component-registry.md | 20 + .../configuration-registry.md | 78 ++ .../conceptual-guides/utility-registry.md | 14 + packages/registry/docs/conf.py | 297 ++++++ .../docs/how-to-guides/access-registry.md | 20 + .../how-to-guides/instantiate-registry.md | 56 ++ .../docs/how-to-guides/register-an-add-on.md | 94 ++ .../register-and-retrieve-components.md | 114 +++ .../register-and-retrieve-utilities.md | 113 +++ .../docs/how-to-guides/shadow-a-component.md | 61 ++ packages/registry/docs/index.md | 43 + packages/registry/docs/robots.txt | 8 + packages/registry/news/6399.breaking | 6 + packages/registry/news/6399.feature | 1 + packages/registry/package.json | 45 +- .../addon-registry.ts} | 375 +++++--- .../create-addons-loader.ts} | 52 +- .../create-theme-loader.ts} | 54 +- packages/registry/src/index.ts | 35 +- packages/registry/tsconfig.json | 2 +- packages/registry/tsconfig.node.json | 24 + packages/registry/vite-plugin.d.ts | 5 + packages/registry/vite-plugin.js | 60 ++ packages/scripts/i18n.cjs | 23 +- packages/scripts/news/6399.feature | 1 + packages/volto/.eslintrc | 1 - packages/volto/.storybook/main.js | 4 +- .../__tests__/addon-registry-project.test.js | 52 +- .../__tests__/addon-registry-volto.test.js | 14 +- .../__tests__/create-addons-loader.test.js | 74 +- .../webpack-relative-resolver.test.js | 4 +- packages/volto/news/6399.internal | 1 + packages/volto/package.json | 6 +- packages/volto/razzle.config.js | 13 +- .../volto/src/express-middleware/static.js | 4 +- patches/jest-resolve@26.6.2.patch | Bin 0 -> 7680 bytes pnpm-lock.yaml | 885 ++++++++++++++++-- 120 files changed, 3400 insertions(+), 552 deletions(-) create mode 100644 packages/generator-volto/news/6399.breaking rename packages/registry/{.eslintrc.js => .eslintrc.cjs} (88%) create mode 100644 packages/registry/.readthedocs.yaml create mode 100644 packages/registry/Makefile create mode 100644 packages/registry/__tests__/addon-registry-app.test.js create mode 100644 packages/registry/__tests__/addon-registry.test.js create mode 100644 packages/registry/__tests__/create-addons-loader.test.js create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js create mode 100644 packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json create mode 100644 packages/registry/__tests__/fixtures/test-app/package.json create mode 100644 packages/registry/__tests__/fixtures/test-app/registry.config.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json rename packages/registry/{pnpm-lock.yaml => __tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js} (100%) create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/src/marker.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js create mode 100644 packages/registry/__tests__/fixtures/test-volto-project/volto.config.js create mode 100644 packages/registry/__tests__/fixtures/volto-addon1.js create mode 100644 packages/registry/__tests__/fixtures/volto-addon2.js create mode 100644 packages/registry/__tests__/fixtures/volto-addon3.js create mode 100644 packages/registry/docs/_static/Plone_logo_square.png create mode 100644 packages/registry/docs/_static/favicon.ico create mode 100644 packages/registry/docs/_static/logo.svg create mode 100644 packages/registry/docs/_static/print.css create mode 100644 packages/registry/docs/conceptual-guides/add-on-registry.md create mode 100644 packages/registry/docs/conceptual-guides/component-registry.md create mode 100644 packages/registry/docs/conceptual-guides/configuration-registry.md create mode 100644 packages/registry/docs/conceptual-guides/utility-registry.md create mode 100644 packages/registry/docs/conf.py create mode 100644 packages/registry/docs/how-to-guides/access-registry.md create mode 100644 packages/registry/docs/how-to-guides/instantiate-registry.md create mode 100644 packages/registry/docs/how-to-guides/register-an-add-on.md create mode 100644 packages/registry/docs/how-to-guides/register-and-retrieve-components.md create mode 100644 packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md create mode 100644 packages/registry/docs/how-to-guides/shadow-a-component.md create mode 100644 packages/registry/docs/index.md create mode 100644 packages/registry/docs/robots.txt create mode 100644 packages/registry/news/6399.breaking create mode 100644 packages/registry/news/6399.feature rename packages/registry/src/{addon-registry.js => addon-registry/addon-registry.ts} (65%) rename packages/registry/src/{create-addons-loader.js => addon-registry/create-addons-loader.ts} (66%) rename packages/registry/src/{create-theme-addons-loader.js => addon-registry/create-theme-loader.ts} (50%) create mode 100644 packages/registry/tsconfig.node.json create mode 100644 packages/registry/vite-plugin.d.ts create mode 100644 packages/registry/vite-plugin.js create mode 100644 packages/scripts/news/6399.feature create mode 100644 packages/volto/news/6399.internal create mode 100644 patches/jest-resolve@26.6.2.patch diff --git a/.eslintignore b/.eslintignore index 7df5c928c1..77fb2df91b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,3 +4,5 @@ packages/volto packages/volto-guillotina !.* dist +packages/registry/lib +packages/registry/docs diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index a52a7502a3..82480849c7 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -521,6 +521,9 @@ jobs: - name: Install yalc run: npm -g install yalc + - name: Build dependencies + run: make build-deps + - name: Install a yalc'ed version of the current Volto in the project - publish run: | yalc publish packages/types diff --git a/.github/workflows/docs-rtd-pr-preview.yml b/.github/workflows/docs-rtd-pr-preview.yml index 83d7e72494..a123d8ef75 100644 --- a/.github/workflows/docs-rtd-pr-preview.yml +++ b/.github/workflows/docs-rtd-pr-preview.yml @@ -10,6 +10,7 @@ on: - "docs/source/**" - .readthedocs.yaml - requirements-docs.txt + - "packages/registry/docs/**" permissions: pull-requests: write diff --git a/.github/workflows/readme-link-check.yml b/.github/workflows/readme-link-check.yml index 7f53b65824..42c96e1455 100644 --- a/.github/workflows/readme-link-check.yml +++ b/.github/workflows/readme-link-check.yml @@ -20,4 +20,4 @@ jobs: - name: Check links in README.md with awesome_bot run: | gem install awesome_bot - awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md + awesome_bot --request-delay 1 --allow-dupe --white-list http://localhost:8080/Plone,http://localhost:8080,http://localhost:3000,https://github.com/kitconcept/volto-blocks-grid.git,https://my-server-DNS-name.tld/api --files PACKAGES.md,README.md,packages/blocks/README.md,packages/client/README.md,packages/components/README.md,packages/generator-volto/README.md,packages/registry/README.md,packages/scripts/README.md,packages/tsconfig/README.md,packages/types/README.md,packages/volto-slate/README.md,apps/nextjs/README.md,apps/remix/README.md,apps/vite-ssr/README.md diff --git a/.prettierignore b/.prettierignore index cf39cfcd94..1749ca9b36 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,6 +5,7 @@ styles/rules/* node_modules packages/volto/types/* +packages/registry/docs/* storybook-static apps/vite-ssr/src/routeTree.gen.ts apps/vite/src/routeTree.gen.ts diff --git a/apps/remix/tsconfig.json b/apps/remix/tsconfig.json index 9d87dd378f..42f049fa4a 100644 --- a/apps/remix/tsconfig.json +++ b/apps/remix/tsconfig.json @@ -25,8 +25,6 @@ "paths": { "~/*": ["./app/*"] }, - - // Vite takes care of building everything, not tsc. "noEmit": true } } diff --git a/apps/remix/vite.config.ts b/apps/remix/vite.config.ts index 1a43ce37db..03bfa30a99 100644 --- a/apps/remix/vite.config.ts +++ b/apps/remix/vite.config.ts @@ -1,6 +1,7 @@ import { vitePlugin as remix } from '@remix-run/dev'; import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; +import { PloneRegistryVitePlugin } from '@plone/registry/vite-plugin'; export default defineConfig({ plugins: [ @@ -12,6 +13,7 @@ export default defineConfig({ }, }), tsconfigPaths(), + PloneRegistryVitePlugin(), ], server: { port: 3000, diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index 60de994f58..b9b67fcf4d 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -522,6 +522,63 @@ The `react/jsx-key` rule has been enabled in ESlint for catching missing `key` i You might catch some violations in your project or add-on code after running ESlint. Adding the missing `key` property whenever the violation is reported will fix it. +### `@plone/registry` moved to ESM + +The `@plone/registry` package has been moved to ESM. +The add-on registry scripts have also been refactored to TypeScript. +For maximum compatibility with CommonJS builds, the default exports have been moved to named exports. +The modules affected are now built, and the import paths have changed, too. +These changes force some import path changes that you should patch in your Plone project or add-on boilerplates. + +```{note} +As always, when something changes in the boilerplate, you may regenerate one from Cookieplone and move your code into it, instead of fiddling with it. +``` + +For example, in your project's {file}`.eslintrc.js`: + +```diff + const fs = require('fs'); + const projectRootPath = __dirname; +-const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); ++const { AddonRegistry } = require('@plone/registry/addon-registry'); + + let voltoPath = './node_modules/@plone/volto'; + +@@ -17,15 +17,15 @@ if (configFile) { + voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`; + } + +-const reg = new AddonConfigurationRegistry(__dirname); ++const { registry } = AddonRegistry.init(__dirname); + + // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons +-const addonAliases = Object.keys(reg.packages).map((o) => [ ++const addonAliases = Object.keys(registry.packages).map((o) => [ + o, +- reg.packages[o].modulePath, ++ registry.packages[o].modulePath, + ]); + +-const addonExtenders = reg.getEslintExtenders().map((m) => require(m)); ++const addonExtenders = registry.getEslintExtenders().map((m) => require(m)); +``` + +Also in the Storybook configuration {file}`.storybook/main.js`. + +```diff +- const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); ++ const { AddonRegistry } = require('@plone/registry/addon-registry'); + +- const registry = new AddonConfigurationRegistry(projectRootPath); ++ const { registry } = AddonRegistry.init(projectRootPath); +``` + +```{versionadded} Volto 18.0.0-alpha.47 +``` + +```{versionadded} @plone/registry 3.0.0-alpha.0 +``` + ### Deprecation notices for Volto 18 #### `@plone/generator-volto` diff --git a/package.json b/package.json index 0b7d97e4de..603b737d9a 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "overrides": { "@pmmmwh/react-refresh-webpack-plugin": "0.5.11", "react-refresh": "0.14.0" + }, + "patchedDependencies": { + "jest-resolve@26.6.2": "patches/jest-resolve@26.6.2.patch" } } } diff --git a/packages/generator-volto/generators/app/templates/.eslintrc.js b/packages/generator-volto/generators/app/templates/.eslintrc.js index 6148e2e62f..a5aaf045b1 100644 --- a/packages/generator-volto/generators/app/templates/.eslintrc.js +++ b/packages/generator-volto/generators/app/templates/.eslintrc.js @@ -1,6 +1,6 @@ const fs = require('fs'); const projectRootPath = __dirname; -const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); +const { AddonRegistry } = require('@plone/registry/addon-registry'); let voltoPath = './node_modules/@plone/volto'; @@ -17,15 +17,15 @@ if (configFile) { voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`; } -const reg = new AddonConfigurationRegistry(__dirname); +const { registry } = AddonRegistry.init(__dirname); // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons -const addonAliases = Object.keys(reg.packages).map((o) => [ +const addonAliases = Object.keys(registry.packages).map((o) => [ o, - reg.packages[o].modulePath, + registry.packages[o].modulePath, ]); -const addonExtenders = reg.getEslintExtenders().map((m) => require(m)); +const addonExtenders = registry.getEslintExtenders().map((m) => require(m)); const defaultConfig = { extends: `${voltoPath}/.eslintrc`, diff --git a/packages/generator-volto/generators/app/templates/.storybook/main.js b/packages/generator-volto/generators/app/templates/.storybook/main.js index aa094cb6d7..da6c799e56 100644 --- a/packages/generator-volto/generators/app/templates/.storybook/main.js +++ b/packages/generator-volto/generators/app/templates/.storybook/main.js @@ -104,9 +104,9 @@ module.exports = { [], defaultRazzleOptions, ); - const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); + const { AddonRegistry } = require('@plone/registry/addon-registry'); - const registry = new AddonConfigurationRegistry(projectRootPath); + const { registry } = AddonRegistry.init(projectRootPath); config = lessPlugin({ registry }).modifyWebpackConfig({ env: { target: 'web', dev: 'dev' }, diff --git a/packages/generator-volto/news/6399.breaking b/packages/generator-volto/news/6399.breaking new file mode 100644 index 0000000000..2b0695d18f --- /dev/null +++ b/packages/generator-volto/news/6399.breaking @@ -0,0 +1,2 @@ +Breaking changes in `.eslintrc.js` and `.storybook/main.js` because of #6399. @sneridagh +Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). diff --git a/packages/registry/.eslintrc.js b/packages/registry/.eslintrc.cjs similarity index 88% rename from packages/registry/.eslintrc.js rename to packages/registry/.eslintrc.cjs index b60637a973..8614b4cd25 100644 --- a/packages/registry/.eslintrc.js +++ b/packages/registry/.eslintrc.cjs @@ -16,6 +16,8 @@ module.exports = { // Base config extends: ['eslint:recommended'], + ignorePatterns: ['docs/_static/searchtools.js'], + overrides: [ // React { @@ -61,7 +63,11 @@ module.exports = { // Node { - files: ['.eslintrc.js', 'src/*.js'], + files: [ + '.eslintrc.cjs', + 'src/addon-registry/**/*.{js,ts}', + '__tests__/**/*.{js,ts}', + ], env: { node: true, es6: true, diff --git a/packages/registry/.gitignore b/packages/registry/.gitignore index d32327c9cd..2c29928dd0 100644 --- a/packages/registry/.gitignore +++ b/packages/registry/.gitignore @@ -1,6 +1,7 @@ .parcel-cache/ dist +/bin +/lib +/include -# yarn 3 -.pnp.* -.yarn/* +docs/_build/ diff --git a/packages/registry/.readthedocs.yaml b/packages/registry/.readthedocs.yaml new file mode 100644 index 0000000000..b1f7970727 --- /dev/null +++ b/packages/registry/.readthedocs.yaml @@ -0,0 +1,34 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + # You can also specify other tool versions: + # nodejs: "19" + # rust: "1.64" + # golang: "1.19" + commands: + - cd ./packages/registry && make docs-rtd-registry + +# Build documentation in the "docs/" directory with Sphinx +#sphinx: +# configuration: packages/registry/docs/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +#python: +# install: +# - requirements: requirements-docs.txt diff --git a/packages/registry/Makefile b/packages/registry/Makefile new file mode 100644 index 0000000000..d0e9c89f6d --- /dev/null +++ b/packages/registry/Makefile @@ -0,0 +1,90 @@ +### Defensive settings for make: +# https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +.SHELLFLAGS:=-eu -o pipefail -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +# Sphinx variables +# You can set these variables from the command line. +SPHINXOPTS ?= +VALEOPTS ?= +# Internal variables. +SPHINXBUILD = "$(realpath bin/sphinx-build)" +SPHINXAUTOBUILD = "$(realpath bin/sphinx-autobuild)" +DOCS_DIR = ./docs/ +BUILDDIR = ./_build/ +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) . +VALEFILES := $(shell find $(DOCS_DIR) -type f -name "*.md" -print) + +# We like colors +# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects +RED=`tput setaf 1` +GREEN=`tput setaf 2` +RESET=`tput sgr0` +YELLOW=`tput setaf 3` + +## Docs + +bin/python: ## Create a Python virtual environment with the latest pip, and install documentation requirements + python3 -m venv . || virtualenv --clear --python=python3 . + bin/python -m pip install --upgrade pip + @echo "Python environment created." + bin/pip install -r ../../requirements-docs.txt + @echo "Requirements installed." + +.PHONY: docs-clean +docs-clean: ## Clean current and legacy docs build directories, and Python virtual environment + rm -rf bin include lib + rm -rf docs/_build + cd $(DOCS_DIR) && rm -rf $(BUILDDIR)/ + +.PHONY: docs-html +docs-html: bin/python ## Build html + cd $(DOCS_DIR) && $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: docs-livehtml +docs-livehtml: bin/python ## Rebuild Sphinx documentation on changes, with live-reload in the browser + cd "$(DOCS_DIR)" && ${SPHINXAUTOBUILD} \ + --ignore "*.swp" \ + -b html . "$(BUILDDIR)/html" $(SPHINXOPTS) + +.PHONY: docs-linkcheck +docs-linkcheck: bin/python ## Run linkcheck + cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/ ." + +.PHONY: docs-linkcheckbroken +docs-linkcheckbroken: bin/python ## Run linkcheck and show only broken links + cd $(DOCS_DIR) && $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck | GREP_COLORS='0;31' grep -wi "broken\|redirect" --color=always | GREP_COLORS='0;31' grep -vi "https://github.com/plone/volto/issues/" --color=always && if test $$? -eq 0; then exit 1; fi || test $$? -ne 0 + +.PHONY: docs-vale +docs-vale: bin/python ## Install (once) and run Vale style, grammar, and spell checks + bin/vale sync + bin/vale --no-wrap $(VALEOPTS) $(VALEFILES) + @echo + @echo "Vale is finished; look for any errors in the above output." + +.PHONY: docs-rtd-pr-preview +docs-rtd-pr-preview: ## Build previews of pull requests that have documentation changes on Read the Docs via CI + pip install -r requirements-docs.txt + cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/ + +.PHONY: docs-rtd-registry +docs-rtd-registry: ## Build Plone Registry docs on RTD + pip install -r ../../requirements-docs.txt && cd $(DOCS_DIR) && sphinx-build -b html $(ALLSPHINXOPTS) ${READTHEDOCS_OUTPUT}/html/ + +## Build + +.PHONY: rename-to-cjs +rename-to-cjs: ## Rename the built files js -> cjs + mv dist/cjs/addon-registry.js dist/cjs/addon-registry.cjs + mv dist/cjs/create-addons-loader.js dist/cjs/create-addons-loader.cjs + mv dist/cjs/create-theme-loader.js dist/cjs/create-theme-loader.cjs diff --git a/packages/registry/README.md b/packages/registry/README.md index 552133573c..0c4ae8865b 100644 --- a/packages/registry/README.md +++ b/packages/registry/README.md @@ -10,198 +10,10 @@ That means you have to build something that has very specific requirements, beha Sometimes you need to build something generic that is pluggable and extensible. In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this. -## Add-on registry +`@plone/registry` provides tools to facilitate pluggability for your app. -An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable. +## Documentation -The add-on registry is a store where you can register a number of add-ons that your app consumes. +You can find the detailed documentation of this package by visiting the following link. -Add-on packages are just CommonJS packages. -The only requirement is that they point the `main` key of their `package.json` to a module that exports as a default function, which acts as a configuration loader. - -An add-on can be published in an npm registry, just as any other package. -However, add-ons are meant to not be transpiled. -They should be released as source packages. - -## Register an add-on - -You should declare your add-on in your project. -This is done in your app's `package.json`'s `addons` key: - -```json -{ - "name": "my-app-project", - "addons": [ - "acme-volto-foo-addon", - "@plone/some-addon", - "collective-another-volto-addon" - ] -} -``` - -The `addons` key ensures the add-on's main default export function is executed, being passed the configuration registry. -In that function, the add-on can customize the registry. -The function needs to return the `config` object (the configuration registry), so that it's passed further along to the other add-ons. - -The add-ons are registered in the order they are found in the `addons` key. -The last add-on takes precedence over the others. -This means that if you configure something in `acme-volto-foo-addon`, then the same thing later in `collective-another-volto-addon`, the latter configured thing will win and its configuration will be applied. - -The default export of any add-on main `index.js` file should be a function with the signature `config => config`. -That is, it should take the configuration registry object and return it, possibly mutated or changed. - -## Configuration registry - -The configuration registry supplements the add-on registry. - -It is a facility that stores app configuration to be shared in the app. - -Let's say that your app is the user interface of a content management system (CMS). -This CMS uses blocks as its main fundamental unit of content. -The pages that the CMS builds are made up of these blocks. -The CMS has some basic available blocks, but it's a requirement that integrators are able to register more blocks in a pluggable way. - -This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to the basic CMS ones. - -Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry: - -```js - config.blocks.blocksConfig.faq_viewer = { - id: 'faq_viewer', - title: 'FAQ Viewer', - edit: FAQBlockEdit, - view: FAQBlockView, - icon: chartIcon, - group: 'common', - restricted: false, - mostUsed: true, - sidebarTab: 1, - }; -``` - -The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS. -Then the CMS will properly populate the available blocks in the user interface. - -The add-on is meant to extend the initial configuration. -From the default export function of our add-on, we should provide the configuration of the new block: - -```ts -export default function applyConfig(config: ConfigData) { - config.blocks.blocksConfig.faq_viewer = { - id: 'faq_viewer', - title: 'FAQ Viewer', - edit: FAQBlockEdit, - view: FAQBlockView, - icon: chartIcon, - group: 'common', - restricted: false, - mostUsed: true, - sidebarTab: 1, - }; - - return config; -} -``` - -Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block. -The add-on will then become available to the CMS when it asks the configuration registry for it. - -## Accessing the configuration registry - -The configuration registry can be accessed by: - -```ts -import config from '@plone/registry' - -const blocksConfig = config.blocks.blocksConfig -``` - -## Component registry - -The configuration registry also stores a components registry in itself. -The components registry is a mapping of name to component. -You can look up a name, and receive a component that you can reference in your code. -This provides an alternative, and more convenient, way to customize components. -You can override programmatically such registrations from your add-on or projects because it's stored in the configuration registry. -You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather then import it directly. -You can even have modifiers to the component registrations through dependencies. -Thus you can adapt the call, given an array of such dependencies. - -## Register components by name using `config.registerComponent` - -You can register components by name, typically from an add-on or project configuration: - -```js -import MyToolbarComponent from './MyToolbarComponent' - -config.registerComponent({ - name: 'Toolbar', - component: MyToolbarComponent, -}); -``` - -## Retrieve a component from the component registry - -You can programmatically retrieve a component from the registry using `config.getComponent`: - -```js -const Toolbar = config.getComponent('Toolbar').component -``` - -Or you can retrieve a component by using the convenience component `Component`, if you want to use it in JSX code directly. - -```jsx -import Component from '@plone/volto/components/theme/Component/Component'; - - -``` - -Note that you can pass `props` down to the retrieved component. - -## Adapt the component using the `dependencies` array - -You can register components, then retrieve them, given a list of modifiers using `dependencies`. - -```js -import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent' - -config.registerComponent({ - name: 'Teaser', - component: MyTeaserNewsItemComponent, - dependencies: 'News Item', - }); -``` - -And then retrieve the component: - -```js -config.getComponent({ - name: 'Teaser', - dependencies: ['News Item'], - }).component -``` - -You can have both, either with or without dependencies: - -```js -import MyTeaserDefaultComponent from './MyTeaserDefaultComponent' -import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent' - -config.registerComponent({ - name: 'Teaser', - component: MyTeaserDefaultComponent, - }); - -config.registerComponent({ - name: 'Teaser', - component: MyTeaserNewsItemComponent, - dependencies: 'News Item', - }); -``` - -Then retrieve them both, depending on the use case. -In the example, given a content type value coming from the `content` prop, you would retrieve them as shown: - -```jsx - -``` +https://plone-registry.readthedocs.io diff --git a/packages/registry/__tests__/addon-registry-app.test.js b/packages/registry/__tests__/addon-registry-app.test.js new file mode 100644 index 0000000000..7c3d7d7e04 --- /dev/null +++ b/packages/registry/__tests__/addon-registry-app.test.js @@ -0,0 +1,18 @@ +import path from 'path'; +import { AddonRegistry } from '../src/addon-registry/addon-registry'; +import { describe, it, expect } from 'vitest'; + +describe('AddonRegistry - get()', () => { + it('Basic information', () => { + const base = path.join(import.meta.dirname, 'fixtures', 'test-app'); + const { addons, shadowAliases, theme } = AddonRegistry.init(base); + expect(addons).toStrictEqual(['@plone/slots', 'my-addon']); + expect(shadowAliases).toStrictEqual([ + { + find: '@plone/slots/components/Logo/Logo.svg', + replacement: `${base}/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg`, + }, + ]); + expect(theme).toStrictEqual(undefined); + }); +}); diff --git a/packages/registry/__tests__/addon-registry.test.js b/packages/registry/__tests__/addon-registry.test.js new file mode 100644 index 0000000000..cc21df88fa --- /dev/null +++ b/packages/registry/__tests__/addon-registry.test.js @@ -0,0 +1,366 @@ +import path from 'path'; +import { + AddonRegistry, + buildDependencyGraph, + getAddonsLoaderChain, +} from '../src/addon-registry/addon-registry'; +import { vi, describe, it, expect, test, beforeEach, afterEach } from 'vitest'; + +vi.mock( + 'fixtures/test-volto-project/node_modules/@plone/volto/package.json', + () => ({ + // TODO: mock the packages folder inside the mocked @plone/volto to work with resolves + coreAddons: {}, + }), + { virtual: true }, +); + +describe('AddonRegistry - get()', () => { + it('Basic information', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { addons, shadowAliases, theme } = AddonRegistry.init(base); + expect(addons).toStrictEqual([ + 'test-released-unmentioned:extra1,extra2', + 'test-released-dummy', + 'test-addon', + 'test-released-addon:extra', + 'test-released-source-addon', + 'my-volto-config-addon', + ]); + expect(shadowAliases).toStrictEqual([ + { + find: 'test-released-source-addon/index', + replacement: `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`, + }, + { + find: '@plone/volto/server', + replacement: `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`, + }, + { + find: '@root/marker', + replacement: `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`, + }, + { + find: '@plone/volto/routes', + replacement: `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`, + }, + { + find: '@plone/volto/client', + replacement: `${base}/node_modules/test-released-source-addon/src/customizations/client.js`, + }, + { + find: '@plone/volto/TSComponent', + replacement: `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`, + }, + { + find: '@plone/volto/LanguageSwitcher', + replacement: `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`, + }, + ]); + expect(theme).toStrictEqual(undefined); + }); +}); + +describe('AddonRegistry - Project', () => { + it('works in a mock project directory', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + + const voltoPath = `${base}/node_modules/@plone/volto`; + + expect(registry.projectRootPath).toStrictEqual(base); + expect(registry.voltoPath).toStrictEqual(voltoPath); + + expect(registry.addonNames).toStrictEqual([ + 'test-addon', + 'test-released-addon', + 'test-released-source-addon', + 'my-volto-config-addon', + 'test-released-dummy', + 'test-released-unmentioned', + ]); + + expect(registry.packages).toEqual({ + 'test-addon': { + isPublishedPackage: false, + modulePath: `${base}/addons/test-addon/src`, + name: 'test-addon', + packageJson: `${base}/addons/test-addon/package.json`, + addons: ['test-released-dummy'], + isRegisteredAddon: true, + version: '0.0.0', + }, + 'test-released-addon': { + basePath: `${base}/node_modules/test-released-addon`, + isPublishedPackage: true, + modulePath: `${base}/node_modules/test-released-addon`, + name: 'test-released-addon', + packageJson: `${base}/node_modules/test-released-addon/package.json`, + addons: ['test-released-unmentioned:extra1,extra2'], + isRegisteredAddon: true, + tsConfigPaths: null, + version: '0.0.0', + }, + 'test-released-source-addon': { + basePath: `${base}/node_modules/test-released-source-addon`, + isPublishedPackage: true, + modulePath: `${base}/node_modules/test-released-source-addon/src`, + name: 'test-released-source-addon', + packageJson: `${base}/node_modules/test-released-source-addon/package.json`, + razzleExtender: `${base}/node_modules/test-released-source-addon/razzle.extend.js`, + addons: [], + isRegisteredAddon: true, + tsConfigPaths: null, + version: '0.0.0', + }, + 'test-released-unmentioned': { + addons: [], + basePath: `${base}/node_modules/test-released-unmentioned`, + isPublishedPackage: true, + modulePath: `${base}/node_modules/test-released-unmentioned`, + name: 'test-released-unmentioned', + packageJson: `${base}/node_modules/test-released-unmentioned/package.json`, + isRegisteredAddon: true, + tsConfigPaths: null, + version: '0.0.0', + }, + 'my-volto-config-addon': { + addons: ['test-released-dummy'], + isPublishedPackage: false, + modulePath: `${base}/addons/my-volto-config-addon/src`, + name: 'my-volto-config-addon', + packageJson: `${base}/addons/my-volto-config-addon/package.json`, + isRegisteredAddon: true, + version: '0.0.0', + }, + 'test-released-dummy': { + addons: ['test-released-unmentioned'], + isPublishedPackage: false, + modulePath: `${base}/addons/test-released-dummy`, + name: 'test-released-dummy', + packageJson: `${base}/addons/test-released-dummy/package.json`, + isRegisteredAddon: true, + version: '0.0.0', + }, + }); + }); + + it('provides aliases for addons', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + expect(registry.getResolveAliases()).toStrictEqual({ + 'my-volto-config-addon': `${base}/addons/my-volto-config-addon/src`, + 'test-addon': `${base}/addons/test-addon/src`, + 'test-released-addon': `${base}/node_modules/test-released-addon`, + 'test-released-dummy': `${base}/addons/test-released-dummy`, + 'test-released-source-addon': `${base}/node_modules/test-released-source-addon/src`, + 'test-released-unmentioned': `${base}/node_modules/test-released-unmentioned`, + }); + }); + + it('provides addon extenders', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + expect(registry.getAddonExtenders().length).toBe(1); + }); + + it('provides a list of addon records ordered by initial package declaration', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + const addons = registry.getAddons(); + expect(addons.map((a) => a.name)).toStrictEqual([ + 'test-released-unmentioned', + 'test-released-dummy', + 'test-addon', + 'test-released-addon', + 'test-released-source-addon', + 'my-volto-config-addon', + ]); + }); + + it('provides customization paths declared in a Volto project', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + expect(registry.getProjectCustomizationPaths()).toStrictEqual({ + '@plone/volto/LanguageSwitcher': `${base}/src/customizations/LanguageSwitcher.js`, + '@plone/volto/TSComponent': `${base}/src/customizations/TSComponent.jsx`, + '@plone/volto/client': `${base}/src/customizations/client.js`, + '@plone/volto/routes': `${base}/src/customizations/routes.tsx`, + 'test-addon/testaddon': `${base}/src/custom-addons/test-addon/testaddon.js`, + '@plone/volto/server': `${base}/src/customizations/server.jsx`, + }); + }); + + it('provides customization paths declared in addons', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + expect(registry.getAddonCustomizationPaths()).toStrictEqual({ + '@plone/volto/LanguageSwitcher': `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`, + '@plone/volto/TSComponent': `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`, + '@plone/volto/client': `${base}/node_modules/test-released-source-addon/src/customizations/client.js`, + '@plone/volto/routes': `${base}/node_modules/test-released-source-addon/src/customizations/routes.tsx`, + '@plone/volto/server': `${base}/addons/test-addon/src/custom-addons/volto/server.jsx`, + '@root/marker': `${base}/node_modules/test-released-source-addon/src/customizations/@root/marker.js`, + 'test-released-source-addon/index': `${base}/addons/test-addon/src/custom-addons/test-released-source-addon/index.js`, + }); + }); +}); + +describe('Addon chain loading dependencies', () => { + const depTree = { + add0: ['add1'], + add1: ['add2:e0', 'add4'], + add2: ['add3:e6', 'add5', 'add6'], + add3: ['add0'], + add4: ['add2:e1,e3'], + add5: ['add6'], + }; + const extractor = (name) => depTree[name] || []; + + test('no addons', () => { + const graph = buildDependencyGraph([], extractor); + const deps = getAddonsLoaderChain(graph); + expect(deps).toEqual([]); + }); + + test('one addon', () => { + const graph = buildDependencyGraph(['volto-addon1'], extractor); + const deps = getAddonsLoaderChain(graph); + expect(deps).toEqual(['volto-addon1']); + }); + + test('two addons', () => { + const graph = buildDependencyGraph( + ['volto-addon1', 'volto-addon2'], + extractor, + ); + const deps = getAddonsLoaderChain(graph); + expect(deps).toEqual(['volto-addon1', 'volto-addon2']); + }); + + test('one addon with dependency', () => { + const graph = buildDependencyGraph(['add5'], extractor); + const deps = getAddonsLoaderChain(graph); + expect(deps).toEqual(['add6', 'add5']); + }); + + test('one addon with circular dependencies', () => { + const graph = buildDependencyGraph(['add0'], extractor); + const deps = getAddonsLoaderChain(graph); + expect(deps).toEqual([ + 'add3:e6', + 'add6', + 'add5', + 'add2:e0,e1,e3', + 'add4', + 'add1', + 'add0', + ]); + }); +}); + +describe('Addon via env var - Released addon (same as dev add-on, when resolved via workspaces)', () => { + const originalEnv = process.env; + + beforeEach(() => { + vi.resetModules(); + process.env = { + ...originalEnv, + ADDONS: 'test-released-via-addons-env-var', + }; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('addons can be specified on the fly using ADDONS env var - Released addon', () => { + const base = path.join( + import.meta.dirname, + 'fixtures', + 'test-volto-project', + ); + const { registry } = AddonRegistry.init(base); + expect( + Object.keys(registry.packages).includes( + 'test-released-via-addons-env-var', + ), + ).toBe(true); + }); +}); + +describe('Add-on via config file provided using an env var', () => { + const originalEnv = process.env; + const base = path.join(import.meta.dirname, 'fixtures', 'test-volto-project'); + + beforeEach(() => { + vi.resetModules(); + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('VOLTOCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => { + process.env = { + ...originalEnv, + VOLTOCONFIG: `${base}/volto.config.envvar.js`, + }; + const { registry } = AddonRegistry.init(base); + const addons = registry.getAddons(); + expect(addons.map((a) => a.name)).toStrictEqual([ + 'test-released-unmentioned', + 'test-released-dummy', + 'test-addon', + 'test-released-addon', + 'test-released-source-addon', + 'my-volto-config-addon-via-env-var', + ]); + }); + + it('[REGISTRYCONFIG - provides a list of addon records ordered using an env var for providing the configuration file', () => { + process.env = { + ...originalEnv, + REGISTRYCONFIG: `${base}/volto.config.envvar.js`, + }; + + const { registry } = AddonRegistry.init(base); + const addons = registry.getAddons(); + expect(addons.map((a) => a.name)).toStrictEqual([ + 'test-released-unmentioned', + 'test-released-dummy', + 'test-addon', + 'test-released-addon', + 'test-released-source-addon', + 'my-volto-config-addon-via-env-var', + ]); + }); +}); diff --git a/packages/registry/__tests__/create-addons-loader.test.js b/packages/registry/__tests__/create-addons-loader.test.js new file mode 100644 index 0000000000..60b611ad67 --- /dev/null +++ b/packages/registry/__tests__/create-addons-loader.test.js @@ -0,0 +1,164 @@ +import { + getAddonsLoaderCode, + nameFromPackage, +} from '../src/addon-registry/create-addons-loader'; +import { describe, expect, test } from 'vitest'; + +describe('create-addons-loader code generation', () => { + test('no addon creates simple loader, default = no loadProjectConfig', () => { + const code = getAddonsLoaderCode([]); + expect(code).toBe(`/* +This file is autogenerated. Don't change it directly. +Instead, change the "addons" setting in your package.json file. +*/ + + +const addonsInfo = {}; +export { addonsInfo }; + +const safeWrapper = (func) => (config) => { + const res = func(config); + if (typeof res === 'undefined') { + throw new Error("Configuration function doesn't return config"); + } + return res; +} + +const projectConfigLoader = false; +const projectConfig = (config) => { + return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; +} + +const load = (config) => { + const addonLoaders = []; + if(!addonLoaders.every((el) => typeof el === "function")) { + throw new TypeError( + 'Each addon has to provide a function applying its configuration to the projects configuration.', + ); + } + return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config)); +}; +export default load; +`); + }); + + test('no addon creates simple loader, loadProjectConfig set to true', () => { + const code = getAddonsLoaderCode([], {}, true); + expect(code).toBe(`/* +This file is autogenerated. Don't change it directly. +Instead, change the "addons" setting in your package.json file. +*/ + +const projectConfigLoader = require('@root/config'); + +const addonsInfo = {}; +export { addonsInfo }; + +const safeWrapper = (func) => (config) => { + const res = func(config); + if (typeof res === 'undefined') { + throw new Error("Configuration function doesn't return config"); + } + return res; +} + + +const projectConfig = (config) => { + return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; +} + +const load = (config) => { + const addonLoaders = []; + if(!addonLoaders.every((el) => typeof el === "function")) { + throw new TypeError( + 'Each addon has to provide a function applying its configuration to the projects configuration.', + ); + } + return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config)); +}; +export default load; +`); + }); + + test('one addon creates loader', () => { + const code = getAddonsLoaderCode(['volto-addon1']); + expect(code.indexOf("import voltoAddon1 from 'volto-addon1';") > 0).toBe( + true, + ); + }); + + test('two addons create loaders', () => { + const code = getAddonsLoaderCode(['volto-addon1', 'volto-addon2']); + expect( + code.indexOf(` +import voltoAddon1 from 'volto-addon1'; +import voltoAddon2 from 'volto-addon2';`) > 0, + ).toBe(true); + }); + + test('one addons plus one extra creates loader', () => { + const code = getAddonsLoaderCode(['volto-addon1:loadExtra1']); + expect( + code.indexOf(` +import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1'; +`) > 0, + ).toBe(true); + }); + + test('one addons plus two extras creates loader', () => { + const code = getAddonsLoaderCode(['volto-addon1:loadExtra1,loadExtra2']); + expect( + code.indexOf(` +import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1'; +`) > 0, + ).toBe(true); + }); + + test('two addons plus extras creates loader', () => { + const code = getAddonsLoaderCode([ + 'volto-addon1:loadExtra1,loadExtra2', + 'volto-addon2:loadExtra3,loadExtra4', + ]); + expect( + code.indexOf(` +import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1'; +import voltoAddon2, { loadExtra3 as loadExtra32, loadExtra4 as loadExtra43 } from 'volto-addon2'; +`) > 0, + ).toBe(true); + }); +}); + +describe('create-addons-loader default name generation', () => { + const getName = nameFromPackage; + + test('passing a simple word returns a word', () => { + expect(getName('something')).toBe('something'); + }); + + test('passing a kebab-name returns a word', () => { + expect(getName('volto-something-else')).toBe('voltoSomethingElse'); + }); + + test('passing a simple relative path returns random string', () => { + const rand = getName('../../'); + expect(rand.length).toBe(10); + expect(new RegExp(/[abcdefghjk]+/).exec(rand)[0].length > 0).toBe(true); + }); + test('passing a tilda relative path with addon strips tilda', () => { + const name = getName('~/addons/volto-addon1'); + expect(name).toBe('addonsvoltoAddon1'); + }); + test('passing a namespace package strips @', () => { + const name = getName('@plone/volto-addon1'); + expect(name).toBe('plonevoltoAddon1'); + }); + test('passing a tilda relative path strips tilda', () => { + const name = getName('~/../'); + expect(name.length).toBe(10); + expect(new RegExp(/[abcdefghjk]+/).exec(name)[0].length > 0).toBe(true); + }); + test('passing a backspaced path strips backspace', () => { + const name = getName('c:\\nodeprojects'); + expect(name).toBe('cnodeprojects'); + }); +}); diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg new file mode 100644 index 0000000000..1aa6adfdf3 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/components/Logo/Logo.svg @@ -0,0 +1 @@ +asdasd diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json new file mode 100644 index 0000000000..68a86c4d37 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/@plone/slots/package.json @@ -0,0 +1,4 @@ +{ + "name": "@plone/slots", + "main": "index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg new file mode 100644 index 0000000000..3e8fcacea6 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/customizations/@plone/slots/components/Logo/Logo.svg @@ -0,0 +1 @@ + diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json new file mode 100644 index 0000000000..ccf0c501c1 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/node_modules/my-addon/package.json @@ -0,0 +1,4 @@ +{ + "name": "my-addon", + "main": "index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-app/package.json b/packages/registry/__tests__/fixtures/test-app/package.json new file mode 100644 index 0000000000..24de726e14 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "test-app" +} diff --git a/packages/registry/__tests__/fixtures/test-app/registry.config.js b/packages/registry/__tests__/fixtures/test-app/registry.config.js new file mode 100644 index 0000000000..f520684acb --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-app/registry.config.js @@ -0,0 +1,3 @@ +module.exports = { + addons: ['@plone/slots', 'my-addon'], +}; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json new file mode 100644 index 0000000000..87e8d2ad96 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/package.json @@ -0,0 +1,10 @@ +{ + "name": "test-addon", + "version": "0.0.0", + "customizationPaths": [ + "src/custom-addons" + ], + "addons": [ + "test-released-dummy" + ] +} diff --git a/packages/registry/pnpm-lock.yaml b/packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js similarity index 100% rename from packages/registry/pnpm-lock.yaml rename to packages/registry/__tests__/fixtures/test-volto-project/addons/my-volto-config-addon/src/testaddon.js diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json new file mode 100644 index 0000000000..c81be50ab7 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/non-volto-addon-lib/package.json @@ -0,0 +1,5 @@ +{ + "name": "non-volto-addon-lib", + "version": "0.0.0", + "main": "index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json new file mode 100644 index 0000000000..87e8d2ad96 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/package.json @@ -0,0 +1,10 @@ +{ + "name": "test-addon", + "version": "0.0.0", + "customizationPaths": [ + "src/custom-addons" + ], + "addons": [ + "test-released-dummy" + ] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/test-released-source-addon/index.js @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/custom-addons/volto/server.jsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-addon/src/testaddon.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json new file mode 100644 index 0000000000..18c940d721 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/addons/test-released-dummy/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-released-dummy", + "version": "0.0.0", + "main": "index.js", + "addons": [ + "test-released-unmentioned" + ] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json new file mode 100644 index 0000000000..4c3beb25ec --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "paths": { + "test-addon": ["test-addon/src"], + "test-released-dummy": ["test-released-dummy"], + "my-volto-config-addon": ["my-volto-config-addon/src"], + "non-volto-addon-lib": ["non-volto-addon-lib/src"] + }, + "baseUrl": "addons" + } +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/LanguageSwitcher.jsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/TSComponent.tsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js new file mode 100644 index 0000000000..0dae97301d --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/client.js @@ -0,0 +1 @@ +//dummy file diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/routes.jsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/@plone/volto/src/server.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json new file mode 100644 index 0000000000..ac0652a9b0 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/my-volto-config-addon-via-env-var/package.json @@ -0,0 +1,11 @@ +{ + "name": "my-volto-config-addon-via-env-var", + "version": "0.0.0", + "main": "index.js", + "customizationPaths": [ + "src/custom-addons" + ], + "addons": [ + "test-released-dummy" + ] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json new file mode 100644 index 0000000000..4f1f61b465 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-addon/package.json @@ -0,0 +1,8 @@ +{ + "name": "test-released-addon", + "version": "0.0.0", + "main": "index.js", + "addons": [ + "test-released-unmentioned:extra1,extra2" + ] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js new file mode 100644 index 0000000000..53e61140f2 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/index.js @@ -0,0 +1,2 @@ +export default (config) => config; + diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json new file mode 100644 index 0000000000..4640873bbf --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-dummy/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-released-dummy", + "main": "index.js", + "addons": [] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json new file mode 100644 index 0000000000..3220bd17cc --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-released-source-addon", + "version": "0.0.0", + "main": "src/index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/razzle.extend.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/@root/marker.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/client.js @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/customizations/routes.tsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-source-addon/src/index.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js new file mode 100644 index 0000000000..53e61140f2 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/index.js @@ -0,0 +1,2 @@ +export default (config) => config; + diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json new file mode 100644 index 0000000000..b3b3c399f1 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-unmentioned/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-released-unmentioned", + "version": "0.0.0", + "main": "index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js new file mode 100644 index 0000000000..53e61140f2 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/index.js @@ -0,0 +1,2 @@ +export default (config) => config; + diff --git a/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json new file mode 100644 index 0000000000..c6a4424310 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/node_modules/test-released-via-addons-env-var/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-released-via-addons-env-var", + "main": "index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/package.json b/packages/registry/__tests__/fixtures/test-volto-project/package.json new file mode 100644 index 0000000000..bb67637bb2 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/package.json @@ -0,0 +1,12 @@ +{ + "name": "test-volto-project", + "addons": [ + "test-addon", + "test-released-addon:extra", + "test-released-source-addon" + ], + "customizationPaths": [ + "src/custom-addons", + "src/customizations/" + ] +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json new file mode 100644 index 0000000000..7e6c4a11fb --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-local-packages-via-addons-env-var", + "main": "src/index.js" +} diff --git a/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js new file mode 100644 index 0000000000..8cb61d300a --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/packages/test-local-packages-via-addons-env-var/src/index.js @@ -0,0 +1 @@ +export default (config) => config; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/test-addon/testaddon.js @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js new file mode 100644 index 0000000000..4d389677f0 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/src/custom-addons/volto/client.js @@ -0,0 +1 @@ +// another customization diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/LanguageSwitcher.js @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/TSComponent.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js new file mode 100644 index 0000000000..b262fa10a3 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/client.js @@ -0,0 +1 @@ +// dummy file diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/routes.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx new file mode 100644 index 0000000000..8337712ea5 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/src/customizations/server.jsx @@ -0,0 +1 @@ +// diff --git a/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js b/packages/registry/__tests__/fixtures/test-volto-project/src/marker.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js new file mode 100644 index 0000000000..2e37cd37c2 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.envvar.js @@ -0,0 +1,3 @@ +module.exports = { + addons: ['my-volto-config-addon-via-env-var'], +}; diff --git a/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js new file mode 100644 index 0000000000..ec05e8e085 --- /dev/null +++ b/packages/registry/__tests__/fixtures/test-volto-project/volto.config.js @@ -0,0 +1,3 @@ +module.exports = { + addons: ['my-volto-config-addon'], +}; diff --git a/packages/registry/__tests__/fixtures/volto-addon1.js b/packages/registry/__tests__/fixtures/volto-addon1.js new file mode 100644 index 0000000000..b406bacbd8 --- /dev/null +++ b/packages/registry/__tests__/fixtures/volto-addon1.js @@ -0,0 +1,14 @@ +export default (config) => { + const settings = { + nonContentRoutes: [], + supportedLanguages: ['en'], + navDepth: 1, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; diff --git a/packages/registry/__tests__/fixtures/volto-addon2.js b/packages/registry/__tests__/fixtures/volto-addon2.js new file mode 100644 index 0000000000..62ec3d2cf0 --- /dev/null +++ b/packages/registry/__tests__/fixtures/volto-addon2.js @@ -0,0 +1,29 @@ +export default (config) => { + const settings = { + nonContentRoutes: [], + supportedLanguages: ['en', 'de'], + navDepth: 3, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; + +const additionalConfig = (config) => { + const settings = { + navDepth: 6, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; + +export { additionalConfig }; diff --git a/packages/registry/__tests__/fixtures/volto-addon3.js b/packages/registry/__tests__/fixtures/volto-addon3.js new file mode 100644 index 0000000000..2231f8a8b1 --- /dev/null +++ b/packages/registry/__tests__/fixtures/volto-addon3.js @@ -0,0 +1,41 @@ +export default (config) => { + const settings = { + nonContentRoutes: [], + supportedLanguages: ['en', 'de'], + navDepth: 3, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; + +const additionalConfig = (config) => { + const settings = { + navDepth: 6, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; + +const alternateAdditionalConfig = (config) => { + const settings = { + navDepth: 10, + }; + + config.settings = { + ...config.settings, + ...settings, + }; + + return config; +}; +export { additionalConfig, alternateAdditionalConfig }; diff --git a/packages/registry/docs/_static/Plone_logo_square.png b/packages/registry/docs/_static/Plone_logo_square.png new file mode 100644 index 0000000000000000000000000000000000000000..f42a1220d761a40ad9f0f0df919136f61ef729e2 GIT binary patch literal 30912 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 LD@#>L7zh0yh&`2n literal 0 HcmV?d00001 diff --git a/packages/registry/docs/_static/favicon.ico b/packages/registry/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ae7bb0cd8b1f4f2e6d657bc7a253472819d77819 GIT binary patch literal 5430 zcmdUzv1$}y6or39*mCm*nMy3Tv=B0n;3G_FX)6dj5Wz;UwcFM%Z{S2y*ogZGGvWhm z#3q7dJYSp}Zf^dW-Bm#h9Pa(^J?Gr{Z)P`;lCe04v=C~c5&99(gR}V4d8U_FC9H+9nPYV{kIiS_C~YBdpE3Vk>v$CV z%oqQ$uo;<6{X}5T9@M5ZJMBUq>^pZi?on>L2OI%z&ZC;rOT)dRu2t=NF1cX$9(~US z9@^-a-Gg|A5ck)pc3{t}d?iG`+WIk~74YyHW)vB2Pb;nr@L;rhn2Sno0`a^%NO%p=Cq^TlH~kz?oDb9c@6@!W^|AI<+;`~Mg3 zU*q0~cH7Ry<*vNLpz?g3OM&+bl)h)V>hbdj*WN#3R)=1_b>PCk^%=8SG2$Kc%>Jj} zza#f5GtbVx{hoYYM}9nK?tb<62cCLW@NRXEe}7kjv-5ns1U$Vaes?#a-}pPI$9KEe z`+VWKF_&)A8@@P&;NMOB(OvIB`AVSfQN?+7Ml0aq`OGMcH&q- zV?-mM^|Q%u>^-BaG8%M^P)2?{!x^}7F6S!pb7%3`L;PKsU#lLU1>1o|YV?ffBVLpD zj?W=}Av7a?-g{e)M-lyZJxf~&>)&_|G}W$iw(;a;@7>f+=<)lQ{5<+ueK+g-`c8kD z((*@2M;C_|hv&~O=Uum)z3-OCXQxMJFHes?zwDNu=iTyr{{7qeygwaKKb`krIMWjc zE*i9WbX`Bg6K`fPiOdT`*PLHiTJtBB+P literal 0 HcmV?d00001 diff --git a/packages/registry/docs/_static/logo.svg b/packages/registry/docs/_static/logo.svg new file mode 100644 index 0000000000..6bfdc1ee90 --- /dev/null +++ b/packages/registry/docs/_static/logo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/registry/docs/_static/print.css b/packages/registry/docs/_static/print.css new file mode 100644 index 0000000000..8dbc2d5794 --- /dev/null +++ b/packages/registry/docs/_static/print.css @@ -0,0 +1,3 @@ +.tooltip { + display: none; +} diff --git a/packages/registry/docs/conceptual-guides/add-on-registry.md b/packages/registry/docs/conceptual-guides/add-on-registry.md new file mode 100644 index 0000000000..763d9f54d9 --- /dev/null +++ b/packages/registry/docs/conceptual-guides/add-on-registry.md @@ -0,0 +1,38 @@ +--- +myst: + html_meta: + "description": "An explanation of the add-on registry in @plone/registry" + "property=og:description": "An explanation of the add-on registry in @plone/registry" + "property=og:title": "Add-on registry" + "keywords": "@plone/registry, registry, add-on" +--- + +# Add-on registry + +An add-on registry is a facility that allows an app, which was built on an existing framework, to itself be extensible and pluggable. + +The add-on registry is a store where you can register a number of add-ons that your app consumes. + +The add-on registry can be queried, so it can provide a list of add-ons installed in the registry and their properties. + + +## What is an add-on? + +Add-on packages are just CommonJS or ESM packages. +Their main purpose is to encapsulate logic, configuration and customizations in a reusable way. +The only requirement is that their primary entry point (the `main` key of their `package.json`) points to a module that exports a default function, which acts as a default configuration loader for that package. + +Add-ons are applied in the order they are declared in the `addons` key of {file}`package.json` or programatically via a provided configuration file. +Add-ons can override configuration coming from other add-ons, providing a hierarchy of configuration stacks. + +An add-on can be published in an npm registry, just as any other package. +However, add-ons are meant to not be transpiled, but built along with your app code. +They can be released as "source" packages or used directly in your app as local code. + +Add-ons can define shadowed components. +"Component shadowing" is a technique for overriding modules of other packages at build time. +This technique builds upon the `resolve.aliases` facilities of bundlers, so modules can be replaced when the app is being built. + +Add-ons can be chained, where each one can configure the app in some way. +If needed, each add-on in the chain can override or extend the previous configuration that other add-ons set. +Thus, the order in which you register add-ons matters. diff --git a/packages/registry/docs/conceptual-guides/component-registry.md b/packages/registry/docs/conceptual-guides/component-registry.md new file mode 100644 index 0000000000..08fb9226d9 --- /dev/null +++ b/packages/registry/docs/conceptual-guides/component-registry.md @@ -0,0 +1,20 @@ +--- +myst: + html_meta: + "description": "An explanation of the component registry in @plone/registry" + "property=og:description": "An explanation of the component registry in @plone/registry" + "property=og:title": "Component registry" + "keywords": "@plone/registry, registry, component" +--- + +# Component registry + +The configuration registry stores a component registry in itself. +The component registry is a mapping of names to components. +You can look up a name, and receive a component that you can reference in your code. +This provides an alternative, and more convenient, way to customize components in a pluggable way. + +You can programmatically override such registrations from your add-on or projects because it's stored in the configuration registry. +You can customize a component without using shadowing at all, if the code that uses the component retrieves from the component registry, rather than import it directly. +You can even have modifiers to the component registrations through dependencies. +Thus you can adapt the call, given an array of such dependencies. diff --git a/packages/registry/docs/conceptual-guides/configuration-registry.md b/packages/registry/docs/conceptual-guides/configuration-registry.md new file mode 100644 index 0000000000..c1fd19db20 --- /dev/null +++ b/packages/registry/docs/conceptual-guides/configuration-registry.md @@ -0,0 +1,78 @@ +--- +myst: + html_meta: + "description": "An explanation of the configuration registry in @plone/registry" + "property=og:description": "An explanation of the configuration registry in @plone/registry" + "property=og:title": "Configuration registry" + "keywords": "@plone/registry, registry, configuration" +--- + +# Configuration registry + +The configuration registry supplements the add-on registry. +They both work together to provide extensibility and pluggability capabilities. +The configuration registry is a facility that stores app configuration to share in the app. +The add-ons save configuration from the registry using their default export function on app bootstrap time. +They retrieve this configuration as needed by the functionality and components they expose. + +## Example use case - Pluggable block system + +Let's say that your app is the user interface of a content management system (CMS). +This CMS uses blocks as its main fundamental unit of content. +The pages that the CMS builds are made up of these blocks. +The CMS has some basic available blocks, yet it's a requirement that integrators can register more blocks in a pluggable way. +This app will use the add-on registry to extend the basic CMS capabilities, so an external add-on can supplement their own add-ons to those in the basic CMS. + +Let's assume we've defined a key in the registry `config.blocks.blocksConfig`, and defined a way to register the available blocks in the CMS as the keys in that object in the configuration registry: + +```js + config.blocks.blocksConfig.faq_viewer = { + id: 'faq_viewer', + title: 'FAQ Viewer', + edit: FAQBlockEdit, + view: FAQBlockView, + icon: chartIcon, + group: 'common', + restricted: false, + mostUsed: true, + sidebarTab: 1, + }; +``` + +The configuration registry will have other keys already set by default, which will compose the initial set of basic blocks used by the CMS. +Then the CMS will populate the available blocks in the user interface. + +The add-on is meant to extend the initial configuration. +From the default export function of our add-on, you should provide the configuration of the new block: + +```ts +export default function applyConfig(config: ConfigData) { + config.blocks.blocksConfig.faq_viewer = { + id: 'faq_viewer', + title: 'FAQ Viewer', + edit: FAQBlockEdit, + view: FAQBlockView, + icon: chartIcon, + group: 'common', + restricted: false, + mostUsed: true, + sidebarTab: 1, + }; + + return config; +} +``` + +Once the app starts, the add-on registry will execute, in order, all the registered add-ons' default export functions, configuring the new block. +The add-on will then become available to the CMS when it asks the configuration registry for it. + + +## Configuration registry artifacts + +The configuration registry also stores special elements that can be queried and retrieved in a pluggable way. + +- Components +- Slots +- Utilities + +Some of the components are particular to the use case of a CMS, such as slots, but the abstraction can be ported and applied to different scenarios. diff --git a/packages/registry/docs/conceptual-guides/utility-registry.md b/packages/registry/docs/conceptual-guides/utility-registry.md new file mode 100644 index 0000000000..5c0d284bae --- /dev/null +++ b/packages/registry/docs/conceptual-guides/utility-registry.md @@ -0,0 +1,14 @@ +--- +myst: + html_meta: + "description": "An explanation of the utility registry in @plone/registry" + "property=og:description": "An explanation of the utility registry in @plone/registry" + "property=og:title": "Utility registry" + "keywords": "@plone/registry, registry, utility" +--- + +# Utility registry + +The configuration registry stores a utility registry in itself. +The component registry is a mapping of a `name` and a `type` to a method or function. +The utility registry works similarly, but for methods and functions, and with additional query argument `type`. diff --git a/packages/registry/docs/conf.py b/packages/registry/docs/conf.py new file mode 100644 index 0000000000..796fc4b620 --- /dev/null +++ b/packages/registry/docs/conf.py @@ -0,0 +1,297 @@ +# Configuration file for the Sphinx documentation builder. +# Plone Documentation build configuration file + + +# -- Path setup -------------------------------------------------------------- + +from datetime import datetime + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath(".")) + +import os +import json + + +# -- Project information ----------------------------------------------------- + +project = "@plone/registry documentation" +copyright = "Plone Foundation" +author = "Plone Community" +trademark_name = "Plone" +now = datetime.now() +year = str(now.year) + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +with open( + os.path.join(os.path.abspath("."), "../package.json"), "r" +) as package_json: + data = package_json.read() + +version_from_package_json = json.loads(data)["version"] + +if version_from_package_json: + # The short X.Y version. + version = version_from_package_json + # The full version, including alpha/beta/rc tags. + release = version_from_package_json +else: + version = "1.8.0" + release = "1.8.0" + + +# -- General configuration ---------------------------------------------------- + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ["_templates"] + +# Add any Sphinx extension module names here, as strings. +# They can be extensions coming with Sphinx (named "sphinx.ext.*") +# or your custom ones. +extensions = [ + "myst_parser", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx_copybutton", + "sphinxext.opengraph", +] + + +# If true, the Docutils Smart Quotes transform, originally based on SmartyPants +# (limited to English) and currently applying to many languages, will be used +# to convert quotes and dashes to typographically correct entities. +# Note to maintainers: setting this to `True` will cause contractions and +# hyphenated words to be marked as misspelled by spellchecker. +smartquotes = False + +# The name of the Pygments (syntax highlighting) style to use. +# pygments_style = "sphinx.pygments_styles.PyramidStyle" +pygments_style = "sphinx" + +# Options for the linkcheck builder +# Ignore localhost +linkcheck_ignore = [ + # TODO: Before release, clean up any links to ignore + r"http://127.0.0.1", + r"http://localhost", + # Ignore pages that require authentication + r"https://github.com/plone/volto/issues/new/choose", # requires auth + # Ignore github.com pages with anchors + r"https://github.com/.*#.*", + # Ignore other specific anchors +] +linkcheck_anchors = True +linkcheck_timeout = 5 +linkcheck_retries = 1 + +# The suffix of source filenames. +source_suffix = { + ".md": "markdown", + ".bugfix": "markdown", + ".breaking": "markdown", + ".documentation": "markdown", + ".feature": "markdown", + ".internal": "markdown", +} + +# The master toctree document. +master_doc = "index" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ +] + +suppress_warnings = [ + # "toc.excluded", # Suppress `WARNING: document isn't included in any toctree` + "toc.not_readable", # Suppress `WARNING: toctree contains reference to nonexisting document 'news*'` +] + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "plone_sphinx_theme" +html_logo = "_static/logo.svg" +html_favicon = "_static/favicon.ico" +html_theme_options = { + "article_header_start": ["toggle-primary-sidebar"], + "extra_footer": """

The text and illustrations in this website are licensed by the Plone Foundation under a Creative Commons Attribution 4.0 International license. Plone and the Plone® logo are registered trademarks of the Plone Foundation, registered in the United States and other countries. For guidelines on the permitted uses of the Plone trademarks, see https://plone.org/foundation/logo. All other trademarks are owned by their respective owners.

+

Pull request previews by Read the Docs.

""", + "footer_end": ["version.html"], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/plone/volto", + "icon": "fa-brands fa-square-github", + "type": "fontawesome", + "attributes": { + "target": "_blank", + "rel": "noopener me", + "class": "nav-link custom-fancy-css" + } + }, + { + "name": "Mastodon", + "url": "https://plone.social/@plone", + "icon": "fa-brands fa-mastodon", + "type": "fontawesome", + "attributes": { + "target": "_blank", + "rel": "noopener me", + "class": "nav-link custom-fancy-css" + } + }, + { + "name": "X (formerly Twitter)", + "url": "https://x.com/plone", + "icon": "fa-brands fa-square-x-twitter", + "type": "fontawesome", + "attributes": { + "target": "_blank", + "rel": "noopener me", + "class": "nav-link custom-fancy-css" + } + }, + ], + "logo": { + "text": "@plone/registry Documentation", + }, + "navigation_with_keys": True, + "path_to_docs": "docs", + "repository_branch": "main", + "repository_url": "https://github.com/plone/volto/tree/main/packages/registry", + "search_bar_text": "Search", # TODO: Confirm usage of search_bar_text in plone-sphinx-theme + "use_edit_page_button": True, + "use_issues_button": True, + "use_repository_button": True, +} + +# Announce that we have an opensearch plugin +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch +html_use_opensearch = "https://plone-registry.readthedocs.io/" # TODO: Confirm usage of opensearch in theme + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "%(project)s v%(release)s" % {"project": project, "release": release} + +html_css_files = ["custom.css", ("print.css", {"media": "print"})] + +# If false, no index is generated. +html_use_index = True + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "%(project)s v%(release)s" % {"project": project, "release": release} + +html_extra_path = [ + "robots.txt", +] + +html_static_path = [ + "_static", +] + + +# -- Options for MyST markdown conversion to HTML ----------------------------- + +# For more information see: +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html +myst_enable_extensions = [ + "deflist", # Support definition lists. + # https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#definition-lists + "linkify", # Identify "bare" web URLs and add hyperlinks. + "colon_fence", # You can also use ::: delimiters to denote code fences,\ + # instead of ```. + "html_image", # For inline images. See https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#html-images +] + + +# -- Intersphinx configuration ---------------------------------- + +# This extension can generate automatic links to the documentation of objects +# in other projects. Usage is simple: whenever Sphinx encounters a +# cross-reference that has no matching target in the current documentation set, +# it looks for targets in the documentation sets configured in +# intersphinx_mapping. A reference like :py:class:`zipfile.ZipFile` can then +# linkto the Python documentation for the ZipFile class, without you having to +# specify where it is located exactly. +# +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html +# +intersphinx_mapping = { + "plone": ("https://6.docs.plone.org/", None), + "python": ("https://docs.python.org/3/", None), + "training": ("https://training.plone.org/", None), +} + + +# -- OpenGraph configuration ---------------------------------- + +ogp_site_url = "https://plone-registry.readthedocs.io/" +ogp_description_length = 200 +ogp_image = "https://plone-registry.readthedocs.io/en/latest/_static/Plone_logo_square.png" +ogp_site_name = "@plone/registry Documentation" +ogp_type = "website" +ogp_custom_meta_tags = [ + '', +] + + +# -- Options for sphinx.ext.todo ----------------------- +# See http://sphinx-doc.org/ext/todo.html#confval-todo_include_todos +todo_include_todos = True + + +# -- Options for HTML help output ------------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "PloneRegistryDocumentation" + + +# -- Options for LaTeX output ------------------------------------------------- + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]) +latex_documents = [ + ( + "index", + "PloneRegistryDocumentation.tex", + "@plone/registry Documentation", + "Plone Community", + "manual", + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = "_static/logo_2x.png" + + +# An extension that allows replacements for code blocks that +# are not supported in `rst_epilog` or other substitutions. +# https://stackoverflow.com/a/56328457/2214933 +def source_replace(app, docname, source): + result = source[0] + for key in app.config.source_replacements: + result = result.replace(key, app.config.source_replacements[key]) + source[0] = result + + +# Dict of replacements. +source_replacements = { + "{NVM_VERSION}": "0.39.5", +} + + +def setup(app): + app.add_config_value("source_replacements", {}, True) + app.connect("source-read", source_replace) diff --git a/packages/registry/docs/how-to-guides/access-registry.md b/packages/registry/docs/how-to-guides/access-registry.md new file mode 100644 index 0000000000..cbe6923633 --- /dev/null +++ b/packages/registry/docs/how-to-guides/access-registry.md @@ -0,0 +1,20 @@ +--- +myst: + html_meta: + "description": "How to access the configuration registry in @plone/registry" + "property=og:description": "How to access the configuration registry in @plone/registry" + "property=og:title": "Access the configuration registry" + "keywords": "@plone/registry, registry, configuration, guide" +--- + +# Access the configuration registry + +You can access the configuration registry as follows. + +```ts +import config from '@plone/registry' + +const blocksConfig = config.blocks.blocksConfig +``` + +This method assumes that either you previously created a `blocksConfig` key in `blocks` in your add-on, or another add-on sets it. diff --git a/packages/registry/docs/how-to-guides/instantiate-registry.md b/packages/registry/docs/how-to-guides/instantiate-registry.md new file mode 100644 index 0000000000..f915e983fe --- /dev/null +++ b/packages/registry/docs/how-to-guides/instantiate-registry.md @@ -0,0 +1,56 @@ +--- +myst: + html_meta: + "description": "How to instantiate the registry in @plone/registry" + "property=og:description": "How to instantiate the registry in @plone/registry" + "property=og:title": "Instantiate the registry" + "keywords": "@plone/registry, registry, instantiate" +--- + +# Instantiate the registry + +The registry is instantiated in the context of your app folder. +It gets your app folder path as argument. + +```js +import path from 'path'; +import { AddonRegistry } from '@plone/registry/addon-registry'; + +const appRootPath = path.resolve('.'); +const { registry } = AddonRegistry.init(appRootPath) +``` + +You have full access to the add-on registry API in the `registry` object. + +By default, you also get these objects after calling `init`. + +```js +const { registry, addons, theme, shadowAliases } = AddonRegistry.init(appRootPath) +``` + +This can be useful for configuring your build process. + + +## Initialization + +By default, the configuration registry is empty. +It only contains the base object keys which are required for it to work properly. +These are the keys present on initialization. +The optional keys are excluded. + +```ts +export type ConfigData = { + settings: SettingsConfig | Record; + blocks: BlocksConfig | Record; + views: ViewsConfig | Record; + widgets: WidgetsConfig | Record; + addonReducers?: AddonReducersConfig; + addonRoutes?: AddonRoutesConfig; + slots: SlotsConfig | Record; + components: ComponentsConfig | Record; + utilities: UtilitiesConfig | Record; + experimental?: ExperimentalConfig; +}; +``` + +In the context of a Volto app, the registry gets initialized by Volto by default. diff --git a/packages/registry/docs/how-to-guides/register-an-add-on.md b/packages/registry/docs/how-to-guides/register-an-add-on.md new file mode 100644 index 0000000000..874b35471d --- /dev/null +++ b/packages/registry/docs/how-to-guides/register-an-add-on.md @@ -0,0 +1,94 @@ +--- +myst: + html_meta: + "description": "How to register an add-on in @plone/registry" + "property=og:description": "How to register an add-on in @plone/registry" + "property=og:title": "Register an add-on" + "keywords": "@plone/registry, registry, add-on" +--- + +# Register an add-on + +You have two ways available to register an add-on in your app. +You can do so either through the `addons` key in your {file}`package.json` or by using a configuration file. + +```{note} +Using a configuration file is useful when you want to add some logic to the `addons` list, if you want it to be dynamic. +For example, you can make the list dynamic given an environment variable. +``` + + +## Via `addons` key in `package.json` + +The following code sample shows how to register your add-on in your app through the `addons` key in your {file}`package.json`. + +```json +{ + "name": "my-app-project", + "addons": [ + "acme-volto-foo-addon", + "@plone/some-addon", + "collective-another-volto-addon" + ] +} +``` + +The add-ons are registered in the order they are found in the `addons` key. +The last add-on takes precedence over the others. +This means that if you configure something in `acme-volto-foo-addon`, then the same thing later in `collective-another-volto-addon`, the latter configured thing will win and its configuration will be applied. + +All add-ons should set the value for the `main` entry module, such as `src/index.js`, in {file}`package.json`. +This function should have a signature of `config => config`. +That is, it should take the configuration registry object and return it, possibly mutated or changed. + +```ts +import type { ConfigType } from '@plone/registry' + +export default function applyConfig(config: ConfigType) { + return config +}; +``` + +The `addons` key ensures the add-on's main default export function is executed, being passed the configuration registry. + + +## Via a configuration file + +The configuration file can be provided via an environment variable. +You can use one of these two environment variables. + +- `REGISTRYCONFIG` +- `VOLTOCONFIG` + +The value of the environment variable must point to a file that exists relative to the app folder, that is, the one you pass to the instantiation of the add-on registry. +You can also pass the full path of the file. + +For example, if your configuration file is named {file}`my-add-on-registry-config.js` and is located at the root of your add-on package, you would set your environment variable as shown. + +```shell +set REGISTRYCONFIG="my-add-on-registry.config.js" +``` + +```{note} +This is useful when you want to provide different `addon` configuration files under different scenarios. +``` + +If the file that you specify in the environment variable exists, then `@plone/registry` uses it to configure your add-on. +If it does not exist, then `@plone/registry` looks for the configuration file in the following locations in the root of your app in order. +The first found configuration file wins. + +- {file}`registry.config.js` +- {file}`volto.config.js` + +This is an example of a configuration file. +You must define it in [CommonJS](https://en.wikipedia.org/wiki/CommonJS) format. + +```js +module.exports = { + addons: ['my-volto-config-addon'], +}; +``` + +If your app is in ESM (`"type": "module"` in {file}`package.json`), then you should use the `.cjs` suffix for the configuration file to mark it as a proper `CommonJS` file. + +If `@plone/registry` finds no configuration file, then it only relies on the configuration, if any, in the `addons` key in {file}`package.json`. diff --git a/packages/registry/docs/how-to-guides/register-and-retrieve-components.md b/packages/registry/docs/how-to-guides/register-and-retrieve-components.md new file mode 100644 index 0000000000..08e8623e0b --- /dev/null +++ b/packages/registry/docs/how-to-guides/register-and-retrieve-components.md @@ -0,0 +1,114 @@ +--- +myst: + html_meta: + "description": "How to register, retrieve, adapt, and load components in @plone/registry" + "property=og:description": "How to register, retrieve, adapt, and load components in @plone/registry" + "property=og:title": "Register and retrieve components" + "keywords": "@plone/registry, registry, register, retrieve, components" +--- + +# Register and retrieve components + +This section of the documentation describes how to register, retrieve, adapt, and load components. + + +## Register components by name + +You can register components by name, typically from an add-on or project configuration, using `config.registerComponent`. + +```js +import MyToolbarComponent from './MyToolbarComponent' + +config.registerComponent({ + name: 'Toolbar', + component: MyToolbarComponent, +}); +``` + + +## Retrieve a component from the component registry + +You can programmatically retrieve a component from the component registry using `config.getComponent`. + +```js +const Toolbar = config.getComponent('Toolbar').component +``` + +Alternatively, you can retrieve a component by using the convenience component `Component`, if you want to use it in JSX code directly. + +```jsx +import Component from '@plone/volto/components/theme/Component/Component'; + + +``` + +Note that you can pass `props` down to the retrieved component. + + +## Adapt the component + +You can register components, then retrieve them, given a list of modifiers using the `dependencies` array. + +```js +import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent' + +config.registerComponent({ + name: 'Teaser', + component: MyTeaserNewsItemComponent, + dependencies: ['News Item'], + }); +``` + +To retrieve the component, use `getComponent`. + +```js +config.getComponent({ + name: 'Teaser', + dependencies: ['News Item'], + }).component +``` + +If you have a single dependency, you can use a string instead of an array. + +You can have both, either with or without dependencies. + +```js +import MyTeaserDefaultComponent from './MyTeaserDefaultComponent' +import MyTeaserNewsItemComponent from './MyTeaserNewsItemComponent' + +config.registerComponent({ + name: 'Teaser', + component: MyTeaserDefaultComponent, + }); + +config.registerComponent({ + name: 'Teaser', + component: MyTeaserNewsItemComponent, + dependencies: 'News Item', + }); +``` + +After you register them, you can retrieve both or either of them, depending on your use case. +In the next example, given a content type value coming from the `content` prop, you can retrieve them. + +```jsx + +``` + + +## Lazy load components + +```{todo} +Test it properly. +``` + +You can lazy load the component in the registry, too, if you need it. + +```js +const MyTeaserDefaultComponent = lazy(()=> import(./MyTeaserDefaultComponent)) + +config.registerComponent({ + name: 'Teaser', + component: MyTeaserDefaultComponent, + }); +``` diff --git a/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md b/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md new file mode 100644 index 0000000000..d58fc92d4c --- /dev/null +++ b/packages/registry/docs/how-to-guides/register-and-retrieve-utilities.md @@ -0,0 +1,113 @@ +--- +myst: + html_meta: + "description": "How to register and retrieve utilities in @plone/registry" + "property=og:description": "How to register and retrieve utilities in @plone/registry" + "property=og:title": "Register and retrieve utilities" + "keywords": "@plone/registry, registry, utilities, register, retrieve" +--- + +# Register and retrieve utilities + +This section of the documentation describes how to register and retrieve utilities. + + +## Register utilities by `name` and `type` + +You can register a utility using `config.registerUtility` by using its specific `name` and `type` arguments. + +```js +config.registerUtility({ + name: 'url', + type: 'validator', + method: () => 'this is a simple validator utility', +}); +``` + +For utilities of the same `type`, you can register different `name` utilities. + +```js +config.registerUtility({ + name: 'url', + type: 'validator', + method: () => 'this is a simple URL validator utility', +}); + +config.registerUtility({ + name: 'email', + type: 'validator', + method: () => 'this is a simple email validator utility', +}); +``` + +However, if you register two utilities under the same `name`, then the latter will override the former. +Thus you can override existing utilities in your add-ons. + +```js +config.registerUtility({ + name: 'url', + type: 'validator', + method: () => 'this is a simple URL validator utility', +}); + +config.registerUtility({ + name: 'url', + type: 'validator', + method: () => 'this registered URL validator utility is the last defined, and therefore overrides all previous utilities with the same `name`', +}); +``` + + +## Register utilities using a `dependencies` object + +It is possible to register utilities using a `dependencies` object. +This is useful to further specify the utility. + +```js +config.registerUtility({ + name: 'email', + type: 'validator', + dependencies: { fieldType: 'email' }, + method: () => 'this is an email validator utility with dependencies for email', +}); +``` + + +## Retrieve a specific utility + +You can retrieve one specific utility using `config.getUtility`, given the `name` and `type`. + +```js +config.getUtility({ name: 'url', type: 'validator' }).method() +``` + +You can do the same using a `dependencies` object. + +```js +config.getUtility({ + name: 'email', + dependencies: { fieldType: 'string' }, + type: 'validator', +}).method() +``` + + +### Retrieve groups of utilities + +You can retrieve all utilities registered under the same `type`. + +```js +config.getUtilities({ type: 'validator' }) +``` + +You can do the same using a `dependencies` object. + +```js +config.getUtilities({ + type: 'validator', + dependencies: { fieldType: 'string' }, +}).length +``` + +This is useful when building pluggable systems, so you can query all the utilities present in the registry. +For example, retrieve all validator utilities with the `fieldType` of `string`. diff --git a/packages/registry/docs/how-to-guides/shadow-a-component.md b/packages/registry/docs/how-to-guides/shadow-a-component.md new file mode 100644 index 0000000000..90179a1f04 --- /dev/null +++ b/packages/registry/docs/how-to-guides/shadow-a-component.md @@ -0,0 +1,61 @@ +--- +myst: + html_meta: + "description": "How to shadow a component or module in @plone/registry" + "property=og:description": "How to shadow a component or module in @plone/registry" + "property=og:title": "Shadow a component or module" + "keywords": "@plone/registry, registry, shadow, component" +--- + +# Shadow a component or module + +Component or module shadowing is a technique that allows you to define an alternative module for a specific module. +You normally would want to override a module from another add-on. +This add-on should not be transpiled. + +This technique relies on the `resolve.alias` feature of the bundlers, so the module is effectively being replaced by the alternative one supplied. +You will need to modify some imports in the alternative module to comply with the new placement and convert relative imports to absolute ones. + +To override the component, first, you should identify the component you want to shadow in the package. +Then, replicate the same folder structure that the original component has in the source code and place it inside the `customizations` folder of your add-on. + +Start by using the name of the package you want to shadow. +If the package has a namespace, then use a folder to define it. + +To identify a component to shadow, you can use several approaches. +The primary method uses [React Developer Tools](https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi). +You can inspect the app and find out the name of the component (the name +of the tag), then search for it in the source code. +Alternatively, you can browse the contents of the source package you want to shadow by searching for it inside {file}`node_modules` of your app folder. + + +## Example: customize the `Logo` resource + +To replace the `Logo` resource, your folder structure needs to match the folder structure of the package in the `customizations` folder. +The `Logo` resource is located in the `@plone/slots` package in the {file}`components/Logo/Logo.svg` file. +├── slots + +```text +node_modules +└── @plone + └── slots + └── components + └── Logo.svg +``` + +The structure inside your `customizations` of the component shadowing the original should be {file}`src/customizations/@plone/slots/components/Logo/Logo.svg`. + +```text +src +└── customizations + └── @plone + └── slots + └── components + └── Logo.svg +``` + +```{warning} +When upgrading add-ons in your project, it's important to review any shadowed components from the updated add-on. +Changes in the add-on's public API could potentially break your application. +Ensure that your shadowed components are updated to align with the new specifications of the original module. +``` diff --git a/packages/registry/docs/index.md b/packages/registry/docs/index.md new file mode 100644 index 0000000000..ad433cc9b6 --- /dev/null +++ b/packages/registry/docs/index.md @@ -0,0 +1,43 @@ +--- +myst: + html_meta: + "description": "@plone/registry provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps." + "property=og:description": "@plone/registry provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps." + "property=og:title": "@plone/registry" + "keywords": "@plone/registry, registry, add-on, configuration, component, utility, JavaScript, TypeScript, app" +--- + +# `@plone/registry` + +`@plone/registry` provides support for building an add-on and configuration registry with infrastructure for JavaScript and TypeScript-based apps. + +As a developer when you build an app, regardless of the framework and technologies used, it's a one-off app. +That means you have to build something that has very specific requirements, behavior, and look and feel. + +Sometimes you need to build something generic that is pluggable and extensible. +In the JavaScript and TypeScript ecosystem, this is often quite complex, and the existing frameworks do not provide the means to do this. +`@plone/registry` helps developers extend their apps in a pluggable way. + + +```{toctree} +:maxdepth: 1 +:hidden: +:caption: How-to guides +how-to-guides/instantiate-registry +how-to-guides/register-an-add-on +how-to-guides/access-registry +how-to-guides/register-and-retrieve-components +how-to-guides/register-and-retrieve-utilities +how-to-guides/shadow-a-component +``` + + +```{toctree} +:maxdepth: 1 +:hidden: +:caption: Conceptual guides +conceptual-guides/add-on-registry +conceptual-guides/configuration-registry +conceptual-guides/component-registry +conceptual-guides/utility-registry +``` diff --git a/packages/registry/docs/robots.txt b/packages/registry/docs/robots.txt new file mode 100644 index 0000000000..fa4599903a --- /dev/null +++ b/packages/registry/docs/robots.txt @@ -0,0 +1,8 @@ +# Disallow all user agents from the following directories and files +User-agent: * +Disallow: /_sources/ +Disallow: /.doctrees/ +Disallow: /*.txt$ +Disallow: /.buildinfo$ +Disallow: /objects.inv$ +Sitemap: https://plone-registry.readthedocs.io/sitemap.xml diff --git a/packages/registry/news/6399.breaking b/packages/registry/news/6399.breaking new file mode 100644 index 0000000000..d0315478f6 --- /dev/null +++ b/packages/registry/news/6399.breaking @@ -0,0 +1,6 @@ +Moved the package to ESM and refactored the add-on registry scripts to TypeScript. @sneridagh +Breaking: +- For maximum compatibility with CommonJS builds, the default exports have been moved to named exports. +- The modules affected are now built, and the import paths have changed, too. +- These changes force the modification in imports in a couple of files. +Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). diff --git a/packages/registry/news/6399.feature b/packages/registry/news/6399.feature new file mode 100644 index 0000000000..1a5f49ddbc --- /dev/null +++ b/packages/registry/news/6399.feature @@ -0,0 +1 @@ +Added an experimental Vite plugin. @sneridagh diff --git a/packages/registry/package.json b/packages/registry/package.json index 5b4d3e8284..78f14702d7 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -31,16 +31,36 @@ "publishConfig": { "access": "public" }, + "type": "module", "source": "src/index.ts", - "main": "dist/main.js", - "module": "dist/module.js", + "main": "dist/index.cjs", + "module": "dist/index.js", "types": "dist/types.d.ts", "exports": { - "./src/*": "./src/*.js", + "./src/*": "./src/*.cjs", + "./addon-registry": { + "require": "./dist/cjs/addon-registry.cjs", + "import": "./dist/esm/addon-registry.js", + "types": "./dist/esm/addon-registry.d.ts" + }, + "./create-addons-loader": { + "require": "./dist/cjs/create-addons-loader.cjs", + "import": "./dist/esm/create-addons-loader.js", + "types": "./dist/esm/create-addons-loader.d.ts" + }, + "./create-theme-loader": { + "require": "./dist/cjs/create-theme-loader.cjs", + "import": "./dist/esm/create-theme-loader.js", + "types": "./dist/esm/create-theme-loader.d.ts" + }, + "./vite-plugin": { + "import": "./vite-plugin.js", + "types": "./vite-plugin.d.ts" + }, ".": { "types": "./dist/types.d.ts", - "import": "./dist/module.js", - "require": "./dist/main.js" + "import": "./dist/index.js", + "require": "./dist/index.cjs" } }, "targets": { @@ -50,9 +70,12 @@ }, "scripts": { "watch": "parcel watch", - "build": "parcel build", - "build:force": "parcel build --no-cache", + "build": "parcel build && pnpm build:node:esm && pnpm build:node:cjs", + "build:force": "rm -rf dist && parcel build --no-cache && pnpm build:node:esm && pnpm build:node:cjs", + "build:node:esm": "tsc --project tsconfig.node.json || true", + "build:node:cjs": "tsc --project tsconfig.node.json --module commonjs --moduleResolution Node --outDir dist/cjs || true && make rename-to-cjs", "test": "vitest", + "test:debug": "vitest --inspect-brk --no-file-parallelism registry", "dry-release": "release-it --dry-run", "release": "release-it", "release-major-alpha": "release-it major --preRelease=alpha", @@ -71,20 +94,26 @@ "crypto-random-string": "3.2.0", "debug": "4.3.2", "dependency-graph": "0.10.0", - "glob": "7.1.6" + "glob": "^10.4.5", + "tmp": "0.2.1" }, "devDependencies": { "@parcel/packager-ts": "^2.12.0", "@parcel/transformer-typescript-types": "^2.12.0", "@plone/types": "workspace:*", + "@types/debug": "^4.1.12", + "@types/glob": "^8.1.0", + "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/tmp": "^0.2.6", "parcel": "^2.12.0", "react": "^18.2.0", "react-dom": "^18.2.0", "release-it": "16.2.1", "tsconfig": "workspace:*", "typescript": "^5.6.3", + "vite": "^5.4.8", "vitest": "^2.1.3" } } diff --git a/packages/registry/src/addon-registry.js b/packages/registry/src/addon-registry/addon-registry.ts similarity index 65% rename from packages/registry/src/addon-registry.js rename to packages/registry/src/addon-registry/addon-registry.ts index 26ae57555c..576d439d4d 100644 --- a/packages/registry/src/addon-registry.js +++ b/packages/registry/src/addon-registry/addon-registry.ts @@ -1,64 +1,120 @@ /* eslint no-console: 0 */ -const glob = require('glob').sync; -const path = require('path'); -const fs = require('fs'); -const debug = require('debug')('shadowing'); -const { DepGraph } = require('dependency-graph'); - -function getPackageBasePath(base) { +import { globSync as glob } from 'glob'; +import path from 'path'; +import fs from 'fs'; +import _debug from 'debug'; +import { DepGraph } from 'dependency-graph'; + +const debug = _debug('shadowing'); + +export type Package = { + name: string; + version: string; + isPublishedPackage: boolean; + isRegisteredAddon: boolean; + modulePath: string; + packageJson: string; + basePath?: string; + tsConfigPaths?: [string, any] | null; + addons: Array; + razzleExtender?: string; + eslintExtender?: string; +}; +type VoltoConfigJS = { + addons: Array; + theme: string; +}; +type Aliases = Record; +type AliasesObject = { find: string; replacement: string }[]; +type CoreAddons = { [x: string]: { package: string } }; +type PackageJsonObject = { + type: 'module' | 'commonjs'; + addons: Array; + coreAddons: CoreAddons; + theme: string; + customizationPaths: string[]; +}; + +type flatAliases = Record; + +type AddonRegistryGet = { + /** The ordered list of addons */ + addons: Array; + /** The theme name */ + theme: string; + /** The customizations (shadows) aliases */ + shadowAliases: AliasesObject; + /** The add-ons aliases - Only for Volto add-ons for which code lives inside `src` */ + addonAliases: AliasesObject; +}; + +function getPackageBasePath(base: string) { while (!fs.existsSync(`${base}/package.json`)) { base = path.join(base, '../'); } return path.resolve(base); } -function fromEntries(pairs) { - const res = {}; +function fromEntries(pairs: [string, any][]) { + const res: { [key: string]: any } = {}; pairs.forEach((p) => { res[p[0]] = p[1]; }); return res; } -function buildDependencyGraph(addons, extractDependency) { +function flatAliasesToObject(flatAliases: flatAliases): AliasesObject { + return Object.entries(flatAliases).map(([key, value]) => ({ + find: key, + replacement: value, + })); +} + +function buildDependencyGraph( + addons: Array, + extractDependency: (name: string) => Array, +) { // getAddonsLoaderChain - const graph = new DepGraph({ circular: true }); + const graph = new DepGraph({ circular: true }); graph.addNode('@root'); const seen = ['@root']; - const stack = [['@root', addons]]; + const stack: Array<[string, Array]> = [['@root', addons]]; while (stack.length > 0) { - const [pkgName, addons] = stack.shift(); - if (!graph.hasNode(pkgName)) { - graph.addNode(pkgName, []); - } - - if (!seen.includes(pkgName)) { - stack.push([pkgName, extractDependency(pkgName)]); - seen.push(pkgName); - } - - addons.forEach((loaderString) => { - const [name, extra] = loaderString.split(':'); - if (!graph.hasNode(name)) { - graph.addNode(name, []); + const [pkgName, addons] = stack.shift() || []; + if (pkgName && addons) { + if (!graph.hasNode(pkgName)) { + graph.addNode(pkgName, []); } - const data = graph.getNodeData(name) || []; - if (extra) { - extra.split(',').forEach((funcName) => { - if (!data.includes(funcName)) data.push(funcName); - }); + if (!seen.includes(pkgName)) { + stack.push([pkgName, extractDependency(pkgName)]); + seen.push(pkgName); } - graph.setNodeData(name, data); - graph.addDependency(pkgName, name); + addons.forEach((loaderString) => { + const [name, extra] = loaderString.split(':'); + if (!graph.hasNode(name)) { + graph.addNode(name, []); + } - if (!seen.includes(name)) { - stack.push([name, extractDependency(name)]); - } - }); + const data = graph.getNodeData(name) || []; + if (extra) { + extra.split(',').forEach((funcName) => { + // @ts-expect-error TODO: fix this + if (!data.includes(funcName)) data.push(funcName); + }); + } + graph.setNodeData(name, data); + + graph.addDependency(pkgName, name); + + if (!seen.includes(name)) { + stack.push([name, extractDependency(name)]); + } + }); + } } return graph; @@ -68,7 +124,7 @@ function buildDependencyGraph(addons, extractDependency) { * Given an addons loader string, it generates an addons loader string with * a resolved chain of dependencies */ -function getAddonsLoaderChain(graph) { +function getAddonsLoaderChain(graph: DepGraph) { return graph.dependenciesOf('@root').map((name) => { const extras = graph.getNodeData(name) || [].join(','); return extras.length ? `${name}:${extras}` : name; @@ -92,26 +148,29 @@ function getAddonsLoaderChain(graph) { * addons to customize the webpack configuration) * */ -class AddonConfigurationRegistry { - constructor(projectRootPath) { - const packageJson = (this.packageJson = require( - path.join(projectRootPath, 'package.json'), +class AddonRegistry { + public packageJson: PackageJsonObject; + public voltoConfigJS: VoltoConfigJS; + public projectRootPath: string; + public isVoltoProject: boolean; + public voltoPath: string; + public coreAddons: CoreAddons; + public resultantMergedAddons: Array; + public addonNames: Array; + public packages: Record; + public customizations: any; + public theme: any; + public dependencyGraph: DepGraph; + + constructor(projectRootPath: string) { + const packageJson = (this.packageJson = JSON.parse( + fs.readFileSync(path.join(projectRootPath, 'package.json'), { + encoding: 'utf-8', + }), )); - this.voltoConfigJS = {}; + // Loads the dynamic config, if any - if (process.env.VOLTOCONFIG) { - if (fs.existsSync(path.resolve(process.env.VOLTOCONFIG))) { - const voltoConfigPath = path.resolve(process.env.VOLTOCONFIG); - console.log(`Using volto.config.js in: ${voltoConfigPath}`); - this.voltoConfigJS = require(voltoConfigPath); - } - } else if (fs.existsSync(path.join(projectRootPath, 'volto.config.js'))) { - this.voltoConfigJS = require( - path.join(projectRootPath, 'volto.config.js'), - ); - } else { - this.voltoConfigJS = {}; - } + this.voltoConfigJS = this.getRegistryConfig(projectRootPath); this.projectRootPath = projectRootPath; this.isVoltoProject = packageJson.name !== '@plone/volto'; @@ -123,15 +182,21 @@ class AddonConfigurationRegistry { this.coreAddons = packageJson.name === '@plone/volto' ? packageJson.coreAddons || {} - : require(`${getPackageBasePath(this.voltoPath)}/package.json`) - .coreAddons || {}; + : JSON.parse( + fs.readFileSync( + `${getPackageBasePath(this.voltoPath)}/package.json`, + 'utf-8', + ), + ).coreAddons || {}; this.resultantMergedAddons = [ ...(packageJson.addons || []), ...(this.voltoConfigJS.addons || []), ]; - this.addonNames = this.resultantMergedAddons.map((s) => s.split(':')[0]); + this.addonNames = this.resultantMergedAddons.map( + (s: string) => s.split(':')[0], + ); this.packages = {}; this.customizations = new Map(); @@ -161,22 +226,77 @@ class AddonConfigurationRegistry { this.initAddonExtenders(); } + public get(): AddonRegistryGet { + return { + addons: this.getAddonDependencies(), + theme: this.theme, + addonAliases: flatAliasesToObject(this.getResolveAliases()), + shadowAliases: flatAliasesToObject(this.getAddonCustomizationPaths()), + }; + } + + public static init(projectRootPath: string) { + const registry = new AddonRegistry(projectRootPath); + return { + registry, + addons: registry.getAddonDependencies(), + theme: registry.theme, + shadowAliases: flatAliasesToObject(registry.getAddonCustomizationPaths()), + }; + } + + isESM = () => this.packageJson.type === 'module'; + + getRegistryConfig(projectRootPath: string) { + let config: VoltoConfigJS = { + addons: [], + theme: '', + }; + const CONFIGMAP = { + REGISTRYCONFIG: this.isESM() + ? 'registry.config.cjs' + : 'registry.config.js', + VOLTOCONFIG: this.isESM() ? 'volto.config.cjs' : 'volto.config.js', + }; + + for (const key in CONFIGMAP) { + if (process.env[key]) { + const resolvedPath = path.resolve(process.env[key]); + if (fs.existsSync(resolvedPath)) { + const voltoConfigPath = resolvedPath; + console.log(`Using configuration file in: ${voltoConfigPath}`); + config = require(voltoConfigPath); + break; + } + } else if (fs.existsSync(path.join(projectRootPath, CONFIGMAP[key]))) { + config = require(path.join(projectRootPath, CONFIGMAP[key])); + break; + } + } + + return config; + } + /** * Gets the `tsconfig.json` `compilerOptions.baseUrl` and `compilerOptions.paths` * Returns a tuple `[baseUrl, pathsConfig]` * */ - getTSConfigPaths(rootPath = this.projectRootPath) { - let configFile; + getTSConfigPaths( + rootPath = this.projectRootPath, + ): [string, Record | undefined] { + let configFile: string | undefined; if (fs.existsSync(`${rootPath}/tsconfig.json`)) configFile = `${rootPath}/tsconfig.json`; else if (fs.existsSync(`${rootPath}/jsconfig.json`)) configFile = `${rootPath}/jsconfig.json`; - let pathsConfig; - let baseUrl; + let pathsConfig: Record | undefined; + let baseUrl: string = ''; if (configFile) { - const jsConfig = require(configFile).compilerOptions; + const jsConfig = JSON.parse( + fs.readFileSync(configFile, 'utf-8'), + ).compilerOptions; pathsConfig = jsConfig.paths; baseUrl = jsConfig.baseUrl; } @@ -209,28 +329,31 @@ class AddonConfigurationRegistry { * Given an add-on name, it registers it as a development package * */ - initDevelopmentPackage(name) { + initDevelopmentPackage(name: string) { const [baseUrl, pathsConfig] = this.getTSConfigPaths(); - const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name][0]}`; - const packageJsonPath = `${getPackageBasePath(packagePath)}/package.json`; - const innerAddons = require(packageJsonPath).addons || []; - const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]); - if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) { - innerAddonsNormalized.forEach((name) => { - if (!this.addonNames.includes(name)) this.addonNames.push(name); - }); - } - const pkg = { - modulePath: packagePath, - packageJson: packageJsonPath, - version: require(packageJsonPath).version, - isPublishedPackage: false, - isRegisteredAddon: this.addonNames.includes(name), - name, - addons: require(packageJsonPath).addons || [], - }; + if (pathsConfig && pathsConfig.hasOwnProperty(name)) { + const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name][0]}`; + const packageJsonPath = `${getPackageBasePath(packagePath)}/package.json`; + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + const innerAddons: Array = packageJson.addons || []; + const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]); + if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) { + innerAddonsNormalized.forEach((name) => { + if (!this.addonNames.includes(name)) this.addonNames.push(name); + }); + } + const pkg = { + modulePath: packagePath, + packageJson: packageJsonPath, + version: packageJson.version, + isPublishedPackage: false, + isRegisteredAddon: this.addonNames.includes(name), + name, + addons: packageJson.addons || [], + }; - this.packages[name] = Object.assign(this.packages[name] || {}, pkg); + this.packages[name] = Object.assign(this.packages[name] || {}, pkg); + } } /** @@ -243,11 +366,11 @@ class AddonConfigurationRegistry { this.addonNames.forEach(this.initPublishedPackage.bind(this)); } - initPublishedPackage(name) { + initPublishedPackage(name: string) { // I am in the paths list, if so, register it as a development package // instead than a published one const [, pathsConfig] = this.getTSConfigPaths(); - if (pathsConfig.hasOwnProperty(name)) { + if (pathsConfig && pathsConfig.hasOwnProperty(name)) { return this.initDevelopmentPackage(name); } @@ -255,10 +378,10 @@ class AddonConfigurationRegistry { const resolved = require.resolve(name, { paths: [this.projectRootPath] }); const basePath = getPackageBasePath(resolved); const packageJson = path.join(basePath, 'package.json'); - const pkg = require(packageJson); + const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf-8')); const main = pkg.main || 'src/index.js'; const modulePath = path.dirname(require.resolve(`${basePath}/${main}`)); - const innerAddons = pkg.addons || []; + const innerAddons: Array = pkg.addons || []; const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]); if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) { innerAddonsNormalized.forEach((name) => { @@ -290,7 +413,7 @@ class AddonConfigurationRegistry { } // An add-on from the ADDONS env var can only be a published one - initAddonFromEnvVar(name) { + initAddonFromEnvVar(name: string) { const normalizedAddonName = name.split(':')[0]; this.initPublishedPackage(normalizedAddonName); } @@ -341,7 +464,10 @@ class AddonConfigurationRegistry { } getCustomThemeAddons() { - const customThemeAddonsInfo = { + const customThemeAddonsInfo: { + variables: string[]; + main: string[]; + } = { variables: [], main: [], }; @@ -372,11 +498,11 @@ class AddonConfigurationRegistry { /** * Returns a list of aliases given the defined paths in `tsconfig.json` */ - getAliasesFromTSConfig(basePath, tsConfig) { + getAliasesFromTSConfig(basePath: string, tsConfig: [string, any]) { const [baseUrl, options] = tsConfig; const fullPathsPath = baseUrl ? `${basePath}/${baseUrl}` : basePath; - let aliases = {}; + const aliases: { [x: string]: string } = {}; Object.keys(options || {}).forEach((item) => { const name = item.replace(/\/\*$/, ''); // webpack5 allows arrays here, fix later @@ -397,12 +523,10 @@ class AddonConfigurationRegistry { * defined in the `tsconfig.json` files of the add-ons. */ getResolveAliases() { - const pairs = [ - ...Object.keys(this.packages).map((o) => [ - o, - this.packages[o].modulePath, - ]), - ]; + const pairs: [string, string][] = Object.keys(this.packages).map((o) => [ + o, + this.packages[o].modulePath, + ]); let aliasesFromTSPaths = {}; Object.keys(this.packages).forEach((o) => { @@ -410,7 +534,7 @@ class AddonConfigurationRegistry { aliasesFromTSPaths = { ...aliasesFromTSPaths, ...this.getAliasesFromTSConfig( - this.packages[o].basePath, + this.packages[o].basePath || '', this.packages[o].tsConfigPaths, ), }; @@ -435,21 +559,25 @@ class AddonConfigurationRegistry { * convention, we use a folder called "volto" inside customizations folder * and separate folder for each addon, identified by its addon (package) name. */ - getCustomizationPaths(packageJson, rootPath) { - const aliases = {}; + getCustomizationPaths(packageJson: PackageJsonObject, rootPath: string) { + const aliases: Aliases = {}; let { customizationPaths } = packageJson; if (!customizationPaths) { - customizationPaths = ['src/customizations']; + customizationPaths = ['src/customizations', 'customizations']; } customizationPaths.forEach((customizationPath) => { customizationPath = customizationPath.endsWith('/') ? customizationPath.slice(0, customizationPath.length - 1) : customizationPath; const base = path.join(rootPath, customizationPath); - const reg = []; - - // All registered addon packages (in tsconfig.json/jsconfig.json and package.json:addons) - // can be customized + const reg: Array<{ + customPath: string; + sourcePath: string; + name: string; + }> = []; + + // All registered addon packages (in tsconfig.json/jsconfig.json and + // package.json:addons) can be customized by other addons Object.values(this.packages).forEach((addon) => { const { name, modulePath } = addon; if (fs.existsSync(path.join(base, name))) { @@ -491,12 +619,14 @@ class AddonConfigurationRegistry { glob( `${customPath}/**/*.*(svg|png|jpg|jpeg|gif|ico|less|js|jsx|ts|tsx)`, ).map((filename) => { - function changeFileExtension(filePath) { + function changeFileExtension(filePath: string) { // Extract the current file extension - const currentExtension = filePath.split('.').pop(); + const currentExtension = filePath.split('.').pop() || ''; // Define the mapping between file extensions - const extensionMapping = { + const extensionMapping: { + [key: string]: string; + } = { jsx: 'tsx', tsx: 'jsx', js: 'ts', @@ -523,7 +653,7 @@ class AddonConfigurationRegistry { * * @param {*} filePath */ - function simpleSwapFileExtension(filePath) { + function simpleSwapFileExtension(filePath: string) { // Extract the current file extension const currentExtension = filePath.split('.').pop(); return filePath.replace(`.${currentExtension}`, '.jsx'); @@ -569,12 +699,12 @@ class AddonConfigurationRegistry { * `addons:volto-addonA,volto-addonB` */ getAddonCustomizationPaths() { - let aliases = {}; + let aliases: Aliases = {}; this.getAddons().forEach((addon) => { aliases = { ...aliases, ...this.getCustomizationPaths( - require(addon.packageJson), + JSON.parse(fs.readFileSync(addon.packageJson, 'utf-8')), getPackageBasePath(addon.modulePath), ), }; @@ -586,9 +716,13 @@ class AddonConfigurationRegistry { * Allow packages from addons set in env vars to customize Volto and other addons. * * Same as the above one, but specific for Volto addons coming from env vars + * + * This is no longer necessary in the pnpm setup, as all valid packages have to be + * released or declared as a workspace + * */ getAddonsFromEnvVarCustomizationPaths() { - let aliases = {}; + let aliases: Aliases = {}; if (process.env.ADDONS) { process.env.ADDONS.split(';').forEach((addon) => { const normalizedAddonName = addon.split(':')[0]; @@ -598,7 +732,10 @@ class AddonConfigurationRegistry { const packageJson = path.join(basePath, 'package.json'); aliases = { ...aliases, - ...this.getCustomizationPaths(require(packageJson), basePath), + ...this.getCustomizationPaths( + JSON.parse(fs.readFileSync(packageJson, 'utf-8')), + basePath, + ), }; } }); @@ -626,11 +763,11 @@ class AddonConfigurationRegistry { "@root" [color = red fillcolor=yellow style=filled] `; - let queue = ['@root']; - let name; + const queue = ['@root']; + let name: string; while (queue.length > 0) { - name = queue.pop(); + name = queue.pop() || ''; const deps = this.dependencyGraph.directDependenciesOf(name); for (let i = 0; i < deps.length; i++) { @@ -648,6 +785,4 @@ class AddonConfigurationRegistry { } } -module.exports = AddonConfigurationRegistry; -module.exports.getAddonsLoaderChain = getAddonsLoaderChain; -module.exports.buildDependencyGraph = buildDependencyGraph; +export { AddonRegistry, getAddonsLoaderChain, buildDependencyGraph }; diff --git a/packages/registry/src/create-addons-loader.js b/packages/registry/src/addon-registry/create-addons-loader.ts similarity index 66% rename from packages/registry/src/create-addons-loader.js rename to packages/registry/src/addon-registry/create-addons-loader.ts index 7eb5f55757..f03349693f 100644 --- a/packages/registry/src/create-addons-loader.js +++ b/packages/registry/src/addon-registry/create-addons-loader.ts @@ -1,13 +1,16 @@ -const fs = require('fs'); -const tmp = require('tmp'); -const cryptoRandomString = require('crypto-random-string'); +import fs from 'fs'; +import path from 'path'; +import tmp from 'tmp'; +import cryptoRandomString from 'crypto-random-string'; +import type { Package } from './addon-registry'; -const titleCase = (w) => w.slice(0, 1).toUpperCase() + w.slice(1, w.length); +const titleCase = (w: string) => + w.slice(0, 1).toUpperCase() + w.slice(1, w.length); /* * Transforms a package name to javascript variable name */ -function nameFromPackage(name) { +function nameFromPackage(name: string) { name = name.replace(/[@~./\\:\s]/gi, '') || cryptoRandomString({ length: 10, characters: 'abcdefghijk' }); @@ -21,19 +24,26 @@ function nameFromPackage(name) { * Creates a static file with code necessary to load the addons configuration * */ -function getAddonsLoaderCode(addons = [], addonsInfo) { +function getAddonsLoaderCode( + addons: string[], + addonsInfo: Package[], + loadProjectConfig?: boolean, +) { let buf = `/* This file is autogenerated. Don't change it directly. Instead, change the "addons" setting in your package.json file. */ -const projectConfigLoader = require('@root/config'); `; - let configsToLoad = [], + if (loadProjectConfig) { + buf += `const projectConfigLoader = require('@root/config');\n`; + } + + let configsToLoad: string[] = [], counter = 0; addons.forEach((addonConfigString) => { - let extras = []; + let extras: string[] | string[][] = []; // TODO: Improve this typing const addonConfigLoadInfo = addonConfigString.split(':'); const pkgName = addonConfigLoadInfo[0]; const defaultImport = nameFromPackage(pkgName); @@ -41,7 +51,6 @@ const projectConfigLoader = require('@root/config'); extras = addonConfigLoadInfo[1].split(','); } extras = extras.map((name) => [name, `${name}${counter++}`]); - const line = `import ${defaultImport}${ extras.length ? `, { ${extras @@ -77,8 +86,9 @@ const safeWrapper = (func) => (config) => { return res; } +${loadProjectConfig ? '' : 'const projectConfigLoader = false;'} const projectConfig = (config) => { - return typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; + return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; } const load = (config) => { @@ -96,21 +106,27 @@ export default load; return buf; } -module.exports = (addons, addonsInfo, { tempInProject } = {}) => { +export function createAddonsLoader( + addons: string[], + addonsInfo: Package[], + { + tempInProject, + loadProjectConfig = false, + }: { tempInProject?: boolean; loadProjectConfig?: boolean } = {}, +) { // Some frameworks do not allow to load code from outside the project. // the `tempInProject` allows to place it inside - let addonsLoaderPath; + let addonsLoaderPath: string; if (tempInProject) { - const path = require('path'); addonsLoaderPath = path.join(process.cwd(), 'src', '.addons-loader.js'); } else { addonsLoaderPath = tmp.tmpNameSync({ postfix: '.js' }); } - const code = getAddonsLoaderCode(addons, addonsInfo); + const code = getAddonsLoaderCode(addons, addonsInfo, loadProjectConfig); + // @ts-expect-error No clue why it's complaining fs.writeFileSync(addonsLoaderPath, Buffer.from(code)); return addonsLoaderPath; -}; +} -module.exports.getAddonsLoaderCode = getAddonsLoaderCode; -module.exports.nameFromPackage = nameFromPackage; +export { getAddonsLoaderCode, nameFromPackage }; diff --git a/packages/registry/src/create-theme-addons-loader.js b/packages/registry/src/addon-registry/create-theme-loader.ts similarity index 50% rename from packages/registry/src/create-theme-addons-loader.js rename to packages/registry/src/addon-registry/create-theme-loader.ts index 6b46257744..d5f4dbbc24 100644 --- a/packages/registry/src/create-theme-addons-loader.js +++ b/packages/registry/src/addon-registry/create-theme-loader.ts @@ -1,13 +1,14 @@ -const fs = require('fs'); -const tmp = require('tmp'); -const cryptoRandomString = require('crypto-random-string'); +import fs from 'fs'; +import tmp from 'tmp'; +import cryptoRandomString from 'crypto-random-string'; -const titleCase = (w) => w.slice(0, 1).toUpperCase() + w.slice(1, w.length); +const titleCase = (w: string) => + w.slice(0, 1).toUpperCase() + w.slice(1, w.length); /* * Transforms a package name to javascript variable name */ -function nameFromPackage(name) { +function nameFromPackage(name: string) { name = name.replace(/[@~./\\:\s]/gi, '') || cryptoRandomString({ length: 10, characters: 'abcdefghijk' }); @@ -21,7 +22,7 @@ function nameFromPackage(name) { * Creates a static file with code necessary to load the addons configuration * */ -function getAddonsLoaderCode(name, customThemeAddons = []) { +function getThemeLoaderCode(name, customThemeAddons = []) { let buf = `/* This file is autogenerated. Don't change it directly. Add a ./theme/_${name}.scss in your add-on to load your theme customizations in the current theme. @@ -37,42 +38,27 @@ Add a ./theme/_${name}.scss in your add-on to load your theme customizations in return buf; } -module.exports = ({ main, variables }) => { - // const addonsThemeLoaderVariablesPath = path.join( - // process.cwd(), - // 'src', - // '_variables.scss', - // ); - // const addonsThemeLoaderMainPath = path.join( - // process.cwd(), - // 'src', - // '_main.scss', - // ); - - // const addonsThemeLoaderVariablesPath = path.join( - // process.cwd(), - // 'src', - // '_variables.scss', - // ); - // const addonsThemeLoaderMainPath = path.join( - // process.cwd(), - // 'src', - // '_main.scss', - // ); - +export function createThemeAddonsLoader({ + main, + variables, +}: { + main: string[]; + variables: string[]; +}) { const addonsThemeLoaderVariablesPath = tmp.tmpNameSync({ postfix: '.scss' }); const addonsThemeLoaderMainPath = tmp.tmpNameSync({ postfix: '.scss' }); fs.writeFileSync( addonsThemeLoaderVariablesPath, - new Buffer.from(getAddonsLoaderCode('variables', variables)), + //@ts-expect-error No clue why this is erroring + new Buffer.from(getThemeLoaderCode('variables', variables)), ); fs.writeFileSync( addonsThemeLoaderMainPath, - new Buffer.from(getAddonsLoaderCode('main', main)), + //@ts-expect-error No clue why this is erroring + new Buffer.from(getThemeLoaderCode('main', main)), ); return [addonsThemeLoaderVariablesPath, addonsThemeLoaderMainPath]; -}; +} -module.exports.getAddonsLoaderCode = getAddonsLoaderCode; -module.exports.nameFromPackage = nameFromPackage; +export { getThemeLoaderCode, nameFromPackage }; diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 7f229a66a8..5394254281 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -16,16 +16,16 @@ import type { } from '@plone/types'; export type ConfigData = { - settings: SettingsConfig; - blocks: BlocksConfig; - views: ViewsConfig; - widgets: WidgetsConfig; - addonReducers: AddonReducersConfig; - addonRoutes: AddonRoutesConfig; - slots: SlotsConfig; - components: ComponentsConfig; - utilities: UtilitiesConfig; - experimental: ExperimentalConfig; + settings: SettingsConfig | Record; + blocks: BlocksConfig | Record; + views: ViewsConfig | Record; + widgets: WidgetsConfig | Record; + addonReducers?: AddonReducersConfig; + addonRoutes?: AddonRoutesConfig; + slots: SlotsConfig | Record; + components: ComponentsConfig | Record; + utilities: UtilitiesConfig | Record; + experimental?: ExperimentalConfig; }; type GetComponentResult = { @@ -44,7 +44,15 @@ class Config { constructor() { if (!Config.instance) { - this._data = {}; + this._data = { + settings: {}, + blocks: {}, + views: {}, + widgets: {}, + slots: {}, + components: {}, + utilities: {}, + }; Config.instance = this; } @@ -205,7 +213,10 @@ class Config { return; } const { slots, data } = this._data.slots[name]; - const slotComponents = []; + const slotComponents: { + component: SlotComponent['component']; + name: string; + }[] = []; // For all enabled slots for (const slotName of slots) { // For all registered components for that slot, inversed, since the last one registered wins diff --git a/packages/registry/tsconfig.json b/packages/registry/tsconfig.json index c25bedd978..1319106020 100644 --- a/packages/registry/tsconfig.json +++ b/packages/registry/tsconfig.json @@ -17,7 +17,7 @@ "jsx": "react-jsx", "paths": {} }, - "include": ["src", "src/**/*.js"], + "include": ["src/index.ts", "src/**/*.js", "src/**/*.cjs"], "exclude": [ "node_modules", "build", diff --git a/packages/registry/tsconfig.node.json b/packages/registry/tsconfig.node.json new file mode 100644 index 0000000000..a25380a557 --- /dev/null +++ b/packages/registry/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + /* Base Options: */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + /* Strictness */ + "strict": true, + "noUncheckedIndexedAccess": true, + /* If transpiling with TypeScript: */ + "moduleResolution": "NodeNext", + "module": "NodeNext", + "outDir": "dist/esm", + "sourceMap": true, + /* If your code doesn't run in the DOM: */ + "lib": ["es2022"], + "declaration": true + }, + "include": ["src/addon-registry"] +} diff --git a/packages/registry/vite-plugin.d.ts b/packages/registry/vite-plugin.d.ts new file mode 100644 index 0000000000..2af076c87a --- /dev/null +++ b/packages/registry/vite-plugin.d.ts @@ -0,0 +1,5 @@ +import * as vite from 'vite'; + +declare function PloneRegistryVitePlugin(): vite.Plugin; + +export { PloneRegistryVitePlugin }; diff --git a/packages/registry/vite-plugin.js b/packages/registry/vite-plugin.js new file mode 100644 index 0000000000..6ec97dbe64 --- /dev/null +++ b/packages/registry/vite-plugin.js @@ -0,0 +1,60 @@ +import path from 'path'; +import { AddonRegistry } from '@plone/registry/addon-registry'; +import { createAddonsLoader } from '@plone/registry/create-addons-loader'; +import { createThemeAddonsLoader } from '@plone/registry/create-theme-loader'; + +export const PloneRegistryVitePlugin = () => { + const projectRootPath = path.resolve('.'); + const { registry, shadowAliases } = AddonRegistry.init(projectRootPath); + + const addonsLoaderPath = createAddonsLoader( + registry.getAddonDependencies(), + registry.getAddons(), + ); + + const [addonsThemeLoaderVariablesPath, addonsThemeLoaderMainPath] = + createThemeAddonsLoader(registry.getCustomThemeAddons()); + + const addOns = Object.keys(registry.packages); + + return { + name: 'plone-registry', + enforce: 'pre', + config: () => ({ + ssr: { + optimizeDeps: { + exclude: addOns, + }, + }, + esbuild: { + supported: { + 'top-level-await': true, //browsers can handle top-level-await features + }, + }, + optimizeDeps: { + exclude: addOns, + }, + resolve: { + alias: [ + ...shadowAliases, + // Remove in case that we have addons aliases (Volto add-ons which need the `src` path hack) + // ...addonAliases, + ...(registry.theme + ? // Load the theme aliases from the theme config + [ + { + find: 'addonsThemeCustomizationsVariables', + replacement: addonsThemeLoaderVariablesPath, + }, + { + find: 'addonsThemeCustomizationsMain', + replacement: addonsThemeLoaderMainPath, + }, + ] + : []), + { find: 'load-plone-registry-addons', replacement: addonsLoaderPath }, + ], + }, + }), + }; +}; diff --git a/packages/scripts/i18n.cjs b/packages/scripts/i18n.cjs index bd4a1a1f87..439c860e67 100755 --- a/packages/scripts/i18n.cjs +++ b/packages/scripts/i18n.cjs @@ -276,10 +276,25 @@ function main({ addonMode }) { console.log('Synchronizing messages to po files...'); syncPoByPot(); if (!addonMode) { - let AddonConfigurationRegistry; + let AddonRegistry, AddonConfigurationRegistry, registry; try { // Detect where is the registry (if we are in Volto 18 or above for either core and projects) if ( + fs.existsSync( + path.join( + projectRootPath, + '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs', + ), + ) + ) { + AddonRegistry = require( + path.join( + projectRootPath, + '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs', + ), + ).AddonRegistry; + // Detect where is the registry (if we are in Volto 18-alpha.46 or below) + } else if ( fs.existsSync( path.join( projectRootPath, @@ -324,7 +339,11 @@ function main({ addonMode }) { process.exit(); } console.log('Generating the language JSON files...'); - const registry = new AddonConfigurationRegistry(projectRootPath); + if (AddonConfigurationRegistry) { + registry = new AddonConfigurationRegistry(projectRootPath); + } else if (AddonRegistry) { + registry = AddonRegistry.init(projectRootPath).registry; + } poToJson({ registry, addonMode }); } console.log('done!'); diff --git a/packages/scripts/news/6399.feature b/packages/scripts/news/6399.feature new file mode 100644 index 0000000000..5558c340af --- /dev/null +++ b/packages/scripts/news/6399.feature @@ -0,0 +1 @@ +Support for the new `@plone/registry` ESM format. @sneridagh diff --git a/packages/volto/.eslintrc b/packages/volto/.eslintrc index 4e24e2badd..9b98fbf685 100644 --- a/packages/volto/.eslintrc +++ b/packages/volto/.eslintrc @@ -37,7 +37,6 @@ "map": [ ["@plone/volto", "./src"], ["@plone/volto-slate", "../volto-slate/src"], - ["@plone/registry", "../registry/src"], ["@plone/types", "../types"], ["@package", "./src"], ["@root", "./src"], diff --git a/packages/volto/.storybook/main.js b/packages/volto/.storybook/main.js index c745006748..36cb9a3017 100644 --- a/packages/volto/.storybook/main.js +++ b/packages/volto/.storybook/main.js @@ -102,8 +102,8 @@ module.exports = { [], defaultRazzleOptions, ); - const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); - const registry = new AddonConfigurationRegistry(projectRootPath); + const { AddonRegistry } = require('@plone/registry/addon-registry'); + const { registry } = AddonRegistry.init(projectRootPath); config = lessPlugin({ registry, }).modifyWebpackConfig({ diff --git a/packages/volto/__tests__/addon-registry-project.test.js b/packages/volto/__tests__/addon-registry-project.test.js index 08d4bad832..fad432bc88 100644 --- a/packages/volto/__tests__/addon-registry-project.test.js +++ b/packages/volto/__tests__/addon-registry-project.test.js @@ -1,9 +1,11 @@ -const path = require('path'); -const AddonConfigurationRegistry = require('../../registry/src/addon-registry'); -const { buildDependencyGraph, getAddonsLoaderChain } = - AddonConfigurationRegistry; +import path from 'path'; +import { + AddonRegistry, + buildDependencyGraph, + getAddonsLoaderChain, +} from '@plone/registry/addon-registry'; -describe('AddonConfigurationRegistry - Project', () => { +describe('AddonRegistry - Project', () => { jest.mock( `${path.join( __dirname, @@ -19,14 +21,14 @@ describe('AddonConfigurationRegistry - Project', () => { it('works in a mock project directory', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); + const { registry } = AddonRegistry.init(base); const voltoPath = `${base}/node_modules/@plone/volto`; - expect(reg.projectRootPath).toStrictEqual(base); - expect(reg.voltoPath).toStrictEqual(voltoPath); + expect(registry.projectRootPath).toStrictEqual(base); + expect(registry.voltoPath).toStrictEqual(voltoPath); - expect(reg.addonNames).toStrictEqual([ + expect(registry.addonNames).toStrictEqual([ 'test-addon', 'test-released-addon', 'test-released-source-addon', @@ -35,7 +37,7 @@ describe('AddonConfigurationRegistry - Project', () => { 'test-released-unmentioned', ]); - expect(reg.packages).toEqual({ + expect(registry.packages).toEqual({ 'test-addon': { isPublishedPackage: false, modulePath: `${base}/addons/test-addon/src`, @@ -102,8 +104,8 @@ describe('AddonConfigurationRegistry - Project', () => { it('provides aliases for addons', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); - expect(reg.getResolveAliases()).toStrictEqual({ + const { registry } = AddonRegistry.init(base); + expect(registry.getResolveAliases()).toStrictEqual({ 'my-volto-config-addon': `${base}/addons/my-volto-config-addon/src`, 'test-addon': `${base}/addons/test-addon/src`, 'test-released-addon': `${base}/node_modules/test-released-addon`, @@ -115,14 +117,14 @@ describe('AddonConfigurationRegistry - Project', () => { it('provides addon extenders', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); - expect(reg.getAddonExtenders().length).toBe(1); + const { registry } = AddonRegistry.init(base); + expect(registry.getAddonExtenders().length).toBe(1); }); it('provides a list of addon records ordered by initial package declaration', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); - const addons = reg.getAddons(); + const { registry } = AddonRegistry.init(base); + const addons = registry.getAddons(); expect(addons.map((a) => a.name)).toStrictEqual([ 'test-released-unmentioned', 'test-released-dummy', @@ -135,8 +137,8 @@ describe('AddonConfigurationRegistry - Project', () => { it('provides customization paths declared in a Volto project', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); - expect(reg.getProjectCustomizationPaths()).toStrictEqual({ + const { registry } = AddonRegistry.init(base); + expect(registry.getProjectCustomizationPaths()).toStrictEqual({ '@plone/volto/LanguageSwitcher': `${base}/src/customizations/LanguageSwitcher.js`, '@plone/volto/TSComponent': `${base}/src/customizations/TSComponent.jsx`, '@plone/volto/client': `${base}/src/customizations/client.js`, @@ -148,8 +150,8 @@ describe('AddonConfigurationRegistry - Project', () => { it('provides customization paths declared in addons', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); - expect(reg.getAddonCustomizationPaths()).toStrictEqual({ + const { registry } = AddonRegistry.init(base); + expect(registry.getAddonCustomizationPaths()).toStrictEqual({ '@plone/volto/LanguageSwitcher': `${base}/node_modules/test-released-source-addon/src/customizations/LanguageSwitcher.js`, '@plone/volto/TSComponent': `${base}/node_modules/test-released-source-addon/src/customizations/TSComponent.jsx`, '@plone/volto/client': `${base}/node_modules/test-released-source-addon/src/customizations/client.js`, @@ -231,9 +233,11 @@ describe('Addon via env var - Released addon', () => { it('addons can be specified on the fly using ADDONS env var - Released addon', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); + const { registry } = AddonRegistry.init(base); expect( - Object.keys(reg.packages).includes('test-released-via-addons-env-var'), + Object.keys(registry.packages).includes( + 'test-released-via-addons-env-var', + ), ).toBe(true); }); }); @@ -255,9 +259,9 @@ describe('Addon via env var - local packages folder addon', () => { it('addons can be specified on the fly using ADDONS env var - local packages folder addon', () => { const base = path.join(__dirname, 'fixtures', 'test-volto-project'); - const reg = new AddonConfigurationRegistry(base); + const { registry } = AddonRegistry.init(base); expect( - Object.keys(reg.packages).includes( + Object.keys(registry.packages).includes( 'test-local-packages-via-addons-env-var', ), ).toBe(true); diff --git a/packages/volto/__tests__/addon-registry-volto.test.js b/packages/volto/__tests__/addon-registry-volto.test.js index 5bea088c21..79fc58af0d 100644 --- a/packages/volto/__tests__/addon-registry-volto.test.js +++ b/packages/volto/__tests__/addon-registry-volto.test.js @@ -1,14 +1,14 @@ -const path = require('path'); -const AddonConfigurationRegistry = require('../../registry/src/addon-registry'); +import path from 'path'; +import { AddonRegistry } from '@plone/registry/addon-registry'; -describe('AddonConfigurationRegistry - Volto', () => { +describe('AddonRegistry - Volto', () => { it('works in Volto', () => { const base = path.join(__dirname, '..'); - const reg = new AddonConfigurationRegistry(base); - expect(reg.projectRootPath).toStrictEqual(base); + const { registry } = AddonRegistry.init(base); + expect(registry.projectRootPath).toStrictEqual(base); // TODO: rename initPackagesFolder to proper name after the refactor - // expect(reg.addonNames).toStrictEqual(['@plone/volto-slate']); - expect(reg.packages['@plone/volto-slate'].name).toStrictEqual( + // expect(registry.addonNames).toStrictEqual(['@plone/volto-slate']); + expect(registry.packages['@plone/volto-slate'].name).toStrictEqual( '@plone/volto-slate', ); }); diff --git a/packages/volto/__tests__/create-addons-loader.test.js b/packages/volto/__tests__/create-addons-loader.test.js index 570fa10a18..dfe9d174cb 100644 --- a/packages/volto/__tests__/create-addons-loader.test.js +++ b/packages/volto/__tests__/create-addons-loader.test.js @@ -1,11 +1,51 @@ -const fs = require('fs'); -const transform = require('@babel/core').transform; - -const getLoader = require('../../registry/src/create-addons-loader'); +import fs from 'fs'; +import { transform } from '@babel/core'; +import { + createAddonsLoader, + getAddonsLoaderCode, + nameFromPackage, +} from '@plone/registry/create-addons-loader'; describe('create-addons-loader code generation', () => { - test('no addon creates simple loader', () => { - const code = getLoader.getAddonsLoaderCode([]); + test('no addon creates simple loader, default = no loadProjectConfig', () => { + const code = getAddonsLoaderCode([]); + expect(code).toBe(`/* +This file is autogenerated. Don't change it directly. +Instead, change the "addons" setting in your package.json file. +*/ + + +const addonsInfo = {}; +export { addonsInfo }; + +const safeWrapper = (func) => (config) => { + const res = func(config); + if (typeof res === 'undefined') { + throw new Error("Configuration function doesn't return config"); + } + return res; +} + +const projectConfigLoader = false; +const projectConfig = (config) => { + return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; +} + +const load = (config) => { + const addonLoaders = []; + if(!addonLoaders.every((el) => typeof el === "function")) { + throw new TypeError( + 'Each addon has to provide a function applying its configuration to the projects configuration.', + ); + } + return projectConfig(addonLoaders.reduce((acc, apply) => safeWrapper(apply)(acc), config)); +}; +export default load; +`); + }); + + test('no addon creates simple loader, loadProjectConfig set to true', () => { + const code = getAddonsLoaderCode([], {}, true); expect(code).toBe(`/* This file is autogenerated. Don't change it directly. Instead, change the "addons" setting in your package.json file. @@ -24,8 +64,9 @@ const safeWrapper = (func) => (config) => { return res; } + const projectConfig = (config) => { - return typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; + return projectConfigLoader && typeof projectConfigLoader.default === "function" ? projectConfigLoader.default(config) : config; } const load = (config) => { @@ -42,17 +83,14 @@ export default load; }); test('one addon creates loader', () => { - const code = getLoader.getAddonsLoaderCode(['volto-addon1']); + const code = getAddonsLoaderCode(['volto-addon1']); expect(code.indexOf("import voltoAddon1 from 'volto-addon1';") > 0).toBe( true, ); }); test('two addons create loaders', () => { - const code = getLoader.getAddonsLoaderCode([ - 'volto-addon1', - 'volto-addon2', - ]); + const code = getAddonsLoaderCode(['volto-addon1', 'volto-addon2']); expect( code.indexOf(` import voltoAddon1 from 'volto-addon1'; @@ -61,7 +99,7 @@ import voltoAddon2 from 'volto-addon2';`) > 0, }); test('one addons plus one extra creates loader', () => { - const code = getLoader.getAddonsLoaderCode(['volto-addon1:loadExtra1']); + const code = getAddonsLoaderCode(['volto-addon1:loadExtra1']); expect( code.indexOf(` import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1'; @@ -70,9 +108,7 @@ import voltoAddon1, { loadExtra1 as loadExtra10 } from 'volto-addon1'; }); test('one addons plus two extras creates loader', () => { - const code = getLoader.getAddonsLoaderCode([ - 'volto-addon1:loadExtra1,loadExtra2', - ]); + const code = getAddonsLoaderCode(['volto-addon1:loadExtra1,loadExtra2']); expect( code.indexOf(` import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } from 'volto-addon1'; @@ -81,7 +117,7 @@ import voltoAddon1, { loadExtra1 as loadExtra10, loadExtra2 as loadExtra21 } fro }); test('two addons plus extras creates loader', () => { - const code = getLoader.getAddonsLoaderCode([ + const code = getAddonsLoaderCode([ 'volto-addon1:loadExtra1,loadExtra2', 'volto-addon2:loadExtra3,loadExtra4', ]); @@ -95,7 +131,7 @@ import voltoAddon2, { loadExtra3 as loadExtra32, loadExtra4 as loadExtra43 } fro }); describe('create-addons-loader default name generation', () => { - const getName = getLoader.nameFromPackage; + const getName = nameFromPackage; test('passing a simple word returns a word', () => { expect(getName('something')).toBe('something'); @@ -149,7 +185,7 @@ function makeAddonLoader(addons, load = true) { : require.resolve(name), ); - const loaderPath = getLoader(addons); + const loaderPath = createAddonsLoader(addons); transpile(loaderPath); if (load) { diff --git a/packages/volto/__tests__/webpack-relative-resolver.test.js b/packages/volto/__tests__/webpack-relative-resolver.test.js index 429946a216..cc91dbfb4f 100644 --- a/packages/volto/__tests__/webpack-relative-resolver.test.js +++ b/packages/volto/__tests__/webpack-relative-resolver.test.js @@ -1,10 +1,10 @@ const path = require('path'); -const AddonConfigurationRegistry = require('../../registry/src/addon-registry'); +const { AddonRegistry } = require('@plone/registry/addon-registry'); const WebpackRelativeResolver = require('../../volto/webpack-plugins/webpack-relative-resolver'); const base = path.join(__dirname, '..'); const makeRegistry = () => { - const registry = new AddonConfigurationRegistry(base); + const { registry } = AddonRegistry.init(base); registry.packages = { '@plone/volto-addon': { modulePath: '/somewhere/volto-addon/src', diff --git a/packages/volto/news/6399.internal b/packages/volto/news/6399.internal new file mode 100644 index 0000000000..363ce2c2a7 --- /dev/null +++ b/packages/volto/news/6399.internal @@ -0,0 +1 @@ +Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh diff --git a/packages/volto/package.json b/packages/volto/package.json index ac4b17141d..2de2443dd7 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -85,6 +85,8 @@ "@plone/volto/babel": "/babel.js", "@plone/volto/(.*)$": "/src/$1", "@plone/volto-slate/(.*)$": "/../volto-slate/src/$1", + "@plone/registry/addon-registry$": "/node_modules/@plone/registry/dist/esm/addon-registry.js", + "@plone/registry/create-addons-loader$": "/node_modules/@plone/registry/dist/esm/create-addons-loader.js", "@plone/registry": "/../registry/src", "@plone/registry/(.*)$": "/../registry/src/$1", "@plone/volto": "/src/index.js", @@ -280,11 +282,11 @@ "@babel/plugin-syntax-export-namespace-from": "7.8.3", "@babel/runtime": "7.20.6", "@babel/types": "7.20.5", - "@fiverr/afterbuild-webpack-plugin": "^1.0.0", - "@jest/globals": "^29.7.0", "@dnd-kit/core": "6.0.8", "@dnd-kit/sortable": "7.0.2", "@dnd-kit/utilities": "3.2.2", + "@fiverr/afterbuild-webpack-plugin": "^1.0.0", + "@jest/globals": "^29.7.0", "@loadable/babel-plugin": "5.13.2", "@loadable/webpack-plugin": "5.15.2", "@plone/types": "workspace:*", diff --git a/packages/volto/razzle.config.js b/packages/volto/razzle.config.js index b2ec16d7f7..a37df657ac 100644 --- a/packages/volto/razzle.config.js +++ b/packages/volto/razzle.config.js @@ -8,9 +8,11 @@ const fs = require('fs'); const RootResolverPlugin = require('./webpack-plugins/webpack-root-resolver'); const RelativeResolverPlugin = require('./webpack-plugins/webpack-relative-resolver'); const { poToJson } = require('@plone/scripts/i18n.cjs'); -const createAddonsLoader = require('@plone/registry/src/create-addons-loader'); -const createThemeAddonsLoader = require('@plone/registry/src/create-theme-addons-loader'); -const AddonConfigurationRegistry = require('@plone/registry/src/addon-registry'); +const { createAddonsLoader } = require('@plone/registry/create-addons-loader'); +const { + createThemeAddonsLoader, +} = require('@plone/registry/create-theme-loader'); +const { AddonRegistry } = require('@plone/registry/addon-registry'); const CircularDependencyPlugin = require('circular-dependency-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); @@ -23,8 +25,7 @@ const projectRootPath = path.resolve('.'); const languages = require('./src/constants/Languages.cjs'); const packageJson = require(path.join(projectRootPath, 'package.json')); - -const registry = new AddonConfigurationRegistry(projectRootPath); +const { registry } = AddonRegistry.init(projectRootPath); const defaultModify = ({ env: { target, dev }, @@ -290,6 +291,8 @@ const defaultModify = ({ const addonsLoaderPath = createAddonsLoader( registry.getAddonDependencies(), registry.getAddons(), + // The load of the project config is deprecated and will be removed in Volto 19. + { loadProjectConfig: true }, ); config.resolve.plugins = [ diff --git a/packages/volto/src/express-middleware/static.js b/packages/volto/src/express-middleware/static.js index 258af2136e..5b648b09f0 100644 --- a/packages/volto/src/express-middleware/static.js +++ b/packages/volto/src/express-middleware/static.js @@ -1,10 +1,10 @@ import express from 'express'; import path from 'path'; -import AddonConfigurationRegistry from '@plone/registry/src/addon-registry'; +import { AddonRegistry } from '@plone/registry/addon-registry'; import config from '@plone/volto/registry'; const projectRootPath = path.resolve('.'); -const registry = new AddonConfigurationRegistry(projectRootPath); +const { registry } = AddonRegistry.init(projectRootPath); const staticDirectory = () => { if (process.env.BUILD_DIR) { diff --git a/patches/jest-resolve@26.6.2.patch b/patches/jest-resolve@26.6.2.patch new file mode 100644 index 0000000000000000000000000000000000000000..20c5d96a80e2dcd5dd0e79f5007c8316473cfadb GIT binary patch literal 7680 zcmeHMO>^5e5Ea`TGVCSC9yf=^QY1nA5WkX1qqx$+Gq%&Ql4)m}RwW70qQj6b0Xgk7 z{b`;4r2d621lX3W#L2Cl4t9oIXn_TmPfsMFN6Vss;qbH+kdDZ7zL<-qWsqIB?V7!X zqO2IaX($8Nal-M~E^Equ1?Llukic)ix;kOA})?Nu^HFotHSWb`W8(%TG6O@MLG<0jhc2|AXVpkwJR zh^1Gz^ghK}SAZ+PI)d-5${hQ|l~1=vn9p9}&9f&Uc()}85m zx{sHnpRGF=C*wcssBGW_=M803tj#;_gG`iASFRN)6v6uSkDlPm}VHuf_&p`jbZ z5zWF>sv#aBZsJov%JSIF$B8%gC_MkP}6 z@I7x3L0`HAg%8N9lyf*?ys0i&3%}a{Q1Wk3xjAa+s$yM~YS100Z&*+D0<=u5+8VlY ztTeGKIaxI4qHJnTmgz=1hX)Vrp&Hh82CyjkKH??HT1`(VE7DaZdRyyYPG9GIdeXah zS4r136E4zL@b5~o?C#a5Iz8I!$&&Q+@WFyM>LI);b9R|dDpsG0We-hGq{@W*3WJhA ztICw`LZ`;{9!vknd--z%mrW+^bZJh-L72FqQ?LNZ5@l{4T=*yUTB}YqwkS%!>xD3w?Bv zq*<}5b4d@xyB4f%&QI7ctFmQN_4Rj~>`c+?;mC#@X(V9w{kOA|gQMB;i)V+6qvz9? zhqF06fgkqXHtlQHF2u5BDdl@VBB&~3|K-8qi-Y5h!BTLXGtre4Tqss_dzYLe^5=2I zM6PK1^rPblE|#aF`@Z^mYLZ9rJUe5#Ag|ePynEg@IpdsUc#kOSQzTL<$_vK#dV09z z5J6oOfzh;sCtt(;(NAlv>a=<_8WF*`(7juEw^oBR|JY^UdN~6dBxmjG@9)O%_p&0q GHT(m=dE%e| literal 0 HcmV?d00001 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ce225d6be..f081099b4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,11 @@ overrides: '@pmmmwh/react-refresh-webpack-plugin': 0.5.11 react-refresh: 0.14.0 +patchedDependencies: + jest-resolve@26.6.2: + hash: tmr5fnfjpir3crfu6ze44v7tue + path: patches/jest-resolve@26.6.2.patch + importers: .: @@ -479,7 +484,7 @@ importers: version: 3.11.11(react@18.2.0) '@storybook/test': specifier: ^8.0.4 - version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -522,7 +527,7 @@ importers: version: 8.0.8(@types/react@18.2.79)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-interactions': specifier: ^8.0.4 - version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + version: 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) '@storybook/addon-links': specifier: ^8.0.4 version: 8.0.8(react@18.2.0) @@ -546,7 +551,7 @@ importers: version: 8.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@testing-library/jest-dom': specifier: 6.4.2 - version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) '@testing-library/react': specifier: 14.2.1 version: 14.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -822,8 +827,11 @@ importers: specifier: 0.10.0 version: 0.10.0 glob: - specifier: 7.1.6 - version: 7.1.6 + specifier: ^10.4.5 + version: 10.4.5 + tmp: + specifier: 0.2.1 + version: 0.2.1 devDependencies: '@parcel/packager-ts': specifier: ^2.12.0 @@ -834,12 +842,24 @@ importers: '@plone/types': specifier: workspace:* version: link:../types + '@types/debug': + specifier: ^4.1.12 + version: 4.1.12 + '@types/glob': + specifier: ^8.1.0 + version: 8.1.0 + '@types/node': + specifier: ^20 + version: 20.12.7 '@types/react': specifier: ^18 version: 18.2.79 '@types/react-dom': specifier: ^18 version: 18.2.25 + '@types/tmp': + specifier: ^0.2.6 + version: 0.2.6 parcel: specifier: ^2.12.0 version: 2.12.0(@swc/helpers@0.5.10)(postcss@8.4.47)(relateurl@0.2.7)(srcset@4.0.0)(terser@5.30.3)(typescript@5.6.3) @@ -858,6 +878,9 @@ importers: typescript: specifier: ^5.6.3 version: 5.6.3 + vite: + specifier: ^5.4.8 + version: 5.4.9(@types/node@20.12.7)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3) vitest: specifier: ^2.1.3 version: 2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3) @@ -1715,7 +1738,7 @@ importers: version: 10.0.1(cypress@13.13.2) '@testing-library/jest-dom': specifier: 6.4.2 - version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) '@testing-library/react': specifier: 12.1.5 version: 12.1.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -3595,10 +3618,23 @@ packages: resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==} engines: {node: '>= 10.14.2'} + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/core@26.6.3': resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==} engines: {node: '>= 10.14.2'} + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/environment@26.6.2': resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==} engines: {node: '>= 10.14.2'} @@ -3635,6 +3671,15 @@ packages: resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==} engines: {node: '>= 10.14.2'} + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3643,14 +3688,26 @@ packages: resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==} engines: {node: '>= 10.14.2'} + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-result@26.6.2': resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==} engines: {node: '>= 10.14.2'} + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-sequencer@26.6.3': resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==} engines: {node: '>= 10.14.2'} + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/transform@26.6.2': resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} engines: {node: '>= 10.14.2'} @@ -6259,6 +6316,9 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/glob@8.1.0': + resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -6473,6 +6533,9 @@ packages: '@types/testing-library__react@9.1.3': resolution: {integrity: sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w==} + '@types/tmp@0.2.6': + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} + '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} @@ -7314,6 +7377,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + babel-loader@8.3.0: resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} engines: {node: '>= 8.9'} @@ -7349,6 +7418,10 @@ packages: resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==} engines: {node: '>= 10.14.2'} + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + babel-plugin-lodash@3.3.4: resolution: {integrity: sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==} @@ -7410,6 +7483,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + babel-preset-razzle@4.2.17: resolution: {integrity: sha512-Pg0yFCn2uTRBKjdj2pu61JWMcokVdxWNmpeBC2W+fNJ3JFyYP379TMIMmRi84g61snAzmwzwIlKMlVsVZT1IiA==} @@ -8304,6 +8383,11 @@ packages: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + croner@8.1.2: resolution: {integrity: sha512-ypfPFcAXHuAZRCzo3vJL6ltENzniTjwe/qsLleH1V2/7SRDjgvRQyrLmumFTLmjFax4IuSxfGXEn79fozXcJog==} engines: {node: '>=18.0'} @@ -8647,6 +8731,14 @@ packages: dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + deep-copy@1.4.2: resolution: {integrity: sha512-VxZwQ/1+WGQPl5nE67uLhh7OqdrmqI1OazrraO9Bbw/M8Bt6Mol/RxzDA6N6ZgRXpsG/W9PgUj8E1LHHBEq2GQ==} engines: {node: '>=4.0.0'} @@ -9007,6 +9099,10 @@ packages: electron-to-chromium@1.5.41: resolution: {integrity: sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==} + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + emittery@0.7.2: resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==} engines: {node: '>=10'} @@ -9686,10 +9782,6 @@ packages: resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} engines: {node: '>=0.10.0'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -10074,9 +10166,8 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - glob@10.3.12: - resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} - engines: {node: '>=16 || 14 >=14.17'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true glob@7.1.6: @@ -10084,6 +10175,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} @@ -11134,6 +11226,10 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -11163,6 +11259,9 @@ packages: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jake@10.8.7: resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} engines: {node: '>=10'} @@ -11179,11 +11278,29 @@ packages: resolution: {integrity: sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==} engines: {node: '>= 10.14.2'} + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-cli@26.6.3: resolution: {integrity: sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==} engines: {node: '>= 10.14.2'} hasBin: true + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jest-config@26.6.3: resolution: {integrity: sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==} engines: {node: '>= 10.14.2'} @@ -11193,6 +11310,18 @@ packages: ts-node: optional: true + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + jest-diff@26.6.2: resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==} engines: {node: '>= 10.14.2'} @@ -11205,10 +11334,18 @@ packages: resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==} engines: {node: '>= 10.14.2'} + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-each@26.6.2: resolution: {integrity: sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==} engines: {node: '>= 10.14.2'} + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-environment-jsdom@26.6.2: resolution: {integrity: sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==} engines: {node: '>= 10.14.2'} @@ -11217,6 +11354,10 @@ packages: resolution: {integrity: sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==} engines: {node: '>= 10.14.2'} + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-file@1.0.0: resolution: {integrity: sha512-QqcbPCfpVCxixoF0gia825AGBGXYWVi4cQpLsXBQVxkWswnZ39XHpT490NvTMtokY6v6BM+vw8rlhtjORabplA==} @@ -11244,6 +11385,10 @@ packages: resolution: {integrity: sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==} engines: {node: '>= 10.14.2'} + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-matcher-utils@26.6.2: resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==} engines: {node: '>= 10.14.2'} @@ -11293,19 +11438,35 @@ packages: resolution: {integrity: sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==} engines: {node: '>= 10.14.2'} + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-resolve@26.6.2: resolution: {integrity: sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==} engines: {node: '>= 10.14.2'} + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runner@26.6.3: resolution: {integrity: sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==} engines: {node: '>= 10.14.2'} + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runtime@26.6.3: resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} engines: {node: '>= 10.14.2'} hasBin: true + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-serializer@26.6.2: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} @@ -11330,10 +11491,18 @@ packages: resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==} engines: {node: '>= 10.14.2'} + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-watcher@26.6.2: resolution: {integrity: sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==} engines: {node: '>= 10.14.2'} + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-worker@25.5.0: resolution: {integrity: sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==} engines: {node: '>= 8.3'} @@ -11355,6 +11524,16 @@ packages: engines: {node: '>= 10.14.2'} hasBin: true + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true @@ -12546,8 +12725,8 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} minizlib@2.1.2: @@ -13191,6 +13370,9 @@ packages: resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} engines: {node: '>= 14'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-json@6.5.0: resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} engines: {node: '>=8'} @@ -13300,9 +13482,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.10.2: - resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} - engines: {node: '>=16 || 14 >=14.17'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} @@ -13902,6 +14084,9 @@ packages: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -14752,10 +14937,12 @@ packages: rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rollup-plugin-visualizer@5.12.0: @@ -15210,6 +15397,9 @@ packages: resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} deprecated: See https://github.com/lydell/source-map-resolve#deprecated + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -16479,6 +16669,10 @@ packages: resolution: {integrity: sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==} engines: {node: '>=10.10.0'} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -17179,7 +17373,7 @@ snapshots: '@babel/code-frame@7.24.2': dependencies: '@babel/highlight': 7.24.2 - picocolors: 1.0.0 + picocolors: 1.1.0 '@babel/code-frame@7.25.7': dependencies: @@ -17429,7 +17623,7 @@ snapshots: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.0 + picocolors: 1.1.0 '@babel/highlight@7.25.7': dependencies: @@ -17558,16 +17752,31 @@ snapshots: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -17613,11 +17822,21 @@ snapshots: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -17633,31 +17852,61 @@ snapshots: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -17673,6 +17922,11 @@ snapshots: '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -18907,6 +19161,16 @@ snapshots: jest-util: 26.6.2 slash: 3.0.0 + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + optional: true + '@jest/core@26.6.3': dependencies: '@jest/console': 26.6.2 @@ -18924,7 +19188,7 @@ snapshots: jest-haste-map: 26.6.2 jest-message-util: 26.6.2 jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) jest-resolve-dependencies: 26.6.3 jest-runner: 26.6.3 jest-runtime: 26.6.3 @@ -18932,7 +19196,7 @@ snapshots: jest-util: 26.6.2 jest-validate: 26.6.2 jest-watcher: 26.6.2 - micromatch: 4.0.5 + micromatch: 4.0.8 p-each-series: 2.2.0 rimraf: 3.0.2 slash: 3.0.0 @@ -18944,6 +19208,42 @@ snapshots: - ts-node - utf-8-validate + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.12.7) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + optional: true + '@jest/environment@26.6.2': dependencies: '@jest/fake-timers': 26.6.2 @@ -19020,7 +19320,7 @@ snapshots: istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.7 jest-haste-map: 26.6.2 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) jest-util: 26.6.2 jest-worker: 26.6.2 slash: 3.0.0 @@ -19033,6 +19333,36 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 20.12.7 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.1.6 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + optional: true + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 @@ -19043,6 +19373,13 @@ snapshots: graceful-fs: 4.2.11 source-map: 0.6.1 + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + optional: true + '@jest/test-result@26.6.2': dependencies: '@jest/console': 26.6.2 @@ -19050,6 +19387,14 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + optional: true + '@jest/test-sequencer@26.6.3': dependencies: '@jest/test-result': 26.6.2 @@ -19064,9 +19409,17 @@ snapshots: - ts-node - utf-8-validate + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + optional: true + '@jest/transform@26.6.2': dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.25.8 '@jest/types': 26.6.2 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 @@ -19076,7 +19429,7 @@ snapshots: jest-haste-map: 26.6.2 jest-regex-util: 26.0.0 jest-util: 26.6.2 - micromatch: 4.0.5 + micromatch: 4.0.8 pirates: 4.0.6 slash: 3.0.0 source-map: 0.6.1 @@ -19086,7 +19439,7 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.25.8 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 babel-plugin-istanbul: 6.1.1 @@ -19097,7 +19450,7 @@ snapshots: jest-haste-map: 29.7.0 jest-regex-util: 29.6.3 jest-util: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.8 pirates: 4.0.6 slash: 3.0.0 write-file-atomic: 4.0.2 @@ -19447,7 +19800,7 @@ snapshots: '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 - semver: 7.6.0 + semver: 7.6.3 '@npmcli/fs@3.1.0': dependencies: @@ -19529,7 +19882,7 @@ snapshots: '@npmcli/package-json@4.0.1': dependencies: '@npmcli/git': 4.1.0 - glob: 10.3.12 + glob: 10.4.5 hosted-git-info: 6.1.1 json-parse-even-better-errors: 3.0.1 normalize-package-data: 5.0.0 @@ -22094,11 +22447,11 @@ snapshots: dependencies: '@storybook/global': 5.0.0 - '@storybook/addon-interactions@8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': + '@storybook/addon-interactions@8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': dependencies: '@storybook/global': 5.0.0 '@storybook/instrumenter': 8.0.8 - '@storybook/test': 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + '@storybook/test': 8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) '@storybook/types': 8.0.8 polished: 4.3.1 ts-dedent: 2.2.0 @@ -22389,7 +22742,7 @@ snapshots: find-cache-dir: 3.3.2 find-up: 5.0.0 fs-extra: 11.2.0 - glob: 10.3.12 + glob: 10.4.5 handlebars: 4.7.8 lazy-universal-dotenv: 4.0.0 node-fetch: 2.7.0(encoding@0.1.13) @@ -22720,14 +23073,14 @@ snapshots: - encoding - supports-color - '@storybook/test@8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': + '@storybook/test@8.0.8(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': dependencies: '@storybook/client-logger': 8.0.8 '@storybook/core-events': 8.0.8 '@storybook/instrumenter': 8.0.8 '@storybook/preview-api': 8.0.8 '@testing-library/dom': 9.3.4 - '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) + '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3)) '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4) '@vitest/expect': 1.3.1 '@vitest/spy': 1.5.0 @@ -23103,7 +23456,7 @@ snapshots: jest: 26.6.3 vitest: 2.1.3(@types/node@20.12.7)(jsdom@16.7.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3) - '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': + '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.20.6 @@ -23116,6 +23469,7 @@ snapshots: optionalDependencies: '@jest/globals': 29.7.0 '@types/jest': 29.5.12 + jest: 29.7.0(@types/node@20.12.7) vitest: 2.1.3(@types/node@20.12.7)(jsdom@22.1.0)(less@3.11.1)(lightningcss@1.24.1)(sass@1.75.0)(terser@5.30.3) '@testing-library/react-hooks@8.0.1(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react-test-renderer@18.2.0(react@18.2.0))(react@18.2.0)': @@ -23267,7 +23621,7 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 8.56.10 - '@types/estree': 0.0.51 + '@types/estree': 1.0.6 '@types/eslint@8.56.10': dependencies: @@ -23307,6 +23661,11 @@ snapshots: '@types/minimatch': 5.1.2 '@types/node': 20.12.7 + '@types/glob@8.1.0': + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 20.12.7 + '@types/graceful-fs@4.1.9': dependencies: '@types/node': 20.12.7 @@ -23543,6 +23902,8 @@ snapshots: '@types/testing-library__dom': 7.5.0 pretty-format: 25.5.0 + '@types/tmp@0.2.6': {} + '@types/unist@2.0.10': {} '@types/unist@3.0.2': {} @@ -24049,7 +24410,7 @@ snapshots: '@vue/shared': 3.4.23 entities: 4.5.0 estree-walker: 2.0.2 - source-map-js: 1.2.0 + source-map-js: 1.2.1 '@vue/compiler-dom@3.4.23': dependencies: @@ -24369,7 +24730,7 @@ snapshots: archiver-utils@5.0.2: dependencies: - glob: 10.3.12 + glob: 10.4.5 graceful-fs: 4.2.11 is-stream: 2.0.1 lazystream: 1.0.1 @@ -24699,6 +25060,34 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@26.6.3(@babel/core@7.25.8): + dependencies: + '@babel/core': 7.25.8 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 26.6.2(@babel/core@7.25.8) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@29.7.0(@babel/core@7.25.8): + dependencies: + '@babel/core': 7.25.8 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.25.8) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-loader@8.3.0(@babel/core@7.24.4)(webpack@5.90.1(esbuild@0.20.2)): dependencies: '@babel/core': 7.24.4 @@ -24745,6 +25134,14 @@ snapshots: '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.5 + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.25.7 + '@babel/types': 7.20.5 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + optional: true + babel-plugin-lodash@3.3.4: dependencies: '@babel/helper-module-imports': 7.24.3 @@ -24840,12 +25237,41 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.4) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.4) + babel-preset-current-node-syntax@1.0.1(@babel/core@7.25.8): + dependencies: + '@babel/core': 7.25.8 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.25.8) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.25.8) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.8) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.8) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.8) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.8) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.25.8) + babel-preset-jest@26.6.2(@babel/core@7.24.4): dependencies: '@babel/core': 7.24.4 babel-plugin-jest-hoist: 26.6.2 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + babel-preset-jest@26.6.2(@babel/core@7.25.8): + dependencies: + '@babel/core': 7.25.8 + babel-plugin-jest-hoist: 26.6.2 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.25.8) + + babel-preset-jest@29.6.3(@babel/core@7.25.8): + dependencies: + '@babel/core': 7.25.8 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.25.8) + optional: true + babel-preset-razzle@4.2.17: dependencies: '@babel/core': 7.24.4 @@ -25123,7 +25549,7 @@ snapshots: braces@3.0.2: dependencies: - fill-range: 7.0.1 + fill-range: 7.1.1 braces@3.0.3: dependencies: @@ -25322,9 +25748,9 @@ snapshots: dependencies: '@npmcli/fs': 3.1.0 fs-minipass: 3.0.3 - glob: 10.3.12 + glob: 10.4.5 lru-cache: 7.18.3 - minipass: 7.0.4 + minipass: 7.1.2 minipass-collect: 1.0.2 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 @@ -25955,6 +26381,22 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.5.2 + create-jest@29.7.0(@types/node@20.12.7): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.12.7) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + optional: true + croner@8.1.2: {} cross-spawn@5.1.0: @@ -26352,6 +26794,9 @@ snapshots: dedent@0.7.0: {} + dedent@1.5.3: + optional: true + deep-copy@1.4.2: {} deep-eql@4.1.3: @@ -26707,6 +27152,9 @@ snapshots: electron-to-chromium@1.5.41: {} + emittery@0.13.1: + optional: true + emittery@0.7.2: {} emoji-regex@10.3.0: {} @@ -27867,10 +28315,6 @@ snapshots: repeat-string: 1.6.1 to-regex-range: 2.1.1 - fill-range@7.0.1: - dependencies: - to-regex-range: 5.0.1 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -28119,7 +28563,7 @@ snapshots: fs-minipass@3.0.3: dependencies: - minipass: 7.0.4 + minipass: 7.1.2 fs-monkey@1.0.5: {} @@ -28317,16 +28761,17 @@ snapshots: foreground-child: 3.1.1 jackspeak: 2.3.6 minimatch: 9.0.4 - minipass: 7.0.4 - path-scurry: 1.10.2 + minipass: 7.1.2 + path-scurry: 1.11.1 - glob@10.3.12: + glob@10.4.5: dependencies: foreground-child: 3.1.1 - jackspeak: 2.3.6 + jackspeak: 3.4.3 minimatch: 9.0.4 - minipass: 7.0.4 - path-scurry: 1.10.2 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 glob@7.1.6: dependencies: @@ -29446,7 +29891,7 @@ snapshots: istanbul-lib-instrument@4.0.3: dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.25.8 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -29463,6 +29908,17 @@ snapshots: transitivePeerDependencies: - supports-color + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.25.8 + '@babel/parser': 7.25.8 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + optional: true + istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 @@ -29511,6 +29967,12 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jake@10.8.7: dependencies: async: 3.2.5 @@ -29533,6 +29995,40 @@ snapshots: execa: 4.1.0 throat: 5.0.0 + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + optional: true + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-cli@26.6.3: dependencies: '@jest/core': 26.6.3 @@ -29555,12 +30051,32 @@ snapshots: - ts-node - utf-8-validate + jest-cli@29.7.0(@types/node@20.12.7): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.12.7) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.12.7) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + optional: true + jest-config@26.6.3: dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.25.8 '@jest/test-sequencer': 26.6.3 '@jest/types': 26.6.2 - babel-jest: 26.6.3(@babel/core@7.24.4) + babel-jest: 26.6.3(@babel/core@7.25.8) chalk: 4.1.2 deepmerge: 4.3.1 glob: 7.1.6 @@ -29570,10 +30086,10 @@ snapshots: jest-get-type: 26.3.0 jest-jasmine2: 26.6.3 jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) jest-util: 26.6.2 jest-validate: 26.6.2 - micromatch: 4.0.5 + micromatch: 4.0.8 pretty-format: 26.6.2 transitivePeerDependencies: - bufferutil @@ -29581,6 +30097,37 @@ snapshots: - supports-color - utf-8-validate + jest-config@29.7.0(@types/node@20.12.7): + dependencies: + '@babel/core': 7.25.8 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.8) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.1.6 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.12.7 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + optional: true + jest-diff@26.6.2: dependencies: chalk: 4.1.2 @@ -29599,6 +30146,11 @@ snapshots: dependencies: detect-newline: 3.1.0 + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + optional: true + jest-each@26.6.2: dependencies: '@jest/types': 26.6.2 @@ -29607,6 +30159,15 @@ snapshots: jest-util: 26.6.2 pretty-format: 26.6.2 + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + optional: true + jest-environment-jsdom@26.6.2: dependencies: '@jest/environment': 26.6.2 @@ -29631,6 +30192,16 @@ snapshots: jest-mock: 26.6.2 jest-util: 26.6.2 + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + jest-mock: 29.7.0 + jest-util: 29.7.0 + optional: true + jest-file@1.0.0: {} jest-get-type@26.3.0: {} @@ -29649,7 +30220,7 @@ snapshots: jest-serializer: 26.6.2 jest-util: 26.6.2 jest-worker: 26.6.2 - micromatch: 4.0.5 + micromatch: 4.0.8 sane: 4.1.0 walker: 1.0.8 optionalDependencies: @@ -29668,14 +30239,14 @@ snapshots: jest-regex-util: 29.6.3 jest-util: 29.7.0 jest-worker: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.8 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 jest-jasmine2@26.6.3: dependencies: - '@babel/traverse': 7.24.1 + '@babel/traverse': 7.25.7 '@jest/environment': 26.6.2 '@jest/source-map': 26.6.2 '@jest/test-result': 26.6.2 @@ -29705,6 +30276,12 @@ snapshots: jest-get-type: 26.3.0 pretty-format: 26.6.2 + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + optional: true + jest-matcher-utils@26.6.2: dependencies: chalk: 4.1.2 @@ -29761,9 +30338,14 @@ snapshots: '@types/node': 20.12.7 jest-util: 29.7.0 - jest-pnp-resolver@1.2.3(jest-resolve@26.6.2): + jest-pnp-resolver@1.2.3(jest-resolve@26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue)): + optionalDependencies: + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): optionalDependencies: - jest-resolve: 26.6.2 + jest-resolve: 29.7.0 + optional: true jest-regex-util@26.0.0: {} @@ -29777,17 +30359,38 @@ snapshots: transitivePeerDependencies: - supports-color - jest-resolve@26.6.2: + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + optional: true + + jest-resolve@26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue): dependencies: '@jest/types': 26.6.2 chalk: 4.1.2 graceful-fs: 4.2.11 - jest-pnp-resolver: 1.2.3(jest-resolve@26.6.2) + jest-pnp-resolver: 1.2.3(jest-resolve@26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue)) jest-util: 26.6.2 read-pkg-up: 7.0.1 resolve: 1.22.8 slash: 3.0.0 + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + optional: true + jest-runner@26.6.3: dependencies: '@jest/console': 26.6.2 @@ -29804,7 +30407,7 @@ snapshots: jest-haste-map: 26.6.2 jest-leak-detector: 26.6.2 jest-message-util: 26.6.2 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) jest-runtime: 26.6.3 jest-util: 26.6.2 jest-worker: 26.6.2 @@ -29817,6 +30420,33 @@ snapshots: - ts-node - utf-8-validate + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + optional: true + jest-runtime@26.6.3: dependencies: '@jest/console': 26.6.2 @@ -29839,7 +30469,7 @@ snapshots: jest-message-util: 26.6.2 jest-mock: 26.6.2 jest-regex-util: 26.0.0 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) jest-snapshot: 26.6.2 jest-util: 26.6.2 jest-validate: 26.6.2 @@ -29853,6 +30483,34 @@ snapshots: - ts-node - utf-8-validate + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.1.6 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + optional: true + jest-serializer@26.6.2: dependencies: '@types/node': 20.12.7 @@ -29862,7 +30520,7 @@ snapshots: dependencies: '@babel/types': 7.20.5 '@jest/types': 26.6.2 - '@types/babel__traverse': 7.20.5 + '@types/babel__traverse': 7.20.6 '@types/prettier': 2.7.3 chalk: 4.1.2 expect: 26.6.2 @@ -29872,24 +30530,24 @@ snapshots: jest-haste-map: 26.6.2 jest-matcher-utils: 26.6.2 jest-message-util: 26.6.2 - jest-resolve: 26.6.2 + jest-resolve: 26.6.2(patch_hash=tmr5fnfjpir3crfu6ze44v7tue) natural-compare: 1.4.0 pretty-format: 26.6.2 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.24.4 - '@babel/generator': 7.24.4 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.4) - '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4) + '@babel/core': 7.25.8 + '@babel/generator': 7.25.7 + '@babel/plugin-syntax-jsx': 7.25.7(@babel/core@7.25.8) + '@babel/plugin-syntax-typescript': 7.25.7(@babel/core@7.25.8) '@babel/types': 7.20.5 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.25.8) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -29900,7 +30558,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.6.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -29931,6 +30589,16 @@ snapshots: leven: 3.1.0 pretty-format: 26.6.2 + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + optional: true + jest-watcher@26.6.2: dependencies: '@jest/test-result': 26.6.2 @@ -29941,6 +30609,18 @@ snapshots: jest-util: 26.6.2 string-length: 4.0.2 + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + optional: true + jest-worker@25.5.0: dependencies: merge-stream: 2.0.0 @@ -29977,6 +30657,19 @@ snapshots: - ts-node - utf-8-validate + jest@29.7.0(@types/node@20.12.7): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.12.7) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + optional: true + jiti@1.21.0: {} jiti@1.21.6: {} @@ -31610,7 +32303,7 @@ snapshots: minipass-fetch@3.0.4: dependencies: - minipass: 7.0.4 + minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: @@ -31639,7 +32332,7 @@ snapshots: minipass@5.0.0: {} - minipass@7.0.4: {} + minipass@7.1.2: {} minizlib@2.1.2: dependencies: @@ -32032,7 +32725,7 @@ snapshots: dependencies: growly: 1.3.0 is-wsl: 2.2.0 - semver: 7.6.0 + semver: 7.6.3 shellwords: 0.1.1 uuid: 8.3.2 which: 2.0.2 @@ -32517,6 +33210,8 @@ snapshots: degenerator: 5.0.1 netmask: 2.0.2 + package-json-from-dist@1.0.1: {} + package-json@6.5.0: dependencies: got: 9.6.0 @@ -32690,10 +33385,10 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.10.2: + path-scurry@1.11.1: dependencies: lru-cache: 10.2.0 - minipass: 7.0.4 + minipass: 7.1.2 path-to-regexp@0.1.10: {} @@ -33088,9 +33783,9 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-sorting@8.0.2(postcss@8.4.38): + postcss-sorting@8.0.2(postcss@8.4.47): dependencies: - postcss: 8.4.38 + postcss: 8.4.47 postcss-svgo@4.0.3: dependencies: @@ -33371,6 +34066,9 @@ snapshots: dependencies: escape-goat: 4.0.0 + pure-rand@6.1.0: + optional: true + q@1.5.1: {} qs@6.10.4: @@ -34192,7 +34890,7 @@ snapshots: read-package-json@6.0.4: dependencies: - glob: 10.3.12 + glob: 10.4.5 json-parse-even-better-errors: 3.0.1 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 @@ -35306,6 +36004,12 @@ snapshots: source-map-url: 0.4.1 urix: 0.1.0 + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + optional: true + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -35394,7 +36098,7 @@ snapshots: ssri@10.0.5: dependencies: - minipass: 7.0.4 + minipass: 7.1.2 ssri@7.1.1: dependencies: @@ -35659,8 +36363,8 @@ snapshots: stylelint-order@6.0.4(stylelint@16.3.1(typescript@5.6.3)): dependencies: - postcss: 8.4.38 - postcss-sorting: 8.0.2(postcss@8.4.38) + postcss: 8.4.47 + postcss-sorting: 8.0.2(postcss@8.4.47) stylelint: 16.3.1(typescript@5.6.3) stylelint-prettier@5.0.0(prettier@3.2.5)(stylelint@16.3.1(typescript@5.6.3)): @@ -35720,7 +36424,7 @@ snapshots: dependencies: '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 10.3.12 + glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.6 @@ -36699,6 +37403,13 @@ snapshots: convert-source-map: 1.9.0 source-map: 0.7.4 + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + optional: true + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 From 1ff127ccb7e58ddac4b053d939744a83407f4d8c Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 27 Oct 2024 15:47:09 +0100 Subject: [PATCH 12/16] Improve @plone/registry release-it config --- packages/registry/.release-it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/registry/.release-it.json b/packages/registry/.release-it.json index f2720cb9f0..a63e1ce21e 100644 --- a/packages/registry/.release-it.json +++ b/packages/registry/.release-it.json @@ -6,7 +6,7 @@ "after:bump": [ "pipx run towncrier build --draft --yes --version ${version} > .changelog.draft", "pipx run towncrier build --yes --version ${version}", - "pnpm build --no-cache" + "pnpm build:force" ], "after:release": "rm .changelog.draft" }, From 4802225f0d538a0ed96b89ad718c574432667392 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 27 Oct 2024 15:48:32 +0100 Subject: [PATCH 13/16] Release @plone/registry 2.0.0-alpha.0 --- packages/registry/CHANGELOG.md | 25 +++++++++++++++++++++++++ packages/registry/news/6371.internal | 1 - packages/registry/news/6373.internal | 1 - packages/registry/news/6399.breaking | 6 ------ packages/registry/news/6399.feature | 1 - packages/registry/news/6407.internal | 1 - packages/registry/news/6422.bugfix | 1 - packages/registry/package.json | 2 +- 8 files changed, 26 insertions(+), 12 deletions(-) delete mode 100644 packages/registry/news/6371.internal delete mode 100644 packages/registry/news/6373.internal delete mode 100644 packages/registry/news/6399.breaking delete mode 100644 packages/registry/news/6399.feature delete mode 100644 packages/registry/news/6407.internal delete mode 100644 packages/registry/news/6422.bugfix diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md index bfc4bc4705..dd27bf57e5 100644 --- a/packages/registry/CHANGELOG.md +++ b/packages/registry/CHANGELOG.md @@ -8,6 +8,31 @@ +## 2.0.0-alpha.0 (2024-10-27) + +### Breaking + +- Moved the package to ESM and refactored the add-on registry scripts to TypeScript. @sneridagh + Breaking: + - For maximum compatibility with CommonJS builds, the default exports have been moved to named exports. + - The modules affected are now built, and the import paths have changed, too. + - These changes force the modification in imports in a couple of files. + Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399) + +### Feature + +- Added an experimental Vite plugin. @sneridagh [#6399](https://github.com/plone/volto/issues/6399) + +### Bugfix + +- Return empty array when `getUtilities` does not match anything. @sneridagh [#6422](https://github.com/plone/volto/issues/6422) + +### Internal + +- Update typescript @sneridagh [#6371](https://github.com/plone/volto/issues/6371) +- Update Vite and vitest versions @sneridagh [#6373](https://github.com/plone/volto/issues/6373) +- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407) + ## 1.8.0 (2024-07-30) ### Feature diff --git a/packages/registry/news/6371.internal b/packages/registry/news/6371.internal deleted file mode 100644 index 7f7dcd6199..0000000000 --- a/packages/registry/news/6371.internal +++ /dev/null @@ -1 +0,0 @@ -Update typescript @sneridagh diff --git a/packages/registry/news/6373.internal b/packages/registry/news/6373.internal deleted file mode 100644 index 2b6d200183..0000000000 --- a/packages/registry/news/6373.internal +++ /dev/null @@ -1 +0,0 @@ -Update Vite and vitest versions @sneridagh diff --git a/packages/registry/news/6399.breaking b/packages/registry/news/6399.breaking deleted file mode 100644 index d0315478f6..0000000000 --- a/packages/registry/news/6399.breaking +++ /dev/null @@ -1,6 +0,0 @@ -Moved the package to ESM and refactored the add-on registry scripts to TypeScript. @sneridagh -Breaking: -- For maximum compatibility with CommonJS builds, the default exports have been moved to named exports. -- The modules affected are now built, and the import paths have changed, too. -- These changes force the modification in imports in a couple of files. -Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). diff --git a/packages/registry/news/6399.feature b/packages/registry/news/6399.feature deleted file mode 100644 index 1a5f49ddbc..0000000000 --- a/packages/registry/news/6399.feature +++ /dev/null @@ -1 +0,0 @@ -Added an experimental Vite plugin. @sneridagh diff --git a/packages/registry/news/6407.internal b/packages/registry/news/6407.internal deleted file mode 100644 index f6daada94d..0000000000 --- a/packages/registry/news/6407.internal +++ /dev/null @@ -1 +0,0 @@ -Update typescript and vitest everywhere @sneridagh diff --git a/packages/registry/news/6422.bugfix b/packages/registry/news/6422.bugfix deleted file mode 100644 index 978f5bfa81..0000000000 --- a/packages/registry/news/6422.bugfix +++ /dev/null @@ -1 +0,0 @@ -Return empty array when `getUtilities` does not match anything. @sneridagh diff --git a/packages/registry/package.json b/packages/registry/package.json index 78f14702d7..27a12e4a7f 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.8.0", + "version": "2.0.0-alpha.0", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From f96fe5b83e87f5dee27dae56f0374683788c5082 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 27 Oct 2024 15:49:33 +0100 Subject: [PATCH 14/16] Release @plone/scripts 3.7.0 --- packages/scripts/CHANGELOG.md | 10 ++++++++++ packages/scripts/news/6354.documentation | 1 - packages/scripts/news/6399.feature | 1 - packages/scripts/package.json | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) delete mode 100644 packages/scripts/news/6354.documentation delete mode 100644 packages/scripts/news/6399.feature diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 34cce11565..f1c4f0278b 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -8,6 +8,16 @@ +## 3.7.0 (2024-10-27) + +### Feature + +- Support for the new `@plone/registry` ESM format. @sneridagh [#6399](https://github.com/plone/volto/issues/6399) + +### Documentation + +- Added the configuration for VSCode not to reformat Markdown and MyST files. @aadityaforwork [#6354](https://github.com/plone/volto/issues/6354) + ## 3.6.2 (2024-06-06) ### Bugfix diff --git a/packages/scripts/news/6354.documentation b/packages/scripts/news/6354.documentation deleted file mode 100644 index b643f0ac32..0000000000 --- a/packages/scripts/news/6354.documentation +++ /dev/null @@ -1 +0,0 @@ -Added the configuration for VSCode not to reformat Markdown and MyST files. @aadityaforwork \ No newline at end of file diff --git a/packages/scripts/news/6399.feature b/packages/scripts/news/6399.feature deleted file mode 100644 index 5558c340af..0000000000 --- a/packages/scripts/news/6399.feature +++ /dev/null @@ -1 +0,0 @@ -Support for the new `@plone/registry` ESM format. @sneridagh diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 66d18276c1..412e88fcee 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -9,7 +9,7 @@ } ], "license": "MIT", - "version": "3.6.2", + "version": "3.7.0", "repository": { "type": "git", "url": "git@github.com:plone/volto.git" From 2f7b7608aae0d2a97bbae8c21d300aca66995a0b Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 27 Oct 2024 15:50:38 +0100 Subject: [PATCH 15/16] Release generate-volto 9.0.0-alpha.20 --- packages/generator-volto/CHANGELOG.md | 9 +++++++++ packages/generator-volto/news/6371.breaking | 2 -- packages/generator-volto/news/6399.breaking | 2 -- packages/generator-volto/package.json | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 packages/generator-volto/news/6371.breaking delete mode 100644 packages/generator-volto/news/6399.breaking diff --git a/packages/generator-volto/CHANGELOG.md b/packages/generator-volto/CHANGELOG.md index 9a16049080..7107897b68 100644 --- a/packages/generator-volto/CHANGELOG.md +++ b/packages/generator-volto/CHANGELOG.md @@ -8,6 +8,15 @@ +## 9.0.0-alpha.20 (2024-10-27) + +### Breaking + +- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh + Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371) +- Breaking changes in `.eslintrc.js` and `.storybook/main.js` because of #6399. @sneridagh + Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). [#6399](https://github.com/plone/volto/issues/6399) + ## 9.0.0-alpha.19 (2024-10-18) ### Internal diff --git a/packages/generator-volto/news/6371.breaking b/packages/generator-volto/news/6371.breaking deleted file mode 100644 index 204eeb8cd6..0000000000 --- a/packages/generator-volto/news/6371.breaking +++ /dev/null @@ -1,2 +0,0 @@ -Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh -Volto might continue to work on it, but it will be no longer tested in CI. diff --git a/packages/generator-volto/news/6399.breaking b/packages/generator-volto/news/6399.breaking deleted file mode 100644 index 2b0695d18f..0000000000 --- a/packages/generator-volto/news/6399.breaking +++ /dev/null @@ -1,2 +0,0 @@ -Breaking changes in `.eslintrc.js` and `.storybook/main.js` because of #6399. @sneridagh -Please see the [Upgrade Guide](https://6.docs.plone.org/volto/upgrade-guide/index.html). diff --git a/packages/generator-volto/package.json b/packages/generator-volto/package.json index 9e352a3300..cf1b46bc7c 100644 --- a/packages/generator-volto/package.json +++ b/packages/generator-volto/package.json @@ -10,7 +10,7 @@ } ], "license": "MIT", - "version": "9.0.0-alpha.19", + "version": "9.0.0-alpha.20", "repository": { "type": "git", "url": "git+https://github.com/plone/generator-volto.git" From 82e4f7b51e0c718e66fc1d42444268c422ef6aae Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Sun, 27 Oct 2024 15:51:42 +0100 Subject: [PATCH 16/16] Release 18.0.0-alpha.47 --- docs/source/release-notes/index.md | 43 +++++++++++++++++++ packages/volto/CHANGELOG.md | 43 +++++++++++++++++++ packages/volto/news/6308.internal | 1 - packages/volto/news/6355.bugfix | 1 - packages/volto/news/6371.breaking | 2 - packages/volto/news/6371.internal | 1 - packages/volto/news/6399.internal | 1 - packages/volto/news/6403.internal | 1 - packages/volto/news/6407.internal | 1 - packages/volto/news/6408.internal | 1 - packages/volto/news/6414.feature | 3 -- packages/volto/news/6418.internal | 1 - packages/volto/news/6419.bugfix | 1 - packages/volto/news/6421.bugfix | 1 - packages/volto/news/6421.feature.1 | 3 -- packages/volto/news/6421.feature.2 | 1 - packages/volto/news/6422.feature | 1 - packages/volto/news/6424.internal | 1 - packages/volto/news/6425.feature | 1 - packages/volto/news/6426.documentation | 1 - packages/volto/news/6433.documentation | 1 - packages/volto/news/6436.bugfix | 1 - packages/volto/news/6438.documentation | 1 - packages/volto/package.json | 2 +- .../volto/types/actions/aliases/aliases.d.ts | 9 +++- .../manage/Contents/__mocks__/index.d.ts | 1 - .../Relations/RelationsMatrix.d.ts | 2 +- .../manage/Controlpanels/index.d.ts | 1 - .../manage/Form/__mocks__/index.d.ts | 1 - .../types/components/manage/Form/index.d.ts | 1 - .../Multilingual/ManageTranslations.d.ts | 2 +- .../manage/Sidebar/ObjectBrowser.d.ts | 2 +- .../manage/Widgets/InternalUrlWidget.d.ts | 2 +- .../components/manage/Widgets/UrlWidget.d.ts | 2 +- .../manage/Widgets/__mocks__/index.d.ts | 1 - .../components/manage/Widgets/index.d.ts | 5 +-- packages/volto/types/config/slots.d.ts | 2 +- .../volto/types/constants/ActionTypes.d.ts | 1 + .../volto/types/helpers/Helmet/Helmet.d.ts | 2 +- 39 files changed, 105 insertions(+), 43 deletions(-) delete mode 100644 packages/volto/news/6308.internal delete mode 100644 packages/volto/news/6355.bugfix delete mode 100644 packages/volto/news/6371.breaking delete mode 100644 packages/volto/news/6371.internal delete mode 100644 packages/volto/news/6399.internal delete mode 100644 packages/volto/news/6403.internal delete mode 100644 packages/volto/news/6407.internal delete mode 100644 packages/volto/news/6408.internal delete mode 100644 packages/volto/news/6414.feature delete mode 100644 packages/volto/news/6418.internal delete mode 100644 packages/volto/news/6419.bugfix delete mode 100644 packages/volto/news/6421.bugfix delete mode 100644 packages/volto/news/6421.feature.1 delete mode 100644 packages/volto/news/6421.feature.2 delete mode 100644 packages/volto/news/6422.feature delete mode 100644 packages/volto/news/6424.internal delete mode 100644 packages/volto/news/6425.feature delete mode 100644 packages/volto/news/6426.documentation delete mode 100644 packages/volto/news/6433.documentation delete mode 100644 packages/volto/news/6436.bugfix delete mode 100644 packages/volto/news/6438.documentation diff --git a/docs/source/release-notes/index.md b/docs/source/release-notes/index.md index 9ac139c7b9..228069b61f 100644 --- a/docs/source/release-notes/index.md +++ b/docs/source/release-notes/index.md @@ -17,6 +17,49 @@ myst: +## 18.0.0-alpha.47 (2024-10-27) + +### Breaking + +- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh + Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371) + +### Feature + +- URL Management control panel: add a filter for redirects created after a given date. + **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later. + @davisagli [#6414](https://github.com/plone/volto/issues/6414) +- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- URL Management control panel: Add feature to bulk upload redirects in a CSV file. + **Note:** This requires `plone.restapi` 9.8.0 or later. + @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422) +- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425) + +### Bugfix + +- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355) +- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419) +- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436) + +### Internal + +- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308) +- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371) +- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399) +- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403) +- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407) +- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408) +- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418) +- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424) + +### Documentation + +- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426) +- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433) +- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438) + ## 18.0.0-alpha.46 (2024-10-10) ### Breaking diff --git a/packages/volto/CHANGELOG.md b/packages/volto/CHANGELOG.md index 9ac139c7b9..228069b61f 100644 --- a/packages/volto/CHANGELOG.md +++ b/packages/volto/CHANGELOG.md @@ -17,6 +17,49 @@ myst: +## 18.0.0-alpha.47 (2024-10-27) + +### Breaking + +- Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh + Volto might continue to work on it, but it will be no longer tested in CI. [#6371](https://github.com/plone/volto/issues/6371) + +### Feature + +- URL Management control panel: add a filter for redirects created after a given date. + **Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later. + @davisagli [#6414](https://github.com/plone/volto/issues/6414) +- URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- URL Management control panel: Add feature to bulk upload redirects in a CSV file. + **Note:** This requires `plone.restapi` 9.8.0 or later. + @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- Introducing "client transforms for Redux reducers". @sneridagh [#6422](https://github.com/plone/volto/issues/6422) +- URL Management control panel: Add a way to edit existing aliases. @davisagli [#6425](https://github.com/plone/volto/issues/6425) + +### Bugfix + +- Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 [#6355](https://github.com/plone/volto/issues/6355) +- Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 [#6419](https://github.com/plone/volto/issues/6419) +- URL Management control panel: Improve layout for long paths. @davisagli [#6421](https://github.com/plone/volto/issues/6421) +- URL Management control panel: add missing translations. @davisagli [#6436](https://github.com/plone/volto/issues/6436) + +### Internal + +- Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 [#6308](https://github.com/plone/volto/issues/6308) +- Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh [#6371](https://github.com/plone/volto/issues/6371) +- Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh [#6399](https://github.com/plone/volto/issues/6399) +- Fix `clean` make command @sneridagh [#6403](https://github.com/plone/volto/issues/6403) +- Update typescript and vitest everywhere @sneridagh [#6407](https://github.com/plone/volto/issues/6407) +- Update `caniuse` Oct24 @sneridagh [#6408](https://github.com/plone/volto/issues/6408) +- Remove last tests still in Node.js 18 @sneridagh [#6418](https://github.com/plone/volto/issues/6418) +- Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh [#6424](https://github.com/plone/volto/issues/6424) + +### Documentation + +- Added deprecation notices to the upgrade guide for Volto 18. @sneridagh [#6426](https://github.com/plone/volto/issues/6426) +- Replace `yarn` with `pnpm` wherever necessary. @sneridagh [#6433](https://github.com/plone/volto/issues/6433) +- Rename page title from Frontend to Volto UI. @stevepiercy [#6438](https://github.com/plone/volto/issues/6438) + ## 18.0.0-alpha.46 (2024-10-10) ### Breaking diff --git a/packages/volto/news/6308.internal b/packages/volto/news/6308.internal deleted file mode 100644 index c2b9271b6b..0000000000 --- a/packages/volto/news/6308.internal +++ /dev/null @@ -1 +0,0 @@ -Used `resource title` instead of `resource type` in page title on edit. @Faakhir30 diff --git a/packages/volto/news/6355.bugfix b/packages/volto/news/6355.bugfix deleted file mode 100644 index 1d6daa2674..0000000000 --- a/packages/volto/news/6355.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix site setup access check by using `@actions` endpoint to validate permissions. @Faakhir30 diff --git a/packages/volto/news/6371.breaking b/packages/volto/news/6371.breaking deleted file mode 100644 index 204eeb8cd6..0000000000 --- a/packages/volto/news/6371.breaking +++ /dev/null @@ -1,2 +0,0 @@ -Dropped support for Node.js 18, since Volto only supports the last two LTS versions. @sneridagh -Volto might continue to work on it, but it will be no longer tested in CI. diff --git a/packages/volto/news/6371.internal b/packages/volto/news/6371.internal deleted file mode 100644 index 38a19474ac..0000000000 --- a/packages/volto/news/6371.internal +++ /dev/null @@ -1 +0,0 @@ -Added support for Node.js 22, since it will become LTS on 2024-10-29. @sneridagh diff --git a/packages/volto/news/6399.internal b/packages/volto/news/6399.internal deleted file mode 100644 index 363ce2c2a7..0000000000 --- a/packages/volto/news/6399.internal +++ /dev/null @@ -1 +0,0 @@ -Adapt `@plone/registry` as an ESM module, and fix its imports. @sneridagh diff --git a/packages/volto/news/6403.internal b/packages/volto/news/6403.internal deleted file mode 100644 index 68fb4d2cab..0000000000 --- a/packages/volto/news/6403.internal +++ /dev/null @@ -1 +0,0 @@ -Fix `clean` make command @sneridagh diff --git a/packages/volto/news/6407.internal b/packages/volto/news/6407.internal deleted file mode 100644 index f6daada94d..0000000000 --- a/packages/volto/news/6407.internal +++ /dev/null @@ -1 +0,0 @@ -Update typescript and vitest everywhere @sneridagh diff --git a/packages/volto/news/6408.internal b/packages/volto/news/6408.internal deleted file mode 100644 index 49ce0fb0f8..0000000000 --- a/packages/volto/news/6408.internal +++ /dev/null @@ -1 +0,0 @@ -Update `caniuse` Oct24 @sneridagh diff --git a/packages/volto/news/6414.feature b/packages/volto/news/6414.feature deleted file mode 100644 index 8ee0a49e68..0000000000 --- a/packages/volto/news/6414.feature +++ /dev/null @@ -1,3 +0,0 @@ -URL Management control panel: add a filter for redirects created after a given date. -**Note:** This requires `Products.CMFPlone` 6.0.14 and `plone.restapi` 9.8.0 or later. -@davisagli diff --git a/packages/volto/news/6418.internal b/packages/volto/news/6418.internal deleted file mode 100644 index fcc36e357f..0000000000 --- a/packages/volto/news/6418.internal +++ /dev/null @@ -1 +0,0 @@ -Remove last tests still in Node.js 18 @sneridagh diff --git a/packages/volto/news/6419.bugfix b/packages/volto/news/6419.bugfix deleted file mode 100644 index e7e843cb57..0000000000 --- a/packages/volto/news/6419.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix redirect after login if the `Login` component is used on a route other than `/login` or `/logout`. @dobri1408 diff --git a/packages/volto/news/6421.bugfix b/packages/volto/news/6421.bugfix deleted file mode 100644 index 3ff3139729..0000000000 --- a/packages/volto/news/6421.bugfix +++ /dev/null @@ -1 +0,0 @@ -URL Management control panel: Improve layout for long paths. @davisagli diff --git a/packages/volto/news/6421.feature.1 b/packages/volto/news/6421.feature.1 deleted file mode 100644 index b20307ca39..0000000000 --- a/packages/volto/news/6421.feature.1 +++ /dev/null @@ -1,3 +0,0 @@ -URL Management control panel: Add feature to bulk upload redirects in a CSV file. -**Note:** This requires `plone.restapi` 9.8.0 or later. -@davisagli diff --git a/packages/volto/news/6421.feature.2 b/packages/volto/news/6421.feature.2 deleted file mode 100644 index 1156bb1ff7..0000000000 --- a/packages/volto/news/6421.feature.2 +++ /dev/null @@ -1 +0,0 @@ -URL Management control panel: Move the form for adding a new redirect into a modal. @davisagli \ No newline at end of file diff --git a/packages/volto/news/6422.feature b/packages/volto/news/6422.feature deleted file mode 100644 index 7eb17ffd98..0000000000 --- a/packages/volto/news/6422.feature +++ /dev/null @@ -1 +0,0 @@ -Introducing "client transforms for Redux reducers". @sneridagh diff --git a/packages/volto/news/6424.internal b/packages/volto/news/6424.internal deleted file mode 100644 index eee9d51424..0000000000 --- a/packages/volto/news/6424.internal +++ /dev/null @@ -1 +0,0 @@ -Remove mention of `SEAMLESS` mode in the server bootstrap. @sneridagh diff --git a/packages/volto/news/6425.feature b/packages/volto/news/6425.feature deleted file mode 100644 index 7a12576c21..0000000000 --- a/packages/volto/news/6425.feature +++ /dev/null @@ -1 +0,0 @@ -URL Management control panel: Add a way to edit existing aliases. @davisagli diff --git a/packages/volto/news/6426.documentation b/packages/volto/news/6426.documentation deleted file mode 100644 index dc5079729a..0000000000 --- a/packages/volto/news/6426.documentation +++ /dev/null @@ -1 +0,0 @@ -Added deprecation notices to the upgrade guide for Volto 18. @sneridagh diff --git a/packages/volto/news/6433.documentation b/packages/volto/news/6433.documentation deleted file mode 100644 index 82111de185..0000000000 --- a/packages/volto/news/6433.documentation +++ /dev/null @@ -1 +0,0 @@ -Replace `yarn` with `pnpm` wherever necessary. @sneridagh diff --git a/packages/volto/news/6436.bugfix b/packages/volto/news/6436.bugfix deleted file mode 100644 index a0e0bf3309..0000000000 --- a/packages/volto/news/6436.bugfix +++ /dev/null @@ -1 +0,0 @@ -URL Management control panel: add missing translations. @davisagli diff --git a/packages/volto/news/6438.documentation b/packages/volto/news/6438.documentation deleted file mode 100644 index f894f6db1b..0000000000 --- a/packages/volto/news/6438.documentation +++ /dev/null @@ -1 +0,0 @@ -Rename page title from Frontend to Volto UI. @stevepiercy diff --git a/packages/volto/package.json b/packages/volto/package.json index 2de2443dd7..809e554ea0 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -9,7 +9,7 @@ } ], "license": "MIT", - "version": "18.0.0-alpha.46", + "version": "18.0.0-alpha.47", "repository": { "type": "git", "url": "git@github.com:plone/volto.git" diff --git a/packages/volto/types/actions/aliases/aliases.d.ts b/packages/volto/types/actions/aliases/aliases.d.ts index 0a08987630..94cda2c98c 100644 --- a/packages/volto/types/actions/aliases/aliases.d.ts +++ b/packages/volto/types/actions/aliases/aliases.d.ts @@ -5,7 +5,7 @@ * @param {Object} options Options data. * @returns {Object} Get aliases action. */ -export function getAliases(url: string, options: any): any; +export function getAliases(url: string, options?: any): any; /** * Add alias function. * @function addAliases @@ -22,3 +22,10 @@ export function addAliases(url: string, data: any): any; * @returns {Object} Remove alias action. */ export function removeAliases(url: string, data: any): any; +/** + * Upload aliases function. + * @function uploadAliases + * @param {Object} file CSV file. + * @returns {Object} Upload aliases action. + */ +export function uploadAliases(file: any): any; diff --git a/packages/volto/types/components/manage/Contents/__mocks__/index.d.ts b/packages/volto/types/components/manage/Contents/__mocks__/index.d.ts index 4a4f62aee7..8a200d866b 100644 --- a/packages/volto/types/components/manage/Contents/__mocks__/index.d.ts +++ b/packages/volto/types/components/manage/Contents/__mocks__/index.d.ts @@ -1,3 +1,2 @@ -/// export declare const Contents: jest.Mock; export declare const ContentsRenameModal: jest.Mock; diff --git a/packages/volto/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts b/packages/volto/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts index b44094f82f..d442a59e1e 100644 --- a/packages/volto/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +++ b/packages/volto/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts @@ -36,7 +36,7 @@ declare const _default: { data: {}; block: string; }; - contextType?: React.Context; + contextType?: React.Context | undefined; }; export default _default; import React from 'react'; diff --git a/packages/volto/types/components/manage/Controlpanels/index.d.ts b/packages/volto/types/components/manage/Controlpanels/index.d.ts index 8feb23523b..b796c15c8a 100644 --- a/packages/volto/types/components/manage/Controlpanels/index.d.ts +++ b/packages/volto/types/components/manage/Controlpanels/index.d.ts @@ -1,4 +1,3 @@ -/// export declare const Controlpanels: any; export declare const Controlpanel: import("@loadable/component").LoadableComponent>, "ref"> & import("react").RefAttributes>>>; export declare const RulesControlpanel: import("@loadable/component").LoadableClassComponent; diff --git a/packages/volto/types/components/manage/Form/__mocks__/index.d.ts b/packages/volto/types/components/manage/Form/__mocks__/index.d.ts index 086c8c23b7..36db79f1a1 100644 --- a/packages/volto/types/components/manage/Form/__mocks__/index.d.ts +++ b/packages/volto/types/components/manage/Form/__mocks__/index.d.ts @@ -1,4 +1,3 @@ -/// export declare const Field: jest.Mock; export declare const InlineForm: jest.Mock; export declare const ModalForm: jest.Mock; diff --git a/packages/volto/types/components/manage/Form/index.d.ts b/packages/volto/types/components/manage/Form/index.d.ts index 80e8e76488..b23c84c307 100644 --- a/packages/volto/types/components/manage/Form/index.d.ts +++ b/packages/volto/types/components/manage/Form/index.d.ts @@ -1,4 +1,3 @@ -/// export declare const Field: import("@loadable/component").LoadableComponent>; export declare const InlineForm: import("@loadable/component").LoadableComponent, "ref"> & import("react").RefAttributes>; export declare const ModalForm: import("@loadable/component").LoadableComponent>; diff --git a/packages/volto/types/components/manage/Multilingual/ManageTranslations.d.ts b/packages/volto/types/components/manage/Multilingual/ManageTranslations.d.ts index b44094f82f..d442a59e1e 100644 --- a/packages/volto/types/components/manage/Multilingual/ManageTranslations.d.ts +++ b/packages/volto/types/components/manage/Multilingual/ManageTranslations.d.ts @@ -36,7 +36,7 @@ declare const _default: { data: {}; block: string; }; - contextType?: React.Context; + contextType?: React.Context | undefined; }; export default _default; import React from 'react'; diff --git a/packages/volto/types/components/manage/Sidebar/ObjectBrowser.d.ts b/packages/volto/types/components/manage/Sidebar/ObjectBrowser.d.ts index 6ed8b67c57..b68432edd0 100644 --- a/packages/volto/types/components/manage/Sidebar/ObjectBrowser.d.ts +++ b/packages/volto/types/components/manage/Sidebar/ObjectBrowser.d.ts @@ -69,6 +69,6 @@ declare function withObjectBrowser(WrappedComponent: any): { data: {}; block: string; }; - contextType?: React.Context; + contextType?: React.Context | undefined; }; import React from 'react'; diff --git a/packages/volto/types/components/manage/Widgets/InternalUrlWidget.d.ts b/packages/volto/types/components/manage/Widgets/InternalUrlWidget.d.ts index 0531e1ed0f..3a0e6f3a16 100644 --- a/packages/volto/types/components/manage/Widgets/InternalUrlWidget.d.ts +++ b/packages/volto/types/components/manage/Widgets/InternalUrlWidget.d.ts @@ -74,7 +74,7 @@ declare const _default: { data: {}; block: string; }; - contextType?: React.Context; + contextType?: React.Context | undefined; }; export default _default; import React from 'react'; diff --git a/packages/volto/types/components/manage/Widgets/UrlWidget.d.ts b/packages/volto/types/components/manage/Widgets/UrlWidget.d.ts index 51ff47f9b5..43a7207080 100644 --- a/packages/volto/types/components/manage/Widgets/UrlWidget.d.ts +++ b/packages/volto/types/components/manage/Widgets/UrlWidget.d.ts @@ -74,7 +74,7 @@ declare const _default: { data: {}; block: string; }; - contextType?: React.Context; + contextType?: React.Context | undefined; }; export default _default; import React from 'react'; diff --git a/packages/volto/types/components/manage/Widgets/__mocks__/index.d.ts b/packages/volto/types/components/manage/Widgets/__mocks__/index.d.ts index 86a10de8b4..b81f2365db 100644 --- a/packages/volto/types/components/manage/Widgets/__mocks__/index.d.ts +++ b/packages/volto/types/components/manage/Widgets/__mocks__/index.d.ts @@ -1,4 +1,3 @@ -/// export declare const AlignWidget: jest.Mock; export declare const ButtonsWidget: jest.Mock; export declare const ArrayWidget: jest.Mock; diff --git a/packages/volto/types/components/manage/Widgets/index.d.ts b/packages/volto/types/components/manage/Widgets/index.d.ts index e6b763ead7..35bdf8311c 100644 --- a/packages/volto/types/components/manage/Widgets/index.d.ts +++ b/packages/volto/types/components/manage/Widgets/index.d.ts @@ -1,4 +1,3 @@ -/// export declare const AlignWidget: import("@loadable/component").LoadableComponent; export declare const ButtonsWidget: import("@loadable/component").LoadableComponent; export declare const ArrayWidget: import("@loadable/component").LoadableClassComponent; @@ -53,7 +52,7 @@ export declare const UrlWidget: import("@loadable/component").LoadableClassCompo data: {}; block: string; }; - contextType?: import("react").Context; + contextType?: import("react").Context | undefined; }>; export declare const InternalUrlWidget: import("@loadable/component").LoadableClassComponent<{ new (): { @@ -93,7 +92,7 @@ export declare const InternalUrlWidget: import("@loadable/component").LoadableCl data: {}; block: string; }; - contextType?: import("react").Context; + contextType?: import("react").Context | undefined; }>; export declare const EmailWidget: import("@loadable/component").LoadableComponent; export declare const NumberWidget: import("@loadable/component").LoadableComponent>; diff --git a/packages/volto/types/config/slots.d.ts b/packages/volto/types/config/slots.d.ts index 059b38b8d0..1a31461914 100644 --- a/packages/volto/types/config/slots.d.ts +++ b/packages/volto/types/config/slots.d.ts @@ -7,7 +7,7 @@ declare namespace slots { content: { subjects?: any[]; }; - }): JSX.Element; + }): JSX.Element | null; propTypes: { content: any; }; diff --git a/packages/volto/types/constants/ActionTypes.d.ts b/packages/volto/types/constants/ActionTypes.d.ts index 4f14d38380..2d1a3beb45 100644 --- a/packages/volto/types/constants/ActionTypes.d.ts +++ b/packages/volto/types/constants/ActionTypes.d.ts @@ -134,6 +134,7 @@ export const MOVE_CONTENT_RULE: "MOVE_CONTENT_RULE"; export const GET_ALIASES: "GET_ALIASES"; export const ADD_ALIASES: "ADD_ALIASES"; export const REMOVE_ALIASES: "REMOVE_ALIASES"; +export const UPLOAD_ALIASES: "UPLOAD_ALIASES"; export const GET_USERSCHEMA: "GET_USERSCHEMA"; export const GET_UPGRADE: "GET_UPGRADE"; export const POST_UPGRADE: "POST_UPGRADE"; diff --git a/packages/volto/types/helpers/Helmet/Helmet.d.ts b/packages/volto/types/helpers/Helmet/Helmet.d.ts index 85bd8d4d2f..e5be73218d 100644 --- a/packages/volto/types/helpers/Helmet/Helmet.d.ts +++ b/packages/volto/types/helpers/Helmet/Helmet.d.ts @@ -133,6 +133,6 @@ declare const HelmetExport: { peek: any; rewind: () => any; canUseDOM: any; - contextType?: React.Context; + contextType?: React.Context | undefined; }; import React from 'react';