Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/aurora/news/+vite-optimize-deps.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `optimizeDeps.include` entries for non-addon workspace packages and app-level deps, eliminating lazy dependency discovery reloads on dev server startup. @arybakov05
35 changes: 35 additions & 0 deletions apps/aurora/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,41 @@ export default defineConfig(({ command, mode, isSsrBuild }) => {
]
: []),
] as PluginOption[],
optimizeDeps: {
include: [
// App-level deps (in apps/aurora/package.json)
'i18next',
'i18next-browser-languagedetector',
'i18next-fs-backend/cjs',
'i18next-http-backend',
Comment on lines +53 to +55
'react-i18next',
// Injected by babel-plugin-react-compiler, not in any package.json
'react/compiler-runtime',
'remix-i18next/client',
'remix-i18next/react',
'remix-i18next/server',
Comment on lines +59 to +61
// @plone/components and @plone/helpers are not registered add-ons, so
// their deps can't be declared in vite.extend.js — list them here
'@plone/components > @internationalized/date',
'@plone/components > @react-aria/utils',
'@plone/components > @react-spectrum/utils',
'@plone/components > clsx',
'@plone/components > react-aria',
'@plone/components > react-aria-components',
'@plone/components > react-aria-components/DropZone',
'@plone/components > react-aria-components/Group',
'@plone/components > react-aria-components/Modal',
'@plone/components > react-aria-components/Table',
'@plone/components > react-aria-components/Tooltip',
'@plone/components > react-aria-components/composeRenderProps',
'@plone/components > react-stately',
'@plone/components > tailwind-merge',
'@plone/components > tailwind-variants',
'@plone/helpers > jotai',
'@plone/helpers > jotai/utils',
'@plone/helpers > jotai-optics',
],
},
resolve: {
tsconfigPaths: true,
},
Expand Down
1 change: 1 addition & 0 deletions docs/development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ images
i18n
editor-slash-menu
configure-editor-block-widths
vite-optimize-deps
```
95 changes: 95 additions & 0 deletions docs/development/vite-optimize-deps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
myst:
html_meta:
"description": "How to declare Vite optimizeDeps for add-ons in Aurora"
"property=og:description": "How to declare Vite optimizeDeps for add-ons in Aurora"
"property=og:title": "Vite dependency pre-bundling"
"keywords": "Plone Aurora, Vite, optimizeDeps, pnpm, add-ons"
---

# Vite dependency pre-bundling

This guide shows you how to declare third-party dependencies for pre-bundling so Vite resolves them at startup rather than lazily during dev.

Aurora runs in a pnpm monorepo.
Vite treats workspace packages as source files and does not scan them upfront for their third-party dependencies.
Instead, it discovers them the first time a page imports them, triggering a re-bundle and a browser reload.
In a large project, this causes several reload cycles and noticeably slows down the first page load in dev mode.

The fix is to declare those third-party dependencies in `optimizeDeps.include`.
Aurora uses two places for these declarations depending on whether the package is a registered add-on.

```{important}
Only declare a package as a registered add-on if it configures the application — registering slots, routes, or components into the Aurora framework.
Pure libraries such as component kits and utility packages must not be add-ons.
Registering them as add-ons causes the addon system to generate unnecessary loaders and blurs the boundary between framework participants and libraries.
If your package is a library, add its entries to `vite.config.ts` in the app that uses it.
```

## Registered add-ons: `vite.extend.js`

Any package that is a registered Aurora add-on can ship a `vite.extend.js` file in its root.
`PloneRegistryVitePlugin` picks these up automatically and merges them into the Vite config at startup — no changes to the app are needed.

The file must export a default function that receives the current config and returns a modified copy:

```js
// packages/my-addon/vite.extend.js
export default function (config) {
return {
...config,
optimizeDeps: {
...config.optimizeDeps,
include: [
...(config.optimizeDeps?.include ?? []),
// Use "pkg > dep" syntax for pnpm — resolves the dep through the
// workspace package that owns it
'@plone/my-addon > some-library',
'@plone/my-addon > some-library/subpath',
],
},
};
}
```

### When to add an entry

Add a `"pkg > dep"` entry for every direct dependency of your add-on that is not itself a workspace package.
You do not need entries for:

- Other Aurora add-ons or workspace packages — Vite excludes them from
optimization automatically because it treats them as source files.
- Dev dependencies.
- Peer dependencies that the host app provides.

If a dependency exports subpaths you use (for example `some-lib/react` or `some-lib/client`), add a separate entry for each subpath.
Vite does not discover subpath exports automatically from the main entry.
Comment on lines +65 to +66

### Existing core add-on files

| Add-on | File |
|--------|------|
| `@plone/plate` | `packages/plate/vite.extend.js` |
| `@plone/layout` | `packages/layout/vite.extend.js` |
| `@plone/cmsui` | `packages/cmsui/vite.extend.js` |

## Non-add-on workspace packages and app deps: `vite.config.ts`

Packages that are not registered add-ons — currently `@plone/components` and `@plone/helpers` — cannot use `vite.extend.js` because `PloneRegistryVitePlugin` never loads their files.
List their dependencies directly in `apps/aurora/vite.config.ts` under `optimizeDeps.include`.

The same rule applies to packages that are direct dependencies of the app itself (listed in `apps/aurora/package.json`).
Those use plain names without the `>` syntax because Vite resolves them directly from the app root.

```ts
// apps/aurora/vite.config.ts
optimizeDeps: {
include: [
// App-level dep — plain name works
'some-app-dep',
// Non-add-on workspace package dep — requires "pkg > dep" syntax
'@plone/components > react-aria-components',
'@plone/helpers > jotai',
]
}
```
1 change: 1 addition & 0 deletions packages/cmsui/news/+vite-optimize-deps.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `vite.extend.js` to pre-bundle CMS UI dependencies, reducing dev server startup reloads. @arybakov05
13 changes: 13 additions & 0 deletions packages/cmsui/vite.extend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function (config) {
return {
...config,
optimizeDeps: {
...config.optimizeDeps,
include: [
...(config.optimizeDeps?.include ?? []),
'@plone/cmsui > jwt-decode',
'@plone/cmsui > usehooks-ts',
],
},
};
}
1 change: 1 addition & 0 deletions packages/layout/news/+vite-optimize-deps.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `vite.extend.js` to pre-bundle layout dependencies, reducing dev server startup reloads. @arybakov05
14 changes: 14 additions & 0 deletions packages/layout/vite.extend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function (config) {
return {
...config,
optimizeDeps: {
...config.optimizeDeps,
include: [
...(config.optimizeDeps?.include ?? []),
'@plone/layout > lodash.sortby',
'@plone/layout > pretty-bytes',
'@plone/layout > rrule',
],
},
};
}
1 change: 1 addition & 0 deletions packages/plate/news/+vite-optimize-deps.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `vite.extend.js` to pre-bundle platejs and Radix UI dependencies, reducing dev server startup reloads. @arybakov05
48 changes: 48 additions & 0 deletions packages/plate/vite.extend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export default function (config) {
return {
...config,
optimizeDeps: {
...config.optimizeDeps,
include: [
...(config.optimizeDeps?.include ?? []),
'@plone/plate > @platejs/ai/react',
'@plone/plate > @platejs/basic-nodes',
'@plone/plate > @platejs/basic-nodes/react',
'@plone/plate > @platejs/basic-styles',
'@plone/plate > @platejs/callout',
'@plone/plate > @platejs/caption',
'@plone/plate > @platejs/code-block',
'@plone/plate > @platejs/comment',
'@plone/plate > @platejs/comment/react',
'@plone/plate > @platejs/floating',
'@plone/plate > @platejs/indent',
'@plone/plate > @platejs/layout',
'@plone/plate > @platejs/link',
'@plone/plate > @platejs/link/react',
'@plone/plate > @platejs/list',
'@plone/plate > @platejs/list/react',
'@plone/plate > @platejs/media',
'@plone/plate > @platejs/mention',
'@plone/plate > @platejs/selection/react',
'@plone/plate > @platejs/suggestion',
'@plone/plate > @platejs/table',
'@plone/plate > @platejs/table/react',
'@plone/plate > @platejs/toc',
'@plone/plate > @platejs/toggle',
'@plone/plate > @platejs/toggle/react',
'@plone/plate > @radix-ui/react-avatar',
'@plone/plate > @radix-ui/react-dropdown-menu',
'@plone/plate > @radix-ui/react-separator',
'@plone/plate > @radix-ui/react-slot',
'@plone/plate > @radix-ui/react-toolbar',
'@plone/plate > @radix-ui/react-tooltip',
'@plone/plate > @udecode/cn',
'@plone/plate > class-variance-authority',
'@plone/plate > lowlight',
'@plone/plate > lucide-react',
'@plone/plate > platejs',
'@plone/plate > platejs/react',
],
},
};
}
1 change: 1 addition & 0 deletions packages/registry/news/+vite-extend-support.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Check whether `.plone/vite.loader.js` content has changed before writing it in `PloneRegistryVitePlugin`. @arybakov05
9 changes: 8 additions & 1 deletion packages/registry/vite-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ const load = (config, context = {}) => {
export default load;
`;

fs.writeFileSync(viteLoaderPath, code);
// Only write if content changed — vite.loader.js is imported by vite.config.ts
// so any write triggers a Vite server restart, causing an infinite loop
const existing = fs.existsSync(viteLoaderPath)
? fs.readFileSync(viteLoaderPath, 'utf-8')
: null;
if (existing !== code) {
fs.writeFileSync(viteLoaderPath, code);
}
return viteLoaderPath;
}

Expand Down
Loading