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 @@
+
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": """