diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 0e5d22de5d..215aafba63 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -56,6 +56,7 @@ const config = {
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/ban-ts-comment': 0,
},
},
{
diff --git a/.gitignore b/.gitignore
index 99907cc0fd..bf0baa3966 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,4 @@ docs/_build/
/.tool-versions
docs/source/news
-.turbo
-.parcel-cache/
tsconfig.tsbuildinfo
diff --git a/.npmrc b/.npmrc
index 71c684383d..bc0f657128 100644
--- a/.npmrc
+++ b/.npmrc
@@ -3,4 +3,3 @@ public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=*stylelint*
public-hoist-pattern[]=*cypress*
public-hoist-pattern[]=*process*
-public-hoist-pattern[]=*parcel*
diff --git a/PACKAGES.md b/PACKAGES.md
index 12193c73fe..5e12ae1955 100644
--- a/PACKAGES.md
+++ b/PACKAGES.md
@@ -17,9 +17,9 @@ It's published "as is", so you can import the type definitions from anywhere in
## Core packages
-- `@plone/registry`
- `@plone/client`
- `@plone/components`
+- `@plone/registry`
### Rules
@@ -28,26 +28,37 @@ Core packages must not depend on any other `@plone/*` package, with only one exc
They must be published and bundled in a traditional (transpiled) way.
The bundle of these packages must work on both CommonJS and ECMAScript Module (ESM) environments.
-## Feature packages
-
-- `@plone/contents`
-
## Utility packages
-- `@plone/blocks`
-- `@plone/helpers`
- `@plone/drivers`
+- `@plone/helpers`
+- `@plone/providers`
- `@plone/rsc`
### Rules
Utility packages can depend on core packages and other utility packages.
-They must be published in a traditional way, bundled.
+They must be published in the traditional way, as a bundle.
This bundle must work on both CommonJS and ESM environments.
+## Feature packages
+
+- `@plone/blocks`
+- `@plone/contents`
+- `@plone/slots`
+
+
+### Rules
+
+Feature packages, or add-on packages, can depend on any other package.
+You must distribute them as source code, and not transpile them.
+They must provide a default configuration registry loader as the default main entry point export.
+They must be loadable as any other add-on.
+
+
## Development utility packages
These are packages that are not bundled, and they are used in conjunction with Volto core or Volto projects.
@@ -55,7 +66,7 @@ They contain utilities that are useful for the development of a Volto project.
Some of them are released:
- `@plone/scripts`
-- `@plone/generator-volto`
+- `@plone/generator-volto` (deprecated)
Some of them are used by the build, and separated in packages for convenience.
diff --git a/RELEASING.md b/RELEASING.md
index 55a6def6e7..44e2dac6a0 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -44,13 +44,15 @@ The release process calls `towncrier`.
It is a Python library that uses the Python utility `pipx`.
This utility allows you to call and execute Python modules without installing them as a prerequisite in your system.
It works similar to the NodeJS `npx` utility.
-On macOS, you can install `pipx` into your system:
+
+Install {term}`pipx` for your active Python, and ensure it is on your `$PATH`.
+Carefully read the console output for further instructions, if needed.
```shell
-brew install pipx
+python3 -m pip install pipx
+pipx ensurepath
```
-Or follow detailed instructions in the `pipx` documentation for [Installation](https://pypa.github.io/pipx/installation/).
## Running the release process
diff --git a/apps/rr7/.eslintrc.cjs b/apps/rr7/.eslintrc.cjs
new file mode 100644
index 0000000000..b4a6a65b4d
--- /dev/null
+++ b/apps/rr7/.eslintrc.cjs
@@ -0,0 +1,80 @@
+/**
+ * This is intended to be a basic starting point for linting in your app.
+ * It relies on recommended configs out of the box for simplicity, but you can
+ * and should modify this configuration to best suit your team's needs.
+ */
+
+/** @type {import('eslint').Linter.Config} */
+module.exports = {
+ root: true,
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ env: {
+ browser: true,
+ commonjs: true,
+ es6: true,
+ },
+
+ // Base config
+ extends: ['eslint:recommended'],
+
+ overrides: [
+ // React
+ {
+ files: ['**/*.{js,jsx,ts,tsx}'],
+ plugins: ['react', 'jsx-a11y'],
+ extends: [
+ 'plugin:react/recommended',
+ 'plugin:react/jsx-runtime',
+ 'plugin:react-hooks/recommended',
+ 'plugin:jsx-a11y/recommended',
+ ],
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ formComponents: ['Form'],
+ linkComponents: [
+ { name: 'Link', linkAttribute: 'to' },
+ { name: 'NavLink', linkAttribute: 'to' },
+ ],
+ },
+ },
+
+ // Typescript
+ {
+ files: ['**/*.{ts,tsx}'],
+ plugins: ['@typescript-eslint', 'import'],
+ parser: '@typescript-eslint/parser',
+ settings: {
+ 'import/internal-regex': '^~/',
+ 'import/resolver': {
+ node: {
+ extensions: ['.ts', '.tsx'],
+ },
+ typescript: {
+ alwaysTryTypes: true,
+ },
+ },
+ },
+ extends: [
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:import/recommended',
+ 'plugin:import/typescript',
+ ],
+ },
+
+ // Node
+ {
+ files: ['.eslintrc.js'],
+ env: {
+ node: true,
+ },
+ },
+ ],
+};
diff --git a/apps/rr7/.gitignore b/apps/rr7/.gitignore
new file mode 100644
index 0000000000..f1eb112b25
--- /dev/null
+++ b/apps/rr7/.gitignore
@@ -0,0 +1,7 @@
+node_modules
+
+/.cache
+/build
+.env
+.react-router
+.registry.loader.js
diff --git a/apps/rr7/README.md b/apps/rr7/README.md
new file mode 100644
index 0000000000..d6b9daf12c
--- /dev/null
+++ b/apps/rr7/README.md
@@ -0,0 +1,30 @@
+# Plone on React Router 7
+
+This is a proof of concept of a [React Router](https://reactrouter.com/dev/docs) app, using the `@plone/*` libraries.
+This is intended to serve as both a playground for the development of both packages and as a demo of Plone using Remix.
+
+> [!WARNING]
+> This package or app is experimental.
+> The community offers no support whatsoever for it.
+> Breaking changes may occur without notice.
+
+## Development
+
+To start, from the root of the monorepo, issue the following commands.
+
+```shell
+pnpm install
+pnpm --filter plone-remix run dev
+```
+
+Then start the Plone backend.
+
+% TODO MAKEFILE
+```shell
+make backend-docker-start
+```
+
+
+## About this app
+
+- [Remix Docs](https://remix.run/docs/en/main)
diff --git a/apps/rr7/app/client.ts b/apps/rr7/app/client.ts
new file mode 100644
index 0000000000..0eec9cd62e
--- /dev/null
+++ b/apps/rr7/app/client.ts
@@ -0,0 +1,8 @@
+import ploneClient from '@plone/client';
+import config from '@plone/registry';
+
+const cli = ploneClient.initialize({
+ apiPath: config.settings.apiPath,
+});
+
+export { cli as ploneClient };
diff --git a/apps/rr7/app/config.ts b/apps/rr7/app/config.ts
new file mode 100644
index 0000000000..e7133efdce
--- /dev/null
+++ b/apps/rr7/app/config.ts
@@ -0,0 +1,15 @@
+import config from '@plone/registry';
+import { blocksConfig, slate } from '@plone/blocks';
+
+const settings = {
+ apiPath: 'http://localhost:3000',
+ slate,
+};
+
+// @ts-expect-error We need to fix typing
+config.set('settings', settings);
+
+// @ts-expect-error We need to fix typing
+config.set('blocks', { blocksConfig });
+
+export default config;
diff --git a/apps/rr7/app/root.tsx b/apps/rr7/app/root.tsx
new file mode 100644
index 0000000000..50808c5b17
--- /dev/null
+++ b/apps/rr7/app/root.tsx
@@ -0,0 +1,98 @@
+import { useState } from 'react';
+import {
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+ useHref,
+ useLocation,
+ useNavigate,
+ useParams,
+} from 'react-router';
+import type { LinksFunction } from 'react-router';
+
+import { QueryClient } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import PloneClient from '@plone/client';
+import { PloneProvider } from '@plone/providers';
+import { flattenToAppURL } from './utils';
+import config from '@plone/registry';
+import './config';
+
+import '@plone/components/dist/basic.css';
+
+function useHrefLocal(to: string) {
+ return useHref(flattenToAppURL(to));
+}
+
+export const links: LinksFunction = () => [
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ {
+ rel: 'preconnect',
+ href: 'https://fonts.gstatic.com',
+ crossOrigin: 'anonymous',
+ },
+ {
+ rel: 'stylesheet',
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+export default function App() {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ }),
+ );
+
+ const [ploneClient] = useState(() =>
+ PloneClient.initialize({
+ apiPath: config.settings.apiPath,
+ }),
+ );
+
+ const RRNavigate = useNavigate();
+ const navigate = (to: string) => {
+ return RRNavigate(flattenToAppURL(to));
+ };
+
+ return (
+
+
+
+
+ );
+}
diff --git a/apps/rr7/app/routes.ts b/apps/rr7/app/routes.ts
new file mode 100644
index 0000000000..579d64cd84
--- /dev/null
+++ b/apps/rr7/app/routes.ts
@@ -0,0 +1,7 @@
+import type { RouteConfig } from '@react-router/dev/routes';
+import { index, route } from '@react-router/dev/routes';
+
+export const routes: RouteConfig = [
+ index('routes/home.tsx'),
+ route('*', 'routes/$.tsx'),
+];
diff --git a/apps/rr7/app/routes/$.tsx b/apps/rr7/app/routes/$.tsx
new file mode 100644
index 0000000000..5216c4d188
--- /dev/null
+++ b/apps/rr7/app/routes/$.tsx
@@ -0,0 +1,2 @@
+import Content, { loader } from './home';
+export { loader, Content as default };
diff --git a/apps/rr7/app/routes/home.tsx b/apps/rr7/app/routes/home.tsx
new file mode 100644
index 0000000000..c470ad9f42
--- /dev/null
+++ b/apps/rr7/app/routes/home.tsx
@@ -0,0 +1,79 @@
+import type { LoaderArgs } from '../routes/+types.home';
+import {
+ dehydrate,
+ QueryClient,
+ HydrationBoundary,
+ useQuery,
+ useQueryClient,
+} from '@tanstack/react-query';
+import { flattenToAppURL } from '../utils';
+import { useLoaderData, useLocation } from 'react-router';
+import { usePloneClient } from '@plone/providers';
+import { Breadcrumbs, RenderBlocks } from '@plone/components';
+import config from '@plone/registry';
+import { ploneClient } from '../client';
+
+import type { MetaFunction } from 'react-router';
+
+export const meta: MetaFunction = () => {
+ return [
+ { title: 'Plone on React Router 7' },
+ { name: 'description', content: 'Welcome to Plone!' },
+ ];
+};
+
+const expand = ['breadcrumbs', 'navigation'];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export async function loader({ params, request }: LoaderArgs) {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // With SSR, we usually want to set some default staleTime
+ // above 0 to avoid refetching immediately on the client
+ staleTime: 60 * 1000,
+ },
+ },
+ });
+
+ const { getContentQuery } = ploneClient;
+
+ await queryClient.prefetchQuery(
+ getContentQuery({ path: flattenToAppURL(request.url), expand }),
+ );
+
+ return { dehydratedState: dehydrate(queryClient) };
+}
+
+function Page() {
+ const { getContentQuery } = usePloneClient();
+ const pathname = useLocation().pathname;
+ const { data } = useQuery(getContentQuery({ path: pathname, expand }));
+
+ if (!data) return 'Loading...';
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default function Content() {
+ const { dehydratedState } = useLoaderData();
+ const queryClient = useQueryClient();
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/rr7/app/utils.ts b/apps/rr7/app/utils.ts
new file mode 100644
index 0000000000..c297613f90
--- /dev/null
+++ b/apps/rr7/app/utils.ts
@@ -0,0 +1,17 @@
+import config from './config';
+
+/**
+ * Flatten to app server URL - Given a URL if it starts with the API server URL
+ * this method flattens it (removes) the server part
+ * TODO: Update it when implementing non-root based app location (on a
+ * directory other than /, eg. /myapp)
+ * @method flattenToAppURL
+ */
+export function flattenToAppURL(url: string) {
+ const { settings } = config;
+
+ return (
+ url &&
+ url.replace(settings.apiPath, '').replace('http://localhost:3000', '')
+ );
+}
diff --git a/apps/rr7/package.json b/apps/rr7/package.json
new file mode 100644
index 0000000000..fa204ea503
--- /dev/null
+++ b/apps/rr7/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "plone-rr7",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "dev": "react-router dev",
+ "build": "react-router build",
+ "start:prod": "react-router-serve ./build/server/index.js",
+ "typecheck": "react-router typegen && tsc",
+ "typegen": "react-router typegen"
+ },
+ "dependencies": {
+ "@react-router/node": "7.0.0-pre.4",
+ "@react-router/serve": "7.0.0-pre.4",
+ "@tanstack/react-query": "^5.59.0",
+ "@tanstack/react-query-devtools": "^5.59.0",
+ "isbot": "^5.1.17",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router": "7.0.0-pre.4"
+ },
+ "devDependencies": {
+ "@plone/blocks": "workspace:*",
+ "@plone/client": "workspace:*",
+ "@plone/components": "workspace:*",
+ "@plone/providers": "workspace:*",
+ "@plone/registry": "workspace:*",
+ "@react-router/dev": "7.0.0-pre.4",
+ "@types/react": "^18.3.9",
+ "@types/react-dom": "^18.3.0",
+ "typescript": "^5.6.3",
+ "vite": "^5.4.9",
+ "vite-tsconfig-paths": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+}
diff --git a/apps/rr7/public/favicon.ico b/apps/rr7/public/favicon.ico
new file mode 100644
index 0000000000..5dbdfcddcb
Binary files /dev/null and b/apps/rr7/public/favicon.ico differ
diff --git a/apps/rr7/tsconfig.json b/apps/rr7/tsconfig.json
new file mode 100644
index 0000000000..29b2316386
--- /dev/null
+++ b/apps/rr7/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ "**/.server/**/*.ts",
+ "**/.server/**/*.tsx",
+ "**/.client/**/*.ts",
+ "**/.client/**/*.tsx",
+ ".react-router/types/**/*"
+ ],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "types": ["@react-router/node", "vite/client"],
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "target": "ES2022",
+ "strict": true,
+ "allowJs": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+ "noEmit": true,
+ "rootDirs": [".", "./.react-router/types"],
+ "plugins": [{ "name": "@react-router/dev" }]
+ }
+}
diff --git a/apps/rr7/vite.config.ts b/apps/rr7/vite.config.ts
new file mode 100644
index 0000000000..723e0323af
--- /dev/null
+++ b/apps/rr7/vite.config.ts
@@ -0,0 +1,28 @@
+import { reactRouter } from '@react-router/dev/vite';
+import tsconfigPaths from 'vite-tsconfig-paths';
+import { defineConfig } from 'vite';
+import { PloneRegistryVitePlugin } from '@plone/registry/vite-plugin';
+
+export default defineConfig({
+ plugins: [
+ reactRouter({
+ // Server-side render by default, to enable SPA mode set this to `false`
+ ssr: true,
+ }),
+ tsconfigPaths(),
+ PloneRegistryVitePlugin(),
+ ],
+ server: {
+ port: 3000,
+ proxy: {
+ '^/\\+\\+api\\+\\+($$|/.*)': {
+ target:
+ 'http://localhost:8080/VirtualHostBase/http/localhost:3000/Plone/++api++/VirtualHostRoot',
+ rewrite: (path) => {
+ console.log(path);
+ return path.replace('/++api++', '');
+ },
+ },
+ },
+ },
+});
diff --git a/docs/source/contributing/branch-policy.md b/docs/source/_inc/_branch-policy.md
similarity index 100%
rename from docs/source/contributing/branch-policy.md
rename to docs/source/_inc/_branch-policy.md
diff --git a/docs/source/contributing/install-docker.md b/docs/source/_inc/_install-docker.md
similarity index 100%
rename from docs/source/contributing/install-docker.md
rename to docs/source/_inc/_install-docker.md
diff --git a/docs/source/contributing/install-git.md b/docs/source/_inc/_install-git.md
similarity index 100%
rename from docs/source/contributing/install-git.md
rename to docs/source/_inc/_install-git.md
diff --git a/docs/source/contributing/install-make.md b/docs/source/_inc/_install-make.md
similarity index 100%
rename from docs/source/contributing/install-make.md
rename to docs/source/_inc/_install-make.md
diff --git a/docs/source/contributing/install-nodejs.md b/docs/source/_inc/_install-nodejs.md
similarity index 100%
rename from docs/source/contributing/install-nodejs.md
rename to docs/source/_inc/_install-nodejs.md
diff --git a/docs/source/contributing/install-nvm.md b/docs/source/_inc/_install-nvm.md
similarity index 100%
rename from docs/source/contributing/install-nvm.md
rename to docs/source/_inc/_install-nvm.md
diff --git a/docs/source/contributing/install-operating-system.md b/docs/source/_inc/_install-operating-system.md
similarity index 100%
rename from docs/source/contributing/install-operating-system.md
rename to docs/source/_inc/_install-operating-system.md
diff --git a/docs/source/_static/plone-home-page.png b/docs/source/_static/plone-home-page.png
new file mode 100644
index 0000000000..b2321e0eaf
Binary files /dev/null and b/docs/source/_static/plone-home-page.png differ
diff --git a/docs/source/_static/searchtools.js b/docs/source/_static/searchtools.js
deleted file mode 100644
index 23ed8bf5a2..0000000000
--- a/docs/source/_static/searchtools.js
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * searchtools.js
- * ~~~~~~~~~~~~~~~~
- *
- * Sphinx JavaScript utilities for the full-text search.
- *
- * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-
-if (!Scorer) {
- /**
- * Simple result scoring code.
- */
- var Scorer = {
- // Implement the following function to further tweak the score for each result
- // The function takes a result array [filename, title, anchor, descr, score]
- // and returns the new score.
- /*
- score: function(result) {
- return result[4];
- },
- */
-
- // query matches the full name of an object
- objNameMatch: 11,
- // or matches in the last dotted part of the object name
- objPartialMatch: 6,
- // Additive scores depending on the priority of the object
- objPrio: {0: 15, // used to be importantResults
- 1: 5, // used to be objectResults
- 2: -5}, // used to be unimportantResults
- // Used when the priority is not in the mapping.
- objPrioDefault: 0,
-
- // query found in title
- title: 15,
- partialTitle: 7,
- // query found in terms
- term: 5,
- partialTerm: 2
- };
-}
-
-if (!splitQuery) {
- function splitQuery(query) {
- return query.split(/\s+/);
- }
-}
-
-/**
- * Search Module
- */
-var Search = {
-
- _index : null,
- _queued_query : null,
- _pulse_status : -1,
-
- htmlToText : function(htmlString) {
- var virtualDocument = document.implementation.createHTMLDocument('virtual');
- var htmlElement = $(htmlString, virtualDocument);
- htmlElement.find('.headerlink').remove();
- docContent = htmlElement.find('[role=main]')[0];
- if(docContent === undefined) {
- console.warn("Content block not found. Sphinx search tries to obtain it " +
- "via '[role=main]'. Could you check your theme or template.");
- return "";
- }
- return docContent.textContent || docContent.innerText;
- },
-
- init : function() {
- var params = $.getQueryParameters();
- if (params.q) {
- var query = params.q[0];
- $('input[name="q"]')[0].value = query;
- $('input[name="q"]')[1].value = query;
- if (params.doc_section) {
- var doc_section = params.doc_section[0];
- $('select[name="doc_section"]')[0].value = doc_section;
- }
- this.performSearch(query, doc_section);
- }
- },
-
- loadIndex : function(url) {
- $.ajax({type: "GET", url: url, data: null,
- dataType: "script", cache: true,
- complete: function(jqxhr, textstatus) {
- if (textstatus != "success") {
- document.getElementById("searchindexloader").src = url;
- }
- }});
- },
-
- setIndex : function(index) {
- var q;
- this._index = index;
- if ((q = this._queued_query) !== null) {
- this._queued_query = null;
- Search.query(q);
- }
- },
-
- hasIndex : function() {
- return this._index !== null;
- },
-
- deferQuery : function(query) {
- this._queued_query = query;
- },
-
- stopPulse : function() {
- this._pulse_status = 0;
- },
-
- startPulse : function() {
- if (this._pulse_status >= 0)
- return;
- function pulse() {
- var i;
- Search._pulse_status = (Search._pulse_status + 1) % 4;
- var dotString = '';
- for (i = 0; i < Search._pulse_status; i++)
- dotString += '.';
- Search.dots.text(dotString);
- if (Search._pulse_status > -1)
- window.setTimeout(pulse, 500);
- }
- pulse();
- },
-
- /**
- * perform a search for something (or wait until index is loaded)
- */
- performSearch : function(query, doc_section) {
- // create the required interface elements
- this.out = $('#search-results');
- this.title = $('').appendTo(this.out);
- this.dots = $('').appendTo(this.title);
- this.status = $('
').appendTo(this.out);
- this.output = $('
').appendTo(this.out);
-
- $('#search-progress').text(_('Preparing search...'));
- this.startPulse();
-
- // index already loaded, the browser was quick!
- if (this.hasIndex()) {
- this.query(query, doc_section);
- } else {
- this.deferQuery(query);
- }
- },
-
- /**
- * execute search (requires search index to be loaded)
- */
- query : function(query, doc_section) {
- var i;
-
- // stem the searchterms and add them to the correct list
- var stemmer = new Stemmer();
- var searchterms = [];
- var excluded = [];
- var hlterms = [];
- var tmp = splitQuery(query);
- var objectterms = [];
- for (i = 0; i < tmp.length; i++) {
- if (tmp[i] !== "") {
- objectterms.push(tmp[i].toLowerCase());
- }
-
- if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
- // skip this "word"
- continue;
- }
- // stem the word
- var word = stemmer.stemWord(tmp[i].toLowerCase());
- // prevent stemmer from cutting word smaller than two chars
- if(word.length < 3 && tmp[i].length >= 3) {
- word = tmp[i];
- }
- var toAppend;
- // select the correct list
- if (word[0] == '-') {
- toAppend = excluded;
- word = word.substr(1);
- }
- else {
- toAppend = searchterms;
- hlterms.push(tmp[i].toLowerCase());
- }
- // only add if not already in the list
- if (!$u.contains(toAppend, word))
- toAppend.push(word);
- }
- var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
-
- // console.debug('SEARCH: searching for:');
- // console.info('required: ', searchterms);
- // console.info('excluded: ', excluded);
-
- // prepare search
- var terms = this._index.terms;
- var titleterms = this._index.titleterms;
-
- // array of [filename, title, anchor, descr, score]
- var results = [];
- $('#search-progress').empty();
-
- // lookup as object
- for (i = 0; i < objectterms.length; i++) {
- var others = [].concat(objectterms.slice(0, i),
- objectterms.slice(i+1, objectterms.length));
- results = results.concat(this.performObjectSearch(objectterms[i], others));
- }
-
- // lookup as search terms in fulltext
- results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
-
- // let the scorer override scores with a custom scoring function
- if (Scorer.score) {
- for (i = 0; i < results.length; i++)
- results[i][4] = Scorer.score(results[i]);
- }
-
- // Filter results by doc_section
- if (doc_section && doc_section !== 'all') {
- results = results.filter(result => {
- let condition = result[0].split('/')[0] === doc_section;
- return condition
- })
- }
-
- // Enrich item with parent doc_section title
- for (i = 0; i < results.length; i++)
- results[i][6] = results[i][6] || 'TODO Documentation title';
-
- // now sort the results by score (in opposite order of appearance, since the
- // display function below uses pop() to retrieve items) and then
- // alphabetically
- results.sort(function(a, b) {
- var left = a[4];
- var right = b[4];
- if (left > right) {
- return 1;
- } else if (left < right) {
- return -1;
- } else {
- // same score: sort alphabetically
- left = a[1].toLowerCase();
- right = b[1].toLowerCase();
- return (left > right) ? -1 : ((left < right) ? 1 : 0);
- }
- });
-
-
- // print the results
- var resultCount = results.length;
- function displayNextItem() {
- // results left, load the summary and display it
- if (results.length) {
- var item = results.pop();
- var listItem = $('');
- var requestUrl = "";
- var linkUrl = "";
- if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
- // dirhtml builder
- var dirname = item[0] + '/';
- if (dirname.match(/\/index\/$/)) {
- dirname = dirname.substring(0, dirname.length-6);
- } else if (dirname == 'index/') {
- dirname = '';
- }
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
- linkUrl = requestUrl;
-
- } else {
- // normal html builders
- requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
- linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
- }
- listItem.append($('').attr('href',
- linkUrl +
- highlightstring + item[2]).html(item[1]));
-
- listItem.append($('' + item[6] + ''));
-
- if (item[3]) {
- listItem.append($(' (' + item[3] + ')'));
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
- $.ajax({url: requestUrl,
- dataType: "text",
- complete: function(jqxhr, textstatus) {
- var data = jqxhr.responseText;
- if (data !== '' && data !== undefined) {
- listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
- }
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }});
- } else {
- // no source available, just display title
- Search.output.append(listItem);
- setTimeout(function() {
- displayNextItem();
- }, 5);
- }
- }
- // search finished, update title and status message
- else {
- Search.stopPulse();
- Search.title.text(_('Search Results'));
- if (!resultCount)
- Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
- else
- Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
- Search.status.fadeIn(500);
- }
- }
- displayNextItem();
- },
-
- /**
- * search for object names
- */
- performObjectSearch : function(object, otherterms) {
- var filenames = this._index.filenames;
- var docnames = this._index.docnames;
- var objects = this._index.objects;
- var objnames = this._index.objnames;
- var titles = this._index.titles;
-
- var i;
- var results = [];
-
- for (var prefix in objects) {
- for (var name in objects[prefix]) {
- var fullname = (prefix ? prefix + '.' : '') + name;
- var fullnameLower = fullname.toLowerCase()
- if (fullnameLower.indexOf(object) > -1) {
- var score = 0;
- var parts = fullnameLower.split('.');
- // check for different match types: exact matches of full name or
- // "last name" (i.e. last dotted part)
- if (fullnameLower == object || parts[parts.length - 1] == object) {
- score += Scorer.objNameMatch;
- // matches in last name
- } else if (parts[parts.length - 1].indexOf(object) > -1) {
- score += Scorer.objPartialMatch;
- }
- var match = objects[prefix][name];
- var objname = objnames[match[1]][2];
- var title = titles[match[0]];
- // If more than one term searched for, we require other words to be
- // found in the name/title/description
- if (otherterms.length > 0) {
- var haystack = (prefix + ' ' + name + ' ' +
- objname + ' ' + title).toLowerCase();
- var allfound = true;
- for (i = 0; i < otherterms.length; i++) {
- if (haystack.indexOf(otherterms[i]) == -1) {
- allfound = false;
- break;
- }
- }
- if (!allfound) {
- continue;
- }
- }
- var descr = objname + _(', in ') + title;
-
- var anchor = match[3];
- if (anchor === '')
- anchor = fullname;
- else if (anchor == '-')
- anchor = objnames[match[1]][1] + '-' + fullname;
- // add custom score for some objects according to scorer
- if (Scorer.objPrio.hasOwnProperty(match[2])) {
- score += Scorer.objPrio[match[2]];
- } else {
- score += Scorer.objPrioDefault;
- }
- results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
- }
- }
- }
-
- return results;
- },
-
- /**
- * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
- */
- escapeRegExp : function(string) {
- return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
- },
-
- /**
- * search for full-text terms in the index
- */
- performTermsSearch : function(searchterms, excluded, terms, titleterms) {
- var docnames = this._index.docnames;
- var filenames = this._index.filenames;
- var titles = this._index.titles;
-
- var i, j, file;
- var fileMap = {};
- var scoreMap = {};
- var results = [];
-
- // perform the search on the required terms
- for (i = 0; i < searchterms.length; i++) {
- var word = searchterms[i];
- var files = [];
- var _o = [
- {files: terms[word], score: Scorer.term},
- {files: titleterms[word], score: Scorer.title}
- ];
- // add support for partial matches
- if (word.length > 2) {
- var word_regex = this.escapeRegExp(word);
- for (var w in terms) {
- if (w.match(word_regex) && !terms[word]) {
- _o.push({files: terms[w], score: Scorer.partialTerm})
- }
- }
- for (var w in titleterms) {
- if (w.match(word_regex) && !titleterms[word]) {
- _o.push({files: titleterms[w], score: Scorer.partialTitle})
- }
- }
- }
-
- // no match but word was a required one
- if ($u.every(_o, function(o){return o.files === undefined;})) {
- break;
- }
- // found search word in contents
- $u.each(_o, function(o) {
- var _files = o.files;
- if (_files === undefined)
- return
-
- if (_files.length === undefined)
- _files = [_files];
- files = files.concat(_files);
-
- // set score for the word in each file to Scorer.term
- for (j = 0; j < _files.length; j++) {
- file = _files[j];
- if (!(file in scoreMap))
- scoreMap[file] = {};
- scoreMap[file][word] = o.score;
- }
- });
-
- // create the mapping
- for (j = 0; j < files.length; j++) {
- file = files[j];
- if (file in fileMap && fileMap[file].indexOf(word) === -1)
- fileMap[file].push(word);
- else
- fileMap[file] = [word];
- }
- }
-
- // now check if the files don't contain excluded terms
- for (file in fileMap) {
- var valid = true;
-
- // check if all requirements are matched
- var filteredTermCount = // as search terms with length < 3 are discarded: ignore
- searchterms.filter(function(term){return term.length > 2}).length
- if (
- fileMap[file].length != searchterms.length &&
- fileMap[file].length != filteredTermCount
- ) continue;
-
- // ensure that none of the excluded terms is in the search result
- for (i = 0; i < excluded.length; i++) {
- if (terms[excluded[i]] == file ||
- titleterms[excluded[i]] == file ||
- $u.contains(terms[excluded[i]] || [], file) ||
- $u.contains(titleterms[excluded[i]] || [], file)) {
- valid = false;
- break;
- }
- }
-
- // if we have still a valid result we can add it to the result list
- if (valid) {
- // select one (max) score for the file.
- // for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
- var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
- function getParentTitle(f) {
- let parentdocname = docnames[f].split('/')[0] + '/index';
- let parentID = docnames.indexOf(parentdocname);
- let title = parentID === -1 ? 'Plone Documentation' : titles[parentID];
- return title
- }
- results.push([docnames[file], titles[file], '', null, score, filenames[file], getParentTitle(file)]);
- }
- }
- return results;
- },
-
- /**
- * helper function to return a node containing the
- * search summary for a given text. keywords is a list
- * of stemmed words, hlwords is the list of normal, unstemmed
- * words. the first one is used to find the occurrence, the
- * latter for highlighting it.
- */
- makeSearchSummary : function(htmlText, keywords, hlwords) {
- var text = Search.htmlToText(htmlText);
- var textLower = text.toLowerCase();
- var start = 0;
- $.each(keywords, function() {
- var i = textLower.indexOf(this.toLowerCase());
- if (i > -1)
- start = i;
- });
- start = Math.max(start - 120, 0);
- var excerpt = ((start > 0) ? '...' : '') +
- $.trim(text.substr(start, 240)) +
- ((start + 240 - text.length) ? '...' : '');
- var rv = $('').text(excerpt);
- $.each(hlwords, function() {
- rv = rv.highlightText(this, 'highlighted');
- });
- return rv;
- }
-};
-
-$(document).ready(function() {
- Search.init();
- if ($.trim($(".topbar-contents .bd-toc").html()) === "") {
- $(".topbar-contents .bd-toc").css("visibility", "hidden");
- }
- $('select[name="doc_section"]').change(function() {
- this.form.submit();
- });
-});
diff --git a/docs/source/addons/best-practices.md b/docs/source/addons/best-practices.md
deleted file mode 100644
index 23545d628a..0000000000
--- a/docs/source/addons/best-practices.md
+++ /dev/null
@@ -1,90 +0,0 @@
----
-myst:
- html_meta:
- "description": "Integrate your add-on with Volto's add-on framework"
- "property=og:description": "Integrate your add-on with Volto's add-on framework"
- "property=og:title": "Best practices for add-ons"
- "keywords": "Volto, Plone, frontend, React, best practices, add-ons"
----
-
-# Best practices for add-ons
-
-Although the add-on framework is relatively new in Volto's world, there are
-quite a few generic add-ons that can be used in any Volto project.
-
-Based on the experience gained developing some of these add-ons, we
-recommend that you follow (no need for strictness, of course) these rough
-guidelines:
-
-## Integrate your add-on with Volto's add-on framework
-
-Just like Plone add-ons provide some features by default, Volto add-ons should
-register some features by default. For example, if your add-on provides widgets,
-register the most basic configuration of that widget with a name that can be
-used.
-
-On more complicated cases, see if you can structure your code to use the
-`settings` {term}`configuration registry`, or stash your configuration in your block
-registration, for example.
-
-As an example: let's say we're building a Color Picker widget and we want to
-provide a palette of colors from which to choose. The widget should integrate
-with a default `settings.colorWidgetPalette`, which would be a list of colors.
-
-And of course, also provide a widget factory so it can be used to create
-multiple instances of that color widget with custom color palettes.
-
-### Provide additional configuration
-
-An add-on can ship with multiple {term}`Volto configuration loader`s. This makes it
-possible to provide configuration methods for demo purposes, for example, or to
-ship with a default "shallow" integration, then provide another separate
-configuration loader for a deeper integration.
-
-## Avoid shadowing Volto files
-
-This rule is meant to be broken. If you find that you need to customize
-a particular file from Volto and you have multiple projects, better to create
-an add-on that holds that customized file, so that you have a single place to
-maintain that "file fork", but otherwise it's a good idea to avoid shipping
-generic add-ons with Volto customizations. Make sure to include this information
-as a warning in your add-on description!
-
-See if your use case is generic enough, maybe Volto needs to be extended to
-cover that use case, directly in core.
-
-## Minimal documentation
-
-Deadlines can be rough and documentation tends to be pushed as last priority,
-but please add a minimal Readme with a couple of lines and, most importantly,
-a screenshot.
-
-Ideally, the Readme should also include install instructions and details on any
-possible settings.
-
-
-(testing-the-add-on-label)=
-
-## Testing the add-on
-
-It is not easy, right now, to ship an add-on with a self-bootstraping and
-testing framework. But you can create a separate minimal Volto project that can
-hold the Cypress integration tests and trigger the CI tests.
-
-## Use appropriate npmjs tags
-
-If you're releasing your add-on to `npmjs.com`, please consider adding the
-following tags, next to your add-on-specific tags:
-
-- `volto-addon`
-- `volto`
-- `plone`
-- `react`
-
-## Include in `collective/awesome-volto`
-
-Even if you think your add-on is not generic or it's tricky to integrate, please
-consider including your add-on in the
-[`collective/awesome-volto`](https://github.com/collective/awesome-volto) add-ons
-list. This provides visibility to your add-on but also further solidifies
-Volto's position in our Plone community.
diff --git a/docs/source/addons/index.md b/docs/source/addons/index.md
deleted file mode 100644
index ed13a38d17..0000000000
--- a/docs/source/addons/index.md
+++ /dev/null
@@ -1,544 +0,0 @@
----
-myst:
- html_meta:
- "description": "Volto add-ons extend the core functionality of the Plone CMS frontend."
- "property=og:description": "Volto add-ons extend the core functionality of the Plone CMS frontend."
- "property=og:title": "Volto add-ons"
- "keywords": "Volto, add-on, extensions, frontend, Plone"
----
-
-# Volto add-ons
-
-```{toctree}
-:maxdepth: 1
-
-i18n
-best-practices
-theme
-public-folder
-```
-
-There are several advanced scenarios where we might want to have more control
-and flexibility beyond using the plain Volto project to build a site.
-
-We can build Volto {term}`add-on` products and make them available as generic
-JavaScript packages that can be included in any Volto project. By doing so we
-can provide code and component reutilization across projects and, of course,
-benefit from open source collaboration.
-
-```{note}
-By declaring a JavaScript package as a Volto add-on, Volto provides
-several integration features: language features (so they can be transpiled
-by Babel), whole-process customization via razzle.extend.js and
-integration with Volto's {term}`configuration registry`.
-```
-
-The add-on can be published to an npm registry or directly installed from github
-by Yarn. By using [mrs-develop](https://github.com/collective/mrs-developer),
-it's possible to have a workflow similar to zc.buildout's mr.developer, where
-you can "checkout" an add-on for development.
-
-An add-on can be almost anything that a Volto project can be. They can:
-
-- provide additional views and blocks
-- override or extend Volto's builtin views, blocks, settings
-- shadow (customize) Volto's (or another add-on's) modules
-- register custom routes
-- provide custom {term}`Redux` actions and reducers
-- register custom Express middleware for Volto's server process
-- tweak Volto's Webpack configuration, load custom Razzle and Webpack plugins
-- even provide a custom theme, just like a regular Volto project does.
-
-## Configuring a Volto project to use an add-on
-
-You can install a Volto add-on just like any other JS package:
-
-```shell
-yarn add name-of-add-on
-```
-
-If the add-on is not published on npm, you can retrieve it directly from Github:
-
-```shell
-yarn add collective/volto-dropdownmenu
-```
-
-Next, you'll need to add the add-on (identified by its JS package name) to the
-`addons` key of your Volto project's `package.json`. More details in the next
-section.
-
-### Loading add-on configuration
-
-As a convenience, an add-on can export configuration functions that can mutate,
-in-place, the overall Volto {term}`configuration registry`. An add-on can export multiple
-configurations methods, making it possible to selectively choose which specific
-add-on functionality you want to load.
-
-In your Volto project's ``package.json`` you can allow the add-on to alter the
-global configuration by adding, in the `addons` key, a list of Volto add-on
-package names, like:
-
-```js
-{
- "name": "my-nice-volto-project",
-
- "addons": [
- "acme-volto-foo-add-on",
- "@plone/some-add-on",
- "collective-another-volto-add-on"
- ],
-
-}
-```
-
-```{warning}
-Adding the add-on package to the `addons` key is mandatory! It allows Volto
-to treat that package properly and provide it with BabelJS language
-features. In Plone terminology, it is like including a Python egg to the
-`zcml` section of zc.buildout.
-```
-
-Some add-ons might choose to allow the Volto project to selectively load some of
-their configuration, so they may offer additional configuration functions,
-which you can load by overloading the add-on name in the `addons` package.json
-key, like so:
-
-```{code-block} json
-:emphasize-lines: 4
-
-{
- "name": "my-nice-volto-project",
- "addons": [
- "acme-volto-foo-add-on:loadOptionalBlocks,overrideSomeDefaultBlock",
- "volto-ga"
- ],
-}
-```
-
-```{note}
-The additional comma-separated names should be exported from the add-on
-package's ``index.js``. The main configuration function should be exported as
-the default. An add-on's default configuration method will always be loaded.
-```
-
-If for some reason, you want to manually load the add-on, you could always do,
-in your project's ``config.js`` module:
-
-```js
-import loadExampleAddon, { enableOptionalBlocks } from 'volto-example-add-on';
-import * as voltoConfig from '@plone/volto/config';
-
-const config = enableOptionalBlocks(loadExampleAddon(voltoConfig));
-
-export blocks = {
- ...config.blocks,
-}
-```
-
-As this is a common operation, Volto provides a helper method for this:
-
-```js
-import { applyConfig } from '@plone/volto/helpers';
-import * as voltoConfig from '@plone/volto/config';
-
-const config = applyConfig([
- enableOptionalBlocks,
- loadExampleAddon
-], voltoConfig);
-
-export blocks = {
- ...config.blocks,
-}
-```
-
-The `applyConfig` helper ensures that each configuration methods returns the
-config object, avoiding odd and hard to track errors when developing add-ons.
-
-## Creating add-ons
-
-Volto 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 that acts as a {term}`Volto configuration loader`.
-
-Although you could simply use `npm init` to generate an add-on initial code,
-we now have a nice
-[Yeoman-based generator](https://github.com/plone/generator-volto) that you can use:
-
-```shell
-npm install -g @plone/generator-volto
-yo @plone/volto:addon [] [options]
-```
-
-Volto will automatically provide aliases for your (unreleased) package, so that
-once you've released it, you don't need to change import paths, since you can
-use the final ones from the very beginning. This means that you can use imports
-such as `import { Something } from '@plone/my-volto-add-on'` without any extra
-configuration.
-
-### Use mrs-developer to manage the development cycle
-
-#### Add mrs-developer dependency and related script
-
-[Eric Brehault](https://github.com/ebrehault) ported this amazing Python tool,
-which provides a way to pull a package from git and set it up as a dependency
-for the current project codebase.
-
-To facilitate add-on development lifecycle we recommend using
-[mrs-developer](https://www.npmjs.com/package/mrs-developer).
-
-By doing this, you can develop both the project and the add-on product as if
-they were both part of the current codebase. Once the add-on development is
-done, you can publish the package to an npm repository.
-
-```shell
-yarn add mrs-developer
-```
-
-Then, in `package.json`:
-
-```{code-block} json
-:emphasize-lines: 2
-"scripts": {
- "develop": "missdev --config=jsconfig.json --output=addons",
-}
-```
-
-We can configure `mrs-developer` to use any directory that you want. Here we
-are telling it to create the directory `src/addons` and put the packages
-managed by `mrs-developer` inside.
-
-#### mrs.developer.json
-
-This is the configuration file that instructs `mrs-developer` from where it has
-to pull the packages. So, create `mrs.developer.json` and add:
-
-```json
-{
- "acme-volto-foo-add-on": {
- "package": "@acme/volto-foo-add-on",
- "url": "git@github.com:acme/my-volto-add-on.git",
- "path": "src"
- }
-}
-```
-
-Then run:
-
-```shell
-make develop
-```
-
-Now the add-on is found in `src/addons/`.
-
-```{note}
-`package` property is optional, set it up only if your package has a scope.
-`src` is required if the content of your add-on is located in the `src`
-directory (but, as that is the convention recommended for all Volto add-on
-packages, you will always include it)
-```
-
-If you want to know more about `mrs-developer` config options, please refer to
-[its npm page](https://www.npmjs.com/package/mrs-developer).
-
-#### tsconfig.json / jsconfig.json
-
-`mrs-developer` automatically creates this file for you, but if you choose not
-to use mrs-developer, you'll have to add something like this to your
-`tsconfig.json` or `jsconfig.json` file in the Volto project root:
-
-```json
-{
- "compilerOptions": {
- "paths": {
- "acme-volto-foo-add-on": [
- "addons/acme-volto-foo-add-on/src"
- ]
- },
- "baseUrl": "src"
- }
-}
-```
-
-```{warning}
-Please note that both `paths` and `baseUrl` are required to match your
-project layout.
-```
-
-```{tip}
-You should use the `src` path inside your package and point the `main` key
-in `package.json` to the `index.js` file in `src/index.js`.
-```
-
-### Customizations
-
-add-on packages can include customization folders, just like the Volto projects.
-The customizations are resolved in the order: add-ons (as sorted in the `addons`
-key of your project's `package.json`) then the customizations in the Volto
-project, last one wins.
-
-```{tip}
-See the {ref}`advanced-customization-scenarios-label`
-section on how to enhance this pattern and how to include customizations
-inside add-ons.
-```
-
-### Providing add-on configuration
-
-The default export of your add-on main `index.js` file should be a function with
-the signature ``config => config``.
-That is, it should take the ``global`` configuration object and return it,
-possibly mutated or changed. So your main `index.js` will look like:
-
-```js
-export default function applyConfig(config) {
- 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;
-}
-```
-
-And the `package.json` file of your add-on:
-
-```json
-{
- "main": "src/index.js",
-}
-```
-
-```{warning}
-An add-on's default configuration method will always be loaded.
-```
-
-#### Multiple add-on configurations
-
-You can export additional configuration functions from your add-on's main
-`index.js`.
-
-```js
-import applyConfig, {loadOptionalBlocks,overrideSomeDefaultBlock} from './config';
-
-export { loadOptionalBlocks, overrideSomeDefaultBlock };
-export default applyConfig;
-```
-
-## Add third-party dependencies to your add-on
-
-If you're developing the add-on and you wish to add an external dependency, you'll have to switch your project to be a [Yarn Workspaces root](https://yarnpkg.com/features/workspaces).
-
-So you'll need to add, in your Volto project's `package.json`:
-
-```json
-"private": true,
-"workspaces": [],
-```
-
-Then populate the `workspaces` key with the path to your development add-ons:
-
-```json
-"workspaces": [
- "src/addons/my-volto-add-on"
-]
-```
-You'll have to manage the add-on dependencies via the workspace root (your Volto
-project). For example, to add a new dependency:
-
-```shell
-yarn workspace @plone/my-volto-add-on add some-third-party-package
-```
-
-You can run `yarn workspaces info` to see a list of workspaces defined.
-
-In case you want to add new dependencies to the Volto project, now you'll have
-to run the `yarn add` command with the `-W` switch:
-
-```shell
-yarn add -W some-dependency
-```
-
-## Extending Razzle from an add-on
-
-Just like you can extend Razzle's configuration from the project, you can do so
-with an add-on, as well. You should provide a `razzle.extend.js` file in your
-add-on root folder. An example of such file where the theme.config alias is
-changed, to enable a custom Semantic theme inside the add-on:
-
-
-```js
-const plugins = (defaultPlugins) => {
- return defaultPlugins;
-};
-const modify = (config, { target, dev }, webpack) => {
- const themeConfigPath = `${__dirname}/theme/theme.config`;
- config.resolve.alias['../../theme.config$'] = themeConfigPath;
-
- return config;
-};
-
-module.exports = {
- plugins,
- modify,
-};
-```
-
-## Extending Eslint configuration from an add-on
-
-Starting with Volto v16.4.0, you can also customize the Eslint configuration
-from an add-on. You should provide a `eslint.extend.js` file in your
-add-on root folder, which exports a `modify(defaultConfig)` function. For
-example, to host some code outside the regular `src/` folder of your add-on,
-this `eslint.extend.js` file is needed:
-
-```js
-const path = require('path');
-
-module.exports = {
- modify(defaultConfig) {
- const aliasMap = defaultConfig.settings['import/resolver'].alias.map;
- const addonPath = aliasMap.find(
- ([name]) => name === '@plone-collective/some-volto-add-on',
- )[1];
-
- const extraPath = path.resolve(`${addonPath}/../extra`);
- aliasMap.push(['@plone-collective/extra', extraPath]);
-
- return defaultConfig;
- },
-};
-```
-
-This would allow the `@plone-collective/some-volto-add-on` to host some code
-outside of its normal `src/` folder, let's say in the `extra` folder, and that
-code would be available under the `@plone-collective/extra` name. Note: this is
-taking care only of the Eslint integration. For proper language support, you'll
-still need to do it in the `razzle.extend.js` of your add-on.
-
-## add-on dependencies
-
-Sometimes your add-on depends on another add-on. You can declare add-on dependency
-in your add-on's `addons` key, just like you do in your project. By doing so,
-that other add-on's configuration loader is executed first, so you can depend on
-the configuration being already applied. Another benefit is that you'll have
-to declare only the "top level" add-on in your project, the dependencies will be
-discovered and automatically treated as Volto add-ons. For example, `volto-slate`
-depends on `volto-object-widget`'s configuration being already applied, so
-`volto-slate` can declare in its `package.json`:
-
-```json
-{
- "name": "volto-slate",
-
- "addons": ["@eeacms/volto-object-widget"]
-}
-```
-
-And of course, the dependency add-on can depend, on its turn, on other add-ons
-which will be loaded as well. Circular dependencies should be avoided.
-
-## Problems with untranspiled add-on dependencies
-
-When using external add-ons in your project, sometimes you will run into add-ons
-that are not securely transpiled or haven't been transpiled at all. In that case
-you might see an error like the following:
-
-```console
-Module parse failed: Unexpected token (10:41) in @react-leaflet/core/esm/path.js
-...
-const options = props.pathOptions ?? {};
-...
-```
-
-Babel automatically transpiles the code in your add-on, but `node_modules` are
-excluded from this process, so we need to include the add-on path in the list of
-modules to be transpiled. This can be accomplished by customizing the webpack
-configuration in the `razzle.config.js` file in your add-on. For example,
-suppose that we want to use react-leaflet, which has a known transpilation
-issue:
-
-```js
-const path = require('path');
-const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder');
-
-const babelLoaderFinder = makeLoaderFinder('babel-loader');
-
-const jsConfig = require('./jsconfig').compilerOptions;
-
-const pathsConfig = jsConfig.paths;
-let voltoPath = './node_modules/@plone/volto';
-Object.keys(pathsConfig).forEach((pkg) => {
- if (pkg === '@plone/volto') {
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
- }
-});
-
-const { modifyWebpackConfig, plugins } = require(`${voltoPath}/razzle.config`);
-
-const customModifyWebpackConfig = ({ env, webpackConfig, webpackObject, options }) => {
- const config = modifyWebpackConfig({
- env,
- webpackConfig,
- webpackObject,
- options,
- });
- const babelLoader = config.module.rules.find(babelLoaderFinder);
- const { include } = babelLoader;
- const corePath = path.join(
- path.dirname(require.resolve('@react-leaflet/core')),
- '..',
- );
- const esmPath = path.join(
- path.dirname(require.resolve('react-leaflet')),
- '..',
- );
-
- include.push(corePath);
- include.push(esmPath);
- return config;
-};
-
-module.exports = { modifyWebpackConfig: customModifyWebpackConfig, plugins };
-```
-
-First we need some setup to get the webpack configuration from Volto's configuration.
-Once we have that, we need to resolve the path to the desired add-ons and push it
-into the Babel loader include list. After this, the add-ons will load correctly.
-
-## Testing add-ons
-
-We should let jest know about our aliases and make them available to it to
-resolve them, so in `package.json`:
-
-```{code-block} json
-:emphasize-lines: 6
-
-"jest": {
- "moduleNameMapper": {
- "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
- "@package/(.*)$": "/src/$1",
- "@plone/some-volto-add-on/(.*)$": "/src/addons/@plone/some-volto-add-on/src/$1",
- "my-volto-add-on/(.*)$": "/src/addons/my-volto-add-on/src/$1",
- "~/(.*)$": "/src/$1"
- },
-```
-
-```{tip}
-We're in the process of moving the default scaffolding generators to
-provide a `jest.config.js` file in Volto, making this step unneeded.
-```
-
-You can use `yarn test src/addons/add-on-name` to run tests.
-
-## Code linting
-
-If you have generated your Volto project recently (after the summer of 2020),
-you don't have to do anything to have automatic integration with ESLint,
-otherwise make sure to upgrade your project's `.eslintrc` to the `.eslintrc.js`
-version, according to the {doc}`../upgrade-guide/index`.
diff --git a/docs/source/addons/theme.md b/docs/source/addons/theme.md
deleted file mode 100644
index 99251fd370..0000000000
--- a/docs/source/addons/theme.md
+++ /dev/null
@@ -1,230 +0,0 @@
----
-myst:
- html_meta:
- "description": "Create a theme add-on"
- "property=og:description": "Create a theme add-on"
- "property=og:title": "Create a theme add-on"
- "keywords": "Volto, Plone, Semantic UI, CSS, Volto theme"
----
-
-# Create a Volto theme add-on
-
-We can create a Volto Add-on that acts as a Volto theme Add-on, so we can detach it from the project files.
-The advantage is that you convert the project Volto theme in a pluggable one, so you can deploy the same theme in different projects.
-You can even have themes depending on conditions that you could inject on build time.
-This is the purpose of `volto.config.js`, the ability of declaring `add-ons` and the active `theme` programmatically. See {ref}`volto-config-js` for more information.
-For convenience, it can also be set via a `THEME` environment variable.
-
-1. Add a `theme` key in your `volto.config.js` file in the root of your project:
-
-```js
-module.exports = {
- addons: [],
- theme: 'volto-my-theme'
-};
-```
-
-or add a key in your `package.json` project:
-
-```json
-"theme": "volto-my-theme"
-```
-
-or via a `THEME` variable:
-
-```shell
-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:
-
-```less
-/*******************************
- Theme Selection
-*******************************/
-
-/* To override a theme for an individual element specify theme name below */
-
-/* Global */
-@site : 'pastanaga';
-@reset : 'pastanaga';
-
-/* Elements */
-@button : 'pastanaga';
-@container : 'pastanaga';
-@divider : 'pastanaga';
-@flag : 'pastanaga';
-@header : 'pastanaga';
-@icon : 'pastanaga';
-@image : 'pastanaga';
-@input : 'pastanaga';
-@label : 'pastanaga';
-@list : 'pastanaga';
-@loader : 'pastanaga';
-@placeholder : 'pastanaga';
-@rail : 'pastanaga';
-@reveal : 'pastanaga';
-@segment : 'pastanaga';
-@step : 'pastanaga';
-
-/* Collections */
-@breadcrumb : 'pastanaga';
-@form : 'pastanaga';
-@grid : 'pastanaga';
-@menu : 'pastanaga';
-@message : 'pastanaga';
-@table : 'pastanaga';
-
-/* Modules */
-@accordion : 'pastanaga';
-@checkbox : 'pastanaga';
-@dimmer : 'pastanaga';
-@dropdown : 'pastanaga';
-@embed : 'pastanaga';
-@modal : 'pastanaga';
-@nag : 'pastanaga';
-@popup : 'pastanaga';
-@progress : 'pastanaga';
-@rating : 'pastanaga';
-@search : 'pastanaga';
-@shape : 'pastanaga';
-@sidebar : 'pastanaga';
-@sticky : 'pastanaga';
-@tab : 'pastanaga';
-@transition : 'pastanaga';
-
-/* Views */
-@ad : 'pastanaga';
-@card : 'pastanaga';
-@comment : 'pastanaga';
-@feed : 'pastanaga';
-@item : 'pastanaga';
-@statistic : 'pastanaga';
-
-/* Extras */
-@main : 'pastanaga';
-@custom : 'pastanaga';
-
-/*******************************
- Folders
-*******************************/
-
-/* Path to theme packages */
-@themesFolder : '~volto-themes';
-
-/* Path to site override folder */
-@siteFolder : "/theme";
-
-/*******************************
- Import Theme
-*******************************/
-
-@import (multiple) "~semantic-ui-less/theme.less";
-@fontPath : "~volto-themes/@{theme}/assets/fonts";
-
-.loadAddonOverrides() {
- @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
-}
-
-/* End Config */
-```
-
-3. Declare the theme as an add-on by adding its name to the value for the `addons` key in either `volto.config.js` or `package.json` of your project.
-4. After starting Volto, the theme should be active.
- Now you can add overrides to the default theme in `src/theme`, same as you would in a project.
-5. Now you can safely delete your project's `theme` folder, since the one in the add-on will take precedence and a project can only have one active theme at a time.
-
-## Using your own theming escape hatch
-
-Volto theming uses Semantic UI theming capabilities to define and extend a theme for your site.
-However, while maintaining and playing well with the Semantic UI Volto base, using a traditional CSS approach can be done using the LESS preprocessor-based `extras` escape hatch.
-
-At the same time, one can either discard or complement the extras escape hatch and add your own, by customizing the `theme.js` module in Volto.
-
-```js
-import 'semantic-ui-less/semantic.less';
-import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
-
-// You can add more entry points for theming
-import '@kitconcept/volto-light-theme/theme/main.scss';
-```
-
-Customizing it is a special use case in Volto: add a `./@root/theme.js` file structure in your `customizations` folder in your add-on or project.
-
-You may want to do this to create a complete new theming experience adapted to your way of doing things that do not match the current Volto theming experience.
-For example, if you want to use another preprocessor in the theme, like SCSS.
-Maybe because your client forces you to have another entirely base of pre-made components based on another library other than Semantic UI:
-See {ref}`volto-custom-theming-strategy` for an example of a custom theme escape hatch.
-
-While building your own escape hatch for theming, you can use the preprocessor of your choice (in the example, SCSS) while maintaining the "base" Volto theme, but customizing it using the resultant CSS.
-
-You can see an example of such a theme in: https://github.com/kitconcept/volto-light-theme
-
-## Modify a custom theme from another add-on
-
-Sometimes you have a custom theme that you want to reuse through all your projects, but with some differences, maintaining the base.
-Usually, the only option would be to use an add-on that adds more CSS to the base theme, using imports that will load after the theme.
-However, there is a problem with this approach.
-You cannot use existing theme variables, including breakpoints, on these new styles.
-Similarly, it gets somewhat detached from the normal flow of the loaded theme.
-The same applies for add-ons, as they are detached from the current theme.
-One could use a Semantic UI approach for making this work, but it's Semantic UI bound.
-
-```{warning}
-This is only possible when using your own escape hatch, and works only with SCSS-based themes, and not with Semantic UI themes, since it enables a couple of entry points that only support SCSS files.
-For an example of how it could be used, see: https://github.com/kitconcept/volto-light-theme
-```
-
-If your custom escape hatch defines a custom theme using SCSS, you can take advantage of this feature.
-Although not limited to this, it would be possible to extend this feature to add more entry points, using another preprocessor or theming approach.
-
-This feature enables two entry points: variables and main.
-From your add-on code, you can extend an existing theme by creating a file corresponding to each entry point:
-
-* `./src/theme/_variables.scss`
-* `./src/theme/_main.scss`
-
-### Variables (`addonsThemeCustomizationsVariables`)
-
-Use this entry point file to modify the original variables of the current loaded theme by adding the entry point before the theme variable definitions.
-In the theme, it should be imported as shown below:
-
-```scss hl_lines="2"
-@import 'addonsThemeCustomizationsVariables';
-@import 'variables';
-@import 'typography';
-@import 'utils';
-@import 'layout';
-```
-
-```{warning}
-Following SCSS best practices, your theme variables should be "overridable" using the `!default` flag.
-This assigns a value to a variable _only_ if that variable isn't defined or its value is [`null`](https://sass-lang.com/documentation/values/null).
-Otherwise, the existing value will be used.
-For more information, see https://sass-lang.com/documentation/variables#default-values
-```
-
-Volto will not only load your add-on entry point files, but it will also detect all the add-ons that have these entry point files and import them grouped under a single file.
-It will also automatically add an `addonsThemeCustomizationsVariables` alias that can be referenced from the theme as shown above.
-
-### Main (`addonsThemeCustomizationsMain`)
-
-This entry point is intended to add your own style definitions, complementing those in the theme.
-You should add it after all the CSS of your theme:
-
-```scss hl_lines="6"
-@import 'blocks/search';
-@import 'blocks/listing';
-
-@import 'temp';
-
-@import 'addonsThemeCustomizationsMain';
-
-/* No CSS beyond this point */
-```
-
-Volto will also detect all the add-ons that have these entry point files, and import them grouped under a single file, and will automatically add an `addonsThemeCustomizationsMain` alias that can be referenced from the theme as shown above.
-
-```{note}
-It will only work in combination with the theme declaration in `volto.config.js` or in `package.json`.
-```
diff --git a/docs/source/conceptual-guides/add-ons.md b/docs/source/conceptual-guides/add-ons.md
new file mode 100644
index 0000000000..da1d986f5d
--- /dev/null
+++ b/docs/source/conceptual-guides/add-ons.md
@@ -0,0 +1,234 @@
+---
+myst:
+ html_meta:
+ "description": "Volto add-ons extend the core functionality of the Plone CMS user interface."
+ "property=og:description": "Volto add-ons extend the core functionality of the Plone CMS user interface."
+ "property=og:title": "Volto add-ons"
+ "keywords": "Volto, add-on, extensions, user interface, frontend, Plone"
+---
+
+# Volto add-on concepts
+
+This guide describes Volto add-on concepts.
+
+
+## What is a Volto add-on?
+
+Volto add-ons are just CommonJS or ESM packages.
+Their main purpose is to encapsulate logic, configuration, components, customizations, and even themes in a reusable way.
+
+Suppose you want to have more control and flexibility beyond the plain Volto project when building a site.
+You can build a Volto {term}`add-on` and make it available as a generic JavaScript package.
+Then you can reuse and include it in any Volto project.
+
+An add-on can configure or provide any of the following aspects of Volto.
+
+- Provide additional views and blocks.
+- Override or extend Volto's built-in views, blocks, and settings.
+- Shadow or customize Volto's, or another add-on's, modules.
+- Register custom routes.
+- Provide custom {term}`Redux` actions and reducers.
+- Register custom Express middleware for Volto's server process.
+- Tweak Volto's webpack configuration, loading custom Razzle and webpack plugins.
+- Provide even a custom theme.
+
+
+## Volto registry
+
+Volto has a built-in extensible and pluggable system to enhance the Plone CMS user interface.
+It helps developers extend Volto in a pluggable way through {term}`add-on`s.
+This system is implemented through Volto's registry.
+
+For Volto 17 and earlier, the registry was integrated into Volto core.
+
+From Volto 18 onward, the Volto registry is in its own package [`@plone/registry`](https://plone-registry.readthedocs.io/).
+
+
+## Add-on configuration pipeline
+
+A Volto app's configuration is determined through a pipeline starting with Volto's default configuration, then each of your app's add-ons' configuration.
+In Volto 17 and earlier, you can also use project configuration at the end of the pipeline after any add-ons.
+
+```{deprecated} Volto 18.0.0
+The project configuration approach is deprecated and will be removed in Volto 19.
+```
+
+Add-ons are applied in the order they are declared in the `addons` key of {file}`package.json` or programmatically via a provided configuration file.
+Add-ons can override configuration coming from other add-ons, providing a hierarchy of configuration stacks.
+
+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.
+
+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.
+
+Volto will automatically provide aliases for your package.
+Once you've released it, you don't need to change import paths, since you can use the final ones from the very beginning.
+This means that you can use imports, such as `import { Something } from '@plone/my-volto-add-on'` without any extra configuration.
+
+```{note}
+By declaring a JavaScript package as a Volto add-on, Volto provides several integration features.
+These include {doc}`JavaScript language features <../contributing/language-features>` with transpilation by Babel, whole-process customization via {file}`razzle.extend.js`, and integration with Volto's {term}`configuration registry`.
+```
+
+
+### Use cases
+
+In practice with the configuration pipeline, for example, you can create a "policy" core add-on for your project, and use another add-on for your project's theme.
+This way the project itself renders as a simple boilerplate, which you can extend or rebuild at any time.
+
+You can also reuse add-ons across projects, and adjust them using other add-ons, depending on the other projects' requirements.
+
+
+% TODO: Should this section be moved to a how-to guide?
+## Add-on configuration
+
+The default export of your add-on's main {file}`index.js` file should be a function with the signature `config => config`.
+That is, it should take the `global` configuration object and return it, possibly mutated or changed.
+An {file}`index.js` file should contain the following code.
+
+```js
+export default function applyConfig(config) {
+ 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,
+ security: {
+ addPermission: [],
+ view: [],
+ },
+ };
+ return config;
+}
+```
+
+And the {file}`package.json` file of your add-on should contain the following code.
+
+```json
+{
+ "main": "src/index.js",
+}
+```
+
+In effect, Volto does the equivalent of the following pseudocode:
+
+```js
+import installMyVoltoAddon from 'my-volto-addon'
+
+// ... in the configuration registry setup step:
+const configRegistry = installMyVoltoAddon(defaultRegistry);
+```
+
+The Volto add-on needs to export a default function that receives the Volto configuration registry.
+Then it is free to change the registry.
+Finally, it must return that registry.
+
+Volto will execute all the add-on configuration functions in a chain to compute the final configuration registry.
+
+```{note}
+An add-on's default configuration method will always be loaded.
+```
+
+```{seealso}
+See [@kitconcept/volto-button-block](https://github.com/kitconcept/volto-button-block) as an example.
+```
+
+
+### Provide optional add-on configurations
+
+You can export additional configuration functions from your add-on's main {file}`index.js`.
+
+```js
+import applyConfig, {loadOptionalBlocks,overrideSomeDefaultBlock} from './config';
+
+export { loadOptionalBlocks, overrideSomeDefaultBlock };
+export default applyConfig;
+```
+
+```{seealso}
+{doc}`../development/add-ons/load-add-on-configuration`
+```
+
+
+% TODO: Should this section be moved to a how-to guide?
+### Define your add-ons programmatically
+
+The `addons` key in the {file}`package.json` file alone might not be flexible enough in complex scenarios.
+You can programmatically load your add-ons outside your {file}`package.json` file using a {file}`volto.config.js` file with the following content.
+
+```js
+module.exports = {
+ addons: ['@eeacms/volto-accordion-block']
+}
+```
+
+This creates an "escape hatch", where you can use logic and environment conditions to define the add-ons to load in the current project, as in the next example.
+The add-ons that you define here will be added to the existing ones in {file}`package.json`.
+
+```js
+let addons = [];
+if (process.env.MY_SPECIAL_ENV_VAR) { // Does not have to be RAZZLE_
+ addons = ['volto-my-awesome-special-add-on'];
+}
+
+if (process.env.MARKER_FOR_MY_SECRET_PROJECT) { // Does not have to be RAZZLE_
+ addons = [
+ '@kitconcept/volto-heading-block',
+ '@kitconcept/volto-slider-block',
+ 'volto-my-secret-project-add-on',
+ ];
+}
+
+module.exports = {
+ addons: addons,
+};
+```
+
+```{important}
+You must add the `addons` key with the value of your add-on package's name wherever you configure it.
+In Plone terminology, it is like including a Python egg in the `zcml` section of `zc.buildout`.
+```
+
+```{seealso}
+{doc}`../configuration/volto-config-js`
+```
+
+
+## Add-on dependencies
+
+Add-ons can depend on any other JavaScript package, including other Volto add-ons.
+To do this, specify the name of your Volto add-on dependency in your `dependencies` key of your {file}`package.json` file.
+Then create a new `addons` key in the {file}`package.json` file of your add-on, where you specify the extra Volto add-on dependency.
+By doing this, the add-ons can "chain load" one another.
+
+```json
+{
+ "name": "volto-slate",
+
+ "addons": ["@eeacms/volto-object-widget"]
+}
+```
+
+
+## Publish an add-on
+
+Volto add-ons should not be transpiled.
+They should be released as "source" packages.
+
+Their primary entry point (the `main` key of their {file}`package.json`) must point to a module that exports a default function, which acts as a default configuration loader for that package.
+
+You can publish an add-on to an npm registry or to a remote repository host such as GitHub or GitLab, like any other package.
+If you publish your add-on to the [npm Registry](https://www.npmjs.com/) or make your repository public, as a bonus, you will benefit from collaborating on open source software.
+
+
+% Where does this go?
+By using [`mrs-developer`](https://github.com/collective/mrs-developer), it's possible to have a workflow similar to `zc.buildout`'s `mr.developer`, where you can "checkout" an add-on for development.
+[Eric Brehault](https://github.com/ebrehault) ported this amazing Python tool.
diff --git a/docs/source/conceptual-guides/index.md b/docs/source/conceptual-guides/index.md
new file mode 100644
index 0000000000..a81d8cd024
--- /dev/null
+++ b/docs/source/conceptual-guides/index.md
@@ -0,0 +1,19 @@
+---
+myst:
+ html_meta:
+ "description": "Volto conceptual guides"
+ "property=og:description": "Volto conceptual guides"
+ "property=og:title": "Conceptual guides"
+ "keywords": "Volto, user interface, frontend, Plone, conceptual guides"
+---
+
+# Conceptual guides
+
+This section of the documentation contains conceptual guides for various aspects of Volto.
+
+```{toctree}
+:hidden:
+:maxdepth: 2
+
+add-ons
+```
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 8619c2a428..4c010d58b4 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -63,6 +63,7 @@
"sphinx_copybutton",
"sphinx_examples",
"sphinxcontrib.video",
+ "sphinxcontrib.youtube",
"sphinxext.opengraph",
]
@@ -89,11 +90,8 @@
# Ignore github.com pages with anchors
r"https://github.com/.*#.*",
# Ignore other specific anchors
- # r"https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi", # TODO retest with latest Sphinx when upgrading theme. chromewebstore recently changed its URL and has "too many redirects".
- # r"https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd", # TODO retest with latest Sphinx when upgrading theme. chromewebstore recently changed its URL and has "too many redirects".
r"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors#Identifying_the_issue",
r"https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0",
- # r"https://stackoverflow.com", # volto and documentation # TODO retest with latest Sphinx.
]
linkcheck_anchors = True
linkcheck_timeout = 5
@@ -117,7 +115,7 @@
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [
"spelling_wordlist.txt",
- "contributing/branch-policy.md",
+ "_inc/*",
]
suppress_warnings = [
@@ -179,7 +177,7 @@
"path_to_docs": "docs",
"repository_branch": "main",
"repository_url": "https://github.com/plone/volto",
- "search_bar_text": "Search", # TODO: Confirm usage of search_bar_text in plone-sphinx-theme
+ "search_bar_text": "Search",
"use_edit_page_button": True,
"use_issues_button": True,
"use_repository_button": True,
@@ -187,7 +185,7 @@
# Announce that we have an opensearch plugin
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch
-html_use_opensearch = "https://6.docs.plone.org" # TODO: Confirm usage of opensearch in theme
+html_use_opensearch = "https://6.docs.plone.org"
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
diff --git a/docs/source/configuration/how-to.md b/docs/source/configuration/how-to.md
index a1023c1837..b8f8da6147 100644
--- a/docs/source/configuration/how-to.md
+++ b/docs/source/configuration/how-to.md
@@ -35,7 +35,8 @@ Both use the same method, using a function as the default export. This function
add-ons, it must be provided in the main `index.js` module of the add-on. For project's
it must be provided in the `src/config.js` module of the project.
-See the {doc}`../addons/index` section for extended information on how to work with add-ons.
+See the {doc}`../conceptual-guides/add-ons` and {doc}`../development/add-ons/index` sections for extended information on how to work with add-ons.
+
## Extending configuration in a project
diff --git a/docs/source/configuration/volto-slate/index.md b/docs/source/configuration/volto-slate/index.md
index d8f51c4726..475cc3653c 100644
--- a/docs/source/configuration/volto-slate/index.md
+++ b/docs/source/configuration/volto-slate/index.md
@@ -13,7 +13,7 @@ myst:
`volto-slate` is an interactive default text editor for Volto, developed on top of {term}`Slate` and integrated into the core system.
It offers enhanced WYSIWYG functionality and behavior.
-See a [brief elevator pitch for `volto-slate`](https://www.youtube.com/watch?v=SOz-rk5e4_w).
+See a [brief elevator pitch for `volto-slate`](https://www.youtube-nocookie.com/embed/SOz-rk5e4_w?privacy_mode=1).
We believe that Volto's rich text form editor (the Volto Composite Page editor) needs strong integration between the rich text capabilities and the rest of the Volto blocks.
Some examples of the kind of strong integration we have in mind:
diff --git a/docs/source/contributing/developing-core.md b/docs/source/contributing/developing-core.md
index 0e961feb81..f0ffb5462e 100644
--- a/docs/source/contributing/developing-core.md
+++ b/docs/source/contributing/developing-core.md
@@ -48,7 +48,8 @@ Volto has the following folder structure.
├─ apps/
│ ├─ plone
│ ├─ nextjs
-│ └─ remix
+│ ├─ remix
+│ └─ rr7
├─ packages/
│ ├─ volto
│ ├─ client
@@ -75,7 +76,7 @@ Volto has the following folder structure.
To set up a Volto core development environment, your system must satisfy the following prerequisites.
-```{include} ./install-operating-system.md
+```{include} ../_inc/_install-operating-system.md
```
- {term}`nvm`
@@ -93,7 +94,7 @@ When developing a project using Plone, Yarn or other package managers may be use
### nvm
-```{include} ./install-nvm.md
+```{include} ../_inc/_install-nvm.md
```
@@ -102,7 +103,7 @@ When developing a project using Plone, Yarn or other package managers may be use
We recommend that you install Node.js using nvm.
Alternatively you can install Node.js using Homebrew or other package installer.
-```{include} ./install-nodejs.md
+```{include} ../_inc/_install-nodejs.md
```
@@ -135,19 +136,19 @@ Compare the output to the [latest pnpm release number](https://www.npmjs.com/pac
### Make
-```{include} ./install-make.md
+```{include} ../_inc/_install-make.md
```
### Docker
-```{include} ./install-docker.md
+```{include} ../_inc/_install-docker.md
```
### Git
-```{include} ../contributing/install-git.md
+```{include} ../_inc/_install-git.md
```
@@ -165,7 +166,7 @@ cd volto
Install the frontend dependencies.
```shell
-pnpm install
+make install
```
@@ -399,6 +400,16 @@ You can try it out using the following command.
pnpm --filter plone-remix dev
```
+### React Router 7
+
+This frontend is a proof of concept using React Router 7 with Plone.
+
+You can try it out using the following command.
+
+```shell
+pnpm --filter plone-rr7 dev
+```
+
### Vite build (client only)
This frontend is a proof of concept using a custom client build based in Vite with Plone.
diff --git a/docs/source/contributing/index.md b/docs/source/contributing/index.md
index cc6b8d03b2..46f2240f2b 100644
--- a/docs/source/contributing/index.md
+++ b/docs/source/contributing/index.md
@@ -44,7 +44,7 @@ The Volto Team reviews pull requests only from people with a GitHub account who
## Branch policy
-```{include} ./branch-policy.md
+```{include} ../_inc/_branch-policy.md
```
diff --git a/docs/source/contributing/language-features.md b/docs/source/contributing/language-features.md
index 258582172a..979fed7793 100644
--- a/docs/source/contributing/language-features.md
+++ b/docs/source/contributing/language-features.md
@@ -1,73 +1,63 @@
---
myst:
html_meta:
- "description": "Volto is developed using Babel to transpile modern JavaScript to JavaScript that browsers are able to understand and execute."
- "property=og:description": "Volto is developed using Babel to transpile modern JavaScript to JavaScript that browsers are able to understand and execute."
- "property=og:title": "Language features and conventions"
- "keywords": "Volto, Plone, frontend, React, Babel, translations, language, internationalization, i18n, localization, transpilation"
+ "description": "Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language."
+ "property=og:description": "Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language."
+ "property=og:title": "JavaScript language features and browser support"
+ "keywords": "Volto, Plone, frontend, React, Babel, JavaScript, transpilation"
---
+% Mixture of conceptual guide and how-to guide
+# JavaScript language features and browser support
-# Language features and conventions
+Volto uses several tools and follows conventions that provide features and browser support for the JavaScript language.
+
+% Conceptual guide
## Babel
-Volto is developed using Babel to transpile modern JavaScript to JavaScript that
-browsers are able to understand and execute.
+Babel transpiles {term}`ECMAScript` code, including React and JSX, into a backwards compatible version of JavaScript in current and older browsers or environments.
-Ecma International's TC39 (https://tc39.es/) is a group of JavaScript developers,
-implementers, academics, and more, collaborating with the community to maintain and
-evolve the definition of JavaScript. They stablished a process
-(https://tc39.es/process-document/) where the proposals are discussed, developed, and
-eventually approved (or dropped). The process has five stages (0 to 4) where reaching
-the stage 4 means the proposal is accepted and it becomes part of the JavaScript
-specification.
+Babel provides features and syntax that you can use in code when you develop on Volto.
+These features derive from the proposals that the {term}`TC39` produces.
-Babel enables a series of features and syntax that the developer can use in code to
-develop Volto on. These features are the proposals the TC39 is working on in the
-different stages of evolution.
+Volto uses `babel-razzle-preset`, which in turns uses `@babel/preset-env`, which together enable the use of all [TC39 finished proposals](https://github.com/tc39/proposals/blob/HEAD/finished-proposals.md#finished-proposals).
-Volto uses `babel-razzle-preset` which in turns uses `@babel/preset-env` which enables
-the use of all TC39 proposals currently in TC39's stage 4
-(https://github.com/tc39/proposals/blob/HEAD/finished-proposals.md#finished-proposals).
-### Browser compatibility
+% How-to guide
+## Browser compatibility
-Babel preset-env uses `browserlist` which gives the ability to micromanage the
-transformations needed by the current project depending of the browser support you are
-currently targeting.
+`@babel/preset-env` uses `browserslist`, which you can use to manage the transformations needed to target specific browser support in your project.
+This reduces the size of bundles, as Babel will apply only the required transforms that your target environment needs.
-By doing this, it enables the bundles to be smaller, as the resulting code does not need to
-support old browsers (thus, transform your code to ES5 compatible code) as Babel will
-apply only the required transforms that your target environments need. For more
-information: https://babeljs.io/docs/babel-preset-env#browserslist-integration
+```{seealso}
+https://babeljs.io/docs/babel-preset-env#browserslist-integration
+```
-Volto project generators use this browserlist by default (you can find it in your local `package.json`):
+Volto project generators use `browserslist` queries by default, which is in your local {file}`package.json`.
+You can adjust this file according to the environments you want to target.
```json
- "browserslist": [
- ">1%",
- "last 4 versions",
- "Firefox ESR",
- "not ie 11",
- "not dead"
- ],
+"browserslist": [
+ ">1%",
+ "last 4 versions",
+ "Firefox ESR",
+ "not dead"
+],
+```
+
+```{seealso}
+For usage and syntax, see the `browserslist` documentation of [Queries](https://github.com/browserslist/browserslist#queries).
```
-which you can adjust depending on the environments you are targeting in your local
-`package.json` file. You can find more information about how the queries in `broserlist`
-works in: https://github.com/browserslist/browserslist#queries
-### Support to deprecated browsers
+% How-to guide
+## Support of deprecated browsers
```{warning}
-Volto does not support deprecated browsers from its vendor (eg. IE11).
+Volto does not support deprecated browsers, such as Internet Explorer 11.
```
-If you still need to support deprecated browsers, you should use `browserslist` in your
-project to enable the required transforms for the target deprecated environments you
-have to support.
+If you still need to support deprecated browsers, you should use `browserslist` in your project to enable the required transforms for the target deprecated environments you must support.
-However, Volto (or its dependencies) might not be compatible with old browsers anyways,
-and you might need to provide some other workarounds to make the build work (and the
-deprecated browser not crash). You can refer to {doc}`this (outdated)
-document <../development/ie11compat>` in order to get some hints on how to do it.
+However, Volto or its dependencies might not be compatible with old browsers.
+You might need to create some workarounds to make the build work, and the deprecated browser not crash.
diff --git a/docs/source/contributing/version-policy.md b/docs/source/contributing/version-policy.md
index 19bac961b8..fe42d3d1d5 100644
--- a/docs/source/contributing/version-policy.md
+++ b/docs/source/contributing/version-policy.md
@@ -92,7 +92,7 @@ We do not guarantee that outdated browsers, such as Internet Explorer 11, are su
## Branch policy
-```{include} ./branch-policy.md
+```{include} ../_inc/_branch-policy.md
```
diff --git a/docs/source/development/add-ons/best-practices.md b/docs/source/development/add-ons/best-practices.md
new file mode 100644
index 0000000000..c023301634
--- /dev/null
+++ b/docs/source/development/add-ons/best-practices.md
@@ -0,0 +1,82 @@
+---
+myst:
+ html_meta:
+ "description": "Best practices for developing Volto add-ons"
+ "property=og:description": "Best practices for developing Volto add-ons"
+ "property=og:title": "Best practices for add-ons"
+ "keywords": "Volto, Plone, frontend, React, best, practices, add-ons"
+---
+
+# Best practices for add-ons
+
+This document describes the best practices when you develop your add-on.
+
+
+## Integrate your add-on with Volto's add-on framework
+
+Just like Plone add-ons provide some features by default, Volto add-ons should register some features by default.
+For example, if your add-on provides widgets, then register the most basic configuration of that widget with a name that it uses.
+
+For more complicated cases, see if you can structure your code to use the `settings` {term}`configuration registry`, or stash your configuration in your block registration.
+
+Let's say you're building a color picker widget.
+You might want to provide a palette of colors from which to choose.
+Your widget should integrate with a default `settings.colorWidgetPalette`, which would be a list of colors.
+
+You should also provide a widget factory, so it can be used to create multiple instances of that color widget with custom color palettes.
+
+
+## Provide additional configuration
+
+An add-on can ship with multiple {term}`Volto configuration loader`s.
+This makes it possible to provide multiple configuration methods for specific demonstration purposes.
+Alternatively you could ship your add-on with a default "shallow" integration, then provide another separate configuration loader for a deeper integration.
+
+
+## Avoid shadowing core Volto files
+
+This rule is meant to be broken.
+If you need to customize a specific file in Volto core and you have multiple projects, it may be better to create an add-on that holds that customized file.
+Doing so creates a single place to maintain that "file fork".
+Otherwise, it's best to avoid shipping generic add-ons with Volto core customizations.
+
+If you customize core Volto files, you should warn consumers of your add-on in its description.
+
+If your use case is generic enough, [file a feature request](https://github.com/plone/volto/issues/new?assignees=&labels=04+type%3A+enhancement&projects=&template=feature_request.md&title=) to discuss with the Volto Team whether your customization should be included directly in core.
+
+
+## Documentation
+
+"If it ain't documented, it's broken."
+
+At least create a README with a brief description and a screenshot or video of what your add-on does.
+
+Ideally, the README should include requirements or compatability with various versions of Volto and Plone, and installation and configuration instructions.
+
+
+## Test the add-on
+
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+Cookieplone provides a self-bootstrapping and testing framework in Volto 18.
+See {doc}`test-add-ons-18`.
+
+Previously in Volto 17 and early alpha versions of Volto 18, it was not easy to ship an add-on with a self-bootstrapping and testing framework.
+However, for these older versions of Volto you can create a separate minimal Volto project that can hold the Cypress integration tests and trigger the CI tests.
+
+
+## Use appropriate npm Registry tags
+
+If you release your add-on to the [npm Registry](https://www.npmjs.com/), consider adding the following tags, next to your add-on-specific tags.
+
+- `volto-addon`
+- `volto`
+- `plone`
+- `react`
+
+
+## Add to `collective/awesome-volto`
+
+Consider adding your add-on to the [`collective/awesome-volto`](https://github.com/collective/awesome-volto) add-ons list.
+This list provides visibility to your add-on, as well as further solidifies Volto's position in the Plone community.
diff --git a/docs/source/development/add-ons/create-an-add-on-17.md b/docs/source/development/add-ons/create-an-add-on-17.md
new file mode 100644
index 0000000000..6034a5ef57
--- /dev/null
+++ b/docs/source/development/add-ons/create-an-add-on-17.md
@@ -0,0 +1,20 @@
+---
+myst:
+ html_meta:
+ "description": "How to create an add-on for Volto 17"
+ "property=og:description": "How to create an add-on for Volto 17"
+ "property=og:title": "Create an add-on for Volto 17"
+ "keywords": "add-on, Volto, create"
+---
+
+# Create an add-on for Volto 17
+
+Volto add-on packages are just CommonJS packages.
+The only requirement is that they point the `main` key of their {file}`package.json` to a module that exports as a default function, acting as a {term}`Volto configuration loader`.
+
+You can use Plone's Yeoman-based generator [`generator-volto`](https://github.com/plone/generator-volto) to create a Volto add-on.
+
+```shell
+npm install -g @plone/generator-volto
+yo @plone/volto:addon [] [options]
+```
diff --git a/docs/source/development/add-ons/create-an-add-on-18.md b/docs/source/development/add-ons/create-an-add-on-18.md
new file mode 100644
index 0000000000..15ffca9d6a
--- /dev/null
+++ b/docs/source/development/add-ons/create-an-add-on-18.md
@@ -0,0 +1,200 @@
+---
+myst:
+ html_meta:
+ "description": "How to create an add-on for Volto 18"
+ "property=og:description": "How to create an add-on for Volto 18"
+ "property=og:title": "Create an add-on for Volto 18"
+ "keywords": "add-on, Volto, create, development"
+---
+
+# Create an add-on for Volto 18
+
+This chapter describes how you can create an add-on using Volto 18 or later for the Plone user interface, while having full control over its development and deployment.
+
+```{versionadded} Volto 18.0.0-alpha.43
+{term}`Cookieplone` is now the method to create a Plone add-on with Volto version 18.0.0-alpha.43 and above.
+```
+
+## System requirements
+
+Follow the section {ref}`plone:create-project-cookieplone-system-requirements` to set up your system.
+
+
+## Generate the add-on project
+
+To develop an add-on for only the frontend, then run the following command to generate your add-on project using the `frontend_addon` Cookieplone template.
+To develop add-ons for each the frontend and backend that work together, then instead use the Cookieplone template `project` in the command.
+See {doc}`plone:install/create-project-cookieplone` for details of the latter scenario.
+The following output assumes the former scenario.
+
+```shell
+pipx run cookieplone frontend_addon
+```
+
+```console
+❯ pipx run cookieplone frontend_addon
+⚠️ cookieplone is already on your PATH and installed at
+ /Users//.local/bin/cookieplone. Downloading and running anyway.
+╭──────────────────────────────── cookieplone ─────────────────────────────────╮
+│ │
+│ .xxxxxxxxxxxxxx. │
+│ ;xxxxxxxxxxxxxxxxxxxxxx; │
+│ ;xxxxxxxxxxxxxxxxxxxxxxxxxxxx; │
+│ xxxxxxxxxx xxxxxxxxxx │
+│ xxxxxxxx. .xxxxxxxx │
+│ xxxxxxx xxxxxxx: xxxxxxx │
+│ :xxxxxx xxxxxxxxxx xxxxxx: │
+│ :xxxxx+ xxxxxxxxxxx +xxxxx: │
+│ .xxxxx. :xxxxxxxxxx .xxxxx. │
+│ xxxxx+ ;xxxxxxxx +xxxxx │
+│ xxxxx +xx. xxxxx. │
+│ xxxxx: .xxxxxxxx :xxxxx │
+│ xxxxx .xxxxxxxxxx xxxxx │
+│ xxxxx xxxxxxxxxxx xxxxx │
+│ xxxxx .xxxxxxxxxx xxxxx │
+│ xxxxx: .xxxxxxxx :xxxxx │
+│ .xxxxx ;xx. ... xxxxx. │
+│ xxxxx+ :xxxxxxxx +xxxxx │
+│ .xxxxx. :xxxxxxxxxx .xxxxx. │
+│ :xxxxx+ xxxxxxxxxxx ;xxxxx: │
+│ :xxxxxx xxxxxxxxxx xxxxxx: │
+│ xxxxxxx xxxxxxx; xxxxxxx │
+│ xxxxxxxx. .xxxxxxxx │
+│ xxxxxxxxxx xxxxxxxxxx │
+│ ;xxxxxxxxxxxxxxxxxxxxxxxxxxxx+ │
+│ ;xxxxxxxxxxxxxxxxxxxxxx; │
+│ .xxxxxxxxxxxxxx. │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+You've downloaded /Users//.cookiecutters/cookieplone-templates before.
+Is it okay to delete and re-download it? [y/n] (y):
+╭─────────────────────────── Volto Addon Generator ────────────────────────────╮
+│ │
+│ Creating a new Volto Addon │
+│ │
+│ Sanity check results: │
+│ │
+│ │
+│ - Node: ✓ │
+│ - git: ✓ │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+ [1/8] Add-on Title (Volto Add-on):
+ [2/8] Add-on (Short name of the addon) (volto-addon):
+ [3/8] A short description of your addon (A new add-on for Volto):
+ [4/8] Author (Plone Community):
+ [5/8] Author E-mail (collective@plone.org):
+ [6/8] GitHub Username or Organization (collective):
+ [7/8] Package name on NPM (volto-addon):
+ [8/8] Volto version (18.0.0-alpha.46):
+╭────────────────────────── Volto Add-on generation ───────────────────────────╮
+│ │
+│ Summary: │
+│ │
+│ - Volto version: 18.0.0-alpha.46 │
+│ - Output folder: /Users//Development/plone/volto-addon │
+│ │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+╭─────────────────────── 🎉 New addon was generated 🎉 ────────────────────────╮
+│ │
+│ volto-addon │
+│ │
+│ Now, enter the generated directory and finish the install: │
+│ │
+│ cd volto-addon │
+│ make install │
+│ │
+│ start coding, and push to your organization. │
+│ │
+│ Sorry for the convenience, │
+│ The Plone Community. │
+│ │
+│ https://plone.org/ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+```
+
+Cookieplone creates a folder with the name of the add-on, in this example, `volto-addon`.
+
+Change your current working directory to {file}`volto-addon`.
+
+```shell
+cd volto-addon
+```
+
+To install the add-on setup, use the following command.
+
+```shell
+make install
+```
+
+
+## Start Plone backend Docker container
+
+In the currently open shell session, issue the following command.
+
+```shell
+make backend-docker-start
+```
+
+```console
+❯ make backend-docker-start
+==> Start Docker-based Plone Backend
+=======================================================================================
+Creating Plone volto SITE: Plone
+Aditional profiles:
+THIS IS NOT MEANT TO BE USED IN PRODUCTION
+Read about it: https://6.docs.plone.org/install/containers/images/backend.html
+=======================================================================================
+Ignoring index for /app/var/filestorage/Data.fs
+INFO:Plone Site Creation:Creating a new Plone site @ Plone
+INFO:Plone Site Creation: - Using the voltolighttheme distribution and answers from /app/scripts/default.json
+INFO:Plone Site Creation: - Stopping site creation, as there is already a site with id Plone at the instance. Set DELETE_EXISTING=1 to delete the existing site before creating a new one.
+Using default configuration
+2024-10-11 16:12:47 INFO [chameleon.config:39][MainThread] directory cache: /app/var/cache.
+2024-10-11 16:12:48 INFO [plone.restapi.patches:16][MainThread] PATCH: Disabled ZPublisher.HTTPRequest.ZopeFieldStorage.VALUE_LIMIT. This enables file uploads larger than 1MB.
+2024-10-11 16:12:49 INFO [plone.volto:23][MainThread] Aliasing collective.folderish classes to plone.volto classes.
+2024-10-11 16:12:50 INFO [Zope:42][MainThread] Ready to handle requests
+Starting server in PID 1.
+2024-10-11 16:12:50 INFO [waitress:486][MainThread] Serving on http://0.0.0.0:8080
+```
+
+This will start a clean Plone server for development purposes so you can start developing your add-on.
+
+
+## Start Plone development frontend
+
+Create a second shell session in a new window.
+Change your current working directory to {file}`volto-addon`.
+Start the Plone development frontend with the following command.
+
+```shell
+make start
+```
+
+```console
+webpack 5.90.1 compiled successfully in 11004 ms
+sswp> Handling Hot Module Reloading
+Using volto.config.js in: //frontend/volto.config.js
+✅ Server-side HMR Enabled!
+Volto is running in SEAMLESS mode
+Proxying API requests from http://localhost:3000/++api++ to http://localhost:8080/Plone
+🎭 Volto started at 0.0.0.0:3000 🚀
+```
+
+Note that the Plone frontend uses an internal proxy server to connect with the Plone backend.
+Open a browser at the following URL to visit your Plone site.
+
+http://localhost:3000
+
+You will see a page similar to the following.
+
+```{image} /_static/plone-home-page.png
+:alt: Plone home page
+:class: figure
+```
+
+Your newly created add-on will be installed with vanilla core Volto.
+You can start developing it in the add-on package located in {file}`packages/volto-addon`.
+
+You can stop the site with {kbd}`ctrl-c`.
diff --git a/docs/source/development/add-ons/extend-eslint-add-on.md b/docs/source/development/add-ons/extend-eslint-add-on.md
new file mode 100644
index 0000000000..6017463332
--- /dev/null
+++ b/docs/source/development/add-ons/extend-eslint-add-on.md
@@ -0,0 +1,43 @@
+---
+myst:
+ html_meta:
+ "description": "Extend ESLint configuration from an add-on"
+ "property=og:description": "Extend ESLint configuration from an add-on"
+ "property=og:title": "Extend ESLint configuration from an add-on"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration, ESLint, lint"
+---
+
+# Extend ESLint configuration from an add-on
+
+```{versionadded} Volto 16.4.0
+```
+
+Starting with Volto v16.4.0, you can customize the ESLint configuration from an add-on.
+You should provide a {file}`eslint.extend.js` file in your add-on's root folder, which exports a `modify(defaultConfig)` function.
+For example, to host some code outside the regular {file}`src/` folder of your add-on, you need to add the following {file}`eslint.extend.js` file:
+
+```js
+const path = require('path');
+
+module.exports = {
+ modify(defaultConfig) {
+ const aliasMap = defaultConfig.settings['import/resolver'].alias.map;
+ const addonPath = aliasMap.find(
+ ([name]) => name === '@plone-collective/some-volto-add-on',
+ )[1];
+
+ const extraPath = path.resolve(`${addonPath}/../extra`);
+ aliasMap.push(['@plone-collective/extra', extraPath]);
+
+ return defaultConfig;
+ },
+};
+```
+
+This allows the add-on `@plone-collective/some-volto-add-on` to host some code outside its normal {file}`src/` folder.
+If you put that code in the {file}`extra` folder, that code would be available under the `@plone-collective/extra` name.
+
+```{note}
+This takes care only of the ESLint integration.
+For proper language support, you'll still need to configure it in the {file}`razzle.extend.js` file of your add-on.
+```
diff --git a/docs/source/development/add-ons/extend-webpack-add-on.md b/docs/source/development/add-ons/extend-webpack-add-on.md
new file mode 100644
index 0000000000..c7fbd69aed
--- /dev/null
+++ b/docs/source/development/add-ons/extend-webpack-add-on.md
@@ -0,0 +1,55 @@
+---
+myst:
+ html_meta:
+ "description": "Extend webpack setup from an add-on with razzle.extend.js"
+ "property=og:description": "Extend webpack setup from an add-on with razzle.extend.js"
+ "property=og:title": "Extend webpack setup from an add-on"
+ "keywords": "Volto, Plone, webpack, add-on, razzle.extend.js, Razzle"
+---
+
+# Extend webpack setup from an add-on
+
+```{deprecated} Volto 18
+The project configuration approach as described in this document is deprecated in Volto 18 and will be removed in Volto 19.
+You should instead follow the add-on approach as described in {doc}`../../conceptual-guides/add-ons`.
+```
+
+Just like you can extend Razzle's configuration from the project, you can do the same with an add-on.
+You should provide a {file}`razzle.extend.js` file in your add-on root folder.
+The following code example manages two things.
+
+- Add a new webpack plugin, [`webpack-bundle-analyzer`](https://www.npmjs.com/package/webpack-bundle-analyzer).
+- Reconfigure the `theme.config` alias, to enable a custom Semantic UI theme inside the add-on.
+
+ ```js
+ const analyzerPlugin = {
+ name: 'bundle-analyzer',
+ options: {
+ analyzerHost: '0.0.0.0',
+ analyzerMode: 'static',
+ generateStatsFile: true,
+ statsFilename: 'stats.json',
+ reportFilename: 'reports.html',
+ openAnalyzer: false,
+ },
+ };
+
+ const plugins = (defaultPlugins) => {
+ return defaultPlugins.concat([analyzerPlugin]);
+ };
+ const modify = (config, { target, dev }, webpack) => {
+ const themeConfigPath = `${__dirname}/theme/theme.config`;
+ config.resolve.alias['../../theme.config$'] = themeConfigPath;
+
+ return config;
+ };
+
+ module.exports = {
+ plugins,
+ modify,
+ };
+ ```
+
+```{seealso}
+[`volto-searchlib`'s {file}`razzle.extend.js`](https://github.com/eea/volto-searchlib/blob/d84fec8eec1def0088d8025eaf5d7197074b95a7/razzle.extend.js) file for an example of how to include additional paths for the Babel configuration, and how to add additional webpack name aliases.
+```
diff --git a/docs/source/addons/i18n.md b/docs/source/development/add-ons/i18n.md
similarity index 61%
rename from docs/source/addons/i18n.md
rename to docs/source/development/add-ons/i18n.md
index 9df832da70..ab8d838f8f 100644
--- a/docs/source/addons/i18n.md
+++ b/docs/source/development/add-ons/i18n.md
@@ -4,17 +4,19 @@ myst:
"description": "Internationalize your add-on and override translations"
"property=og:description": "Internationalize your add-on and override translations"
"property=og:title": "Add-on Internationalization"
- "keywords": "Internationalization, i18n, add-on"
+ "keywords": "Volto, internationalization, i18n, add-on"
---
-# Add-on Internationalization
+# Add-on internationalization
+
+The {term}`internationalization` (i18n) workflow in and add-on is similar to core Volto.
+You develop your add-on, then add the translations to your code.
-The internationalization workflow is the same as in main Volto: you develop your add-on, then add the translations to your code.
See {ref}`create-i18n-strings` for how to mark strings and phrases as translatable.
-Your add-on has a `locales` folder with a `.pot` file.
+Your add-on has a {file}`locales` folder with a `.pot` file.
-1. Create the following structure in your add-ons `locales` folder for every language you want to support.
+1. Create the following structure in your add-ons {file}`locales` folder for every language you want to support.
As an example for the language Italian:
```text
@@ -24,14 +26,14 @@ Your add-on has a `locales` folder with a `.pot` file.
```
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.
+1. Go to each `.po` file in your {file}`locales` folder, and write the translations for each translation literal.
In the context of your project, run `pnpm i18n` to merge the add-on translations with the ones of your project.
## Override 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 you have multiple add-ons installed in your project, the translations are loaded in the order your add-ons are listed in {file}`package.json`.
If two add-ons provide different translations for the same message, then the last defined add-on wins.
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/development/add-ons/index.md b/docs/source/development/add-ons/index.md
new file mode 100644
index 0000000000..97296c4a12
--- /dev/null
+++ b/docs/source/development/add-ons/index.md
@@ -0,0 +1,30 @@
+---
+myst:
+ html_meta:
+ "description": "How to develop Volto add-ons"
+ "property=og:description": "How to develop Volto add-ons"
+ "property=og:title": "Develop Volto add-ons"
+ "keywords": "Volto, Plone, CMS, add-on"
+---
+
+# Develop Volto add-ons
+
+```{toctree}
+:maxdepth: 1
+
+install-an-add-on
+install-an-add-on-dev-18
+install-an-add-on-dev-17
+load-add-on-configuration
+create-an-add-on-18
+create-an-add-on-17
+test-add-ons-18
+test-add-ons-17
+extend-webpack-add-on
+extend-eslint-add-on
+troubleshoot-transpilation
+i18n
+best-practices
+theme
+public-folder
+```
diff --git a/docs/source/development/add-ons/install-an-add-on-dev-17.md b/docs/source/development/add-ons/install-an-add-on-dev-17.md
new file mode 100644
index 0000000000..8c938c6391
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on-dev-17.md
@@ -0,0 +1,124 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in development mode in Volto 17 in your Plone project"
+ "property=og:description": "How to install an add-on in development mode in Volto 17 in your Plone project"
+ "property=og:title": "Install an add-on in development mode in Volto 17"
+ "keywords": "Volto, Plone, add-on, stable, development, mode"
+---
+
+# Install an add-on in development mode in Volto 17
+
+Use [`mrs-developer`](https://www.npmjs.com/package/mrs-developer) to manage the development cycle of Volto add-ons.
+This tool pulls the remote code and configures the current project, making the add-on available for the build.
+By doing this, you can develop both the project and the add-on product as if they were both part of the current codebase.
+
+`mrs-developer` is included and installed by default when you generate a project with the generator.
+Use the following command to install the configuration of `mrs.developer.json` in your project.
+
+```shell
+make install
+```
+
+
+## Configure `mrs-developer`
+
+{file}`mrs.developer.json` is the configuration file that instructs `mrs-developer` from where it should pull the packages.
+The generator includes an empty one for you.
+Edit {file}`mrs.developer.json` and add the following code.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src"
+ }
+}
+```
+
+Then run:
+
+```bash
+make install
+```
+
+Now the add-on appears in `src/addons/`.
+
+```{note}
+The `package` property is optional.
+Use it only if your package has a namespace.
+
+`src` is required if the content of your add-on is located in the `src` directory.
+Since that is the convention for all Volto add-on packages, you must always include it.
+```
+
+```{seealso}
+See [`mrs-developer` configuration options](https://www.npmjs.com/package/mrs-developer).
+```
+
+
+## Resolve import paths
+
+Your project uses a file to configure import paths, either {file}`tsconfig.json` or {file}`jsconfig.json` at the Volto project root.
+`mrs-developer` automatically manages the content of this file for you.
+If you choose not to use `mrs-developer`, you'll have to manually add configuration to this file.
+
+```json
+{
+ "compilerOptions": {
+ "paths": {
+ "acme-volto-foo-addon": [
+ "addons/acme-volto-foo-addon/src"
+ ]
+ },
+ "baseUrl": "src"
+ }
+}
+```
+
+
+```{warning}
+Both values for `paths` and `baseUrl` must match your project's layout.
+```
+
+```{tip}
+You should use the `src` path inside your package and point the `main` key in {file}`package.json` to the {file}`index.js` file in {file}`src/index.js`.
+```
+
+
+## Add-on development lifecycle
+
+If you want to "disable" using the development version of an add-on, or keep a more stable version of `mrs.developer.json` in your source code repository, you can set its status by adding a `develop` key to {file}`mrs.developer.json` as shown.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src",
+ "develop": true
+ }
+}
+```
+
+Whenever you change a value in your {file}`mrs.developer.json`, you must run `make install` again.
+
+
+## Add-on dependencies, yarn workspaces
+
+If your add-on needs to bring in additional JavaScript package dependencies, you'll have to set your add-on package as a "Yarn workspace".
+You should add a `workspaces` key to the {file}`package.json` of your Volto project.
+
+```json
+"workspaces": ["src/addons/my-volto-addon"],
+```
+
+It is common practice to use a star (`*`) glob pattern for the workspaces.
+
+```json
+"workspaces": ["src/addons/*"],
+```
+
+If you do this, make sure to always clean up the `src/addons` folder whenever you toggle the development status of an add-on, as the existence of the add-on folder under `src/addons` will still influence yarn.
+To do so, run `make install` again to remove the no longer required package.
diff --git a/docs/source/development/add-ons/install-an-add-on-dev-18.md b/docs/source/development/add-ons/install-an-add-on-dev-18.md
new file mode 100644
index 0000000000..7828759fc7
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on-dev-18.md
@@ -0,0 +1,143 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in development mode in Volto 18 in your Plone project"
+ "property=og:description": "How to install an add-on in development mode in Volto 18 in your Plone project"
+ "property=og:title": "Install an add-on in development mode in Volto 18"
+ "keywords": "Volto, Plone, add-on, development, mode"
+---
+
+# Install an add-on in development mode in Volto 18
+
+Use [`mrs-developer`](https://www.npmjs.com/package/mrs-developer) to manage the development cycle of Volto add-ons.
+This tool pulls the remote code and configures the current project, making the add-on available for the build.
+By doing this, you can develop both the project and the add-on product as if they were both part of the current codebase.
+
+`mrs-developer` is included and installed by default when you generate a project with Cookieplone.
+Use the following command to install the configuration of `mrs.developer.json` in your project.
+
+```shell
+make install
+```
+
+Next, you need to add the add-on to the `addons` key of your Plone project's {file}`package.json`.
+
+```json
+{
+ "name": "my-volto-project",
+ "addons": [
+ "name-of-add-on"
+ ]
+}
+```
+
+```{seealso}
+Alternatively, you can use {file}`volto.config.js` to declare add-ons in your Plone project.
+See {doc}`../../configuration/volto-config-js`.
+```
+
+## Configure `mrs-developer`
+
+{file}`mrs.developer.json` is the configuration file that instructs `mrs-developer` from where it should pull the packages.
+Cookieplone includes an empty one for you.
+Edit {file}`mrs.developer.json` and add the following code.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "output": "packages",
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src"
+ }
+}
+```
+
+Then run:
+
+```bash
+make install
+```
+
+Now the add-on appears in `packages/acme-volto-foo-addon/`.
+
+```{note}
+The `package` property is optional.
+Use it only if your package has a namespace.
+
+`src` is required if the content of your add-on is located in the `src` directory.
+Since that is the convention for all Volto add-on packages, you must always include it.
+```
+
+```{seealso}
+See [`mrs-developer` configuration options](https://www.npmjs.com/package/mrs-developer).
+```
+
+
+## Resolve import paths
+
+```{versionadded} Volto 18.0.0-alpha.43
+```
+
+The Cookieplone setup uses `pnpm` to resolve import paths.
+You have nothing to do here.
+
+
+## Add-on development lifecycle
+
+If you want to "disable" using the development version of an add-on, or keep a more stable version of `mrs.developer.json` in your source code repository, you can set its status by adding a `develop` key to {file}`mrs.developer.json` as shown.
+
+```json
+{
+ "acme-volto-foo-addon": {
+ "output": "packages",
+ "package": "@acme/volto-foo-addon",
+ "url": "git@github.com:acme/my-volto-addon.git",
+ "path": "src",
+ "develop": true
+ }
+}
+```
+
+Whenever you change a value in your {file}`mrs.developer.json`, you must run `make install` again.
+
+
+## Add-on dependencies
+
+If your add-on needs to bring in additional JavaScript package dependencies, you'll have to declare them as normal package dependencies.
+
+
+## `pnpm` workspaces
+
+You need to configure your add-ons using a `pnpm` workspace.
+You can configure them using the file {file}`pnpm-workspace.yaml` and declare all your development add-ons in there.
+
+```yaml
+packages:
+ - 'core/packages/*'
+ - 'packages/*'
+```
+
+If the add-on you are developing was created using {term}`Cookieplone`, then you have to add the following to {file}`pnpm-workspace.yaml` detect them.
+
+```yaml
+packages:
+ - 'core/packages'
+ - 'packages/my-policy-addon'
+ - 'packages/**/packages/*'
+```
+
+Note the nesting of `packages` since a {term}`Cookieplone` generated add-on will have a `packages` folder in itself.
+You can explicitly declare the add-ons, too.
+
+```yaml
+packages:
+ - 'core/packages'
+ - 'packages/my-policy-addon'
+ - 'packages/my-development-mode-addon/packages/my-development-mode-addon'
+ - 'packages/**/packages/*'
+```
+
+```{important}
+Run `make install` after any change in {file}`pnpm-workspace.yaml` to update the setup.
+```
diff --git a/docs/source/development/add-ons/install-an-add-on.md b/docs/source/development/add-ons/install-an-add-on.md
new file mode 100644
index 0000000000..8dfc9df327
--- /dev/null
+++ b/docs/source/development/add-ons/install-an-add-on.md
@@ -0,0 +1,69 @@
+---
+myst:
+ html_meta:
+ "description": "How to install an add-on in Volto"
+ "property=og:description": "How to install an add-on in Volto"
+ "property=og:title": "Install an add-on in Volto"
+ "keywords": "add-on, Volto, install"
+---
+
+# Install an add-on in Volto
+
+This document describes how to install an add-on in Volto.
+
+You can install an add-on just like any other JavaScript package from the [npm Registry](https://www.npmjs.com/).
+
+`````{tab-set}
+:sync-group: install-add-on
+
+````{tab-item} Volto 18
+:sync: volto-18
+```shell
+pnpm --filter add
+```
+````
+
+````{tab-item} Volto 17
+:sync: volto-17
+```shell
+yarn add
+```
+````
+`````
+
+If the add-on is not published on the npm Registry, [you can install it directly from GitHub](https://pnpm.io/cli/add#install-from-git-repository).
+
+
+`````{tab-set}
+:sync-group: install-add-on
+
+````{tab-item} Volto 18
+:sync: volto-18
+```shell
+pnpm add collective/volto-dropdownmenu
+```
+````
+
+````{tab-item} Volto 17
+:sync: volto-17
+```shell
+yarn add collective/volto-dropdownmenu
+```
+````
+`````
+
+Next, you need to add the add-on to the `addons` key of your Plone project's {file}`package.json`.
+
+```json
+{
+ "name": "my-volto-project",
+ "addons": [
+ "name-of-add-on"
+ ]
+}
+```
+
+```{seealso}
+Alternatively, you can use {file}`volto.config.js` to declare add-ons in your Plone project.
+See {doc}`../../configuration/volto-config-js`.
+```
diff --git a/docs/source/development/add-ons/load-add-on-configuration.md b/docs/source/development/add-ons/load-add-on-configuration.md
new file mode 100644
index 0000000000..33f0a21c9b
--- /dev/null
+++ b/docs/source/development/add-ons/load-add-on-configuration.md
@@ -0,0 +1,65 @@
+---
+myst:
+ html_meta:
+ "description": "Load configuration from add-ons"
+ "property=og:description": "Load configuration from add-ons"
+ "property=og:title": "Load configuration from add-ons"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration"
+---
+
+# Load configuration from add-ons
+
+As a convenience, an add-on can export configuration functions that can mutate in-place the overall Volto {term}`configuration registry`.
+An add-on can export multiple configuration methods, making it possible to selectively choose which specific add-on functionality you want to load.
+
+Some add-ons might allow the Volto project to selectively load some of their configuration, so they may offer additional configuration functions.
+You can load them by overloading the add-on name in the `addons` {file}`package.json` key, as shown.
+
+```{code-block} json
+:emphasize-lines: 4
+
+{
+ "name": "my-nice-volto-project",
+ "addons": [
+ "acme-volto-foo-add-on:loadOptionalBlocks,overrideSomeDefaultBlock",
+ "volto-ga"
+ ],
+}
+```
+
+```{note}
+The additional comma-separated names should be exported from the add-on package's {file}`index.js`.
+The main configuration function should be exported as the default.
+An add-on's default configuration method will always be loaded.
+```
+
+If for some reason you want to manually load the add-on, you can edit your project's {file}`config.js` module:
+
+```js
+import loadExampleAddon, { enableOptionalBlocks } from 'volto-example-add-on';
+import * as voltoConfig from '@plone/volto/config';
+
+const config = enableOptionalBlocks(loadExampleAddon(voltoConfig));
+
+export blocks = {
+ ...config.blocks,
+}
+```
+
+Volto provides a helper method `applyConfig` to do the same.
+
+```js
+import { applyConfig } from '@plone/volto/helpers';
+import * as voltoConfig from '@plone/volto/config';
+
+const config = applyConfig([
+ enableOptionalBlocks,
+ loadExampleAddon
+], voltoConfig);
+
+export blocks = {
+ ...config.blocks,
+}
+```
+
+The `applyConfig` helper ensures that each configuration method returns the configuration object, avoiding errors when developing add-ons.
diff --git a/docs/source/addons/public-folder.md b/docs/source/development/add-ons/public-folder.md
similarity index 70%
rename from docs/source/addons/public-folder.md
rename to docs/source/development/add-ons/public-folder.md
index 188a9e155a..aec4ab220c 100644
--- a/docs/source/addons/public-folder.md
+++ b/docs/source/development/add-ons/public-folder.md
@@ -1,10 +1,10 @@
---
myst:
html_meta:
- "description": "How to add static served files from your add-on to your build"
- "property=og:description": "How to add static served files to the build from an add-on"
+ "description": "How to add static files from your add-on to your build"
+ "property=og:description": "How to add static files from your add-on to your build"
"property=og:title": "Add static files from your add-on to your build"
- "keywords": "Volto, Plone, Semantic UI, CSS, Volto theme, add-on, static, assets, files, build"
+ "keywords": "Volto, Plone, Semantic UI, CSS, theme, add-on, static, assets, files, build"
---
# Add static files from your add-on to your build
@@ -22,8 +22,7 @@ It is useful to define static files such as the following:
## Procedure to include static files
-Create a folder named `public` at the root of your add-on, and add the static files to it.
+Create a folder named {file}`public` at the root of your add-on, and add the static files to it.
The build process will copy the files, taking into account all add-ons' defined order.
The build process copies first the static files defined by Volto, then the static files from add-ons as defined by their configuration order.
The last defined file overwrites any previously defined files.
-
diff --git a/docs/source/development/add-ons/test-add-ons-17.md b/docs/source/development/add-ons/test-add-ons-17.md
new file mode 100644
index 0000000000..047c508869
--- /dev/null
+++ b/docs/source/development/add-ons/test-add-ons-17.md
@@ -0,0 +1,104 @@
+---
+myst:
+ html_meta:
+ "description": "How to test add-ons in Volto 17"
+ "property=og:description": "How to test add-ons in Volto 17"
+ "property=og:title": "Test add-ons in Volto 17"
+ "keywords": "Volto, Plone, testing, CI, add-ons"
+---
+
+# Test add-ons in Volto 17
+
+Volto uses {term}`Jest` for unit tests.
+You must configure {file}`package.json` to let Jest know about your aliases and make them available to it to resolve them.
+
+```{code-block} json
+:emphasize-lines: 6
+
+"jest": {
+ "moduleNameMapper": {
+ "@plone/volto/(.*)$": "/node_modules/@plone/volto/src/$1",
+ "@package/(.*)$": "/src/$1",
+ "@plone/some-volto-addon/(.*)$": "/src/addons/@plone/some-volto-addon/src/$1",
+ "my-volto-addon/(.*)$": "/src/addons/my-volto-addon/src/$1",
+ "~/(.*)$": "/src/$1"
+ }
+}
+```
+
+You can use `yarn test src/addons/addon-name` to run tests.
+
+
+## Override Jest configuration
+
+In {term}`CI` or for testing add-ons, it's useful to modify Jest's {file}`package.json` configuration file.
+You can use a {file}`jest.config.js` file, or point the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable.
+
+```shell
+RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test
+```
+
+Both configurations are merged in a way that the keys of the configuration provided override the initial {file}`package.json` configuration, either in Volto or in your projects.
+
+
+## Test add-ons in isolation
+
+Testing an add-on in isolation, as you would when you develop a Plone Python backend add-on, can be a bit challenging, since an add-on needs a working project in order to bootstrap itself.
+The latest generator has the boilerplate needed to bootstrap a dockerized environment where you can run any test to your add-on.
+
+
+### Set up the environment
+
+Run the following command once.
+
+```shell
+make dev
+```
+
+
+### Build the containers manually
+
+Run the following commands.
+
+```shell
+make build-backend
+make build-addon
+```
+
+
+### Unit tests
+
+Run the following command.
+
+```shell
+make test
+```
+
+
+### Acceptance tests
+
+Use {term}`Cypress` to run acceptance tests.
+Run the following command once.
+
+```shell
+make install-acceptance
+```
+
+To start the servers, run the following command.
+
+```shell
+make start-test-acceptance-server
+```
+
+You run the frontend in development mode, so you can develop while writing tests.
+Run the following command to run Cypress tests afterward.
+
+```shell
+make test-acceptance
+```
+
+When finished, shut down the backend server.
+
+```shell
+make stop-test-acceptance-server
+```
diff --git a/docs/source/development/add-ons/test-add-ons-18.md b/docs/source/development/add-ons/test-add-ons-18.md
new file mode 100644
index 0000000000..cb55a72ad3
--- /dev/null
+++ b/docs/source/development/add-ons/test-add-ons-18.md
@@ -0,0 +1,63 @@
+---
+myst:
+ html_meta:
+ "description": "Test add-ons in Volto 18"
+ "property=og:description": "Test add-ons in Volto 18"
+ "property=og:title": "Test add-ons in Volto 18"
+ "keywords": "Volto, Plone, testing, test, CI, add-ons"
+---
+
+# Test add-ons in Volto 18
+
+```{warning}
+This guide assumes that you've used {term}`Cookieplone` to create your add-on boilerplate.
+```
+
+Volto uses {term}`Jest` for unit tests.
+You can create unit tests for testing your add-on.
+
+Run the following command.
+
+```shell
+make test
+```
+
+## Override Jest configuration
+
+In {term}`CI` or for testing add-ons, it's useful to modify Jest's {file}`package.json` configuration file.
+You can use the file {file}`jest.config.js` provided by the boilerplate.
+The test command will load it and apply it.
+
+```{warning}
+Do not modify the existing keys in there if you don't know what you are doing, since some of them are required for the tests to run properly in the Volto context.
+```
+
+Both configurations are merged in a way that the keys of the configuration provided override the initial {file}`package.json` configuration, either in Volto or in your projects.
+
+```{note}
+For more background on testing add-ons in Volto 18, see {doc}`../../contributing/testing`, since the developer experience has been unified for both add-ons and Volto core.
+```
+
+### Acceptance tests
+
+Use {term}`Cypress` to run acceptance tests.
+
+To start the backend server, run the following command.
+This will start a Docker container with a vanilla Plone backend.
+
+```shell
+make acceptance-backend-start
+```
+
+To start the frontend acceptance server in development mode, run the following command.
+
+```shell
+make acceptance-frontend-dev-start
+```
+
+You can run the frontend in development mode, so you can develop while writing tests.
+Run the following command to run Cypress tests afterward.
+
+```shell
+make acceptance-test
+```
diff --git a/docs/source/development/add-ons/theme.md b/docs/source/development/add-ons/theme.md
new file mode 100644
index 0000000000..914e3b1295
--- /dev/null
+++ b/docs/source/development/add-ons/theme.md
@@ -0,0 +1,248 @@
+---
+myst:
+ html_meta:
+ "description": "How to create a Volto theme add-on"
+ "property=og:description": "How to create a Volto theme add-on"
+ "property=og:title": "Create a Volto theme add-on"
+ "keywords": "Volto, Plone, Semantic UI, CSS, theme, add-on"
+---
+
+# Create a Volto theme add-on
+
+You can create a Volto theme add-on, keeping it separate from your project files.
+By making your Volto theme add-on pluggable, you can deploy the same theme in different projects.
+You can even create themes that depend on conditions that you inject at build time.
+
+The file {file}`volto.config.js` provides the ability to programmatically declare add-ons and the active theme.
+See {ref}`volto-config-js` for more information.
+For convenience, it can also be set via a `THEME` environment variable.
+
+1. In your {file}`volto.config.js` file at the root of your project, add a `theme` key with the value of your theme's name.
+
+ ```js
+ module.exports = {
+ addons: [],
+ theme: 'volto-my-theme'
+ };
+ ```
+
+ Alternatively, you can add a `theme` key in your {file}`package.json` project.
+
+ ```json
+ "theme": "volto-my-theme"
+ ```
+
+ Or you can set the theme name through the `THEME` environment variable.
+
+ ```shell
+ THEME='volto-my-theme' pnpm start
+ ```
+
+2. Create a directory {file}`src/theme` in your add-on.
+ Inside that directory, create a new file {file}`theme.config`, adding the following content, but replacing `` with your add-on name.
+
+ ```less
+ /*******************************
+ Theme Selection
+ *******************************/
+
+ /* To override a theme for an individual element specify theme name below */
+
+ /* Global */
+ @site : 'pastanaga';
+ @reset : 'pastanaga';
+
+ /* Elements */
+ @button : 'pastanaga';
+ @container : 'pastanaga';
+ @divider : 'pastanaga';
+ @flag : 'pastanaga';
+ @header : 'pastanaga';
+ @icon : 'pastanaga';
+ @image : 'pastanaga';
+ @input : 'pastanaga';
+ @label : 'pastanaga';
+ @list : 'pastanaga';
+ @loader : 'pastanaga';
+ @placeholder : 'pastanaga';
+ @rail : 'pastanaga';
+ @reveal : 'pastanaga';
+ @segment : 'pastanaga';
+ @step : 'pastanaga';
+
+ /* Collections */
+ @breadcrumb : 'pastanaga';
+ @form : 'pastanaga';
+ @grid : 'pastanaga';
+ @menu : 'pastanaga';
+ @message : 'pastanaga';
+ @table : 'pastanaga';
+
+ /* Modules */
+ @accordion : 'pastanaga';
+ @checkbox : 'pastanaga';
+ @dimmer : 'pastanaga';
+ @dropdown : 'pastanaga';
+ @embed : 'pastanaga';
+ @modal : 'pastanaga';
+ @nag : 'pastanaga';
+ @popup : 'pastanaga';
+ @progress : 'pastanaga';
+ @rating : 'pastanaga';
+ @search : 'pastanaga';
+ @shape : 'pastanaga';
+ @sidebar : 'pastanaga';
+ @sticky : 'pastanaga';
+ @tab : 'pastanaga';
+ @transition : 'pastanaga';
+
+ /* Views */
+ @ad : 'pastanaga';
+ @card : 'pastanaga';
+ @comment : 'pastanaga';
+ @feed : 'pastanaga';
+ @item : 'pastanaga';
+ @statistic : 'pastanaga';
+
+ /* Extras */
+ @main : 'pastanaga';
+ @custom : 'pastanaga';
+
+ /*******************************
+ Folders
+ *******************************/
+
+ /* Path to theme packages */
+ @themesFolder : '~volto-themes';
+
+ /* Path to site override folder */
+ @siteFolder : "/theme";
+
+ /*******************************
+ Import Theme
+ *******************************/
+
+ @import (multiple) "~semantic-ui-less/theme.less";
+ @fontPath : "~volto-themes/@{theme}/assets/fonts";
+
+ .loadAddonOverrides() {
+ @import (optional) "@{siteFolder}/@{addon}/@{addontype}s/@{addonelement}.overrides";
+ }
+
+ /* End Config */
+ ```
+
+3. Declare the theme as an add-on by adding its name to the value for the `addons` key in either {file}`volto.config.js` or {file}`package.json` in your project.
+
+4. After starting Volto, the theme should be active.
+ Now you can add overrides to the default theme in {file}`src/theme`, the same as you would in a project.
+
+5. Finally, you can safely delete your project's original {file}`theme` folder, since the one in the add-on will take precedence, and a project can only have one active theme at a time.
+
+
+## Using your own theming escape hatch
+
+Volto theming uses Semantic UI theming capabilities to define and extend a theme for your site.
+However, while maintaining and playing well with the Semantic UI Volto base, you can use a traditional CSS approach using the LESS preprocessor-based `extras` escape hatch.
+
+At the same time, you can either discard or complement the `extras` escape hatch and add your own, by customizing the {file}`theme.js` module in Volto.
+
+```js
+import 'semantic-ui-less/semantic.less';
+import '@plone/volto/../theme/themes/pastanaga/extras/extras.less';
+
+// You can add more entry points for theming
+import '@kitconcept/volto-light-theme/theme/main.scss';
+```
+
+Customizing the base theme is a special use case in Volto.
+To begin, add a {file}`./@root/theme.js` file structure in your {file}`customizations` folder in your add-on or project.
+
+You may want to do this to create a completely new theming experience adapted to your way of doing things that do not match the current Volto theming experience.
+For example, if you want to use another preprocessor in the theme, such as SCSS.
+Or perhaps your client requires the base consist entirely of pre-made components based on another library beside Semantic UI.
+See {ref}`volto-custom-theming-strategy` for an example of a custom theme escape hatch.
+
+While building your own escape hatch for theming, you can use the preprocessor of your choice, while maintaining the "base" Volto theme, but customizing it using the resultant CSS.
+
+You can see an example of such a theme in [Volto Light Theme](https://github.com/kitconcept/volto-light-theme).
+
+
+## Modify a custom theme from another add-on
+
+Sometimes you have a custom theme that you want to reuse through all your projects, but with some differences, maintaining the base.
+Usually, the only option would be to use an add-on that adds more CSS to the base theme, using imports that will load after the theme.
+However, there is a problem with this approach.
+You cannot use existing theme variables, including breakpoints, on these new styles.
+Similarly, it gets somewhat detached from the normal flow of the loaded theme.
+The same applies for add-ons, as they are detached from the current theme.
+You could use a Semantic UI approach for making this work, but then it's bound to Semantic UI.
+
+```{warning}
+This is only possible when using your own escape hatch, and works only with SCSS-based themes, and not with Semantic UI themes, since it enables a couple of entry points that only support SCSS files.
+For an example of how it could be used, see [Volto Light Theme](https://github.com/kitconcept/volto-light-theme).
+```
+
+If your custom escape hatch defines a custom theme using SCSS, you can take advantage of this feature.
+Although not limited to this, it would be possible to extend this feature to add more entry points, using another preprocessor or theming approach.
+
+This feature enables two entry point files, {file}`_variables.scss` and {file}`_main.scss`.
+From your add-on code, you can extend an existing theme by creating a file corresponding to each entry point:
+
+- {file}`./src/theme/_variables.scss`
+- {file}`./src/theme/_main.scss`
+
+
+### Variables
+
+You can use the entry point `addonsThemeCustomizationsVariables` to modify the original variables of the currently loaded theme by adding the entry point before the theme variable definitions.
+In the theme, it should be imported as shown below.
+
+```{code-block} scss
+:emphasize-lines: 2
+
+@import 'addonsThemeCustomizationsVariables';
+@import 'variables';
+@import 'typography';
+@import 'utils';
+@import 'layout';
+```
+
+````{warning}
+Following SCSS best practices, your theme variables should be "overridable" using the `!default` flag.
+This assigns a value to a variable _only_ if that variable isn't defined or its value is [`null`](https://sass-lang.com/documentation/values/null).
+Otherwise, the existing value will be used.
+
+```{seealso}
+https://sass-lang.com/documentation/variables#default-values
+```
+````
+
+Volto will not only load your add-on entry point files, but it will also detect all the add-ons that have these entry point files, and import them grouped under a single file.
+It will also automatically add an `addonsThemeCustomizationsVariables` alias that you can reference from the theme as shown above.
+
+
+### Main
+
+You can use the entry point `addonsThemeCustomizationsMain` to add your own style definitions, complementing those in the theme.
+You should add it after all the CSS of your theme:
+
+```{code-block} scss
+:emphasize-lines: 6
+
+@import 'blocks/search';
+@import 'blocks/listing';
+
+@import 'temp';
+
+@import 'addonsThemeCustomizationsMain';
+
+/* No CSS beyond this point */
+```
+
+Volto will also detect all the add-ons that have these entry point files, and import them grouped under a single file.
+It will also automatically add an `addonsThemeCustomizationsMain` alias that you can reference from the theme as shown above.
+
+```{note}
+It will only work in combination with the theme declaration in {file}`volto.config.js` or in {file}`package.json`.
+```
diff --git a/docs/source/development/add-ons/troubleshoot-transpilation.md b/docs/source/development/add-ons/troubleshoot-transpilation.md
new file mode 100644
index 0000000000..2cf7e761b6
--- /dev/null
+++ b/docs/source/development/add-ons/troubleshoot-transpilation.md
@@ -0,0 +1,78 @@
+---
+myst:
+ html_meta:
+ "description": "Troubleshoot untranspiled add-on dependencies"
+ "property=og:description": "Troubleshoot untranspiled add-on dependencies"
+ "property=og:title": "Troubleshoot untranspiled add-on dependencies"
+ "keywords": "Volto, add-on, extensions, frontend, Plone, configuration, troubleshoot"
+---
+
+# Troubleshoot untranspiled add-on dependencies
+
+```{note}
+In Volto 18 and later, Babel improved support for ES specifications, such as for the null coalescence operator.
+However the following procedure can be useful in other scenarios.
+```
+
+When using external add-ons in your project, sometimes you will run into add-ons that are not securely transpiled or haven't been transpiled at all.
+In that case, you might see an error such as the following:
+
+```console
+Module parse failed: Unexpected token (10:41) in @react-leaflet/core/esm/path.js
+...
+const options = props.pathOptions ?? {};
+...
+```
+
+Babel automatically transpiles the code in your add-on, but {file}`node_modules` are excluded from this process.
+You need to include the add-on path in the list of modules to be transpiled.
+To do so, customize the webpack configuration in the {file}`razzle.config.js` file in your add-on.
+For example, suppose that you want to use react-leaflet, which has a known transpilation issue.
+
+```js
+const path = require('path');
+const makeLoaderFinder = require('razzle-dev-utils/makeLoaderFinder');
+
+const babelLoaderFinder = makeLoaderFinder('babel-loader');
+
+const jsConfig = require('./jsconfig').compilerOptions;
+
+const pathsConfig = jsConfig.paths;
+let voltoPath = './node_modules/@plone/volto';
+Object.keys(pathsConfig).forEach((pkg) => {
+ if (pkg === '@plone/volto') {
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
+ }
+});
+
+const { modifyWebpackConfig, plugins } = require(`${voltoPath}/razzle.config`);
+
+const customModifyWebpackConfig = ({ env, webpackConfig, webpackObject, options }) => {
+ const config = modifyWebpackConfig({
+ env,
+ webpackConfig,
+ webpackObject,
+ options,
+ });
+ const babelLoader = config.module.rules.find(babelLoaderFinder);
+ const { include } = babelLoader;
+ const corePath = path.join(
+ path.dirname(require.resolve('@react-leaflet/core')),
+ '..',
+ );
+ const esmPath = path.join(
+ path.dirname(require.resolve('react-leaflet')),
+ '..',
+ );
+
+ include.push(corePath);
+ include.push(esmPath);
+ return config;
+};
+
+module.exports = { modifyWebpackConfig: customModifyWebpackConfig, plugins };
+```
+
+First you need some setup to get the webpack configuration from Volto's configuration.
+Once you have that, you need to resolve the path to the desired add-ons, and push it into the Babel loader include list.
+After this, the add-ons will load correctly.
diff --git a/docs/source/development/i18n.md b/docs/source/development/i18n.md
index f9f2f4dd03..7fb232a9b6 100644
--- a/docs/source/development/i18n.md
+++ b/docs/source/development/i18n.md
@@ -11,7 +11,7 @@ myst:
{term}`Internationalization` (i18n) is the process of creating user interfaces which are suitable for different languages and cultural contexts.
-This chapter describes the most common use cases for internationalization when developing your {doc}`../addons/index` or contributing to the Volto core itself.
+This chapter describes the most common use cases for internationalization when developing your {doc}`../development/add-ons/index` or contributing to the Volto core itself.
## Process and file structure overview
diff --git a/docs/source/development/index.md b/docs/source/development/index.md
index a1e5952999..b0e1d64b1a 100644
--- a/docs/source/development/index.md
+++ b/docs/source/development/index.md
@@ -21,6 +21,7 @@ Or jump in to any topic listed below.
overview
creating-project
+add-ons/index
folder-structure
environment-variables
customizing-components
diff --git a/docs/source/index.md b/docs/source/index.md
index 2b47ebce16..e96dcc0e1d 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -71,6 +71,7 @@ tutorials/index
contributing/index
release-notes/index
release-management-notes/index
+conceptual-guides/index
```
% Only check change log entries in Volto documentation—not when it is included in the main Plone documentation—to ensure links work and do not redirect.
diff --git a/docs/source/release-notes/index.md b/docs/source/release-notes/index.md
index 5a57570ddc..040f15972a 100644
--- a/docs/source/release-notes/index.md
+++ b/docs/source/release-notes/index.md
@@ -17,9 +17,72 @@ myst:
-## 18.0.0 (2024-10-31)
+## 18.1.1 (2024-11-21)
+
+### Bugfix
+
+- Do not break toolbar if layout id is not registered in layoutViewsNamesMapping. @cekk [#6485](https://github.com/plone/volto/issues/6485)
+- Replace _all_ spaces with `-` in `BodyClass` classes, instead of with `-` or `` depending on the content type or section. @giuliaghisini [#6487](https://github.com/plone/volto/issues/6487)
+
+### Internal
+
+- Update instructions to install `pipx` in `RELEASING.md`. @stevepiercy [#6496](https://github.com/plone/volto/issues/6496)
+
+### Documentation
+
+- More privacy concerning youtube links and fixing link check warnings for youtube playlist links. @stevepiercy @ksuess [#4203](https://github.com/plone/volto/issues/4203)
+- Remove conflicting `searchtools.js` file from documentation to allow default Sphinx search in main Plone documentation. @stevepiercy [#6482](https://github.com/plone/volto/issues/6482)
+- Add support for sphinxcontrib-youtube. @stevepiercy [#6486](https://github.com/plone/volto/issues/6486)
+- Refactor documentation includes to align with main documentation pattern. @stevepiercy [#6495](https://github.com/plone/volto/issues/6495)
+
+## 18.1.0 (2024-11-11)
+
+### Feature
+
+- Update Dutch translations. @fredvd [#6476](https://github.com/plone/volto/issues/6476)
+
+### Bugfix
+
+- URL Management control panel: Show errors from a failed CSV upload. @davisagli [#6473](https://github.com/plone/volto/issues/6473)
+- Added missing style Helmet serialization in the HTML component to make it work in SSR. @sneridagh
+ Fix deprecation notice for the usage of apple-mobile-web-app-capable. [#6480](https://github.com/plone/volto/issues/6480)
+
+### Internal
+
+- Added React Router 7 experimental PoC. @sneridagh [#6472](https://github.com/plone/volto/issues/6472)
+
+### Documentation
+
+- Overhaul and update of the add-ons section in documentation. @sneridagh [#6397](https://github.com/plone/volto/issues/6397)
+
+## 18.0.3 (2024-11-05)
+
+### Bugfix
+
+- Fixed image generation because of a bug in the docker build with registry typings. @sneridagh [#6471](https://github.com/plone/volto/issues/6471)
+
+## 18.0.2 (2024-11-05)
+
+### Internal
+
+- Fix missing export src in @plone/components Also improve packaging in packages. @sneridagh [#6470](https://github.com/plone/volto/issues/6470)
+
+## 18.0.1 (2024-11-05)
-## 18.0.0 (Unreleased)
+### Bugfix
+
+- Fix ERR_REQUIRE from ESM module requiring CJS module in `@plone/registry` fix tests. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+
+### Internal
+
+- Improve exports in @plone/registry, adapt the Jest test setup. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+- Removed `parcel` completely from core. @sneridagh [#6469](https://github.com/plone/volto/issues/6469)
+
+### Documentation
+
+- Fix display of nvm version when installing it for Contributing to Volto. @stevepiercy [#6460](https://github.com/plone/volto/issues/6460)
+
+## 18.0.0 (2024-10-31)
(Summary)
diff --git a/docs/source/theming/index.md b/docs/source/theming/index.md
index fe401772f3..e2577b2c31 100644
--- a/docs/source/theming/index.md
+++ b/docs/source/theming/index.md
@@ -9,6 +9,10 @@ myst:
# Theming
+This section of the documentation describes theming in Volto.
+
+
+## Conceptual guides
```{toctree}
:maxdepth: 1
@@ -17,7 +21,19 @@ about-semantic
semanticui-theming
theming-engine
theming-strategy
+```
+
+
+## How-to guides
+
+```{toctree}
+:maxdepth: 1
+
custom-styling
using-third-party-themes
theming-a-base-theme
```
+
+```{seealso}
+For how to create your theme as an add-on in Volto 18 and later, see {doc}`../development/add-ons/theme`.
+```
diff --git a/docs/source/tutorials/index.md b/docs/source/tutorials/index.md
index 67360cb366..4a1ce15a4e 100644
--- a/docs/source/tutorials/index.md
+++ b/docs/source/tutorials/index.md
@@ -18,6 +18,8 @@ On the [Plone Training website](https://training.plone.org), you'll find Volto-d
- [Mastering Plone 6 Development](https://training.plone.org/mastering-plone/)
The comprehensive training on Plone 6 with best practice tips for developers and integrators.
+- [Customizing Volto Light Theme](https://training.plone.org/customizing-volto-light-theme/index.html)
+- [Volto Customization for JavaScript Beginners](https://training.plone.org/volto-customization/index.html)
- [Volto Hands-On](https://training.plone.org/voltohandson/index.html)
- [Volto Add-ons Development](https://training.plone.org/voltoaddons/index.html)
- [Effective Volto](https://training.plone.org/effective-volto/index.html)
@@ -30,7 +32,11 @@ On the [Plone Training website](https://training.plone.org), you'll find Volto-d
You can watch the talk during the World Plone Day 2021:
-
+```{youtube} kHec4MXH8vo
+:privacy_mode:
+:url_parameters: ?privacy_mode=1
+:width: 100%
+```
## Presentations at Plone Conferences (PloneConf) and other events
@@ -40,43 +46,43 @@ In recent years the React based Volto frontend for Plone has been presented in m
### PloneConf 2023
-- [State of Plone Keynote](https://www.youtube.com/watch?v=jl19wuC0wtw&%3Blist=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&%3Bindex=1)
-- [Piero Nicolli - Theming Volto in 2024](https://www.youtube.com/watch?v=LkPOsIn1jYY&%3Blist=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&%3Bindex=6)
-- [Víctor Fernández de Alba - Breaking boundaries: Plone as headless CMS](https://www.youtube.com/watch?v=43LVtjYyo28&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=7)
-- [Rob Gietema - How to build a site using Nick](https://www.youtube.com/watch?v=ZbdYvNAnamM&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=18)
-- [Alok Kumar - Is your Volto add-on developer friendly?](https://www.youtube.com/watch?v=E6fH3NhR2Hc&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=20)
-- [Dylan Jay and Jefferson Bledsoe - How to implement a Gov Design System in Plone 6](https://www.youtube.com/watch?v=_XmKc7jNIE8&list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=25)
-- [Víctor Fernández de Alba - Volto-light-theme: Volto Theming, Reimagined](https://www.youtube.com/watch?v=t2X2NO62J-8)
+- [State of Plone Keynote](https://www.youtube-nocookie.com/embed/jl19wuC0wtw?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=1&privacy_mode=1)
+- [Piero Nicolli - Theming Volto in 2024](https://www.youtube-nocookie.com/embed/LkPOsIn1jYY?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=6&privacy_mode=1)
+- [Víctor Fernández de Alba - Breaking boundaries: Plone as headless CMS](https://www.youtube-nocookie.com/embed/43LVtjYyo28?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=7&privacy_mode=1)
+- [Rob Gietema - How to build a site using Nick](https://www.youtube-nocookie.com/embed/ZbdYvNAnamM?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=18&privacy_mode=1)
+- [Alok Kumar - Is your Volto add-on developer friendly?](https://www.youtube-nocookie.com/embed/E6fH3NhR2Hc?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=20&privacy_mode=1)
+- [Dylan Jay and Jefferson Bledsoe - How to implement a Gov Design System in Plone 6](https://www.youtube-nocookie.com/embed/_XmKc7jNIE8?list=PLGN9BI-OAQkSXMXVBXLWQAQr0AF2xM_NU&index=25&privacy_mode=1)
+- [Víctor Fernández de Alba - Volto-light-theme: Volto Theming, Reimagined](https://www.youtube-nocookie.com/embed/t2X2NO62J-8)
### PloneConf 2022
-[PloneConf 2022 full Playlist on Youtube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+[PloneConf 2022 full Playlist on Youtube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
Plone 6 site presentations:
-- [Rai Way: Plone6 supporting the world of italian information, sports and entertainment](https://www.youtube.com/watch?v=hHHGlSjf5O4&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [How Plone Powers Hundreds of Websites at one of the Largest Research Institutions in Europe](https://www.youtube.com/watch?v=bxWt-GEmPcc&%3Blist=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+- [Rai Way: Plone6 supporting the world of italian information, sports and entertainment](https://www.youtube-nocookie.com/embed/hHHGlSjf5O4?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [How Plone Powers Hundreds of Websites at one of the Largest Research Institutions in Europe](https://www.youtube-nocookie.com/embed/bxWt-GEmPcc?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
Developer/integrator talks:
-- [Anatomy of a Volto project](https://www.youtube.com/watch?v=JtNufyFlgc8&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [Creating a Volto Theme](https://www.youtube.com/watch?v=AMHN74Jr27Y&%3Blist=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [A Deep Dive Into Internals Of Volto](https://www.youtube.com/watch?v=sMeTDRgp3uI&list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
-- [DevOps Bird's Eye View on the Plone 6 Backend](https://www.youtube.com/watch?v=L5PvGwWC9P4&%3Blist=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z)
+- [Anatomy of a Volto project](https://www.youtube-nocookie.com/embed/JtNufyFlgc8?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [Creating a Volto Theme](https://www.youtube-nocookie.com/embed/AMHN74Jr27Y?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [A Deep Dive Into Internals Of Volto](https://www.youtube-nocookie.com/embed/sMeTDRgp3uI?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
+- [DevOps Bird's Eye View on the Plone 6 Backend](https://www.youtube-nocookie.com/embed/L5PvGwWC9P4?list=PLGN9BI-OAQkQxqQcCZeJefMC8XlA_qv3Z&privacy_mode=1)
### Previous PloneConfs
-- [PloneConf 2021 full Playlist on YouTube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQDLQinBwdEXpebDTQCwdGi)
-- [PloneConf 2020 full Playlist on YouTube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkTJPayNdKIZ8lLDm5RVOLV3)
+- [PloneConf 2021 full Playlist on YouTube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQDLQinBwdEXpebDTQCwdGi&privacy_mode=1)
+- [PloneConf 2020 full Playlist on YouTube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkTJPayNdKIZ8lLDm5RVOLV3&privacy_mode=1)
## World Plone Day 2022
-World Plone Day is a 24-hour streaming event, with the goal was to promote and educate the public about the benefits of using Plone and of being part of the Plone community. [Full World Plone Day 2022 playlist on Youtube](https://www.youtube.com/playlist?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
+World Plone Day is a 24-hour streaming event, with the goal was to promote and educate the public about the benefits of using Plone and of being part of the Plone community. [Full World Plone Day 2022 playlist on Youtube](https://www.youtube-nocookie.com/embed/playlist?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
-- [Plone 6 Volto's Seamless Mode](https://www.youtube.com/watch?v=Mj8pHRBls-w&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Volto add ons separator and carousel](https://www.youtube.com/watch?v=eyTMI5TYcVg&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Weekly Volto Live – Retrospective](https://www.youtube.com/watch?v=WT6OjkSrB20&%3Blist=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
-- [Migrating from Classic to Volto](https://www.youtube.com/watch?v=09fg456T90s&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL)
+- [Plone 6 Volto's Seamless Mode](https://www.youtube-nocookie.com/embed/Mj8pHRBls-w?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Volto add ons separator and carousel](https://www.youtube-nocookie.com/embed/eyTMI5TYcVg?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Weekly Volto Live – Retrospective](https://www.youtube-nocookie.com/embed/WT6OjkSrB20?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
+- [Migrating from Classic to Volto](https://www.youtube-nocookie.com/embed/09fg456T90s?list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&privacy_mode=1)
diff --git a/docs/source/user-manual/copy-paste-blocks.md b/docs/source/user-manual/copy-paste-blocks.md
index f15f3b1616..4fbc68e769 100644
--- a/docs/source/user-manual/copy-paste-blocks.md
+++ b/docs/source/user-manual/copy-paste-blocks.md
@@ -28,6 +28,7 @@ This will select all the blocks between the start and end blocks, allowing you t
````{only} not text
```{video} ../_static/user-manual/blocks/block-copy-cut.mp4
+:alt: Copy or cut a block in Volto
```
````
@@ -44,5 +45,6 @@ Also if you hold the {kbd}`ctrl` key while clicking the paste button, it keeps t
````{only} not text
```{video} ../_static/user-manual/blocks/block-paste.mp4
+:alt: Paste a block in Volto
```
````
diff --git a/package.json b/package.json
index 6414015cca..1cf8ebd0e3 100644
--- a/package.json
+++ b/package.json
@@ -31,8 +31,6 @@
"update:browserlist": "pnpm --filter @plone/volto add caniuse-lite && pnpm --filter @plone/volto remove caniuse-lite"
},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"concurrently": "^8.2.2",
"husky": "9.0.11",
"lint-staged": "15.2.2",
diff --git a/packages/blocks/.npmignore b/packages/blocks/.npmignore
index 0d8afd5727..a6d10baa1e 100644
--- a/packages/blocks/.npmignore
+++ b/packages/blocks/.npmignore
@@ -2,7 +2,5 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
diff --git a/packages/blocks/package.json b/packages/blocks/package.json
index d8b191191e..05d801297d 100644
--- a/packages/blocks/package.json
+++ b/packages/blocks/package.json
@@ -29,11 +29,6 @@
"access": "public"
},
"main": "src/index.ts",
- "targets": {
- "main": {
- "includeNodeModules": false
- }
- },
"scripts": {
"test": "vitest",
"dry-release": "release-it --dry-run",
@@ -52,13 +47,10 @@
},
"dependencies": {},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"@plone/registry": "workspace:*",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
"typescript": "^5.6.3",
diff --git a/packages/client/.parcelrc b/packages/client/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/client/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md
index 0761493643..57c72746b1 100644
--- a/packages/client/CHANGELOG.md
+++ b/packages/client/CHANGELOG.md
@@ -8,6 +8,13 @@
+## 1.0.0-alpha.20 (2024-11-05)
+
+### Internal
+
+- Bump local `typescript` version. @sneridagh [#6461](https://github.com/plone/volto/pull/6461)
+- Replace `parcel` with `tsup`. @sneridagh [#6466](https://github.com/plone/volto/pull/6466)
+
## 1.0.0-alpha.19 (2024-10-18)
### Feature
diff --git a/packages/client/package.json b/packages/client/package.json
index d33b80c4f4..327f15c87c 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "1.0.0-alpha.19",
+ "version": "1.0.0-alpha.20",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
@@ -19,20 +19,15 @@
"type": "module",
"files": [
"dist",
- "src",
"README.md"
],
- "source": "./src/index.ts",
- "main": "./dist/index.cjs",
- "module": "./dist/index.js",
- "types": "./dist/index.d.ts",
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "require": "./dist/index.cjs"
- },
- "./src/*": "./src/*"
+ "default": "./dist/index.cjs"
+ }
},
"homepage": "https://plone.org",
"keywords": [
@@ -46,11 +41,11 @@
"react-query"
],
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
- "build:force": "parcel build --no-cache",
+ "build": "tsup",
+ "build:force": "tsup",
"test": "vitest",
- "check-ts": "tsc --project tsconfig.json",
+ "check:exports": "attw --pack .",
+ "check:ts": "tsc --project tsconfig.json",
"coverage": "vitest run --coverage --no-threads",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -61,13 +56,7 @@
"access": "public"
},
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
- "@parcel/optimizer-terser": "2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace: *",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
@@ -79,12 +68,11 @@
"@vitest/coverage-v8": "^1.3.1",
"glob": "7.1.6",
"jsdom": "^21.1.1",
- "parcel": "^2.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"release-it": "17.1.1",
- "tsup": "^8.0.2",
- "typescript": "^5.4.5",
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
"uuid": "^9.0.1",
"vite": "^5.4.8",
"vite-plugin-dts": "^3.7.3",
diff --git a/packages/client/src/API.ts b/packages/client/src/API.ts
index 5862f3084d..12e53ff42e 100644
--- a/packages/client/src/API.ts
+++ b/packages/client/src/API.ts
@@ -1,5 +1,5 @@
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
-import { PloneClientConfig } from './validation/config';
+import type { PloneClientConfig } from './validation/config';
import qs from 'query-string';
import debugFactory from 'debug';
diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts
index 93fdb7e4b6..b0d5e5289a 100644
--- a/packages/client/src/client.ts
+++ b/packages/client/src/client.ts
@@ -109,7 +109,7 @@ import {
queryWithConfig,
mutationHookFromMutation,
} from './utils/misc';
-import { PloneClientConfig } from './validation/config';
+import type { PloneClientConfig } from './validation/config';
const PLONECLIENT_DEFAULT_CONFIG = { apiPath: 'http://localhost:8080/Plone' };
diff --git a/packages/client/src/restapi/actions/get.ts b/packages/client/src/restapi/actions/get.ts
index 5ad55b06b3..7d9ffd3c8b 100644
--- a/packages/client/src/restapi/actions/get.ts
+++ b/packages/client/src/restapi/actions/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { ActionsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { ActionsResponse } from '@plone/types';
import { z } from 'zod';
const getActionsSchema = z.object({
diff --git a/packages/client/src/restapi/addons/get.ts b/packages/client/src/restapi/addons/get.ts
index f4ce7c67cb..2fdf5669d3 100644
--- a/packages/client/src/restapi/addons/get.ts
+++ b/packages/client/src/restapi/addons/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAddonResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAddonResponse } from '@plone/types';
import { z } from 'zod';
const getAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/get_list.ts b/packages/client/src/restapi/addons/get_list.ts
index 16a9b54f59..f283e21c38 100644
--- a/packages/client/src/restapi/addons/get_list.ts
+++ b/packages/client/src/restapi/addons/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { Addons } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { Addons } from '@plone/types';
export type AddonsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/addons/install.ts b/packages/client/src/restapi/addons/install.ts
index 39d1cd516a..1e0812fe00 100644
--- a/packages/client/src/restapi/addons/install.ts
+++ b/packages/client/src/restapi/addons/install.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const installAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/install_profile.ts b/packages/client/src/restapi/addons/install_profile.ts
index eff76e0bb8..3be0e2a5bd 100644
--- a/packages/client/src/restapi/addons/install_profile.ts
+++ b/packages/client/src/restapi/addons/install_profile.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const installAddonProfileSchema = z.object({
diff --git a/packages/client/src/restapi/addons/unistall.ts b/packages/client/src/restapi/addons/unistall.ts
index ca4033a1a4..e0b480a4a5 100644
--- a/packages/client/src/restapi/addons/unistall.ts
+++ b/packages/client/src/restapi/addons/unistall.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const uninstallAddonSchema = z.object({
diff --git a/packages/client/src/restapi/addons/upgrade.ts b/packages/client/src/restapi/addons/upgrade.ts
index f1275b53cb..b75f0400d6 100644
--- a/packages/client/src/restapi/addons/upgrade.ts
+++ b/packages/client/src/restapi/addons/upgrade.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const upgradeAddonSchema = z.object({
diff --git a/packages/client/src/restapi/aliases/add.ts b/packages/client/src/restapi/aliases/add.ts
index 07e9eee3ff..2d9bfd80ca 100644
--- a/packages/client/src/restapi/aliases/add.ts
+++ b/packages/client/src/restapi/aliases/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createAliasesDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/add_multiple.ts b/packages/client/src/restapi/aliases/add_multiple.ts
index 2cd990bc41..77c1c6a518 100644
--- a/packages/client/src/restapi/aliases/add_multiple.ts
+++ b/packages/client/src/restapi/aliases/add_multiple.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createAliasesMultipleDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/delete.ts b/packages/client/src/restapi/aliases/delete.ts
index af2401daf8..73d408cc7b 100644
--- a/packages/client/src/restapi/aliases/delete.ts
+++ b/packages/client/src/restapi/aliases/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteAliasesDataSchema } from '../../validation/aliases';
diff --git a/packages/client/src/restapi/aliases/get.ts b/packages/client/src/restapi/aliases/get.ts
index 7919d859a4..e535175537 100644
--- a/packages/client/src/restapi/aliases/get.ts
+++ b/packages/client/src/restapi/aliases/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAliasesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAliasesResponse } from '@plone/types';
import { z } from 'zod';
const getAliasesSchema = z.object({
diff --git a/packages/client/src/restapi/aliases/get_list.ts b/packages/client/src/restapi/aliases/get_list.ts
index a19d8e193f..767325feb1 100644
--- a/packages/client/src/restapi/aliases/get_list.ts
+++ b/packages/client/src/restapi/aliases/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetAliasesListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetAliasesListResponse } from '@plone/types';
export type AliasesListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/breadcrumbs/get.ts b/packages/client/src/restapi/breadcrumbs/get.ts
index b882abdc7a..65a8e1095a 100644
--- a/packages/client/src/restapi/breadcrumbs/get.ts
+++ b/packages/client/src/restapi/breadcrumbs/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { BreadcrumbsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { BreadcrumbsResponse } from '@plone/types';
import { z } from 'zod';
const getBreadcrumbsSchema = z.object({
diff --git a/packages/client/src/restapi/comments/add.ts b/packages/client/src/restapi/comments/add.ts
index 601ec9f2a8..961c7c092c 100644
--- a/packages/client/src/restapi/comments/add.ts
+++ b/packages/client/src/restapi/comments/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { newCommentDataSchema as createCommentDataSchema } from '../../validation/comments';
diff --git a/packages/client/src/restapi/comments/delete.ts b/packages/client/src/restapi/comments/delete.ts
index 70c0353960..403999308c 100644
--- a/packages/client/src/restapi/comments/delete.ts
+++ b/packages/client/src/restapi/comments/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/comments/get.ts b/packages/client/src/restapi/comments/get.ts
index 3d225e39e8..9495f96354 100644
--- a/packages/client/src/restapi/comments/get.ts
+++ b/packages/client/src/restapi/comments/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetCommentsResponse } from '@plone/types';
+import type { GetCommentsResponse } from '@plone/types';
const getCommentsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/comments/update.ts b/packages/client/src/restapi/comments/update.ts
index a0cde49faa..32a3efd4b5 100644
--- a/packages/client/src/restapi/comments/update.ts
+++ b/packages/client/src/restapi/comments/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { newCommentDataSchema as updateCommentDataSchema } from '../../validation/comments';
diff --git a/packages/client/src/restapi/content/add.ts b/packages/client/src/restapi/content/add.ts
index 1cff2e06b3..671b1b1406 100644
--- a/packages/client/src/restapi/content/add.ts
+++ b/packages/client/src/restapi/content/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createContentDataSchema } from '../../validation/content';
-import { CreateContentResponse } from '@plone/types';
+import type { CreateContentResponse } from '@plone/types';
export const createContentArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/content/delete.ts b/packages/client/src/restapi/content/delete.ts
index fbf8ec0230..95ce268eeb 100644
--- a/packages/client/src/restapi/content/delete.ts
+++ b/packages/client/src/restapi/content/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/content/get.ts b/packages/client/src/restapi/content/get.ts
index 18103e1427..5d5cf8fa96 100644
--- a/packages/client/src/restapi/content/get.ts
+++ b/packages/client/src/restapi/content/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { Content } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { Content } from '@plone/types';
import { z } from 'zod';
const getContentArgsSchema = z.object({
diff --git a/packages/client/src/restapi/content/update.ts b/packages/client/src/restapi/content/update.ts
index fe751e7a68..83ee95571b 100644
--- a/packages/client/src/restapi/content/update.ts
+++ b/packages/client/src/restapi/content/update.ts
@@ -1,11 +1,11 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { z } from 'zod';
import { updateContentDataSchema } from '../../validation/content';
-import { UpdateContentResponse } from '@plone/types';
+import type { UpdateContentResponse } from '@plone/types';
export const updateContentArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/contextnavigation/get.ts b/packages/client/src/restapi/contextnavigation/get.ts
index af0c1b7398..ca586a2e8a 100644
--- a/packages/client/src/restapi/contextnavigation/get.ts
+++ b/packages/client/src/restapi/contextnavigation/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { ContextNavigationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { ContextNavigationResponse } from '@plone/types';
import { z } from 'zod';
const getContextNavigationSchema = z.object({
diff --git a/packages/client/src/restapi/controlpanels/add.ts b/packages/client/src/restapi/controlpanels/add.ts
index 9b25859833..a48fefda63 100644
--- a/packages/client/src/restapi/controlpanels/add.ts
+++ b/packages/client/src/restapi/controlpanels/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/controlpanels/delete.ts b/packages/client/src/restapi/controlpanels/delete.ts
index 5abff355f8..34cdfe983b 100644
--- a/packages/client/src/restapi/controlpanels/delete.ts
+++ b/packages/client/src/restapi/controlpanels/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/controlpanels/get.ts b/packages/client/src/restapi/controlpanels/get.ts
index 3cb54124dc..2c18b4a25a 100644
--- a/packages/client/src/restapi/controlpanels/get.ts
+++ b/packages/client/src/restapi/controlpanels/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetControlpanelResponse } from '@plone/types';
+import type { GetControlpanelResponse } from '@plone/types';
const getControlpanelSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/controlpanels/get_list.ts b/packages/client/src/restapi/controlpanels/get_list.ts
index e5c171c8e5..24f9b54cc6 100644
--- a/packages/client/src/restapi/controlpanels/get_list.ts
+++ b/packages/client/src/restapi/controlpanels/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetControlpanelsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetControlpanelsResponse } from '@plone/types';
export type ControlpanelsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/controlpanels/update.ts b/packages/client/src/restapi/controlpanels/update.ts
index cdd7815db3..1eb8ff8482 100644
--- a/packages/client/src/restapi/controlpanels/update.ts
+++ b/packages/client/src/restapi/controlpanels/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/copymove/copy.ts b/packages/client/src/restapi/copymove/copy.ts
index bf300f958f..e62302a1a7 100644
--- a/packages/client/src/restapi/copymove/copy.ts
+++ b/packages/client/src/restapi/copymove/copy.ts
@@ -1,8 +1,8 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { type ApiRequestParams, apiRequest } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as copyDataSchema } from '../../validation/copymove';
-import { CopyMoveResponse as CopyResponse } from '@plone/types';
+import type { CopyMoveResponse as CopyResponse } from '@plone/types';
export type CopyArgs = z.infer & {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/copymove/move.ts b/packages/client/src/restapi/copymove/move.ts
index 6f1015caad..ebd11e193e 100644
--- a/packages/client/src/restapi/copymove/move.ts
+++ b/packages/client/src/restapi/copymove/move.ts
@@ -1,8 +1,8 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { type ApiRequestParams, apiRequest } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { copyMoveDataSchema as moveDataSchema } from '../../validation/copymove';
-import { CopyMoveResponse as MoveResponse } from '@plone/types';
+import type { CopyMoveResponse as MoveResponse } from '@plone/types';
export type MoveArgs = z.infer & {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/database/get.ts b/packages/client/src/restapi/database/get.ts
index 53769b3c41..5a10438edd 100644
--- a/packages/client/src/restapi/database/get.ts
+++ b/packages/client/src/restapi/database/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { DatabaseResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { DatabaseResponse } from '@plone/types';
export type DatabaseArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/email-notification/post.ts b/packages/client/src/restapi/email-notification/post.ts
index 54a81b340b..718ab59f7b 100644
--- a/packages/client/src/restapi/email-notification/post.ts
+++ b/packages/client/src/restapi/email-notification/post.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/email-send/post.ts b/packages/client/src/restapi/email-send/post.ts
index f1d5863ed0..23cc727f2e 100644
--- a/packages/client/src/restapi/email-send/post.ts
+++ b/packages/client/src/restapi/email-send/post.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/groups/add.ts b/packages/client/src/restapi/groups/add.ts
index b9dd8fa2cc..27f0fc8d17 100644
--- a/packages/client/src/restapi/groups/add.ts
+++ b/packages/client/src/restapi/groups/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createGroupDataSchema } from '../../validation/groups';
-import { CreateGroupResponse } from '@plone/types';
+import type { CreateGroupResponse } from '@plone/types';
export const createGroupArgsSchema = z.object({
data: createGroupDataSchema,
diff --git a/packages/client/src/restapi/groups/delete.ts b/packages/client/src/restapi/groups/delete.ts
index 4d2565ae3d..e135f1b691 100644
--- a/packages/client/src/restapi/groups/delete.ts
+++ b/packages/client/src/restapi/groups/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/groups/get.ts b/packages/client/src/restapi/groups/get.ts
index 7048b40fb9..3d23c1690e 100644
--- a/packages/client/src/restapi/groups/get.ts
+++ b/packages/client/src/restapi/groups/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetGroupResponse } from '@plone/types';
+import type { GetGroupResponse } from '@plone/types';
const getGroupSchema = z.object({
groupId: z.string(),
diff --git a/packages/client/src/restapi/groups/get_list.ts b/packages/client/src/restapi/groups/get_list.ts
index 7c7928bbec..e3b1724492 100644
--- a/packages/client/src/restapi/groups/get_list.ts
+++ b/packages/client/src/restapi/groups/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetGroupsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetGroupsResponse } from '@plone/types';
export type GroupsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/groups/update.ts b/packages/client/src/restapi/groups/update.ts
index c189643e2a..d31ca1a680 100644
--- a/packages/client/src/restapi/groups/update.ts
+++ b/packages/client/src/restapi/groups/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateGroupDataSchema } from '../../validation/groups';
diff --git a/packages/client/src/restapi/history/get.ts b/packages/client/src/restapi/history/get.ts
index 15e2ca1169..70f2d2bfbf 100644
--- a/packages/client/src/restapi/history/get.ts
+++ b/packages/client/src/restapi/history/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetHistoryResponse } from '@plone/types';
+import type { GetHistoryResponse } from '@plone/types';
const getHistorySchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/history/get_versioned.ts b/packages/client/src/restapi/history/get_versioned.ts
index de016b50fd..b6e33c86f9 100644
--- a/packages/client/src/restapi/history/get_versioned.ts
+++ b/packages/client/src/restapi/history/get_versioned.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const getHistoryVersionedSchema = z.object({
diff --git a/packages/client/src/restapi/history/revert.ts b/packages/client/src/restapi/history/revert.ts
index 3e0ad44580..616e4053f4 100644
--- a/packages/client/src/restapi/history/revert.ts
+++ b/packages/client/src/restapi/history/revert.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { revertHistoryDataSchema } from '../../validation/history';
diff --git a/packages/client/src/restapi/linkintegrity/get.ts b/packages/client/src/restapi/linkintegrity/get.ts
index 9af2dd5551..8cf9f359eb 100644
--- a/packages/client/src/restapi/linkintegrity/get.ts
+++ b/packages/client/src/restapi/linkintegrity/get.ts
@@ -1,5 +1,5 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { PloneClientConfigSchema } from '../../validation/config';
const getLinkintegriyArgsSchema = z.object({
diff --git a/packages/client/src/restapi/lock/add.ts b/packages/client/src/restapi/lock/add.ts
index 2ba71b85c3..608b3e38a6 100644
--- a/packages/client/src/restapi/lock/add.ts
+++ b/packages/client/src/restapi/lock/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createLockDataSchema } from '../../validation/lock';
-import { CreateLockResponse } from '@plone/types';
+import type { CreateLockResponse } from '@plone/types';
export const createLockArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/delete.ts b/packages/client/src/restapi/lock/delete.ts
index c18cd46d17..ee06ad8d62 100644
--- a/packages/client/src/restapi/lock/delete.ts
+++ b/packages/client/src/restapi/lock/delete.ts
@@ -1,11 +1,11 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteLockDataSchema } from '../../validation/lock';
-import { LockInfo as DeleteLockResponse } from '@plone/types';
+import type { LockInfo as DeleteLockResponse } from '@plone/types';
export const deleteLockArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/get.ts b/packages/client/src/restapi/lock/get.ts
index 9c1962e16a..9c09841968 100644
--- a/packages/client/src/restapi/lock/get.ts
+++ b/packages/client/src/restapi/lock/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { LockInfo as GetLockResponse } from '@plone/types';
+import type { LockInfo as GetLockResponse } from '@plone/types';
const getLockSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/lock/update.ts b/packages/client/src/restapi/lock/update.ts
index d9c0a3553a..42fc26866c 100644
--- a/packages/client/src/restapi/lock/update.ts
+++ b/packages/client/src/restapi/lock/update.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { z } from 'zod';
diff --git a/packages/client/src/restapi/login/post.ts b/packages/client/src/restapi/login/post.ts
index 5d893fef9f..e35f1d87f6 100644
--- a/packages/client/src/restapi/login/post.ts
+++ b/packages/client/src/restapi/login/post.ts
@@ -1,10 +1,10 @@
-import { Login } from '@plone/types';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
+import type { Login } from '@plone/types';
export const loginArgsSchema = z.object({
username: z.string(),
diff --git a/packages/client/src/restapi/navigation/get.ts b/packages/client/src/restapi/navigation/get.ts
index d5b6577a17..71e64fb94f 100644
--- a/packages/client/src/restapi/navigation/get.ts
+++ b/packages/client/src/restapi/navigation/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { NavigationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { NavigationResponse } from '@plone/types';
import { z } from 'zod';
const getNavigationSchema = z.object({
diff --git a/packages/client/src/restapi/navroot/get.ts b/packages/client/src/restapi/navroot/get.ts
index a43d9d1d51..d5dea3d4f2 100644
--- a/packages/client/src/restapi/navroot/get.ts
+++ b/packages/client/src/restapi/navroot/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetNavrootResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetNavrootResponse } from '@plone/types';
import { z } from 'zod';
const getNavrootSchema = z.object({
diff --git a/packages/client/src/restapi/principals/get.ts b/packages/client/src/restapi/principals/get.ts
index d42abc930c..4ae976781e 100644
--- a/packages/client/src/restapi/principals/get.ts
+++ b/packages/client/src/restapi/principals/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetPrincipalsResponse } from '@plone/types';
+import type { GetPrincipalsResponse } from '@plone/types';
const getPrincipalsSchema = z.object({
search: z.string(),
diff --git a/packages/client/src/restapi/querysources/get.ts b/packages/client/src/restapi/querysources/get.ts
index 46949b2d5c..47207b9507 100644
--- a/packages/client/src/restapi/querysources/get.ts
+++ b/packages/client/src/restapi/querysources/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetQuerysourceResponse } from '@plone/types';
+import type { GetQuerysourceResponse } from '@plone/types';
const getQuerysourceSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/querystring-search/get.ts b/packages/client/src/restapi/querystring-search/get.ts
index 926453dfbd..588b50eddc 100644
--- a/packages/client/src/restapi/querystring-search/get.ts
+++ b/packages/client/src/restapi/querystring-search/get.ts
@@ -1,8 +1,8 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
import { querystringSearchDataSchema as getQuerystringSearchSchema } from '../../validation/querystring-search';
-import { QuerystringSearchResponse as GetQuerystringSearchResponse } from '@plone/types';
+import type { QuerystringSearchResponse as GetQuerystringSearchResponse } from '@plone/types';
export type QuerystringSearchArgs = z.infer<
typeof getQuerystringSearchSchema
diff --git a/packages/client/src/restapi/querystring-search/post.ts b/packages/client/src/restapi/querystring-search/post.ts
index 95c07e6be5..decf6dad71 100644
--- a/packages/client/src/restapi/querystring-search/post.ts
+++ b/packages/client/src/restapi/querystring-search/post.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { querystringSearchDataSchema as postQuerystringSearchDataSchema } from '../../validation/querystring-search';
-import { QuerystringSearchResponse as PostQuerystringSearchResponse } from '@plone/types';
+import type { QuerystringSearchResponse as PostQuerystringSearchResponse } from '@plone/types';
export const postQuerystringSearchArgsSchema = z.object({
data: postQuerystringSearchDataSchema,
diff --git a/packages/client/src/restapi/querystring/get.ts b/packages/client/src/restapi/querystring/get.ts
index 008f1747ea..acea020e41 100644
--- a/packages/client/src/restapi/querystring/get.ts
+++ b/packages/client/src/restapi/querystring/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetQueryStringResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetQueryStringResponse } from '@plone/types';
export type QueryStringArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/registry/get.ts b/packages/client/src/restapi/registry/get.ts
index d4642ee39a..90784f77fe 100644
--- a/packages/client/src/restapi/registry/get.ts
+++ b/packages/client/src/restapi/registry/get.ts
@@ -1,5 +1,5 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
const getRegistrySchema = z.object({
diff --git a/packages/client/src/restapi/registry/get_list.ts b/packages/client/src/restapi/registry/get_list.ts
index c3c43faaa9..438a12a771 100644
--- a/packages/client/src/restapi/registry/get_list.ts
+++ b/packages/client/src/restapi/registry/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRegistriesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRegistriesResponse } from '@plone/types';
export type GetRegistriesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/registry/update.ts b/packages/client/src/restapi/registry/update.ts
index ee24c141e6..0bd30eeff9 100644
--- a/packages/client/src/restapi/registry/update.ts
+++ b/packages/client/src/restapi/registry/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateRegistryDataSchema } from '../../validation/registry';
diff --git a/packages/client/src/restapi/relations/add.ts b/packages/client/src/restapi/relations/add.ts
index c979571172..70a90b1673 100644
--- a/packages/client/src/restapi/relations/add.ts
+++ b/packages/client/src/restapi/relations/add.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/delete.ts b/packages/client/src/restapi/relations/delete.ts
index 6276712db2..97eef66aab 100644
--- a/packages/client/src/restapi/relations/delete.ts
+++ b/packages/client/src/restapi/relations/delete.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/fix.ts b/packages/client/src/restapi/relations/fix.ts
index 53daba35be..e52b0306fd 100644
--- a/packages/client/src/restapi/relations/fix.ts
+++ b/packages/client/src/restapi/relations/fix.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { fixRelationsDataSchema } from '../../validation/relations';
diff --git a/packages/client/src/restapi/relations/get.ts b/packages/client/src/restapi/relations/get.ts
index 6ac85f6a14..44e608a276 100644
--- a/packages/client/src/restapi/relations/get.ts
+++ b/packages/client/src/restapi/relations/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetRelationsResponse } from '@plone/types';
+import type { GetRelationsResponse } from '@plone/types';
export const getRelationsSchema = z
.object({
diff --git a/packages/client/src/restapi/relations/get_list.ts b/packages/client/src/restapi/relations/get_list.ts
index e37cdc2381..07490cf19f 100644
--- a/packages/client/src/restapi/relations/get_list.ts
+++ b/packages/client/src/restapi/relations/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRelationsListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRelationsListResponse } from '@plone/types';
export type GetRelationsListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/roles/get.ts b/packages/client/src/restapi/roles/get.ts
index 96dfd7e18a..6fb5d23504 100644
--- a/packages/client/src/restapi/roles/get.ts
+++ b/packages/client/src/restapi/roles/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRolesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRolesResponse } from '@plone/types';
export type GetRolesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/rules/add.ts b/packages/client/src/restapi/rules/add.ts
index b187c64b4d..10009142c0 100644
--- a/packages/client/src/restapi/rules/add.ts
+++ b/packages/client/src/restapi/rules/add.ts
@@ -1,10 +1,10 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
-import { RuleRespose as CreateRuleResponse } from '@plone/types';
+import type { RuleRespose as CreateRuleResponse } from '@plone/types';
export const createRuleArgsSchema = z.object({
ruleId: z.string(),
diff --git a/packages/client/src/restapi/rules/delete.ts b/packages/client/src/restapi/rules/delete.ts
index 343e71cd65..9f53b63508 100644
--- a/packages/client/src/restapi/rules/delete.ts
+++ b/packages/client/src/restapi/rules/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { deleteRulesDataSchema } from '../../validation/rules';
diff --git a/packages/client/src/restapi/rules/get.ts b/packages/client/src/restapi/rules/get.ts
index a5b9f930f1..141204c5f8 100644
--- a/packages/client/src/restapi/rules/get.ts
+++ b/packages/client/src/restapi/rules/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetRulesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetRulesResponse } from '@plone/types';
export type GetRulesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/rules/update.ts b/packages/client/src/restapi/rules/update.ts
index 302a0d0592..73260fb05c 100644
--- a/packages/client/src/restapi/rules/update.ts
+++ b/packages/client/src/restapi/rules/update.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateRulesDataSchema } from '../../validation/rules';
-import { RuleRespose as UpdateRuleRespose } from '@plone/types';
+import type { RuleRespose as UpdateRuleRespose } from '@plone/types';
export const updateRulesArgsSchema = z.object({
data: updateRulesDataSchema,
diff --git a/packages/client/src/restapi/search/get.ts b/packages/client/src/restapi/search/get.ts
index 53e44aadd0..ab75b82fd9 100644
--- a/packages/client/src/restapi/search/get.ts
+++ b/packages/client/src/restapi/search/get.ts
@@ -1,8 +1,8 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
import { getSearchSchema } from '../../validation/search';
-import { GetSearchResponse } from '@plone/types';
+import type { GetSearchResponse } from '@plone/types';
import { flattenToDottedNotation } from '../../utils/misc';
export type SearchArgs = z.infer & {
diff --git a/packages/client/src/restapi/site/get.ts b/packages/client/src/restapi/site/get.ts
index a857df017d..dcfe189eb6 100644
--- a/packages/client/src/restapi/site/get.ts
+++ b/packages/client/src/restapi/site/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetSiteResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetSiteResponse } from '@plone/types';
export type SiteArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/sources/get.ts b/packages/client/src/restapi/sources/get.ts
index 7fc5fa4e0d..fc4a6ecbb5 100644
--- a/packages/client/src/restapi/sources/get.ts
+++ b/packages/client/src/restapi/sources/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetSourceResponse } from '@plone/types';
+import type { GetSourceResponse } from '@plone/types';
const getSourceSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/system/get.ts b/packages/client/src/restapi/system/get.ts
index 41c447bc6e..ed801c6e4d 100644
--- a/packages/client/src/restapi/system/get.ts
+++ b/packages/client/src/restapi/system/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetSystemResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetSystemResponse } from '@plone/types';
export type GetSystemArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/transactions/get.ts b/packages/client/src/restapi/transactions/get.ts
index c9f7f5ffb5..c45b3790ca 100644
--- a/packages/client/src/restapi/transactions/get.ts
+++ b/packages/client/src/restapi/transactions/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTransactionsResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTransactionsResponse } from '@plone/types';
export type GetTransactionsArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/transactions/revert.ts b/packages/client/src/restapi/transactions/revert.ts
index e9e5f45fbc..1e9723620c 100644
--- a/packages/client/src/restapi/transactions/revert.ts
+++ b/packages/client/src/restapi/transactions/revert.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { revertTransactionsDataSchema } from '../../validation/transactions';
-import { RevertTransactionsResponse } from '@plone/types';
+import type { RevertTransactionsResponse } from '@plone/types';
export const revertTransactionsArgsSchema = z.object({
data: revertTransactionsDataSchema,
diff --git a/packages/client/src/restapi/translations/get.ts b/packages/client/src/restapi/translations/get.ts
index 726112310a..fb383f0bd2 100644
--- a/packages/client/src/restapi/translations/get.ts
+++ b/packages/client/src/restapi/translations/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTranslationResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTranslationResponse } from '@plone/types';
import { z } from 'zod';
const getTranslationSchema = z.object({
diff --git a/packages/client/src/restapi/translations/link.ts b/packages/client/src/restapi/translations/link.ts
index 4b5a34c2b5..468aa23654 100644
--- a/packages/client/src/restapi/translations/link.ts
+++ b/packages/client/src/restapi/translations/link.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { linkTranslationDataSchema } from '../../validation/translations';
diff --git a/packages/client/src/restapi/translations/unlink.ts b/packages/client/src/restapi/translations/unlink.ts
index ab360e7ff0..321ee63a07 100644
--- a/packages/client/src/restapi/translations/unlink.ts
+++ b/packages/client/src/restapi/translations/unlink.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { unlinkTranslationDataSchema } from '../../validation/translations';
diff --git a/packages/client/src/restapi/types/add.ts b/packages/client/src/restapi/types/add.ts
index 7516b00227..6182a97126 100644
--- a/packages/client/src/restapi/types/add.ts
+++ b/packages/client/src/restapi/types/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createTypeFieldDataSchema } from '../../validation/types';
-import { CreateTypeFieldResponse } from '@plone/types';
+import type { CreateTypeFieldResponse } from '@plone/types';
export const createTypeFieldArgsSchema = z.object({
contentPath: z.string(),
diff --git a/packages/client/src/restapi/types/get.ts b/packages/client/src/restapi/types/get.ts
index ccbf2278a5..86dd3c5a11 100644
--- a/packages/client/src/restapi/types/get.ts
+++ b/packages/client/src/restapi/types/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetTypeResponse } from '@plone/types';
+import type { GetTypeResponse } from '@plone/types';
const getTypeSchema = z.object({
contentPath: z.string(),
diff --git a/packages/client/src/restapi/types/get_list.ts b/packages/client/src/restapi/types/get_list.ts
index 314b2d273d..31cb883e7b 100644
--- a/packages/client/src/restapi/types/get_list.ts
+++ b/packages/client/src/restapi/types/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetTypesResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetTypesResponse } from '@plone/types';
export type GetTypesArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/types/get_type_field.ts b/packages/client/src/restapi/types/get_type_field.ts
index 32a0f771ba..2891792e44 100644
--- a/packages/client/src/restapi/types/get_type_field.ts
+++ b/packages/client/src/restapi/types/get_type_field.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetTypeFieldResponse } from '@plone/types';
+import type { GetTypeFieldResponse } from '@plone/types';
const getTypeFieldSchema = z.object({
contentFieldPath: z.string(),
diff --git a/packages/client/src/restapi/types/update.ts b/packages/client/src/restapi/types/update.ts
index c3e0eff2ef..bae958ffc0 100644
--- a/packages/client/src/restapi/types/update.ts
+++ b/packages/client/src/restapi/types/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateTypeFieldDataSchema } from '../../validation/types';
diff --git a/packages/client/src/restapi/upgrade/get.ts b/packages/client/src/restapi/upgrade/get.ts
index 4edb5f08f4..1f05437ada 100644
--- a/packages/client/src/restapi/upgrade/get.ts
+++ b/packages/client/src/restapi/upgrade/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUpgradeResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUpgradeResponse } from '@plone/types';
export type GetUpgradeArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/upgrade/run.ts b/packages/client/src/restapi/upgrade/run.ts
index c9cb34faab..3d847045fd 100644
--- a/packages/client/src/restapi/upgrade/run.ts
+++ b/packages/client/src/restapi/upgrade/run.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { runUpgradeDataSchema } from '../../validation/upgrade';
-import { RunUpgradeResponse } from '@plone/types';
+import type { RunUpgradeResponse } from '@plone/types';
export const runUpgradeArgsSchema = z.object({
data: runUpgradeDataSchema,
diff --git a/packages/client/src/restapi/users/add.ts b/packages/client/src/restapi/users/add.ts
index e9896013dc..de76be19d1 100644
--- a/packages/client/src/restapi/users/add.ts
+++ b/packages/client/src/restapi/users/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createUserDataSchema } from '../../validation/users';
-import { User as CreateUserResponse } from '@plone/types';
+import type { User as CreateUserResponse } from '@plone/types';
export const createUserArgsSchema = z.object({
data: createUserDataSchema,
diff --git a/packages/client/src/restapi/users/delete.ts b/packages/client/src/restapi/users/delete.ts
index c3f5c175cc..59203b8bc2 100644
--- a/packages/client/src/restapi/users/delete.ts
+++ b/packages/client/src/restapi/users/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/users/get.ts b/packages/client/src/restapi/users/get.ts
index 1f8ab45bfb..dd29774f12 100644
--- a/packages/client/src/restapi/users/get.ts
+++ b/packages/client/src/restapi/users/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { User as GetUserResponse } from '@plone/types';
+import type { User as GetUserResponse } from '@plone/types';
const getUserSchema = z.object({
userId: z.string(),
diff --git a/packages/client/src/restapi/users/get_list.ts b/packages/client/src/restapi/users/get_list.ts
index f441084903..d0319f2e62 100644
--- a/packages/client/src/restapi/users/get_list.ts
+++ b/packages/client/src/restapi/users/get_list.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUsersResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUsersResponse } from '@plone/types';
const getUsersSchema = z.object({
query: z.string().optional(),
diff --git a/packages/client/src/restapi/users/reset_password.ts b/packages/client/src/restapi/users/reset_password.ts
index 6b831048e8..ec13bc4f5c 100644
--- a/packages/client/src/restapi/users/reset_password.ts
+++ b/packages/client/src/restapi/users/reset_password.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/users/reset_password_with_token.ts b/packages/client/src/restapi/users/reset_password_with_token.ts
index 65d9ead9e1..8355b11544 100644
--- a/packages/client/src/restapi/users/reset_password_with_token.ts
+++ b/packages/client/src/restapi/users/reset_password_with_token.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { resetPasswordWithTokenDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/users/update.ts b/packages/client/src/restapi/users/update.ts
index 83f71fb396..cc0e7a36ef 100644
--- a/packages/client/src/restapi/users/update.ts
+++ b/packages/client/src/restapi/users/update.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updateUserDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/users/update_password.ts b/packages/client/src/restapi/users/update_password.ts
index d5a52996df..21b29143b6 100644
--- a/packages/client/src/restapi/users/update_password.ts
+++ b/packages/client/src/restapi/users/update_password.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { ApiRequestParams, apiRequest } from '../../API';
+import { type ApiRequestParams, apiRequest } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { updatePasswordDataSchema } from '../../validation/users';
diff --git a/packages/client/src/restapi/userschema/get.ts b/packages/client/src/restapi/userschema/get.ts
index c5cf6c55d3..2d7e0fbf17 100644
--- a/packages/client/src/restapi/userschema/get.ts
+++ b/packages/client/src/restapi/userschema/get.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetUserschemaResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetUserschemaResponse } from '@plone/types';
export type GetUserschemaArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/vocabularies/get.ts b/packages/client/src/restapi/vocabularies/get.ts
index b25c9b00cf..a8e00488c3 100644
--- a/packages/client/src/restapi/vocabularies/get.ts
+++ b/packages/client/src/restapi/vocabularies/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetVocabulariesResponse } from '@plone/types';
import { z } from 'zod';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetVocabulariesResponse } from '@plone/types';
const getVocabulariesSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/vocabularies/get_list.ts b/packages/client/src/restapi/vocabularies/get_list.ts
index 1a6b18b83a..312be656aa 100644
--- a/packages/client/src/restapi/vocabularies/get_list.ts
+++ b/packages/client/src/restapi/vocabularies/get_list.ts
@@ -1,6 +1,6 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { GetVocabulariesListResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetVocabulariesListResponse } from '@plone/types';
export type VocabulariesListArgs = {
config: PloneClientConfig;
diff --git a/packages/client/src/restapi/workflow/add.ts b/packages/client/src/restapi/workflow/add.ts
index 91b355bff9..86988f021c 100644
--- a/packages/client/src/restapi/workflow/add.ts
+++ b/packages/client/src/restapi/workflow/add.ts
@@ -1,11 +1,11 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
import { createWorkflowDataSchema } from '../../validation/workflow';
-import { CreateWorkflowResponse } from '@plone/types';
+import type { CreateWorkflowResponse } from '@plone/types';
export const createWorkflowArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workflow/get.ts b/packages/client/src/restapi/workflow/get.ts
index 7b0e733dfe..1e1f16801d 100644
--- a/packages/client/src/restapi/workflow/get.ts
+++ b/packages/client/src/restapi/workflow/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
-import { WorkflowResponse } from '@plone/types';
import { z } from 'zod';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { WorkflowResponse } from '@plone/types';
const getWorkflowSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workingcopy/add.ts b/packages/client/src/restapi/workingcopy/add.ts
index b5d3bcfa49..534a00eae0 100644
--- a/packages/client/src/restapi/workingcopy/add.ts
+++ b/packages/client/src/restapi/workingcopy/add.ts
@@ -1,10 +1,10 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
-import { CreateWorkingcopyResponse } from '@plone/types';
+import type { CreateWorkingcopyResponse } from '@plone/types';
export const createWorkingcopyArgsSchema = z.object({
path: z.string(),
diff --git a/packages/client/src/restapi/workingcopy/check-in.ts b/packages/client/src/restapi/workingcopy/check-in.ts
index d2c91d7c81..36627ac325 100644
--- a/packages/client/src/restapi/workingcopy/check-in.ts
+++ b/packages/client/src/restapi/workingcopy/check-in.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/workingcopy/delete.ts b/packages/client/src/restapi/workingcopy/delete.ts
index 9efab73607..11db8c026c 100644
--- a/packages/client/src/restapi/workingcopy/delete.ts
+++ b/packages/client/src/restapi/workingcopy/delete.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
+import { apiRequest, type ApiRequestParams } from '../../API';
import { z } from 'zod';
import {
- PloneClientConfig,
+ type PloneClientConfig,
PloneClientConfigSchema,
} from '../../validation/config';
diff --git a/packages/client/src/restapi/workingcopy/get.ts b/packages/client/src/restapi/workingcopy/get.ts
index 1149d49863..4426a0314a 100644
--- a/packages/client/src/restapi/workingcopy/get.ts
+++ b/packages/client/src/restapi/workingcopy/get.ts
@@ -1,7 +1,7 @@
-import { apiRequest, ApiRequestParams } from '../../API';
-import { PloneClientConfig } from '../../validation/config';
import { z } from 'zod';
-import { GetWorkingcopyResponse } from '@plone/types';
+import { apiRequest, type ApiRequestParams } from '../../API';
+import type { PloneClientConfig } from '../../validation/config';
+import type { GetWorkingcopyResponse } from '@plone/types';
const getWorkingcopySchema = z.object({
path: z.string(),
diff --git a/packages/client/src/utils/misc.ts b/packages/client/src/utils/misc.ts
index 6486c48f7d..d668d3971e 100644
--- a/packages/client/src/utils/misc.ts
+++ b/packages/client/src/utils/misc.ts
@@ -4,7 +4,7 @@ import {
useMutation,
useQuery,
} from '@tanstack/react-query';
-import { PloneClientConfig } from '../validation/config';
+import type { PloneClientConfig } from '../validation/config';
/*
configGetter is required instead of using the config directly to make sure
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
index dfe27120f2..8d104e76ee 100644
--- a/packages/client/tsconfig.json
+++ b/packages/client/tsconfig.json
@@ -1,21 +1,28 @@
{
"compilerOptions": {
- "target": "ESNext",
- "useDefineForClassFields": true,
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "allowJs": false,
- "skipLibCheck": true,
"esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "module": "ESNext",
- "moduleResolution": "Node",
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
"resolveJsonModule": true,
+ "moduleDetection": "force",
"isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
"noEmit": true,
+
"jsx": "react-jsx",
- "types": ["vite", "vitest/globals"]
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "strictPropertyInitialization": false,
+
+ "useDefineForClassFields": true
},
"exclude": ["node_modules", "dist", "coverage", "src/**/*.test.{ts,tsx}"],
"include": ["src"],
diff --git a/packages/client/tsup.config.ts b/packages/client/tsup.config.ts
new file mode 100644
index 0000000000..82b88a425c
--- /dev/null
+++ b/packages/client/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/components/.gitignore b/packages/components/.gitignore
index ead174f021..3fbb0086d7 100644
--- a/packages/components/.gitignore
+++ b/packages/components/.gitignore
@@ -12,5 +12,3 @@ lib
!.yarn/releases
!.yarn/sdks
!.yarn/versions
-
-.parcel-cache/
diff --git a/packages/components/.parcelrc b/packages/components/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/components/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 2ae08a29e3..8b6360ff57 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -8,6 +8,24 @@
+## 2.2.0 (2024-11-21)
+
+### Feature
+
+- Update RAC to 1.5.0 @sneridagh [#6498](https://github.com/plone/volto/issues/6498)
+
+## 2.1.1 (2024-11-05)
+
+### Internal
+
+- Improve packaging and bring back the export for `src` folder. @sneridagh
+
+## 2.1.0 (2024-11-05)
+
+### Internal
+
+- Replace `parcel` with `tsup`. Better types, better tsconfig. Move to ESM. @sneridagh [#6467](https://github.com/plone/volto/issues/6467)
+
## 2.0.0 (2024-10-31)
### Internal
diff --git a/packages/components/package.json b/packages/components/package.json
index d76dbe9972..18004f5337 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -8,7 +8,7 @@
}
],
"license": "MIT",
- "version": "2.0.0",
+ "version": "2.2.0",
"repository": {
"type": "git",
"url": "http://github.com/plone/components.git"
@@ -16,20 +16,18 @@
"bugs": {
"url": "https://github.com/plone/components/issues"
},
+ "type": "module",
"files": [
"dist",
"src",
"README.md"
],
- "source": "./src/index.ts",
- "main": "./dist/main.js",
- "module": "./dist/module.mjs",
- "types": "./dist/index.d.ts",
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/module.mjs",
- "require": "./dist/main.js"
+ "import": "./dist/index.js",
+ "default": "./dist/index.cjs"
},
"./dist/*.css": "./dist/*.css",
"./src/*": "./src/*"
@@ -45,12 +43,13 @@
"quanta"
],
"scripts": {
- "build": "parcel build && pnpm build:css",
- "build:force": "parcel build --no-cache && pnpm build:css",
+ "build": "tsup && pnpm build:css",
+ "build:force": "tsup && pnpm build:css",
"build:css": "pnpm build:basic && pnpm build:quanta",
"build:basic": "lightningcss --browserslist --bundle --sourcemap src/styles/basic/main.css -o basic.css && mv basic.css* dist/.",
"build:quanta": "lightningcss --browserslist --bundle --sourcemap src/styles/quanta/main.css -o quanta.css && mv quanta.css* dist/.",
- "check-ts": "tsc --project tsconfig.json",
+ "check:exports": "attw --pack .",
+ "check:ts": "tsc --project tsconfig.json",
"test": "vitest --passWithNoTests",
"coverage": "vitest run --coverage --no-threads",
"lint": "pnpm eslint && pnpm prettier && pnpm stylelint && pnpm check-ts",
@@ -77,12 +76,7 @@
"not dead"
],
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace: *",
"@react-types/shared": "^3.22.0",
"@storybook/addon-essentials": "^8.0.4",
@@ -107,20 +101,20 @@
"jsdom": "^22.1.0",
"lightningcss": "^1.24.0",
"lightningcss-cli": "^1.24.0",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"storybook": "^8.0.4",
+ "tsup": "^8.3.5",
"typescript": "^5.6.3",
"vite": "^5.4.8",
"vitest": "^2.1.3",
"vitest-axe": "^0.1.0"
},
"dependencies": {
- "@react-aria/utils": "^3.25.3",
- "@react-spectrum/utils": "^3.11.11",
+ "@react-aria/utils": "^3.26.0",
+ "@react-spectrum/utils": "^3.12.0",
"@storybook/test": "^8.0.4",
"clsx": "^2.1.1",
- "react-aria-components": "^1.4.0"
+ "react-aria-components": "^1.5.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
diff --git a/packages/components/src/components/BlockToolbar/BlockToolbar.tsx b/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
index 62f1b47922..f76ba5ef83 100644
--- a/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
+++ b/packages/components/src/components/BlockToolbar/BlockToolbar.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Toolbar as RACToolbar, ToolbarProps } from 'react-aria-components';
+import {
+ Toolbar as RACToolbar,
+ type ToolbarProps,
+} from 'react-aria-components';
export function BlockToolbar(props: ToolbarProps) {
return ;
diff --git a/packages/components/src/components/Button/Button.tsx b/packages/components/src/components/Button/Button.tsx
index 9e8c76b371..c466e8a1dd 100644
--- a/packages/components/src/components/Button/Button.tsx
+++ b/packages/components/src/components/Button/Button.tsx
@@ -1,5 +1,5 @@
-import React, { forwardRef, ForwardedRef } from 'react';
-import { Button as RACButton, ButtonProps } from 'react-aria-components';
+import React, { forwardRef, type ForwardedRef } from 'react';
+import { Button as RACButton, type ButtonProps } from 'react-aria-components';
export const Button = forwardRef(function _Button(
props: ButtonProps,
diff --git a/packages/components/src/components/Calendar/Calendar.tsx b/packages/components/src/components/Calendar/Calendar.tsx
index 37f3b489c4..faf32a12d7 100644
--- a/packages/components/src/components/Calendar/Calendar.tsx
+++ b/packages/components/src/components/Calendar/Calendar.tsx
@@ -4,8 +4,8 @@ import {
Calendar as RACCalendar,
CalendarCell,
CalendarGrid,
- CalendarProps as RACCalendarProps,
- DateValue,
+ type CalendarProps as RACCalendarProps,
+ type DateValue,
Heading,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/Checkbox/Checkbox.tsx b/packages/components/src/components/Checkbox/Checkbox.tsx
index a24407dd5b..47eaca3ef5 100644
--- a/packages/components/src/components/Checkbox/Checkbox.tsx
+++ b/packages/components/src/components/Checkbox/Checkbox.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
Checkbox as RACCheckbox,
- CheckboxProps as RACCheckboxProps,
+ type CheckboxProps as RACCheckboxProps,
} from 'react-aria-components';
interface CheckboxProps extends RACCheckboxProps {
diff --git a/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx b/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
index 0c29059708..d0106355dd 100644
--- a/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
+++ b/packages/components/src/components/CheckboxGroup/CheckboxGroup.tsx
@@ -1,10 +1,10 @@
import React from 'react';
import {
CheckboxGroup as RACCheckboxGroup,
- CheckboxGroupProps as RACCheckboxGroupProps,
+ type CheckboxGroupProps as RACCheckboxGroupProps,
FieldError,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface CheckboxGroupProps
diff --git a/packages/components/src/components/ColorArea/ColorArea.tsx b/packages/components/src/components/ColorArea/ColorArea.tsx
index 8464766f70..3f060b6721 100644
--- a/packages/components/src/components/ColorArea/ColorArea.tsx
+++ b/packages/components/src/components/ColorArea/ColorArea.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import {
ColorArea as RACColorArea,
- ColorAreaProps,
+ type ColorAreaProps,
ColorThumb,
} from 'react-aria-components';
diff --git a/packages/components/src/components/ColorField/ColorField.tsx b/packages/components/src/components/ColorField/ColorField.tsx
index fff7c0f912..ac3f00f62e 100644
--- a/packages/components/src/components/ColorField/ColorField.tsx
+++ b/packages/components/src/components/ColorField/ColorField.tsx
@@ -1,12 +1,12 @@
import * as React from 'react';
import {
ColorField as RACColorField,
- ColorFieldProps as RACColorFieldProps,
+ type ColorFieldProps as RACColorFieldProps,
FieldError,
Input,
Label,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface ColorFieldProps extends RACColorFieldProps {
diff --git a/packages/components/src/components/ColorPicker/ColorPicker.tsx b/packages/components/src/components/ColorPicker/ColorPicker.tsx
index b5256e24c2..4bedc19b43 100644
--- a/packages/components/src/components/ColorPicker/ColorPicker.tsx
+++ b/packages/components/src/components/ColorPicker/ColorPicker.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import {
Button,
ColorPicker as RACColorPicker,
- ColorPickerProps as RACColorPickerProps,
+ type ColorPickerProps as RACColorPickerProps,
Dialog,
DialogTrigger,
Popover,
diff --git a/packages/components/src/components/ColorSlider/ColorSlider.tsx b/packages/components/src/components/ColorSlider/ColorSlider.tsx
index 55b81b9351..de9afa32c2 100644
--- a/packages/components/src/components/ColorSlider/ColorSlider.tsx
+++ b/packages/components/src/components/ColorSlider/ColorSlider.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import {
ColorSlider as RACColorSlider,
- ColorSliderProps as RACColorSliderProps,
+ type ColorSliderProps as RACColorSliderProps,
ColorThumb,
Label,
SliderOutput,
diff --git a/packages/components/src/components/ColorSwatch/ColorSwatch.tsx b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
index b80a13c31c..1415622497 100644
--- a/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
+++ b/packages/components/src/components/ColorSwatch/ColorSwatch.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import {
ColorSwatch as RACColorSwatch,
- ColorSwatchProps,
+ type ColorSwatchProps,
} from 'react-aria-components';
export function ColorSwatch(props: ColorSwatchProps) {
diff --git a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
index 9ecdc9dac1..2d1598980a 100644
--- a/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
+++ b/packages/components/src/components/ColorSwatchPicker/ColorSwatchPicker.tsx
@@ -2,8 +2,8 @@ import * as React from 'react';
import {
ColorSwatchPicker as AriaColorSwatchPicker,
ColorSwatchPickerItem as AriaColorSwatchPickerItem,
- ColorSwatchPickerItemProps,
- ColorSwatchPickerProps,
+ type ColorSwatchPickerItemProps,
+ type ColorSwatchPickerProps,
} from 'react-aria-components';
import { ColorSwatch } from '../ColorSwatch/ColorSwatch';
diff --git a/packages/components/src/components/ColorWheel/ColorWheel.tsx b/packages/components/src/components/ColorWheel/ColorWheel.tsx
index a9dff1f543..fd601243bb 100644
--- a/packages/components/src/components/ColorWheel/ColorWheel.tsx
+++ b/packages/components/src/components/ColorWheel/ColorWheel.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import {
ColorThumb,
ColorWheel as AriaColorWheel,
- ColorWheelProps as AriaColorWheelProps,
+ type ColorWheelProps as AriaColorWheelProps,
ColorWheelTrack,
} from 'react-aria-components';
diff --git a/packages/components/src/components/ComboBox/ComboBox.tsx b/packages/components/src/components/ComboBox/ComboBox.tsx
index f3cc54422c..dae5e3767d 100644
--- a/packages/components/src/components/ComboBox/ComboBox.tsx
+++ b/packages/components/src/components/ComboBox/ComboBox.tsx
@@ -2,16 +2,16 @@ import React from 'react';
import {
Button,
ComboBox as RACComboBox,
- ComboBoxProps as RACComboBoxProps,
+ type ComboBoxProps as RACComboBoxProps,
FieldError,
Input,
Label,
ListBox,
ListBoxItem,
- ListBoxItemProps,
+ type ListBoxItemProps,
Popover,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface ComboBoxProps
diff --git a/packages/components/src/components/DateField/DateField.tsx b/packages/components/src/components/DateField/DateField.tsx
index 8f41e0a680..6bbca4b9c8 100644
--- a/packages/components/src/components/DateField/DateField.tsx
+++ b/packages/components/src/components/DateField/DateField.tsx
@@ -1,14 +1,14 @@
import React from 'react';
import {
DateField as RACDateField,
- DateFieldProps as RACDateFieldProps,
+ type DateFieldProps as RACDateFieldProps,
DateInput,
DateSegment,
- DateValue,
+ type DateValue,
FieldError,
Label,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DateFieldProps
diff --git a/packages/components/src/components/DatePicker/DatePicker.tsx b/packages/components/src/components/DatePicker/DatePicker.tsx
index b49bc7e493..9db14f1278 100644
--- a/packages/components/src/components/DatePicker/DatePicker.tsx
+++ b/packages/components/src/components/DatePicker/DatePicker.tsx
@@ -6,9 +6,9 @@ import {
CalendarGrid,
DateInput,
DatePicker as RACDatePicker,
- DatePickerProps as RACDatePickerProps,
+ type DatePickerProps as RACDatePickerProps,
DateSegment,
- DateValue,
+ type DateValue,
Dialog,
FieldError,
Group,
@@ -16,7 +16,7 @@ import {
Label,
Popover,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DatePickerProps
diff --git a/packages/components/src/components/DateRangePicker/DateRangePicker.tsx b/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
index 55b76c5fde..9a00fd0a44 100644
--- a/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
+++ b/packages/components/src/components/DateRangePicker/DateRangePicker.tsx
@@ -5,9 +5,9 @@ import {
CalendarGrid,
DateInput,
DateRangePicker as RACDateRangePicker,
- DateRangePickerProps as RACDateRangePickerProps,
+ type DateRangePickerProps as RACDateRangePickerProps,
DateSegment,
- DateValue,
+ type DateValue,
Dialog,
FieldError,
Group,
@@ -16,7 +16,7 @@ import {
Popover,
RangeCalendar,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface DateRangePickerProps
diff --git a/packages/components/src/components/Dialog/Dialog.tsx b/packages/components/src/components/Dialog/Dialog.tsx
index 39de5afd8e..503f87f9d5 100644
--- a/packages/components/src/components/Dialog/Dialog.tsx
+++ b/packages/components/src/components/Dialog/Dialog.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Dialog as RACDialog, DialogProps } from 'react-aria-components';
+import { Dialog as RACDialog, type DialogProps } from 'react-aria-components';
export function Dialog(props: DialogProps) {
return ;
diff --git a/packages/components/src/components/Disclosure/Disclosure.stories.tsx b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
index 6f41082267..c318a693b4 100644
--- a/packages/components/src/components/Disclosure/Disclosure.stories.tsx
+++ b/packages/components/src/components/Disclosure/Disclosure.stories.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import { Disclosure } from './Disclosure';
import {
Button,
- UNSTABLE_DisclosurePanel as DisclosurePanel,
+ DisclosurePanel as DisclosurePanel,
} from 'react-aria-components';
import type { Meta, StoryObj } from '@storybook/react';
diff --git a/packages/components/src/components/Disclosure/Disclosure.tsx b/packages/components/src/components/Disclosure/Disclosure.tsx
index 8e062a8d61..7322a5818b 100644
--- a/packages/components/src/components/Disclosure/Disclosure.tsx
+++ b/packages/components/src/components/Disclosure/Disclosure.tsx
@@ -1,14 +1,11 @@
import * as React from 'react';
import {
- UNSTABLE_Disclosure as RACDisclosure,
- DisclosureProps,
+ Disclosure as RACDisclosure,
+ type DisclosureProps,
} from 'react-aria-components';
/**
* A Disclosure is used to show or hide content that is not visible by default.
- *
- * NOTE: This component is in alpha in RAC thus it's unstable and is subjects of change
- * in the API, behavior, and appearance.
*/
export function Disclosure(props: DisclosureProps) {
return ;
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
index 1871ade147..c51881e461 100644
--- a/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.stories.tsx
@@ -2,8 +2,8 @@ import * as React from 'react';
import { DisclosureGroup } from './DisclosureGroup';
import {
Button,
- UNSTABLE_Disclosure as Disclosure,
- UNSTABLE_DisclosurePanel as DisclosurePanel,
+ Disclosure as Disclosure,
+ DisclosurePanel as DisclosurePanel,
} from 'react-aria-components';
import type { Meta, StoryObj } from '@storybook/react';
diff --git a/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
index 4196e558c6..980950ee75 100644
--- a/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
+++ b/packages/components/src/components/DisclosureGroup/DisclosureGroup.tsx
@@ -1,14 +1,11 @@
import * as React from 'react';
import {
- UNSTABLE_DisclosureGroup as RACDisclosureGroup,
- DisclosureGroupProps,
+ DisclosureGroup as RACDisclosureGroup,
+ type DisclosureGroupProps,
} from 'react-aria-components';
/**
* A DisclosureGroup is used to group Disclosures together to create an accordion.
- *
- * NOTE: This component is in alpha in RAC thus it's unstable and is subjects of change
- * in the API, behavior, and appearance.
*/
export function DisclosureGroup(props: DisclosureGroupProps) {
return ;
diff --git a/packages/components/src/components/Form/Form.tsx b/packages/components/src/components/Form/Form.tsx
index f2f2e9b70a..03fd907bef 100644
--- a/packages/components/src/components/Form/Form.tsx
+++ b/packages/components/src/components/Form/Form.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Form as RACForm, FormProps } from 'react-aria-components';
+import { Form as RACForm, type FormProps } from 'react-aria-components';
export function Form(props: FormProps) {
return ;
diff --git a/packages/components/src/components/GridList/GridList.tsx b/packages/components/src/components/GridList/GridList.tsx
index d195b71cfd..088235641d 100644
--- a/packages/components/src/components/GridList/GridList.tsx
+++ b/packages/components/src/components/GridList/GridList.tsx
@@ -3,8 +3,8 @@ import {
Button,
GridList as RACGridList,
GridListItem as RACGridListItem,
- GridListItemProps,
- GridListProps,
+ type GridListItemProps,
+ type GridListProps,
} from 'react-aria-components';
import { Checkbox } from '../Checkbox/Checkbox';
diff --git a/packages/components/src/components/Icon/Icon.tsx b/packages/components/src/components/Icon/Icon.tsx
index 0056688024..c55a91521e 100644
--- a/packages/components/src/components/Icon/Icon.tsx
+++ b/packages/components/src/components/Icon/Icon.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement } from 'react';
+import React, { type ReactElement } from 'react';
import type {
AriaLabelingProps,
DOMProps,
@@ -7,7 +7,7 @@ import type {
} from '@react-types/shared';
import {
baseStyleProps,
- StyleHandlers,
+ type StyleHandlers,
useSlotProps,
useStyleProps,
} from '@react-spectrum/utils';
diff --git a/packages/components/src/components/ListBox/ListBox.tsx b/packages/components/src/components/ListBox/ListBox.tsx
index ac90dbe68f..244efb16a4 100644
--- a/packages/components/src/components/ListBox/ListBox.tsx
+++ b/packages/components/src/components/ListBox/ListBox.tsx
@@ -2,8 +2,8 @@ import React from 'react';
import {
ListBox as RACListBox,
ListBoxItem as RACListBoxItem,
- ListBoxItemProps,
- ListBoxProps,
+ type ListBoxItemProps,
+ type ListBoxProps,
} from 'react-aria-components';
export function ListBox({
diff --git a/packages/components/src/components/Menu/Menu.tsx b/packages/components/src/components/Menu/Menu.tsx
index db153971a6..14834205cd 100644
--- a/packages/components/src/components/Menu/Menu.tsx
+++ b/packages/components/src/components/Menu/Menu.tsx
@@ -2,12 +2,12 @@ import React from 'react';
import {
Menu as RACMenu,
MenuItem as RACMenuItem,
- MenuItemProps,
- MenuProps,
+ type MenuItemProps,
+ type MenuProps,
MenuTrigger,
- MenuTriggerProps,
+ type MenuTriggerProps,
Popover,
- PressEvent,
+ type PressEvent,
} from 'react-aria-components';
import { Button } from '../Button/Button';
diff --git a/packages/components/src/components/Meter/Meter.tsx b/packages/components/src/components/Meter/Meter.tsx
index fb06f8c57c..71da9815c6 100644
--- a/packages/components/src/components/Meter/Meter.tsx
+++ b/packages/components/src/components/Meter/Meter.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
Label,
Meter as RACMeter,
- MeterProps as RACMeterProps,
+ type MeterProps as RACMeterProps,
} from 'react-aria-components';
export interface MeterProps extends RACMeterProps {
diff --git a/packages/components/src/components/Modal/Modal.tsx b/packages/components/src/components/Modal/Modal.tsx
index 83eb2e061c..2ef1c970cb 100644
--- a/packages/components/src/components/Modal/Modal.tsx
+++ b/packages/components/src/components/Modal/Modal.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Modal as RACModal, ModalOverlayProps } from 'react-aria-components';
+import {
+ Modal as RACModal,
+ type ModalOverlayProps,
+} from 'react-aria-components';
export function Modal(props: ModalOverlayProps) {
return ;
diff --git a/packages/components/src/components/NumberField/NumberField.tsx b/packages/components/src/components/NumberField/NumberField.tsx
index 98781966fe..462910c32d 100644
--- a/packages/components/src/components/NumberField/NumberField.tsx
+++ b/packages/components/src/components/NumberField/NumberField.tsx
@@ -5,9 +5,9 @@ import {
Input,
Label,
NumberField as RACNumberField,
- NumberFieldProps as RACNumberFieldProps,
+ type NumberFieldProps as RACNumberFieldProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
import { Button } from '../Button/Button';
import { AddIcon } from '../Icons/AddIcon';
diff --git a/packages/components/src/components/Popover/Popover.tsx b/packages/components/src/components/Popover/Popover.tsx
index a79c3d1418..8a0614c9b6 100644
--- a/packages/components/src/components/Popover/Popover.tsx
+++ b/packages/components/src/components/Popover/Popover.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
OverlayArrow,
Popover as RACPopover,
- PopoverProps as RACPopoverProps,
+ type PopoverProps as RACPopoverProps,
} from 'react-aria-components';
import { Dialog } from '../Dialog/Dialog';
diff --git a/packages/components/src/components/ProgressBar/ProgressBar.tsx b/packages/components/src/components/ProgressBar/ProgressBar.tsx
index cb72f500f5..2fa1f49ca8 100644
--- a/packages/components/src/components/ProgressBar/ProgressBar.tsx
+++ b/packages/components/src/components/ProgressBar/ProgressBar.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
Label,
ProgressBar as RACProgressBar,
- ProgressBarProps as RACProgressBarProps,
+ type ProgressBarProps as RACProgressBarProps,
} from 'react-aria-components';
export interface ProgressBarProps extends RACProgressBarProps {
diff --git a/packages/components/src/components/RadioGroup/RadioGroup.tsx b/packages/components/src/components/RadioGroup/RadioGroup.tsx
index 90fea840d4..7090d0f15a 100644
--- a/packages/components/src/components/RadioGroup/RadioGroup.tsx
+++ b/packages/components/src/components/RadioGroup/RadioGroup.tsx
@@ -3,9 +3,9 @@ import {
FieldError,
Label,
RadioGroup as RACRadioGroup,
- RadioGroupProps as RACRadioGroupProps,
+ type RadioGroupProps as RACRadioGroupProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface RadioGroupProps extends Omit {
diff --git a/packages/components/src/components/RangeCalendar/RangeCalendar.tsx b/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
index 7881028477..63e32cdcab 100644
--- a/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
+++ b/packages/components/src/components/RangeCalendar/RangeCalendar.tsx
@@ -3,10 +3,10 @@ import {
Button,
CalendarCell,
CalendarGrid,
- DateValue,
+ type DateValue,
Heading,
RangeCalendar as RACRangeCalendar,
- RangeCalendarProps as RACRangeCalendarProps,
+ type RangeCalendarProps as RACRangeCalendarProps,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/SearchField/SearchField.tsx b/packages/components/src/components/SearchField/SearchField.tsx
index 57f7946d4f..dc5c3964b3 100644
--- a/packages/components/src/components/SearchField/SearchField.tsx
+++ b/packages/components/src/components/SearchField/SearchField.tsx
@@ -5,9 +5,9 @@ import {
Input,
Label,
SearchField as RACSearchField,
- SearchFieldProps as RACSearchFieldProps,
+ type SearchFieldProps as RACSearchFieldProps,
Text,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
export interface SearchFieldProps extends RACSearchFieldProps {
diff --git a/packages/components/src/components/Select/Select.stories.tsx b/packages/components/src/components/Select/Select.stories.tsx
index 97842eda90..7adb2e0afc 100644
--- a/packages/components/src/components/Select/Select.stories.tsx
+++ b/packages/components/src/components/Select/Select.stories.tsx
@@ -4,11 +4,6 @@ import type { Meta, StoryObj } from '@storybook/react';
import '../../styles/basic/Select.css';
-export interface SelectItemObject {
- label: string;
- value: string;
-}
-
// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
const meta = {
title: 'Forms/Select',
diff --git a/packages/components/src/components/Select/Select.tsx b/packages/components/src/components/Select/Select.tsx
index d57402e238..5cb8f71488 100644
--- a/packages/components/src/components/Select/Select.tsx
+++ b/packages/components/src/components/Select/Select.tsx
@@ -5,15 +5,15 @@ import {
Label,
ListBox,
ListBoxItem,
- ListBoxItemProps,
+ type ListBoxItemProps,
Popover,
PopoverContext,
Select as RACSelect,
- SelectProps as RACSelectProps,
+ type SelectProps as RACSelectProps,
SelectValue,
Text,
useContextProps,
- ValidationResult,
+ type ValidationResult,
} from 'react-aria-components';
import { ChevrondownIcon } from '../Icons/ChevrondownIcon';
@@ -43,7 +43,7 @@ export interface SelectProps
* the data.
*
*/
-export function Select({
+export function Select({
label,
description,
errorMessage,
@@ -73,7 +73,7 @@ export function Select({
{children}
) : (
- {(item: SelectItemObject) => (
+ {(item) => (
{item.value}
)}
@@ -85,6 +85,11 @@ export function Select({
);
}
+export type SelectItemObject = {
+ label: string;
+ value: string;
+};
+
export function SelectItem(props: ListBoxItemProps) {
return ;
}
diff --git a/packages/components/src/components/Slider/Slider.tsx b/packages/components/src/components/Slider/Slider.tsx
index f09e907195..4fab667edf 100644
--- a/packages/components/src/components/Slider/Slider.tsx
+++ b/packages/components/src/components/Slider/Slider.tsx
@@ -3,7 +3,7 @@ import {
Label,
Slider as RACSlider,
SliderOutput,
- SliderProps as RACSliderProps,
+ type SliderProps as RACSliderProps,
SliderThumb,
SliderTrack,
} from 'react-aria-components';
diff --git a/packages/components/src/components/Switch/Switch.tsx b/packages/components/src/components/Switch/Switch.tsx
index 5f0f2d0b64..0cd187b092 100644
--- a/packages/components/src/components/Switch/Switch.tsx
+++ b/packages/components/src/components/Switch/Switch.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
Switch as RACSwitch,
- SwitchProps as RACSwitchProps,
+ type SwitchProps as RACSwitchProps,
} from 'react-aria-components';
export interface SwitchProps extends Omit {
diff --git a/packages/components/src/components/Tabs/Tabs.tsx b/packages/components/src/components/Tabs/Tabs.tsx
index a0d4ab8cfd..7190ee2cee 100644
--- a/packages/components/src/components/Tabs/Tabs.tsx
+++ b/packages/components/src/components/Tabs/Tabs.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Tabs as RACTabs, TabsProps } from 'react-aria-components';
+import { Tabs as RACTabs, type TabsProps } from 'react-aria-components';
export function Tabs(props: TabsProps) {
return ;
diff --git a/packages/components/src/components/TagGroup/TagGroup.tsx b/packages/components/src/components/TagGroup/TagGroup.tsx
index 1424dea383..05230519f1 100644
--- a/packages/components/src/components/TagGroup/TagGroup.tsx
+++ b/packages/components/src/components/TagGroup/TagGroup.tsx
@@ -4,10 +4,10 @@ import {
Label,
Tag as RACTag,
TagGroup as RACTagGroup,
- TagGroupProps as RACTagGroupProps,
+ type TagGroupProps as RACTagGroupProps,
TagList,
- TagListProps,
- TagProps,
+ type TagListProps,
+ type TagProps,
Text,
} from 'react-aria-components';
diff --git a/packages/components/src/components/TextAreaField/TextAreaField.tsx b/packages/components/src/components/TextAreaField/TextAreaField.tsx
index e79f5e78bb..76ee55ea8d 100644
--- a/packages/components/src/components/TextAreaField/TextAreaField.tsx
+++ b/packages/components/src/components/TextAreaField/TextAreaField.tsx
@@ -5,8 +5,8 @@ import {
Label,
Text,
TextField as RACTextField,
- TextFieldProps as RACTextFieldProps,
- ValidationResult,
+ type TextFieldProps as RACTextFieldProps,
+ type ValidationResult,
} from 'react-aria-components';
export interface TextAreaFieldProps extends RACTextFieldProps {
diff --git a/packages/components/src/components/TextField/TextField.tsx b/packages/components/src/components/TextField/TextField.tsx
index 2b29fa3e79..1190aea2a1 100644
--- a/packages/components/src/components/TextField/TextField.tsx
+++ b/packages/components/src/components/TextField/TextField.tsx
@@ -5,8 +5,8 @@ import {
Label,
Text,
TextField as RACTextField,
- TextFieldProps as RACTextFieldProps,
- ValidationResult,
+ type TextFieldProps as RACTextFieldProps,
+ type ValidationResult,
} from 'react-aria-components';
export interface TextFieldProps extends RACTextFieldProps {
diff --git a/packages/components/src/components/TimeField/TimeField.tsx b/packages/components/src/components/TimeField/TimeField.tsx
index 4133c46c1c..c92e531326 100644
--- a/packages/components/src/components/TimeField/TimeField.tsx
+++ b/packages/components/src/components/TimeField/TimeField.tsx
@@ -6,9 +6,9 @@ import {
Label,
Text,
TimeField as RACTimeField,
- TimeFieldProps as RACTimeFieldProps,
- TimeValue,
- ValidationResult,
+ type TimeFieldProps as RACTimeFieldProps,
+ type TimeValue,
+ type ValidationResult,
} from 'react-aria-components';
export interface TimeFieldProps
diff --git a/packages/components/src/components/ToggleButton/ToggleButton.tsx b/packages/components/src/components/ToggleButton/ToggleButton.tsx
index f7599fd6af..546d47fc2b 100644
--- a/packages/components/src/components/ToggleButton/ToggleButton.tsx
+++ b/packages/components/src/components/ToggleButton/ToggleButton.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import {
ToggleButton as RACToggleButton,
- ToggleButtonProps,
+ type ToggleButtonProps,
} from 'react-aria-components';
export function ToggleButton(props: ToggleButtonProps) {
diff --git a/packages/components/src/components/Toolbar/Toolbar.tsx b/packages/components/src/components/Toolbar/Toolbar.tsx
index f2b151bcce..b99af182f4 100644
--- a/packages/components/src/components/Toolbar/Toolbar.tsx
+++ b/packages/components/src/components/Toolbar/Toolbar.tsx
@@ -1,5 +1,8 @@
import React from 'react';
-import { Toolbar as RACToolbar, ToolbarProps } from 'react-aria-components';
+import {
+ Toolbar as RACToolbar,
+ type ToolbarProps,
+} from 'react-aria-components';
export function Toolbar(props: ToolbarProps) {
return ;
diff --git a/packages/components/src/components/Tooltip/Tooltip.tsx b/packages/components/src/components/Tooltip/Tooltip.tsx
index 17213e6ce3..b629c5f39e 100644
--- a/packages/components/src/components/Tooltip/Tooltip.tsx
+++ b/packages/components/src/components/Tooltip/Tooltip.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import {
OverlayArrow,
Tooltip as RACTooltip,
- TooltipProps as RACTooltipProps,
+ type TooltipProps as RACTooltipProps,
} from 'react-aria-components';
export interface TooltipProps extends Omit {
diff --git a/packages/components/src/components/quanta/Select/Select.tsx b/packages/components/src/components/quanta/Select/Select.tsx
index 6e43494084..0f446c6b7d 100644
--- a/packages/components/src/components/quanta/Select/Select.tsx
+++ b/packages/components/src/components/quanta/Select/Select.tsx
@@ -1,8 +1,15 @@
import React from 'react';
import { SelectContext, PopoverContext } from 'react-aria-components';
-import { Select, SelectItem, SelectProps } from '../../Select/Select';
+import {
+ Select,
+ SelectItem,
+ type SelectProps,
+ type SelectItemObject,
+} from '../../Select/Select';
-export function QuantaSelect(props: SelectProps) {
+export function QuantaSelect(
+ props: SelectProps,
+) {
return (
diff --git a/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx b/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
index 449dd56d6d..b092fa86aa 100644
--- a/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
+++ b/packages/components/src/components/quanta/TextAreaField/TextAreaField.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { TextFieldContext } from 'react-aria-components';
import {
TextAreaField,
- TextAreaFieldProps,
+ type TextAreaFieldProps,
} from '../../TextAreaField/TextAreaField';
export function QuantaTextAreaField(props: TextAreaFieldProps) {
diff --git a/packages/components/src/components/quanta/TextField/TextField.tsx b/packages/components/src/components/quanta/TextField/TextField.tsx
index eb222e5078..08e8ae7df9 100644
--- a/packages/components/src/components/quanta/TextField/TextField.tsx
+++ b/packages/components/src/components/quanta/TextField/TextField.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { TextFieldContext } from 'react-aria-components';
-import { TextField, TextFieldProps } from '../../TextField/TextField';
+import { TextField, type TextFieldProps } from '../../TextField/TextField';
export function QuantaTextField(props: TextFieldProps) {
return (
diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json
index 7bb0a59429..c682dc35d7 100644
--- a/packages/components/tsconfig.json
+++ b/packages/components/tsconfig.json
@@ -1,20 +1,25 @@
{
"compilerOptions": {
- "target": "ESNext",
- "useDefineForClassFields": true,
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "allowJs": false,
- "skipLibCheck": true,
"esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "module": "ESNext",
- "moduleResolution": "Node",
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
"resolveJsonModule": true,
+ "moduleDetection": "force",
"isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
"noEmit": true,
- "jsx": "react",
+
+ "jsx": "react-jsx",
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true
},
"include": ["src", "./setupTesting.ts"],
"exclude": [
diff --git a/packages/components/tsup.config.ts b/packages/components/tsup.config.ts
new file mode 100644
index 0000000000..82b88a425c
--- /dev/null
+++ b/packages/components/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/components/vite.config.mts b/packages/components/vite.config.ts
similarity index 100%
rename from packages/components/vite.config.mts
rename to packages/components/vite.config.ts
diff --git a/packages/components/vitest.config.mts b/packages/components/vitest.config.ts
similarity index 100%
rename from packages/components/vitest.config.mts
rename to packages/components/vitest.config.ts
diff --git a/packages/helpers/package.json b/packages/helpers/package.json
index db697e84dc..33cf0204a5 100644
--- a/packages/helpers/package.json
+++ b/packages/helpers/package.json
@@ -29,14 +29,9 @@
"access": "public"
},
"main": "src/index.ts",
- "targets": {
- "main": {
- "includeNodeModules": false
- }
- },
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
+ "watch": "tsup",
+ "build": "tsup",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -54,15 +49,13 @@
},
"dependencies": {},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "^17.1.1",
"tsconfig": "workspace:*",
- "typescript": "^5.4.5",
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
"vitest": "^2.1.3"
}
}
diff --git a/packages/providers/.npmignore b/packages/providers/.npmignore
index 0d8afd5727..a6d10baa1e 100644
--- a/packages/providers/.npmignore
+++ b/packages/providers/.npmignore
@@ -2,7 +2,5 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
diff --git a/packages/providers/.parcelrc b/packages/providers/.parcelrc
deleted file mode 100644
index db2d15099d..0000000000
--- a/packages/providers/.parcelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@parcel/config-default",
- "transformers": {
- "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
- "@parcel/transformer-js",
- "@parcel/transformer-react-refresh-wrap"
- ]
- }
-}
diff --git a/packages/providers/CHANGELOG.md b/packages/providers/CHANGELOG.md
index c8326e298c..96a8e9b472 100644
--- a/packages/providers/CHANGELOG.md
+++ b/packages/providers/CHANGELOG.md
@@ -8,6 +8,25 @@
+## 1.0.0-alpha.6 (2024-11-21)
+
+### Feature
+
+- Update RAC to 1.5.0 @sneridagh [#6498](https://github.com/plone/volto/issues/6498)
+
+## 1.0.0-alpha.5 (2024-11-05)
+
+### Internal
+
+- Improve packaging. @sneridagh
+
+## 1.0.0-alpha.4 (2024-11-05)
+
+### Internal
+
+- Bump local `typescript` version. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+- Replace `parcel` with `tsup`. Better types, better tsconfig. Move to ESM. @sneridagh [#6468](https://github.com/plone/volto/issues/6468)
+
## 1.0.0-alpha.3 (2024-10-18)
## 1.0.0-alpha.2 (2024-10-18)
diff --git a/packages/providers/package.json b/packages/providers/package.json
index 100c519771..8a12623112 100644
--- a/packages/providers/package.json
+++ b/packages/providers/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "1.0.0-alpha.3",
+ "version": "1.0.0-alpha.6",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -28,22 +28,23 @@
"publishConfig": {
"access": "public"
},
- "source": "./src/index.ts",
- "main": "./dist/main.js",
- "module": "./dist/module.mjs",
- "types": "./dist/index.d.ts",
+ "type": "module",
+ "files": [
+ "dist",
+ "README.md"
+ ],
+ "main": "./dist/index.js",
"exports": {
+ "./package.json": "./package.json",
".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/module.mjs",
- "require": "./dist/main.js"
- },
- "./src/*": "./src/*"
+ "import": "./dist/index.js",
+ "default": "./dist/index.cjs"
+ }
},
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
- "build:force": "rm -rf dist && parcel build --no-cache",
+ "build": "tsup",
+ "build:force": "tsup",
+ "check:exports": "attw --pack .",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -64,22 +65,17 @@
"@plone/components": "workspace:*",
"@plone/registry": "workspace:*",
"@tanstack/react-query": "^5.59.0",
- "react-aria-components": "^1.4.0"
+ "react-aria-components": "^1.5.0"
},
"devDependencies": {
- "@parcel/config-default": "^2.12.0",
- "@parcel/core": "^2.12.0",
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-js": "^2.12.0",
- "@parcel/transformer-react-refresh-wrap": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
- "typescript": "^5.4.5",
+ "tsup": "^8.3.5",
+ "typescript": "^5.6.3",
"vitest": "^2.1.3"
}
}
diff --git a/packages/providers/src/AppRouter.tsx b/packages/providers/src/AppRouter.tsx
index 86b2547114..cf4005fa6b 100644
--- a/packages/providers/src/AppRouter.tsx
+++ b/packages/providers/src/AppRouter.tsx
@@ -1,4 +1,9 @@
-import React, { createContext, ReactNode, useContext, useMemo } from 'react';
+import React, {
+ createContext,
+ type ReactNode,
+ useContext,
+ useMemo,
+} from 'react';
import { RouterProvider } from 'react-aria-components';
import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
@@ -17,7 +22,7 @@ interface AppRouter {
useLocation: () => Location | undefined;
useParams: (opts?: any) => Record;
navigate: (path: string) => void;
- useHref: (to: string, options?: any) => string;
+ useHref?: (to: string, options?: any) => string;
flattenToAppURL: (path: string | undefined) => string | undefined;
}
diff --git a/packages/providers/src/PloneProvider.tsx b/packages/providers/src/PloneProvider.tsx
index 31bae3d27f..0912536a22 100644
--- a/packages/providers/src/PloneProvider.tsx
+++ b/packages/providers/src/PloneProvider.tsx
@@ -1,23 +1,26 @@
-import React, { createContext, ReactNode, useContext, useMemo } from 'react';
+import React, {
+ createContext,
+ type ReactNode,
+ useContext,
+ useMemo,
+} from 'react';
import { QueryClient } from '@tanstack/react-query';
-import { AppRouterProvider, Location } from './AppRouter';
+import { AppRouterProvider, type Location } from './AppRouter';
import { PloneClientProvider } from './PloneClient';
import PloneClient from '@plone/client';
import { flattenToAppURL as defaultFlattenToAppURL } from './utils';
interface PloneProvider {
- ploneClient: InstanceType;
- queryClient: QueryClient;
+ ploneClient?: InstanceType;
+ queryClient?: QueryClient;
useLocation: () => Location | undefined;
useParams: (opts?: any) => Record;
navigate: (path: string) => void;
- useHref: (to: string, options?: any) => string;
+ useHref?: (to: string, options?: any) => string;
flattenToAppURL: (path: string | undefined) => string | undefined;
}
const PloneProviderContext = createContext({
- ploneClient: null,
- queryClient: null,
useLocation: () => ({
href: '',
pathname: '',
diff --git a/packages/providers/tsconfig.json b/packages/providers/tsconfig.json
index 86da1e79f4..24722f2f4c 100644
--- a/packages/providers/tsconfig.json
+++ b/packages/providers/tsconfig.json
@@ -1,6 +1,27 @@
{
- "extends": "tsconfig/react-library.json",
- "include": ["src", "src/**/*.js"],
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "resolveJsonModule": true,
+ "moduleDetection": "force",
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
+ "noEmit": true,
+
+ "jsx": "react-jsx",
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["src"],
"exclude": [
"node_modules",
"build",
diff --git a/packages/providers/tsup.config.ts b/packages/providers/tsup.config.ts
new file mode 100644
index 0000000000..82b88a425c
--- /dev/null
+++ b/packages/providers/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/registry/.eslintrc.cjs b/packages/registry/.eslintrc.cjs
index 8614b4cd25..f7665b4d37 100644
--- a/packages/registry/.eslintrc.cjs
+++ b/packages/registry/.eslintrc.cjs
@@ -74,6 +74,7 @@ module.exports = {
},
rules: {
'no-prototype-builtins': 0,
+ '@typescript-eslint/ban-ts-comment': 0,
},
},
],
diff --git a/packages/registry/.gitignore b/packages/registry/.gitignore
index 2c29928dd0..290e94407a 100644
--- a/packages/registry/.gitignore
+++ b/packages/registry/.gitignore
@@ -1,4 +1,3 @@
-.parcel-cache/
dist
/bin
/lib
diff --git a/packages/registry/.npmignore b/packages/registry/.npmignore
index 0d8afd5727..7e2134f27e 100644
--- a/packages/registry/.npmignore
+++ b/packages/registry/.npmignore
@@ -2,7 +2,9 @@ news
towncrier.toml
.changelog.draft
node_modules/
-.parcel-cache
-.parcelrc
.release-it.json
.eslintrc.js
+lib
+bin
+docs
+include
diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md
index d35a65a0fa..da8626074f 100644
--- a/packages/registry/CHANGELOG.md
+++ b/packages/registry/CHANGELOG.md
@@ -8,6 +8,37 @@
+## 2.1.2 (2024-11-05)
+
+### Bugfix
+
+- Fix weird typings issue happening in docker build but not locally. @sneridagh [#6471](https://github.com/plone/volto/issues/6471)
+
+### Internal
+
+- Improve packaging. @sneridagh
+
+## 2.1.1 (2024-11-05)
+
+### Internal
+
+- Repackage registry, the previous build was including the docs. @sneridagh
+
+## 2.1.0 (2024-11-05)
+
+### Feature
+
+- Allow any type `js`, `cjs`, `mjs`, `ts` as configuration for the add-on registry. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+
+### Bugfix
+
+- Fix ERR_REQUIRE from ESM module requiring CJS module. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+- Fix types for add-on's TypeScript. Fix `.tsconfig` for Node.js side. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+
+### Internal
+
+- Replace `parcel` with `tsup` for build. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+
## 2.0.0 (2024-10-31)
### Internal
diff --git a/packages/registry/docs/conf.py b/packages/registry/docs/conf.py
index 796fc4b620..fa39be8168 100644
--- a/packages/registry/docs/conf.py
+++ b/packages/registry/docs/conf.py
@@ -177,7 +177,7 @@
# Announce that we have an opensearch plugin
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_use_opensearch
-html_use_opensearch = "https://plone-registry.readthedocs.io/" # TODO: Confirm usage of opensearch in theme
+html_use_opensearch = "https://plone-registry.readthedocs.io"
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
diff --git a/packages/registry/news/6458.bugfix b/packages/registry/news/6458.bugfix
deleted file mode 100644
index e5a6eceff0..0000000000
--- a/packages/registry/news/6458.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix ERR_REQUIRE from ESM module requiring CJS module. @sneridagh
diff --git a/packages/registry/news/6458.feature b/packages/registry/news/6458.feature
deleted file mode 100644
index 30054385d5..0000000000
--- a/packages/registry/news/6458.feature
+++ /dev/null
@@ -1 +0,0 @@
-Allow any type `js`, `cjs`, `mjs`, `ts` as configuration for the add-on registry. @sneridagh
diff --git a/packages/registry/news/6502.documentation b/packages/registry/news/6502.documentation
new file mode 100644
index 0000000000..d46a5b6816
--- /dev/null
+++ b/packages/registry/news/6502.documentation
@@ -0,0 +1 @@
+`html_use_opensearch` value must not have a trailing slash. Clean up comments. @stevepiercy
diff --git a/packages/registry/package.json b/packages/registry/package.json
index 743a6c38d1..ee487c8bf8 100644
--- a/packages/registry/package.json
+++ b/packages/registry/package.json
@@ -9,7 +9,7 @@
],
"funding": "https://github.com/sponsors/plone",
"license": "MIT",
- "version": "2.0.0",
+ "version": "2.1.2",
"repository": {
"type": "git",
"url": "https://github.com/plone/volto.git"
@@ -32,48 +32,39 @@
"access": "public"
},
"type": "module",
- "source": "src/index.ts",
- "main": "dist/index.cjs",
- "module": "dist/index.js",
- "types": "dist/types.d.ts",
+ "files": [
+ "dist",
+ "README.md",
+ "vite-plugin.*"
+ ],
+ "main": "dist/index.js",
"exports": {
- "./src/*": "./src/*.cjs",
+ "./package.json": "./package.json",
"./addon-registry": {
- "require": "./dist/cjs/addon-registry.cjs",
- "import": "./dist/esm/addon-registry.js",
- "types": "./dist/esm/addon-registry.d.ts"
+ "import": "./dist/addon-registry/addon-registry.js",
+ "default": "./dist/addon-registry/addon-registry.cjs"
},
"./create-addons-loader": {
- "require": "./dist/cjs/create-addons-loader.cjs",
- "import": "./dist/esm/create-addons-loader.js",
- "types": "./dist/esm/create-addons-loader.d.ts"
+ "import": "./dist/addon-registry/create-addons-loader.js",
+ "default": "./dist/addon-registry/create-addons-loader.cjs"
},
"./create-theme-loader": {
- "require": "./dist/cjs/create-theme-loader.cjs",
- "import": "./dist/esm/create-theme-loader.js",
- "types": "./dist/esm/create-theme-loader.d.ts"
+ "import": "./dist/addon-registry/create-theme-loader.js",
+ "default": "./dist/addon-registry/create-theme-loader.cjs"
},
"./vite-plugin": {
"import": "./vite-plugin.js",
"types": "./vite-plugin.d.ts"
},
".": {
- "types": "./dist/types.d.ts",
"import": "./dist/index.js",
- "require": "./dist/index.cjs"
- }
- },
- "targets": {
- "main": {
- "includeNodeModules": false
+ "default": "./dist/index.cjs"
}
},
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build && pnpm build:node:esm && pnpm build:node:cjs",
- "build:force": "rm -rf dist && parcel build --no-cache && pnpm build:node:esm && pnpm build:node:cjs",
- "build:node:esm": "tsc --project tsconfig.node.json || true",
- "build:node:cjs": "tsc --project tsconfig.node.json --module commonjs --moduleResolution Node --outDir dist/cjs || true && make fix-build",
+ "build": "tsup",
+ "build:force": "rm -rf dist && tsup",
+ "check:exports": "attw --pack .",
"test": "vitest",
"test:debug": "vitest --inspect-brk --no-file-parallelism registry",
"dry-release": "release-it --dry-run",
@@ -99,8 +90,7 @@
"tmp": "0.2.1"
},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
+ "@arethetypeswrong/cli": "^0.16.4",
"@plone/types": "workspace:*",
"@types/debug": "^4.1.12",
"@types/glob": "^8.1.0",
@@ -108,11 +98,11 @@
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/tmp": "^0.2.6",
- "parcel": "^2.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"release-it": "16.2.1",
"tsconfig": "workspace:*",
+ "tsup": "^8.3.5",
"typescript": "^5.6.3",
"vite": "^5.4.8",
"vitest": "^2.1.3"
diff --git a/packages/registry/src/addon-registry/addon-registry.ts b/packages/registry/src/addon-registry/addon-registry.ts
index 596d69e230..e4eb233c89 100644
--- a/packages/registry/src/addon-registry/addon-registry.ts
+++ b/packages/registry/src/addon-registry/addon-registry.ts
@@ -97,7 +97,10 @@ function buildDependencyGraph(
}
addons.forEach((loaderString) => {
- const [name, extra] = loaderString.split(':');
+ const [name, extra] = loaderString.split(':') as [
+ string,
+ string | undefined,
+ ];
if (!graph.hasNode(name)) {
graph.addNode(name, []);
}
@@ -199,7 +202,7 @@ class AddonRegistry {
this.addonNames = this.resultantMergedAddons.map(
(s: string) => s.split(':')[0],
- );
+ ) as Array;
this.packages = {};
this.customizations = new Map();
@@ -215,14 +218,14 @@ class AddonRegistry {
this.dependencyGraph = buildDependencyGraph(
[
...(Object.keys(this.coreAddons).map(
- (key) => this.coreAddons[key].package,
+ (key) => this.coreAddons[key]?.package as string,
) || []),
...this.resultantMergedAddons,
...(process.env.ADDONS ? process.env.ADDONS.split(';') : []),
],
(name) => {
this.initPublishedPackage(name);
- return this.packages[name].addons || [];
+ return this.packages[name]?.addons || [];
},
);
@@ -361,14 +364,15 @@ class AddonRegistry {
initDevelopmentPackage(name: string) {
const [baseUrl, pathsConfig] = this.getTSConfigPaths();
if (pathsConfig && pathsConfig.hasOwnProperty(name)) {
- const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name][0]}`;
+ const packagePath = `${this.projectRootPath}/${baseUrl}/${pathsConfig[name]![0]}`;
const packageJsonPath = `${getPackageBasePath(packagePath)}/package.json`;
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const innerAddons: Array = packageJson.addons || [];
const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]);
if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) {
innerAddonsNormalized.forEach((name) => {
- if (!this.addonNames.includes(name)) this.addonNames.push(name);
+ if (!this.addonNames.includes(name as string))
+ this.addonNames.push(name as string);
});
}
const pkg = {
@@ -415,7 +419,8 @@ class AddonRegistry {
const innerAddonsNormalized = innerAddons.map((s) => s.split(':')[0]);
if (this.addonNames.includes(name) && innerAddonsNormalized.length > 0) {
innerAddonsNormalized.forEach((name) => {
- if (!this.addonNames.includes(name)) this.addonNames.push(name);
+ if (!this.addonNames.includes(name as string))
+ this.addonNames.push(name as string);
});
}
const packageTSConfig = this.getTSConfigPaths(basePath);
@@ -444,7 +449,7 @@ class AddonRegistry {
// An add-on from the ADDONS env var can only be a published one
initAddonFromEnvVar(name: string) {
- const normalizedAddonName = name.split(':')[0];
+ const normalizedAddonName = name.split(':')[0] as string;
this.initPublishedPackage(normalizedAddonName);
}
@@ -460,14 +465,14 @@ class AddonRegistry {
*/
initAddonExtenders() {
this.getAddons().forEach((addon) => {
- const base = path.dirname(addon.packageJson);
+ const base = path.dirname(addon!.packageJson);
const razzlePath = path.resolve(`${base}/razzle.extend.js`);
if (fs.existsSync(razzlePath)) {
- addon.razzleExtender = razzlePath;
+ addon!.razzleExtender = razzlePath;
}
const eslintPath = path.resolve(`${base}/eslint.extend.js`);
if (fs.existsSync(eslintPath)) {
- addon.eslintExtender = eslintPath;
+ addon!.eslintExtender = eslintPath;
}
});
}
@@ -483,13 +488,13 @@ class AddonRegistry {
getAddonExtenders() {
return this.getAddons()
- .map((o) => o.razzleExtender)
+ .map((o) => o?.razzleExtender)
.filter((e) => e);
}
getEslintExtenders() {
return this.getAddons()
- .map((o) => o.eslintExtender)
+ .map((o) => o?.eslintExtender)
.filter((e) => e);
}
@@ -503,11 +508,11 @@ class AddonRegistry {
};
this.getAddonDependencies().forEach((addon) => {
- const normalizedAddonName = addon.split(':')[0];
+ const normalizedAddonName = addon.split(':')[0] as string;
// We have two possible insertion points, variables and main
- const customThemeVariables = `${this.packages[normalizedAddonName].modulePath}/theme/_variables.scss`;
- const customThemeMain = `${this.packages[normalizedAddonName].modulePath}/theme/_main.scss`;
+ const customThemeVariables = `${this.packages[normalizedAddonName]?.modulePath}/theme/_variables.scss`;
+ const customThemeMain = `${this.packages[normalizedAddonName]?.modulePath}/theme/_main.scss`;
if (
fs.existsSync(customThemeVariables) &&
normalizedAddonName !== this.theme
@@ -555,12 +560,12 @@ class AddonRegistry {
getResolveAliases() {
const pairs: [string, string][] = Object.keys(this.packages).map((o) => [
o,
- this.packages[o].modulePath,
+ this.packages[o]?.modulePath || '',
]);
let aliasesFromTSPaths = {};
Object.keys(this.packages).forEach((o) => {
- if (this.packages[o].tsConfigPaths) {
+ if (this.packages[o]?.tsConfigPaths) {
aliasesFromTSPaths = {
...aliasesFromTSPaths,
...this.getAliasesFromTSConfig(
@@ -734,8 +739,8 @@ class AddonRegistry {
aliases = {
...aliases,
...this.getCustomizationPaths(
- JSON.parse(fs.readFileSync(addon.packageJson, 'utf-8')),
- getPackageBasePath(addon.modulePath),
+ JSON.parse(fs.readFileSync(addon!.packageJson, 'utf-8')),
+ getPackageBasePath(addon!.modulePath),
),
};
});
@@ -805,7 +810,7 @@ class AddonRegistry {
if (!seen.has(dep)) {
seen.add(dep);
- queue.push(dep);
+ queue.push(dep as string);
}
out += ` "${name}" -> "${dep}"\n`;
}
diff --git a/packages/registry/src/addon-registry/create-addons-loader.ts b/packages/registry/src/addon-registry/create-addons-loader.ts
index 25b7d97545..cd5b4f6989 100644
--- a/packages/registry/src/addon-registry/create-addons-loader.ts
+++ b/packages/registry/src/addon-registry/create-addons-loader.ts
@@ -46,10 +46,11 @@ Instead, change the "addons" setting in your package.json file.
let extras: string[] | string[][] = []; // TODO: Improve this typing
const addonConfigLoadInfo = addonConfigString.split(':');
const pkgName = addonConfigLoadInfo[0];
- const defaultImport = nameFromPackage(pkgName);
+ const defaultImport = nameFromPackage(pkgName as string);
if (addonConfigLoadInfo.length > 1) {
extras = addonConfigLoadInfo[1].split(',');
}
+ // @ts-expect-error This forEach is a complete mess
extras = extras.map((name) => [name, `${name}${counter++}`]);
const line = `import ${defaultImport}${
extras.length
@@ -124,8 +125,7 @@ export function createAddonsLoader(
}
const code = getAddonsLoaderCode(addons, addonsInfo, loadProjectConfig);
- // @ts-expect-error No clue why it's complaining
- fs.writeFileSync(addonsLoaderPath, Buffer.from(code));
+ fs.writeFileSync(addonsLoaderPath, code);
return addonsLoaderPath;
}
diff --git a/packages/registry/src/addon-registry/create-theme-loader.ts b/packages/registry/src/addon-registry/create-theme-loader.ts
index d5f4dbbc24..cd5cde78d4 100644
--- a/packages/registry/src/addon-registry/create-theme-loader.ts
+++ b/packages/registry/src/addon-registry/create-theme-loader.ts
@@ -22,7 +22,10 @@ function nameFromPackage(name: string) {
* Creates a static file with code necessary to load the addons configuration
*
*/
-function getThemeLoaderCode(name, customThemeAddons = []) {
+function getThemeLoaderCode(
+ name: string,
+ customThemeAddons: Array = [],
+) {
let buf = `/*
This file is autogenerated. Don't change it directly.
Add a ./theme/_${name}.scss in your add-on to load your theme customizations in the current theme.
@@ -49,14 +52,9 @@ export function createThemeAddonsLoader({
const addonsThemeLoaderMainPath = tmp.tmpNameSync({ postfix: '.scss' });
fs.writeFileSync(
addonsThemeLoaderVariablesPath,
- //@ts-expect-error No clue why this is erroring
- new Buffer.from(getThemeLoaderCode('variables', variables)),
- );
- fs.writeFileSync(
- addonsThemeLoaderMainPath,
- //@ts-expect-error No clue why this is erroring
- new Buffer.from(getThemeLoaderCode('main', main)),
+ getThemeLoaderCode('variables', variables),
);
+ fs.writeFileSync(addonsThemeLoaderMainPath, getThemeLoaderCode('main', main));
return [addonsThemeLoaderVariablesPath, addonsThemeLoaderMainPath];
}
diff --git a/packages/registry/tsconfig.json b/packages/registry/tsconfig.json
index 1319106020..5ab3185eba 100644
--- a/packages/registry/tsconfig.json
+++ b/packages/registry/tsconfig.json
@@ -1,23 +1,30 @@
{
"compilerOptions": {
- "target": "ESNext",
- "lib": ["DOM", "DOM.Iterable", "ESNext"],
- "module": "commonjs",
- "allowJs": true,
- "skipLibCheck": true,
"esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "strictPropertyInitialization": false,
- "moduleResolution": "Node",
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
"resolveJsonModule": true,
+ "moduleDetection": "force",
"isolatedModules": true,
+ "verbatimModuleSyntax": true,
+
+ "strict": true,
+ "noImplicitOverride": true,
+
+ "lib": ["es2022", "dom", "dom.iterable"],
+ "module": "preserve",
"noEmit": true,
+
"jsx": "react-jsx",
+
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "strictPropertyInitialization": false,
+
"paths": {}
},
- "include": ["src/index.ts", "src/**/*.js", "src/**/*.cjs"],
+ "include": ["src/index.ts"],
"exclude": [
"node_modules",
"build",
@@ -26,5 +33,6 @@
"src/**/*.test.{js,jsx,ts,tsx}",
"src/**/*.spec.{js,jsx,ts,tsx}",
"src/**/*.stories.{js,jsx,ts,tsx}"
- ]
+ ],
+ "references": [{ "path": "./tsconfig.node.json" }]
}
diff --git a/packages/registry/tsconfig.node.json b/packages/registry/tsconfig.node.json
index a25380a557..72d408d611 100644
--- a/packages/registry/tsconfig.node.json
+++ b/packages/registry/tsconfig.node.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "composite": true,
/* Base Options: */
"esModuleInterop": true,
"skipLibCheck": true,
@@ -10,11 +11,10 @@
"isolatedModules": true,
/* Strictness */
"strict": true,
- "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
/* If transpiling with TypeScript: */
- "moduleResolution": "NodeNext",
- "module": "NodeNext",
- "outDir": "dist/esm",
+ "module": "Preserve",
+ "outDir": "dist",
"sourceMap": true,
/* If your code doesn't run in the DOM: */
"lib": ["es2022"],
diff --git a/packages/registry/tsup.config.ts b/packages/registry/tsup.config.ts
new file mode 100644
index 0000000000..e5f2cb8fe2
--- /dev/null
+++ b/packages/registry/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig({
+ entryPoints: ['src/index.ts', 'src/addon-registry/**/*.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ outDir: 'dist',
+ clean: true,
+});
diff --git a/packages/scripts/.npmignore b/packages/scripts/.npmignore
index f7657cc352..bd26255519 100644
--- a/packages/scripts/.npmignore
+++ b/packages/scripts/.npmignore
@@ -2,6 +2,8 @@ news
towncrier.toml
.changelog.draft
node_modules/
+.release-it.json
+.eslintrc.cjs
# yarn 3
.pnp.*
diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md
index f1c4f0278b..87d5fcec90 100644
--- a/packages/scripts/CHANGELOG.md
+++ b/packages/scripts/CHANGELOG.md
@@ -8,6 +8,18 @@
+## 3.8.1 (2024-11-05)
+
+### Internal
+
+- Improve packaging. @sneridagh
+
+## 3.8.0 (2024-11-05)
+
+### Internal
+
+- Improve exports in `@plone/registry`. Adapt its detection in i18n script. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+
## 3.7.0 (2024-10-27)
### Feature
diff --git a/packages/scripts/i18n.cjs b/packages/scripts/i18n.cjs
index 439c860e67..e0c95cf399 100755
--- a/packages/scripts/i18n.cjs
+++ b/packages/scripts/i18n.cjs
@@ -283,14 +283,14 @@ function main({ addonMode }) {
fs.existsSync(
path.join(
projectRootPath,
- '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs',
+ '/node_modules/@plone/registry/dist/addon-registry/addon-registry.cjs',
),
)
) {
AddonRegistry = require(
path.join(
projectRootPath,
- '/node_modules/@plone/registry/dist/cjs/addon-registry.cjs',
+ '/node_modules/@plone/registry/dist/addon-registry/addon-registry.cjs',
),
).AddonRegistry;
// Detect where is the registry (if we are in Volto 18-alpha.46 or below)
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 412e88fcee..843127008c 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -9,7 +9,7 @@
}
],
"license": "MIT",
- "version": "3.7.0",
+ "version": "3.8.1",
"repository": {
"type": "git",
"url": "git@github.com:plone/volto.git"
diff --git a/packages/slots/package.json b/packages/slots/package.json
index 2daece72e1..9766de2823 100644
--- a/packages/slots/package.json
+++ b/packages/slots/package.json
@@ -29,14 +29,7 @@
"access": "public"
},
"main": "src/index.ts",
- "targets": {
- "main": {
- "includeNodeModules": false
- }
- },
"scripts": {
- "watch": "parcel watch",
- "build": "parcel build",
"test": "vitest",
"dry-release": "release-it --dry-run",
"release": "release-it",
@@ -57,12 +50,9 @@
"@plone/registry": "workspace:*"
},
"devDependencies": {
- "@parcel/packager-ts": "^2.12.0",
- "@parcel/transformer-typescript-types": "^2.12.0",
"@plone/types": "workspace:*",
"@types/react": "^18",
"@types/react-dom": "^18",
- "parcel": "^2.12.0",
"release-it": "17.1.1",
"tsconfig": "workspace:*",
"typescript": "^5.6.3",
diff --git a/packages/volto-slate/CHANGELOG.md b/packages/volto-slate/CHANGELOG.md
index b0c2442906..b930fd335e 100644
--- a/packages/volto-slate/CHANGELOG.md
+++ b/packages/volto-slate/CHANGELOG.md
@@ -8,6 +8,12 @@
+## 18.0.1 (2024-11-11)
+
+### Feature
+
+- Update Dutch translations. @fredvd [#6476](https://github.com/plone/volto/issues/6476)
+
## 18.0.0 (2024-10-31)
### Internal
diff --git a/packages/volto-slate/locales/nl/LC_MESSAGES/volto.po b/packages/volto-slate/locales/nl/LC_MESSAGES/volto.po
new file mode 100644
index 0000000000..beaf619400
--- /dev/null
+++ b/packages/volto-slate/locales/nl/LC_MESSAGES/volto.po
@@ -0,0 +1,148 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: \n"
+"Language: \n"
+"Language-Team: \n"
+"Content-Type: \n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#: editor/plugins/Link/index
+msgid "Add link"
+msgstr "Koppeling toevoegen"
+
+#: widgets/HtmlSlateWidget
+msgid "An error has occurred while editing "{name}" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator."
+msgstr "Er is een fout opgetreden bij het bewerken van het "{name}" veld. We zijn op de hoogte gebracht en gaan dit onderzoeken. Bewaar je werk en probeer opnieuw aub. Indien het probleem blijft, contacteer dan de site beheerder."
+
+#: widgets/RichTextWidgetView
+msgid "An error has occurred while rendering "{name}" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator."
+msgstr "Er is een fout opgetreden bij het weergeven van het "{name}" veld. We zijn op de hoogte gebracht en gaan dit onderzoeken. Indien het probleem blijft, contacteer dan de site beheerder."
+
+#: blocks/Table/TableBlockEdit
+msgid "Bottom"
+msgstr "Onderaan"
+
+#: blocks/Table/TableBlockEdit
+msgid "Center"
+msgstr "Centreren"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Delete col"
+msgstr "Kolom verwijderen"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Delete row"
+msgstr "Rij verwijderen"
+
+#: editor/plugins/Table/index
+msgid "Delete table"
+msgstr "Tabel verwijderen"
+
+#: blocks/Table/TableBlockEdit
+msgid "Divide each row into separate cells"
+msgstr "Verdeel elke rij in meerdere cellen"
+
+#: elementEditor/messages
+msgid "Edit element"
+msgstr "Element bewerken"
+
+#: editor/plugins/AdvancedLink/index
+#: editor/plugins/Link/index
+msgid "Edit link"
+msgstr "Koppeling bewerken"
+
+#: blocks/Table/TableBlockEdit
+msgid "Fixed width table cells"
+msgstr "Tabelcellen met vaste breedte"
+
+#: blocks/Table/TableBlockEdit
+msgid "Hide headers"
+msgstr "Koppen verbergen"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Insert col after"
+msgstr "Kolom rechts invoegen"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Insert col before"
+msgstr "Kolom links invoegen"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Insert row after"
+msgstr "Rij onder toevoegen"
+
+#: blocks/Table/TableBlockEdit
+#: editor/plugins/Table/index
+msgid "Insert row before"
+msgstr "Rij boven invoegen"
+
+#: blocks/Table/TableBlockEdit
+msgid "Left"
+msgstr "Links"
+
+#: blocks/Table/TableBlockEdit
+msgid "Make the table compact"
+msgstr "Maak tabel compacter"
+
+#: blocks/Table/TableBlockEdit
+msgid "Make the table sortable"
+msgstr "Maak tabel sorteerbaar"
+
+#: blocks/Table/TableBlockEdit
+msgid "Middle"
+msgstr "Midden"
+
+#: blocks/Text/SlashMenu
+msgid "No matching blocks"
+msgstr "Geen overeenkomstige blokken"
+
+#: blocks/Table/TableBlockEdit
+msgid "Reduce complexity"
+msgstr "Complexiteit verminderen"
+
+#: elementEditor/messages
+msgid "Remove element"
+msgstr "Element verwijderen"
+
+#: editor/plugins/AdvancedLink/index
+msgid "Remove link"
+msgstr "Koppeling verwijderen"
+
+#: blocks/Table/TableBlockEdit
+msgid "Right"
+msgstr "Rechts"
+
+#: blocks/Table/TableBlockEdit
+msgid "Stripe alternate rows with color"
+msgstr "Markeer afwisselend rijen met kleur"
+
+#: blocks/Table/TableBlockEdit
+msgid "Table"
+msgstr "Tabel"
+
+#: blocks/Table/TableBlockEdit
+msgid "Table color inverted"
+msgstr "Tabelkleur omgekeerd"
+
+#: blocks/Table/TableBlockEdit
+msgid "Top"
+msgstr "Top"
+
+#: blocks/Text/DefaultTextBlockEditor
+#: blocks/Text/DetachedTextBlockEditor
+msgid "Type text…"
+msgstr "Typ tekst…"
+
+#: blocks/Table/TableBlockEdit
+msgid "Visible only in view mode"
+msgstr "Enkel zichtbaar in weergavemodus"
diff --git a/packages/volto-slate/package.json b/packages/volto-slate/package.json
index d053c3c247..6113f34a3b 100644
--- a/packages/volto-slate/package.json
+++ b/packages/volto-slate/package.json
@@ -1,6 +1,6 @@
{
"name": "@plone/volto-slate",
- "version": "18.0.0",
+ "version": "18.0.1",
"description": "Slate.js integration with Volto",
"main": "src/index.js",
"author": "European Environment Agency: IDM2 A-Team",
diff --git a/packages/volto/CHANGELOG.md b/packages/volto/CHANGELOG.md
index 59175be216..040f15972a 100644
--- a/packages/volto/CHANGELOG.md
+++ b/packages/volto/CHANGELOG.md
@@ -17,6 +17,71 @@ myst:
+## 18.1.1 (2024-11-21)
+
+### Bugfix
+
+- Do not break toolbar if layout id is not registered in layoutViewsNamesMapping. @cekk [#6485](https://github.com/plone/volto/issues/6485)
+- Replace _all_ spaces with `-` in `BodyClass` classes, instead of with `-` or `` depending on the content type or section. @giuliaghisini [#6487](https://github.com/plone/volto/issues/6487)
+
+### Internal
+
+- Update instructions to install `pipx` in `RELEASING.md`. @stevepiercy [#6496](https://github.com/plone/volto/issues/6496)
+
+### Documentation
+
+- More privacy concerning youtube links and fixing link check warnings for youtube playlist links. @stevepiercy @ksuess [#4203](https://github.com/plone/volto/issues/4203)
+- Remove conflicting `searchtools.js` file from documentation to allow default Sphinx search in main Plone documentation. @stevepiercy [#6482](https://github.com/plone/volto/issues/6482)
+- Add support for sphinxcontrib-youtube. @stevepiercy [#6486](https://github.com/plone/volto/issues/6486)
+- Refactor documentation includes to align with main documentation pattern. @stevepiercy [#6495](https://github.com/plone/volto/issues/6495)
+
+## 18.1.0 (2024-11-11)
+
+### Feature
+
+- Update Dutch translations. @fredvd [#6476](https://github.com/plone/volto/issues/6476)
+
+### Bugfix
+
+- URL Management control panel: Show errors from a failed CSV upload. @davisagli [#6473](https://github.com/plone/volto/issues/6473)
+- Added missing style Helmet serialization in the HTML component to make it work in SSR. @sneridagh
+ Fix deprecation notice for the usage of apple-mobile-web-app-capable. [#6480](https://github.com/plone/volto/issues/6480)
+
+### Internal
+
+- Added React Router 7 experimental PoC. @sneridagh [#6472](https://github.com/plone/volto/issues/6472)
+
+### Documentation
+
+- Overhaul and update of the add-ons section in documentation. @sneridagh [#6397](https://github.com/plone/volto/issues/6397)
+
+## 18.0.3 (2024-11-05)
+
+### Bugfix
+
+- Fixed image generation because of a bug in the docker build with registry typings. @sneridagh [#6471](https://github.com/plone/volto/issues/6471)
+
+## 18.0.2 (2024-11-05)
+
+### Internal
+
+- Fix missing export src in @plone/components Also improve packaging in packages. @sneridagh [#6470](https://github.com/plone/volto/issues/6470)
+
+## 18.0.1 (2024-11-05)
+
+### Bugfix
+
+- Fix ERR_REQUIRE from ESM module requiring CJS module in `@plone/registry` fix tests. @sneridagh [#6458](https://github.com/plone/volto/issues/6458)
+
+### Internal
+
+- Improve exports in @plone/registry, adapt the Jest test setup. @sneridagh [#6461](https://github.com/plone/volto/issues/6461)
+- Removed `parcel` completely from core. @sneridagh [#6469](https://github.com/plone/volto/issues/6469)
+
+### Documentation
+
+- Fix display of nvm version when installing it for Contributing to Volto. @stevepiercy [#6460](https://github.com/plone/volto/issues/6460)
+
## 18.0.0 (2024-10-31)
(Summary)
diff --git a/packages/volto/cypress/tests/core/a11y/content.js b/packages/volto/cypress/tests/core/a11y/content.js
new file mode 100644
index 0000000000..c266399a4b
--- /dev/null
+++ b/packages/volto/cypress/tests/core/a11y/content.js
@@ -0,0 +1,100 @@
+describe('Accessibility Tests Content Types', () => {
+ beforeEach(() => {
+ cy.autologin();
+ cy.visit('/');
+ cy.injectAxe(); // make sure axe is available on the page
+ });
+
+ it('Event tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-event').click();
+ cy.get('.documentFirstHeading').type('Test Event Content Type');
+
+ cy.get('#toolbar-save').click();
+
+ cy.wait(1000);
+ cy.get('.ics-download').contains('Download Event').focus();
+ cy.checkA11y();
+ });
+
+ it('File tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-file').click();
+ cy.get('#field-title').type('Test File Content Type');
+ cy.get('#field-description').type(
+ 'A11y cypress test for File content type',
+ );
+
+ cy.get('input[id="field-file"]').attachFile('file.pdf', {
+ subjectType: 'input',
+ });
+
+ cy.get('#toolbar-save').focus().click();
+
+ cy.wait(1000);
+ cy.contains('file.pdf').focus();
+ cy.checkA11y();
+ });
+
+ it('Image tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-image').click();
+ cy.get('#field-title').type('Test Image Content Type');
+ cy.get('#field-description').type('Image description');
+ cy.fixture('image.png', 'base64')
+ .then((fc) => {
+ return Cypress.Blob.base64StringToBlob(fc);
+ })
+ .then((fileContent) => {
+ cy.get('input#field-image').attachFile(
+ { fileContent, fileName: 'image.png', mimeType: 'image/png' },
+ { subjectType: 'input' },
+ );
+ cy.get('#field-image-image').parent().parent().contains('image.png');
+ });
+ cy.get('#toolbar-save').click();
+
+ cy.wait(1000);
+ cy.get('#view img').should('have.attr', 'alt', 'Test Image Content Type');
+ cy.checkA11y();
+ });
+
+ it('Link tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-link').click();
+ cy.get('#field-title').type('Test Link Content Type');
+ cy.get('#field-description').type(
+ 'A11y cypress test for Link content type',
+ );
+ cy.get('#field-remoteUrl').type('https://google.com');
+ cy.get('#toolbar-save').click();
+
+ cy.wait(1000);
+ cy.get('a.external')
+ .should('have.attr', 'href', 'https://google.com')
+ .focus();
+ cy.checkA11y();
+ });
+
+ it('News Item tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-news-item').click();
+ cy.get('.documentFirstHeading').type('Test News Content Type');
+ cy.get('#field-description').type('test summary');
+ cy.get('#field-subjects').type('test');
+ cy.get('#toolbar-save').click();
+
+ cy.wait(1000);
+ cy.checkA11y();
+ });
+
+ it('Page tested for a11y axe violations', () => {
+ cy.get('#toolbar-add').click();
+ cy.get('#toolbar-add-document').click();
+ cy.get('.documentFirstHeading').type('My Page');
+ cy.get('#toolbar-save').click();
+
+ cy.wait(1000);
+ cy.checkA11y();
+ });
+});
diff --git a/packages/volto/cypress/tests/core/basic/metadata.js b/packages/volto/cypress/tests/core/basic/metadata.js
index 6d5b414aad..7c7924cd1e 100644
--- a/packages/volto/cypress/tests/core/basic/metadata.js
+++ b/packages/volto/cypress/tests/core/basic/metadata.js
@@ -41,6 +41,7 @@ describe('Add Content Tests', () => {
});
it('After removing value of widget the focus should be removed from the field', () => {
+ cy.wait(2000);
cy.get('#field-creators').type('aaa');
cy.get('#field-creators')
.type('aaa{Enter}')
diff --git a/packages/volto/locales/nl/LC_MESSAGES/volto.po b/packages/volto/locales/nl/LC_MESSAGES/volto.po
index ab60d22d27..9b1adebbf3 100644
--- a/packages/volto/locales/nl/LC_MESSAGES/volto.po
+++ b/packages/volto/locales/nl/LC_MESSAGES/volto.po
@@ -18,32 +18,32 @@ msgstr ""
#. Default: "