refactor(bundler): unify per-bundler entries into bundler factory#754
Conversation
…tory
Replace per-bundler subpath exports (`@unhead/{vue,react,svelte,solid-js}/webpack`)
with a single `bundler` entry that returns a factory object exposing
`.vite()`, `.webpack()`, `.rspack()`, and `.rollup()` dispatch methods.
Framework `vite` entries kept as thin wrappers over the new bundler factory.
The `@unhead/bundler/webpack` and `@unhead/addons/webpack` entries are removed;
consumers should import from `@unhead/<framework>/bundler` and call the matching
dispatch method (e.g. `Unhead({ streaming: true }).webpack()`), or pass the
factory directly to Nuxt Kit's `addBuildPlugin`.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
💤 Files with no reviewable changes (4)
📝 WalkthroughWalkthroughConsolidates per-framework bundler entrypoints into a unified createFrameworkPlugin factory exposing a ./bundler dispatch (vite/webpack/rspack/rollup). Removes framework-specific webpack unplugin files and ./webpack package exports; Vite wrappers delegate to the new bundler entry; package exports, build outputs, tests, and docs updated accordingly. Changes
Sequence DiagramsequenceDiagram
participant Dev as Developer
participant BundlerEntry as "framework/bundler"
participant Factory as createFrameworkPlugin
participant CoreResolver as resolveCoreDefs
participant Dispatcher as UnheadBundlerFactory
participant Vite as .vite()
participant Webpack as .webpack()
Dev->>BundlerEntry: import { Unhead } and call Unhead(options)
BundlerEntry->>Factory: initialize with framework & streamingPlugin
Factory->>CoreResolver: resolve core transforms (treeshake, seo, minify...)
CoreResolver-->>Factory: core plugin definitions
Factory->>Dispatcher: return dispatch object (vite/webpack/rspack/rollup)
Dev->>Dispatcher: call .vite()
Dispatcher->>Vite: assemble Vite plugins (validate?, devtools?, CreateHeadTransform, SSRStaticReplace, streaming...)
Vite-->>Dev: Plugin[] for Vite
Dev->>Dispatcher: call .webpack()
Dispatcher->>Webpack: assemble Webpack plugins (SSRStaticReplace, streaming?)
Webpack-->>Dev: Plugin[] for Webpack
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 5
🧹 Nitpick comments (3)
packages/unhead/src/stream/unplugin.ts (1)
282-289: Minor: the example is Vue-specific but the factory is framework-agnostic.Since
createStreamingPluginis shared across Vue/React/Solid/Svelte, consider a framework-agnostic phrasing like "the unified framework bundler entry (e.g.@unhead/{vue,react,svelte,solid-js}/bundler)" so React/Solid/Svelte consumers aren't inadvertently steered toward the Vue subpath. Purely a doc tweak.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/unhead/src/stream/unplugin.ts` around lines 282 - 289, The comment above the createStreamingPlugin factory is Vue-specific; update the docstring to a framework-agnostic example so it doesn't single out Vue. Edit the block that references createStreamingPlugin/createUnplugin (and related symbols StreamingPluginOptions and buildStreamingPluginOptions) to replace "`@unhead/vue/bundler`" with a neutral example such as "`@unhead/{vue,react,svelte,solid-js}/bundler`" or a generic "the unified framework bundler entry" phrasing so all framework consumers are equally represented.packages/solid-js/src/bundler.ts (1)
8-17: Optional: mention theaddBuildPluginpath like the Vue entry.The Vue
bundler.tsdocstring documents both the direct Vite call site and the Nuxt KitaddBuildPlugin(Unhead(...))forwarding path, which is one of the headline wins called out in the PR description. Adding the same snippet here keeps the docs symmetrical across framework entries.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/solid-js/src/bundler.ts` around lines 8 - 17, Update the docstring in the unified bundler plugin factory for `@unhead/solid-js` to include the Nuxt Kit forwarding path similar to the Vue entry: add a small example showing addBuildPlugin(Unhead({ streaming: true })) alongside the existing Vite usage (reference the Unhead factory exported in this file and the addBuildPlugin call) so the docs mention both direct Vite usage and the Nuxt Kit addBuildPlugin forwarding path.packages/svelte/src/bundler.ts (1)
8-15: Optional: add a usage@examplefor parity with Vue/React bundler entries.The Vue and React bundler entries include a JSDoc
@exampledemonstrating...Unhead({ streaming: true }).vite(). Adding the same here (and inpackages/solid-js/src/bundler.ts) would keep the public API surface discoverable and consistent across frameworks.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/svelte/src/bundler.ts` around lines 8 - 15, Add a JSDoc `@example` to the Unhead bundler export to match Vue/React parity: update the comment above the Unhead constant (created via createFrameworkPlugin and typed as UnheadSvelteStreamingOptions, using unheadSvelteStreamingPlugin) to include an example like calling Unhead({ streaming: true }).vite() so the public API usage is discoverable and consistent across frameworks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages-aliased/addons/src/webpack.ts`:
- Around line 1-5: Update the thrown Error message in the
'@unhead/addons/webpack' removal notice to be framework-agnostic: replace the
hard-coded example '`@unhead/vue/bundler`' in the Error string with a generic
phrasing like "your framework bundler entry (e.g.
`@unhead/{vue,react,svelte,solid-js}/bundler`)" or "your framework's bundler
entry" so users of React/Svelte/Solid aren't misled; edit the string inside the
throw new Error(...) expression in the file to reflect this wording change.
In `@packages/bundler/src/unplugin/framework.ts`:
- Around line 53-58: The code treats options.minify === true as an empty object
so MinifyTransform never gets added; update the minifyOpts assignment so a
boolean true enables both JS and CSS minification: replace minifyOpts = typeof
options.minify === 'object' ? options.minify : {} with logic that if
options.minify === true set minifyOpts = { js: true, css: true }, else if it's
an object use it, otherwise {} — then the existing guard (if (minifyOpts.js ||
minifyOpts.css)) will correctly push { instance: MinifyTransform, options: {
...common, ...minifyOpts } } into defs.
- Around line 110-147: UnheadFrameworkOptions currently exposes validate and
devtools but those options are only honored in the .vite() branch (see
CreateHeadTransform injection of ValidatePlugin and unheadDevtools usage), so
update the type definition for UnheadFrameworkOptions to either remove validate
(if you want it unavailable outside Vite) or add explicit JSDoc noting that
validate and devtools are Vite-only and will be ignored by .webpack(),
.rspack(), and .rollup(); ensure the JSDoc mentions the related symbols
(CreateHeadTransform, ValidatePlugin, unheadDevtools) and that only .vite()
consumes these options so callers aren’t misled.
In `@packages/react/test/unified-plugin.test.ts`:
- Around line 25-29: The test currently only checks for any plugin with an apply
function but must assert the actual streaming plugin is present; update the test
in unified-plugin.test.ts (the Unhead({ streaming: true }).webpack() call) to
search the returned plugins array for a streaming-specific marker instead of a
generic apply check — for example look for a unique identifier on the plugin
such as its constructor name ("UnheadStreamingPlugin"), a public name property
(plugin.name === "UnheadStreamingPlugin"), or a dedicated flag (e.g.
plugin.__unheadStreaming) and assert that such a plugin exists; this ensures the
streaming webpack plugin introduced by
createFrameworkPlugin/Unhead(...).webpack() is actually included when streaming:
true.
In `@packages/vue/src/vite.ts`:
- Around line 11-16: Update the `@example` to reflect that Unhead now returns
Plugin[]: change the example usage of Unhead in vite integration so it spreads
the returned array into the plugins array (matching the bundler.ts and migration
docs) or remove the example; specifically update the example line that currently
shows plugins: [Unhead({ streaming: true })] to use the spread form plugins:
[...Unhead({ streaming: true })] (referencing the Unhead function and the
Plugin[] return shape).
---
Nitpick comments:
In `@packages/solid-js/src/bundler.ts`:
- Around line 8-17: Update the docstring in the unified bundler plugin factory
for `@unhead/solid-js` to include the Nuxt Kit forwarding path similar to the
Vue entry: add a small example showing addBuildPlugin(Unhead({ streaming: true
})) alongside the existing Vite usage (reference the Unhead factory exported in
this file and the addBuildPlugin call) so the docs mention both direct Vite
usage and the Nuxt Kit addBuildPlugin forwarding path.
In `@packages/svelte/src/bundler.ts`:
- Around line 8-15: Add a JSDoc `@example` to the Unhead bundler export to match
Vue/React parity: update the comment above the Unhead constant (created via
createFrameworkPlugin and typed as UnheadSvelteStreamingOptions, using
unheadSvelteStreamingPlugin) to include an example like calling Unhead({
streaming: true }).vite() so the public API usage is discoverable and consistent
across frameworks.
In `@packages/unhead/src/stream/unplugin.ts`:
- Around line 282-289: The comment above the createStreamingPlugin factory is
Vue-specific; update the docstring to a framework-agnostic example so it doesn't
single out Vue. Edit the block that references
createStreamingPlugin/createUnplugin (and related symbols StreamingPluginOptions
and buildStreamingPluginOptions) to replace "`@unhead/vue/bundler`" with a
neutral example such as "`@unhead/{vue,react,svelte,solid-js}/bundler`" or a
generic "the unified framework bundler entry" phrasing so all framework
consumers are equally represented.
🪄 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: e62317cf-ec4a-48e9-8c02-8560500403cf
📒 Files selected for processing (43)
packages-aliased/addons/src/webpack.tspackages/bundler/build.config.tspackages/bundler/package.jsonpackages/bundler/src/unplugin/framework.tspackages/bundler/src/unplugin/webpack.tspackages/react/build.config.tspackages/react/package.jsonpackages/react/src/bundler.tspackages/react/src/stream/vite.tspackages/react/src/vite.tspackages/react/src/webpack.tspackages/react/test/unified-plugin.test.tspackages/react/vitest.config.tspackages/solid-js/build.config.tspackages/solid-js/package.jsonpackages/solid-js/src/bundler.tspackages/solid-js/src/stream/vite.tspackages/solid-js/src/vite.tspackages/solid-js/src/webpack.tspackages/solid-js/test/unified-plugin.test.tspackages/solid-js/vitest.config.tspackages/svelte/build.config.tspackages/svelte/package.jsonpackages/svelte/src/bundler.tspackages/svelte/src/stream/vite.tspackages/svelte/src/vite.tspackages/svelte/src/webpack.tspackages/svelte/test/unified-plugin.test.tspackages/svelte/vitest.config.tspackages/unhead/src/stream/unplugin.tspackages/vue/build.config.tspackages/vue/package.jsonpackages/vue/src/bundler.tspackages/vue/src/stream/vite.tspackages/vue/src/vite.tspackages/vue/src/webpack.tspackages/vue/test/unified-plugin.test.tspackages/vue/vitest.config.tstest/exports/bundler.yamltest/exports/react.yamltest/exports/solid-js.yamltest/exports/svelte.yamltest/exports/vue.yaml
💤 Files with no reviewable changes (11)
- packages/bundler/build.config.ts
- packages/svelte/vitest.config.ts
- packages/react/src/webpack.ts
- packages/svelte/src/webpack.ts
- packages/solid-js/vitest.config.ts
- packages/vue/src/webpack.ts
- packages/bundler/package.json
- packages/vue/vitest.config.ts
- packages/react/vitest.config.ts
- packages/bundler/src/unplugin/webpack.ts
- packages/solid-js/src/webpack.ts
| it('webpack returns a plugin array including the streaming plugin when enabled', () => { | ||
| const plugins = UnheadWebpack({ streaming: true }) as any[] | ||
| const plugins = Unhead({ streaming: true }).webpack() as any[] | ||
| expect(Array.isArray(plugins)).toBe(true) | ||
| expect(plugins.some(p => typeof p?.apply === 'function')).toBe(true) | ||
| }) |
There was a problem hiding this comment.
Minor: webpack assertion does not specifically verify the streaming plugin is present.
The test title says "including the streaming plugin when enabled", but the assertion only checks that some plugin with an apply function exists — which is true even without streaming (e.g., SSRStaticReplace.webpack). Consider asserting on a streaming-specific marker (e.g., by name or instance type) to match the test description and guard against regressions where the streaming webpack plugin is dropped from createFrameworkPlugin's webpack() branch.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/react/test/unified-plugin.test.ts` around lines 25 - 29, The test
currently only checks for any plugin with an apply function but must assert the
actual streaming plugin is present; update the test in unified-plugin.test.ts
(the Unhead({ streaming: true }).webpack() call) to search the returned plugins
array for a streaming-specific marker instead of a generic apply check — for
example look for a unique identifier on the plugin such as its constructor name
("UnheadStreamingPlugin"), a public name property (plugin.name ===
"UnheadStreamingPlugin"), or a dedicated flag (e.g. plugin.__unheadStreaming)
and assert that such a plugin exists; this ensures the streaming webpack plugin
introduced by createFrameworkPlugin/Unhead(...).webpack() is actually included
when streaming: true.
…options Address CodeRabbit findings: - `minify: true` now enables both JS + CSS minification instead of silently no-oping (asymmetric with `treeshake: true`/`transformSeoMeta: true`). - Document `validate` and `devtools` as Vite-only on `UnheadFrameworkOptions`; webpack/rspack/rollup ignore them. - Make `@unhead/addons/webpack` removal error framework-agnostic. - Strengthen webpack streaming-plugin tests via baseline count delta (unplugin webpack wrappers expose only `.apply`, no `name`). - Fix `@unhead/<fw>/vite` example to spread the returned `Plugin[]`. - Reword streaming unplugin doc to cover all framework bundler entries.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bundler/src/unplugin/framework.ts`:
- Around line 145-162: UnheadBundlerFactory.rollup currently calls
dispatch('rollup', defs) and then SSRStaticReplace.rollup({}) which leaves
SSRStaticReplace's internal ssr flag at its default false; update the rollup
path so SSRStaticReplace receives the correct SSR signal by either (A) plumbing
an explicit ssr option through dispatch/defs and passing it into
SSRStaticReplace.rollup({ ssr }) or (B) implementing a rollup-specific hook
inside SSRStaticReplace that inspects rollup build options (e.g.,
options.output.format or other env hints) to set ssr, and ensure
streamingPlugin.rollup(streamOpts) remains unchanged; if rollup SSR cannot be
supported, add a clear JSDoc note on UnheadBundlerFactory.rollup documenting
that SSR is not supported.
- Around line 138-162: The streaming plugin results are pushed directly
(streamingPlugin.vite/webpack/rspack/rollup) which can produce either a single
plugin or an array; mirror the dispatch() handling by normalizing before
pushing: call the streamingPlugin.* method, then if the result is an array
spread it into plugins, otherwise if truthy push the single item. Apply this
normalization to the vite, webpack, rspack and rollup branches so plugins stays
flat (instead of changing FrameworkPluginConfig.streamingPlugin types).
🪄 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: d1fac7d4-8baa-4c4d-b47c-dbcf5ea50d48
📒 Files selected for processing (8)
packages-aliased/addons/src/webpack.tspackages/bundler/src/unplugin/framework.tspackages/react/test/unified-plugin.test.tspackages/solid-js/test/unified-plugin.test.tspackages/svelte/test/unified-plugin.test.tspackages/unhead/src/stream/unplugin.tspackages/vue/src/vite.tspackages/vue/test/unified-plugin.test.ts
✅ Files skipped from review due to trivial changes (2)
- packages-aliased/addons/src/webpack.ts
- packages/unhead/src/stream/unplugin.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/vue/src/vite.ts
- packages/vue/test/unified-plugin.test.ts
- packages/solid-js/test/unified-plugin.test.ts
- packages/svelte/test/unified-plugin.test.ts
- packages/react/test/unified-plugin.test.ts
| plugins.push(SSRStaticReplace.webpack({})) | ||
| if (wantStreaming) | ||
| plugins.push(streamingPlugin.webpack(streamOpts)) | ||
| return plugins | ||
| }, | ||
| rspack: () => { | ||
| const plugins = dispatch('rspack', defs) | ||
| plugins.push(SSRStaticReplace.rspack({})) | ||
| if (wantStreaming) | ||
| plugins.push(streamingPlugin.rspack(streamOpts)) | ||
| return plugins | ||
| }, | ||
| rollup: () => { | ||
| const plugins = dispatch('rollup', defs) | ||
| plugins.push(SSRStaticReplace.rollup({})) | ||
| if (wantStreaming) | ||
| plugins.push(streamingPlugin.rollup(streamOpts) as any) | ||
| return plugins |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm no rollup-specific SSR detection exists for SSRStaticReplace and check
# whether any caller relies on rollup SSR in this repo.
fd -t f 'SSRStaticReplace.ts' packages/bundler/src
rg -nP -C3 '\b(rollup|isSsrBuild|ctx\.name)\b' packages/bundler/src/unplugin/SSRStaticReplace.ts
rg -nP -C2 '\.rollup\(' packages --type ts -g '!**/node_modules/**'Repository: unjs/unhead
Length of output: 4231
🏁 Script executed:
# Read the full SSRStaticReplace.ts to understand how the ssr flag is used
cat -n packages/bundler/src/unplugin/SSRStaticReplace.ts | head -100Repository: unjs/unhead
Length of output: 2181
🏁 Script executed:
# Check if there's any rollup-specific handling in the dispatch function or framework context
rg -nP -A5 "dispatch\('rollup'" packages/bundler/src/unplugin/Repository: unjs/unhead
Length of output: 561
🏁 Script executed:
# Search for any rollup SSR builds or configuration in tests/docs
rg -nP "rollup.*ssr|ssr.*rollup" packages --type ts --type jsRepository: unjs/unhead
Length of output: 37
🏁 Script executed:
# Check the dispatch function definition and what it returns for rollup
rg -nP -B5 -A10 "function dispatch|const dispatch" packages/bundler/src/unplugin/framework.tsRepository: unjs/unhead
Length of output: 463
🏁 Script executed:
# Look for any unplugin documentation or examples on how rollup context is typically accessed
rg -nP "rollup.*options|options\.output" packages/bundler/src --type tsRepository: unjs/unhead
Length of output: 37
🏁 Script executed:
# Check if there are any rollup-specific build tests that verify SSR behavior
fd -t f '\.test\.|\.spec\.' packages --type ts | xargs grep -l "rollup.*ssr\|ssr.*build" 2>/dev/null | head -5Repository: unjs/unhead
Length of output: 225
🏁 Script executed:
# Check if there are rollup SSR tests or configuration
find packages -name '*.test.*' -o -name '*.spec.*' | xargs grep -l "rollup" | head -10Repository: unjs/unhead
Length of output: 258
🏁 Script executed:
# Look for any rollup configuration or entry points in package.json files
fd 'package.json' packages --max-depth 2 | xargs grep -l "rollup" | head -5Repository: unjs/unhead
Length of output: 114
🏁 Script executed:
# Search for any mention of rollup SSR support in documentation or comments
rg -nP "rollup.*SSR|rollup.*ssr|ssr.*rollup" packages/bundler/src --type ts -iRepository: unjs/unhead
Length of output: 152
Rollup path unconditionally replaces head.ssr with false.
SSRStaticReplace detects SSR builds via the webpack(ctx) hook (checking ctx.name === 'server') and the vite.apply hook (checking env.isSsrBuild). The rollup export has no hook to detect SSR context, leaving the internal ssr flag at its default false value. Consequently, all head.ssr references in unhead modules are statically rewritten to false during rollup builds, regardless of whether it's an SSR build.
If rollup SSR is an intended use case, SSRStaticReplace needs a way to receive the SSR signal—either by plumbing ssr as an explicit option through dispatch() or by implementing a rollup-specific hook that inspects options.output.format or environment hints. If rollup SSR is not supported, document it in the UnheadBundlerFactory.rollup JSDoc so users know what to expect.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bundler/src/unplugin/framework.ts` around lines 145 - 162,
UnheadBundlerFactory.rollup currently calls dispatch('rollup', defs) and then
SSRStaticReplace.rollup({}) which leaves SSRStaticReplace's internal ssr flag at
its default false; update the rollup path so SSRStaticReplace receives the
correct SSR signal by either (A) plumbing an explicit ssr option through
dispatch/defs and passing it into SSRStaticReplace.rollup({ ssr }) or (B)
implementing a rollup-specific hook inside SSRStaticReplace that inspects rollup
build options (e.g., options.output.format or other env hints) to set ssr, and
ensure streamingPlugin.rollup(streamOpts) remains unchanged; if rollup SSR
cannot be supported, add a clear JSDoc note on UnheadBundlerFactory.rollup
documenting that SSR is not supported.
… limitation Address remaining CodeRabbit findings: - `pushPlugin` helper flattens streaming plugin results so an `UnpluginInstance<S, true>` (Nested) wouldn't leak nested arrays into the dispatched output, matching the existing `dispatch` helper's array handling. - Document on `UnheadBundlerFactory` that `.rollup()` cannot detect SSR context, so `head.ssr` references are always rewritten to `false`. Use `.vite()` or `.webpack()` for SSR builds.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/bundler/src/unplugin/framework.ts (1)
58-95: Optional: tightenanyusage inCoreDef/dispatch.
CoreDef.options: anyand theinstance[bundler] as (opts: any) => anycast discard the option/plugin shape that each transform'sUnpluginInstancealready encodes. MakingCoreDefgeneric and inferring the per-bundler return type fromUnpluginInstance<O, false>would preserve type safety across the dispatch pipeline without changing runtime behavior.♻️ Sketch
-interface CoreDef { instance: UnpluginInstance<any, false>, options: any } +interface CoreDef<O = unknown> { + instance: UnpluginInstance<O, false> + options: O +} @@ -function dispatch(bundler: 'vite' | 'webpack' | 'rspack' | 'rollup', defs: CoreDef[]): any[] { - const out: any[] = [] - for (const { instance, options } of defs) { - const plugin = (instance[bundler] as (opts: any) => any)(options) +type Bundler = 'vite' | 'webpack' | 'rspack' | 'rollup' +function dispatch(bundler: Bundler, defs: CoreDef[]): unknown[] { + const out: unknown[] = [] + for (const { instance, options } of defs) { + const plugin = instance[bundler](options) if (Array.isArray(plugin)) out.push(...plugin) else out.push(plugin) } return out }Leaving this optional since the current shape works and the factory's public signature (
UnheadBundlerFactory) is the contract consumers depend on.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bundler/src/unplugin/framework.ts` around lines 58 - 95, Make CoreDef and related functions generic so you preserve each plugin's option and return types instead of using any: change CoreDef to parameterize the options/instance types (e.g., CoreDef<O, R> with instance: UnpluginInstance<O, false> and options: O), update resolveCoreDefs to return CoreDef<...>[] where the concrete option types come from the specific instances you construct (TreeshakeServerComposables, UseSeoMetaTransform, MinifyTransform), and update dispatch to infer the bundler-specific plugin factory signature from UnpluginInstance so you can call instance[bundler](options) without casting to any and return the correct aggregated array type; this removes the (opts: any) => any cast while keeping runtime behavior unchanged and preserves per-plugin type safety.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/bundler/src/unplugin/framework.ts`:
- Around line 58-95: Make CoreDef and related functions generic so you preserve
each plugin's option and return types instead of using any: change CoreDef to
parameterize the options/instance types (e.g., CoreDef<O, R> with instance:
UnpluginInstance<O, false> and options: O), update resolveCoreDefs to return
CoreDef<...>[] where the concrete option types come from the specific instances
you construct (TreeshakeServerComposables, UseSeoMetaTransform,
MinifyTransform), and update dispatch to infer the bundler-specific plugin
factory signature from UnpluginInstance so you can call
instance[bundler](options) without casting to any and return the correct
aggregated array type; this removes the (opts: any) => any cast while keeping
runtime behavior unchanged and preserves per-plugin type safety.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 98b72596-9e1a-4adc-a89e-e3ce7ccbbc70
📒 Files selected for processing (1)
packages/bundler/src/unplugin/framework.ts
The /webpack subpaths were never published to npm, so we can drop the addons/webpack throw stub and its README example without a deprecation window.
🔗 Linked issue
N/A
❓ Type of change
📚 Description
Replaces the per-bundler subpath exports (
@unhead/{vue,react,svelte,solid-js}/webpackand@unhead/bundler/webpack) with a singlebundlerentry that returns a factory object exposing.vite(),.webpack(),.rspack(), and.rollup()dispatch methods. One call site now covers every supported builder, and the factory can be forwarded directly to Nuxt Kit'saddBuildPlugin. Framework/viteentries stay as thin wrappers over the new factory for backwards compatibility.@unhead/bundler/webpacksubpath export.@unhead/{vue,react,svelte,solid-js}/webpacksubpath exports.@unhead/addons/webpacknow throws at import time instead of warning.📝 Migration
Summary by CodeRabbit
New Features
Removals
Documentation