feat: @unhead/bundler and /vite subpath exports#726
Conversation
β¦head/bundler
Adds `./vite` export to each framework package (@unhead/vue, @unhead/react,
@unhead/svelte, @unhead/solid-js) that composes build optimizations and
streaming SSR into a single plugin call.
Before:
import Unhead from '@unhead/addons/vite'
import { unheadVuePlugin } from '@unhead/vue/stream/vite'
plugins: [vue(), Unhead(), unheadVuePlugin()]
After:
import unhead from '@unhead/vue/vite'
plugins: [vue(), unhead()]
Other changes:
- Rename @unhead/addons to @unhead/bundler
- Deprecated @unhead/addons alias in packages-aliased/
- Normalize plugin options: all accept `false` to disable
- Streaming is opt-in (`streaming: true`)
- Deprecate `treeshake.enabled` in favor of `treeshake: false`
- Add `moduleType: 'js'` to streaming load hooks for Rolldown compat
- Use hook filter objects on resolveId/load for Rolldown performance
- Remove stale Vite 4 compat code
π WalkthroughWalkthroughReplaces Changes
Sequence Diagram(s)sequenceDiagram
participant App as Framework App
participant UnheadVite as unhead() Vite Factory
participant Bundler as `@unhead/bundler` (buildPlugins)
participant StreamPlugin as Streaming Plugin
participant Vite as Vite (final plugins)
App->>UnheadVite: unhead({ streaming: true })
UnheadVite->>Bundler: buildPlugins(options)
Bundler-->>UnheadVite: [Treeshake, SEO, Minify...]
UnheadVite->>StreamPlugin: create streaming plugin (mode?)
StreamPlugin-->>UnheadVite: streaming plugin
UnheadVite->>Vite: return Plugin[]
sequenceDiagram
participant Consumer as Consumer Code
participant AddonsShim as `@unhead/addons` shim
participant BundlerPkg as `@unhead/bundler`
participant Console as Console
Consumer->>AddonsShim: import { InferSeoMetaPlugin }
AddonsShim->>Console: console.warn("deprecated, use `@unhead/bundler`")
AddonsShim->>BundlerPkg: re-export requested symbol
BundlerPkg-->>AddonsShim: implementation
AddonsShim-->>Consumer: provide InferSeoMetaPlugin
Estimated code review effortπ― 4 (Complex) | β±οΈ ~60 minutes Poem
π₯ Pre-merge checks | β 3β Passed checks (3 passed)
βοΈ Tip: You can configure your own custom pre-merge checks in the settings. β¨ Finishing Touchesπ Generate docstrings
π§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bundle Size Analysis
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
docs/head/1.guides/2.advanced/9.vite-plugin.md (1)
23-29:β οΈ Potential issue | π‘ MinorUpdate stale βaddons packageβ wording.
Line 23 still says βaddons packageβ after the rename to
@unhead/bundler, which is confusing in migration docs.π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/head/1.guides/2.advanced/9.vite-plugin.md` around lines 23 - 29, The docs still refer to the "addons package" which is stale; update the wording around the install snippet to refer to the renamed package `@unhead/bundler` (e.g., "install the `@unhead/bundler` package that contains the build plugins") and ensure the explanatory sentence that precedes the pnpm add -D `@unhead/bundler` command uses "@unhead/bundler" (or "bundler package") instead of "addons package" so the text matches the code sample.
π§Ή Nitpick comments (3)
packages-aliased/addons/src/webpack.ts (1)
1-4: Deprecation warning may flood build logs.The
console.warnexecutes unconditionally at module load time, which could produce repeated warnings in build environments where the module is imported multiple times across different files.Consider guarding the warning to only emit once:
π‘ Suggested improvement to emit warning only once
+let warned = false +if (!warned) { + warned = true console.warn('[unhead] `@unhead/addons/webpack` is deprecated. Please migrate to `@unhead/bundler/webpack` instead.') +} export { default } from '@unhead/bundler/webpack' export * from '@unhead/bundler/webpack'Alternatively, this may be acceptable if the intent is to be noisy about the deprecation during the migration period.
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages-aliased/addons/src/webpack.ts` around lines 1 - 4, The unconditional console.warn at module load will spam logs when the package is imported multiple times; change it to emit only once by checking/storing a module-global sentinel (e.g., a symbol or boolean on globalThis) before calling console.warn so subsequent imports skip the message; update the top-level console.warn invocation in this file (the deprecation message and the export statements referencing '@unhead/bundler/webpack') to first check the sentinel and set it after emitting the warning.packages-aliased/addons/build.config.ts (1)
3-10: Consider adding externals for@unhead/bundler.Since this package only re-exports from
@unhead/bundler, consider adding externals to ensure the bundler package isn't accidentally inlined:π‘ Suggested improvement
export default defineBuildConfig({ clean: true, declaration: true, + externals: ['@unhead/bundler', '@unhead/bundler/vite', '@unhead/bundler/webpack'], entries: [ { input: 'src/index', name: 'index' }, { input: 'src/vite', name: 'vite' }, { input: 'src/webpack', name: 'webpack' }, ], })π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages-aliased/addons/build.config.ts` around lines 3 - 10, The build config currently bundles all imports for the entries defined in defineBuildConfig (entries: [{ input: 'src/index' ... }]), which can inline re-exports from `@unhead/bundler`; add an externals setting to the defineBuildConfig call to mark "@unhead/bundler" as external (e.g., externals: ['@unhead/bundler'] or the equivalent rollup/tsup externals option) so the package is not inlined into the addon outputβupdate the build config where defineBuildConfig is invoked to include this externals array.packages-aliased/addons/package.json (1)
40-48: Consider adding.d.tsextension totypesVersionspaths for clarity.The
typesVersionsentries (dist/vite,dist/webpack) omit the.d.tsextension. While TypeScript can resolve these correctly in most cases, being explicit withdist/vite.d.tsanddist/webpack.d.tswould match the explicit pattern used in theexportsfield and improve consistency.Proposed fix
"typesVersions": { "*": { "vite": [ - "dist/vite" + "dist/vite.d.ts" ], "webpack": [ - "dist/webpack" + "dist/webpack.d.ts" ] } },π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages-aliased/addons/package.json` around lines 40 - 48, The typesVersions entries currently point to "dist/vite" and "dist/webpack" without file extensions; update the typesVersions mapping so the "vite" and "webpack" target arrays reference the explicit declaration files (e.g., "dist/vite.d.ts" and "dist/webpack.d.ts") to match the explicit pattern used in exports and improve consistency; locate the typesVersions block and replace the paths for the "vite" and "webpack" keys accordingly.
π€ Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/0.react/schema-org/guides/get-started/0.installation.md`:
- Line 75: The import currently uses the bundler package; change the import
source from '@unhead/bundler/vite' to the unified React Vite plugin
'@unhead/react/vite' for the existing import declaration (the line that defines
import UnheadVite). Ensure the identifier (UnheadVite) continues to be used
unchanged elsewhere in the document after updating the import so references like
UnheadVite remain valid.
In `@docs/head/1.guides/2.advanced/9.vite-plugin.md`:
- Line 63: The example incorrectly uses a named import "UnheadWebpack" from
"@unhead/bundler/webpack" even though that package only provides a default
export; update the import statement to use a default import for UnheadWebpack
(replace the named import form with a default import of UnheadWebpack from
"@unhead/bundler/webpack") so the module resolves at runtime.
---
Outside diff comments:
In `@docs/head/1.guides/2.advanced/9.vite-plugin.md`:
- Around line 23-29: The docs still refer to the "addons package" which is
stale; update the wording around the install snippet to refer to the renamed
package `@unhead/bundler` (e.g., "install the `@unhead/bundler` package that
contains the build plugins") and ensure the explanatory sentence that precedes
the pnpm add -D `@unhead/bundler` command uses "@unhead/bundler" (or "bundler
package") instead of "addons package" so the text matches the code sample.
---
Nitpick comments:
In `@packages-aliased/addons/build.config.ts`:
- Around line 3-10: The build config currently bundles all imports for the
entries defined in defineBuildConfig (entries: [{ input: 'src/index' ... }]),
which can inline re-exports from `@unhead/bundler`; add an externals setting to
the defineBuildConfig call to mark "@unhead/bundler" as external (e.g.,
externals: ['@unhead/bundler'] or the equivalent rollup/tsup externals option)
so the package is not inlined into the addon outputβupdate the build config
where defineBuildConfig is invoked to include this externals array.
In `@packages-aliased/addons/package.json`:
- Around line 40-48: The typesVersions entries currently point to "dist/vite"
and "dist/webpack" without file extensions; update the typesVersions mapping so
the "vite" and "webpack" target arrays reference the explicit declaration files
(e.g., "dist/vite.d.ts" and "dist/webpack.d.ts") to match the explicit pattern
used in exports and improve consistency; locate the typesVersions block and
replace the paths for the "vite" and "webpack" keys accordingly.
In `@packages-aliased/addons/src/webpack.ts`:
- Around line 1-4: The unconditional console.warn at module load will spam logs
when the package is imported multiple times; change it to emit only once by
checking/storing a module-global sentinel (e.g., a symbol or boolean on
globalThis) before calling console.warn so subsequent imports skip the message;
update the top-level console.warn invocation in this file (the deprecation
message and the export statements referencing '@unhead/bundler/webpack') to
first check the sentinel and set it after emitting the warning.
πͺ Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
βΉοΈ Review info
βοΈ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 03d237af-ca78-4146-af6b-163eb09992d4
β Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
π Files selected for processing (67)
bench/ssr-harlanzw-com-e2e.bench.tsbench/ssr-harlanzw-com-e2e.test.tsbench/ssr-harlanzw-no-schema-e2e.bench.tsbench/vitest.config.tsdocs/0.angular/schema-org/guides/get-started/0.installation.mddocs/0.react/schema-org/guides/get-started/0.installation.mddocs/0.solid-js/schema-org/guides/get-started/0.installation.mddocs/0.svelte/schema-org/guides/get-started/0.installation.mddocs/0.typescript/schema-org/guides/get-started/0.installation.mddocs/0.vue/schema-org/guides/0.get-started/0.installation.mddocs/head/1.guides/2.advanced/9.vite-plugin.mddocs/head/1.guides/plugins/minify.mdexamples/vite-ssr-react-streaming/vite.config.tsexamples/vite-ssr-react-ts/vite.config.tsexamples/vite-ssr-solidjs-streaming/vite.config.tsexamples/vite-ssr-svelte-streaming/vite.config.tsexamples/vite-ssr-vue-prerender/vite.config.jsexamples/vite-ssr-vue-streaming/vite.config.tspackage.jsonpackages-aliased/addons/build.config.tspackages-aliased/addons/package.jsonpackages-aliased/addons/src/index.tspackages-aliased/addons/src/vite.tspackages-aliased/addons/src/webpack.tspackages/bundler/.attw.jsonpackages/bundler/README.mdpackages/bundler/build.config.tspackages/bundler/package.jsonpackages/bundler/src/constants.tspackages/bundler/src/index.tspackages/bundler/src/minify/esbuild.tspackages/bundler/src/minify/lightningcss.tspackages/bundler/src/minify/rolldown.tspackages/bundler/src/unplugin/MinifyTransform.tspackages/bundler/src/unplugin/TreeshakeServerComposables.tspackages/bundler/src/unplugin/UseSeoMetaTransform.tspackages/bundler/src/unplugin/types.tspackages/bundler/src/unplugin/vite.tspackages/bundler/src/unplugin/webpack.tspackages/bundler/test/minify.test.tspackages/bundler/test/minifyTransform.test.tspackages/bundler/test/treeshakeServerComposables.test.tspackages/bundler/test/useSeoMetaTransform.test.tspackages/bundler/tsconfig.jsonpackages/bundler/vite.d.tspackages/bundler/webpack.d.tspackages/react/build.config.tspackages/react/package.jsonpackages/react/src/vite.tspackages/solid-js/build.config.tspackages/solid-js/package.jsonpackages/solid-js/src/vite.tspackages/svelte/build.config.tspackages/svelte/package.jsonpackages/svelte/src/vite.tspackages/unhead/src/stream/vite.tspackages/vue/build.config.tspackages/vue/package.jsonpackages/vue/src/vite.tspackages/vue/test/bench/ssr-harlanzw-no-schema-e2e.bench.tspackages/vue/vitest.config.tstest/exports/bundler.yamltest/exports/react.yamltest/exports/solid-js.yamltest/exports/svelte.yamltest/exports/vue.yamltsconfig.json
|
|
||
| ```ts [@unhead/schema-org] | ||
| import UnheadVite from '@unhead/addons/vite' | ||
| import UnheadVite from '@unhead/bundler/vite' |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
#!/bin/bash
set -euo pipefail
# Verify package export maps include ./vite
rg -n '"\./vite"' packages/react/package.json packages/bundler/package.json
# Verify React vite entrypoint file exists
fd -i "vite.ts" packages/react/srcRepository: unjs/unhead
Length of output: 211
π Script executed:
#!/bin/bash
set -euo pipefail
# Check the documentation file at the specified location
cat -n docs/0.react/schema-org/guides/get-started/0.installation.md | head -100Repository: unjs/unhead
Length of output: 3081
π Script executed:
#!/bin/bash
set -euo pipefail
# Check react package.json exports
echo "=== React package.json exports ==="
rg -A 5 '"./vite"' packages/react/package.json
echo ""
echo "=== Bundler package.json exports ==="
rg -A 5 '"./vite"' packages/bundler/package.jsonRepository: unjs/unhead
Length of output: 435
π Script executed:
#!/bin/bash
set -euo pipefail
echo "=== React vite.ts ==="
cat -n packages/react/src/vite.ts
echo ""
echo "=== Finding bundler vite.ts ==="
fd "vite.ts" packages/bundler/src
echo ""
echo "=== Bundler vite.ts (if exists) ==="
if [ -f "packages/bundler/src/vite.ts" ]; then
cat -n packages/bundler/src/vite.ts
fiRepository: unjs/unhead
Length of output: 1715
Update import to use the unified React Vite plugin.
Line 75 should use @unhead/react/vite instead of @unhead/bundler/vite. This aligns the React guide with the unified Vite plugin designed for React apps, which combines build optimizations and streaming SSR support into a single plugin.
Proposed doc snippet update
-import UnheadVite from '@unhead/bundler/vite'
+import unhead from '@unhead/react/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
- UnheadVite(),
+ unhead(),
]
})π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/0.react/schema-org/guides/get-started/0.installation.md` at line 75, The
import currently uses the bundler package; change the import source from
'@unhead/bundler/vite' to the unified React Vite plugin '@unhead/react/vite' for
the existing import declaration (the line that defines import UnheadVite).
Ensure the identifier (UnheadVite) continues to be used unchanged elsewhere in
the document after updating the import so references like UnheadVite remain
valid.
|
|
||
| ```ts | ||
| import { UnheadWebpack } from '@unhead/addons/webpack' | ||
| import { UnheadWebpack } from '@unhead/bundler/webpack' |
There was a problem hiding this comment.
π§© Analysis chain
π Script executed:
#!/bin/bash
set -euo pipefail
echo "== package exports =="
sed -n '1,140p' packages/bundler/package.json
echo
echo "== webpack-related source files =="
fd -i 'webpack.ts' packages/bundler/src --exec sh -c 'echo "--- {}"; sed -n "1,180p" "{}"'Repository: unjs/unhead
Length of output: 3893
Change import to default export.
The docs example uses a named import, but @unhead/bundler/webpack only exports a default export. This will fail at runtime.
Apply this fix
-import { UnheadWebpack } from '@unhead/bundler/webpack'
+import UnheadWebpack from '@unhead/bundler/webpack'π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/head/1.guides/2.advanced/9.vite-plugin.md` at line 63, The example
incorrectly uses a named import "UnheadWebpack" from "@unhead/bundler/webpack"
even though that package only provides a default export; update the import
statement to use a default import for UnheadWebpack (replace the named import
form with a default import of UnheadWebpack from "@unhead/bundler/webpack") so
the module resolves at runtime.
Replace duplicated tree-shaking snippets in schema-org install docs with framework-specific examples using the new unified plugin. Rewrite the main Vite Plugin guide as the canonical reference for all frameworks. Update minify docs to reference the unified plugin.
Focus on the primary benefits (tree-shaking server code, transforming head composables) rather than the niche schema-org stripping.
There was a problem hiding this comment.
π§Ή Nitpick comments (1)
docs/0.vue/schema-org/guides/0.get-started/0.installation.md (1)
76-83: Consider adding thedefineConfigimport for completeness.The code example uses
defineConfigbut doesn't show its import. While experienced developers will know this comes from'vite', adding the import would make the example more self-contained and easier to copy-paste.π Suggested addition for a complete example
```ts +import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import unhead from '@unhead/vue/vite' export default defineConfig({ plugins: [vue(), unhead()], })</details> <details> <summary>π€ Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/0.vue/schema-org/guides/0.get-started/0.installation.mdaround lines 76
- 83, Add the missing import for defineConfig so the example is self-contained:
import defineConfig from 'vite' (i.e., include "import { defineConfig } from
'vite'") at the top of the snippet before using defineConfig in the export; this
ensures the referenced symbol defineConfig is defined while keeping the existing
vue() and unhead() plugin usages unchanged.</details> </blockquote></details> </blockquote></details> <details> <summary>π€ Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In@docs/0.vue/schema-org/guides/0.get-started/0.installation.md:
- Around line 76-83: Add the missing import for defineConfig so the example is
self-contained: import defineConfig from 'vite' (i.e., include "import {
defineConfig } from 'vite'") at the top of the snippet before using defineConfig
in the export; this ensures the referenced symbol defineConfig is defined while
keeping the existing vue() and unhead() plugin usages unchanged.</details> --- <details> <summary>βΉοΈ Review info</summary> <details> <summary>βοΈ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `df022062-2d07-425c-b3d1-21db1f6c3b22` </details> <details> <summary>π₯ Commits</summary> Reviewing files that changed from the base of the PR and between 2faa69b76fdc43e5606daaf17fe86785d6d1c8eb and bbc9d708b79c947527a8a6112f7be33748850b9e. </details> <details> <summary>π Files selected for processing (6)</summary> * `docs/0.angular/schema-org/guides/get-started/0.installation.md` * `docs/0.react/schema-org/guides/get-started/0.installation.md` * `docs/0.solid-js/schema-org/guides/get-started/0.installation.md` * `docs/0.svelte/schema-org/guides/get-started/0.installation.md` * `docs/0.typescript/schema-org/guides/get-started/0.installation.md` * `docs/0.vue/schema-org/guides/0.get-started/0.installation.md` </details> <details> <summary>β Files skipped from review due to trivial changes (2)</summary> * docs/0.react/schema-org/guides/get-started/0.installation.md * docs/0.angular/schema-org/guides/get-started/0.installation.md </details> <details> <summary>π§ Files skipped from review as they are similar to previous changes (3)</summary> * docs/0.typescript/schema-org/guides/get-started/0.installation.md * docs/0.solid-js/schema-org/guides/get-started/0.installation.md * docs/0.svelte/schema-org/guides/get-started/0.installation.md </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@unhead/bundler and /vite subpath exports
β Type of change
π Description
Adds a unified
./viteexport to each framework package so users get build optimizations + streaming SSR from a single plugin call instead of manually composing multiple plugins.Also renames
@unhead/addonsto@unhead/bundler(deprecated alias provided).@unhead/addonsrenamed to@unhead/bundler. The old package name still works with a deprecation warning viapackages-aliased/addons.streaming: true(was previously a separate plugin import).Changes
@unhead/vue/vite,@unhead/react/vite,@unhead/svelte/vite,@unhead/solid-js/vitefalseto disable (treeshake: false,transformSeoMeta: false,minify: false,streaming: false)moduleType: 'js'on load hooks, hook filter objects onresolveId/load/transformfor reduced RustβJS overheadtreeshake.enabledin favor oftreeshake: falseπ Migration
For streaming SSR:
Summary by CodeRabbit
New Features
Chores
Documentation
Tests