Skip to content

Commit cb7d0b0

Browse files
authored
feat(bundler): transpile static inline scripts for Vite targets (#862)
* feat(bundler): transpile static inline scripts for Vite targets Static inline scripts emitted from useHead bypass Vite's normal transpilation and can ship unsupported syntax. Run literals through Vite's resolved target in MinifyTransform, with an explicit override and opt-out. * refactor(bundler): centralize inline transform options Keep the Vite and framework factories on the same defaulting path. Clarify the Vite-only transpilation scope and document target failures. * test(bundler): verify Vite transform backends Cover Oxc selection and the esbuild fallback. Document that Nuxt app.head and Nitro-injected HTML sit outside the Vite module graph. * fix(bundler): handle inline script transform edge cases * fix(bundler): normalize baseline target on Vite 6
1 parent a834ef6 commit cb7d0b0

11 files changed

Lines changed: 520 additions & 73 deletions

File tree

docs/head/1.guides/build-plugins/0.overview.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Build Plugins
3-
description: Unified Vite and webpack plugins for Unhead. Build optimizations, tree-shaking, and useSeoMeta transforms in a single plugin call.
3+
description: Unified Vite and webpack plugins for Unhead. Build optimizations, inline script transpilation, tree-shaking, and useSeoMeta transforms in a single plugin call.
44
navigation.title: Overview
55
---
66

@@ -66,7 +66,8 @@ The plugin combines several build-time optimizations:
6666

6767
- **[Tree-shaking](/docs/head/guides/build-plugins/tree-shaking)**: Removes deprecated server composables and `useSchemaOrg` calls from client bundles
6868
- **[useSeoMeta transform](/docs/head/guides/build-plugins/seo-meta-transform)**: Converts `useSeoMeta()` calls into raw `useHead()` calls at build time
69-
- **[Minify transform](/docs/head/guides/build-plugins/minify-transform)**: Pre-minify static inline script/style content at build time
69+
- **[Inline script transform](/docs/head/guides/build-plugins/minify-transform)**: Transpiles static inline scripts to Vite's configured browser target
70+
- **[Minify transform](/docs/head/guides/build-plugins/minify-transform)**: Optionally pre-minifies static inline script/style content in the same AST pass
7071
- **Dev validation**: Auto-injects `ValidatePlugin` in dev so head tag warnings surface in the browser console (enabled by default)
7172
- **[DevTools](/docs/head/guides/build-plugins/devtools)**: Registers Unhead's Vite DevTools integration by default; the panel activates when Vite DevTools is enabled
7273

@@ -78,6 +79,8 @@ Unhead({
7879
treeshake: false,
7980
// Disable useSeoMeta → useHead transform
8081
transformSeoMeta: false,
82+
// Disable automatic inline script transpilation
83+
transformInlineScripts: false,
8184
// Pre-minify inline script/style content
8285
minify: { js: createJSMinifier(), css: createCSSMinifier() },
8386
// File filter (shared across all transforms)
@@ -89,6 +92,7 @@ Unhead({
8992
| -------- | ------ | --------- | ------------- |
9093
| `treeshake` | `object \| false` | enabled | [Tree-shake server composables](/docs/head/guides/build-plugins/tree-shaking) from client bundles |
9194
| `transformSeoMeta` | `object \| false` | enabled | [Transform `useSeoMeta()`](/docs/head/guides/build-plugins/seo-meta-transform) to `useHead()` |
95+
| `transformInlineScripts` | `object \| false` | enabled | Transpile static inline scripts to Vite's resolved `build.target` |
9296
| `minify` | `object \| false` | disabled | [Pre-minify static inline script/style](/docs/head/guides/build-plugins/minify-transform) |
9397
| `validate` | `boolean` | enabled | Auto-inject `ValidatePlugin` in dev for head tag warnings |
9498
| `devtools` | `object \| false` | included | [Vite DevTools integration](/docs/head/guides/build-plugins/devtools) (dev only) |
@@ -97,6 +101,15 @@ Unhead({
97101

98102
The three transforms accept `false` or an options object. `devtools` follows the same pattern, while `validate` and `sourcemap` are booleans and `filter` is an object.
99103

104+
Inline scripts inherit Vite's resolved `build.target`. Override it without
105+
changing the target for the rest of the bundle:
106+
107+
```ts
108+
Unhead({
109+
transformInlineScripts: { target: 'chrome77' },
110+
})
111+
```
112+
100113
## Other Bundlers
101114

102115
The Vue, React, Svelte, and Solid.js packages expose a unified `/bundler` entry with per-bundler methods:
@@ -110,3 +123,5 @@ export default {
110123
```
111124

112125
The bundler entry also exposes `.rspack()` and `.rollup()`. Validation and DevTools injection are Vite-only. Rollup cannot infer whether it is building for the client or server, so target-dependent transforms remain conservative; use `.vite()` or `.webpack()` when you need target-aware optimization.
126+
127+
Inline script target inheritance is Vite-only. Rollup has no browser target contract. Unplugin provides the webpack and Rspack targets, but not their transpilers, so Unhead cannot apply those targets to an inline string. Existing explicit minifier configuration is unchanged.

docs/head/1.guides/build-plugins/3.minify-transform.md

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,67 @@
11
---
2-
title: Minify Transform
3-
description: Pre-minify static inline script and style content inside useHead() calls at build time using rolldown, esbuild, or lightningcss.
4-
navigation.title: Minify Transform
2+
title: Inline Code Transform
3+
description: Transpile static inline scripts and optionally minify static inline script and style content inside useHead() calls at build time.
4+
navigation.title: Inline Code Transform
55
---
66

7-
The minify transform processes static string literals in `useHead()` script and style tags at build time. It supports Rolldown or esbuild for JavaScript and Lightning CSS for CSS, without adding those tools to the SSR runtime bundle. The transform is disabled until you provide a minifier.
7+
**Quick Answer:** Static inline scripts inside `useHead()` inherit Vite's
8+
configured `build.target`. The same AST pass can optionally minify script and
9+
style strings using rolldown, esbuild, or lightningcss.
810

9-
## Transformation
11+
## Inline Script Transpilation
12+
13+
Inline scripts are HTML strings, so the normal application bundling pipeline
14+
cannot see the JavaScript inside them. The Unhead build plugin finds static
15+
`innerHTML` and `textContent` values and runs them through Vite's JavaScript
16+
transform using the resolved browser target:
17+
18+
Transpilation is available only through the Vite integration. Webpack, Rspack,
19+
and Rollup leave inline script syntax unchanged, while configured JavaScript and
20+
CSS minifiers still run.
21+
22+
```ts [vite.config.ts]
23+
import { Unhead } from '@unhead/vue/vite'
24+
25+
export default defineConfig({
26+
build: {
27+
target: 'chrome77',
28+
},
29+
plugins: [Unhead()],
30+
})
31+
```
32+
33+
```ts [Source]
34+
useHead({
35+
script: [{
36+
textContent: 'window.theme = storage?.theme ?? "light"',
37+
}],
38+
})
39+
```
40+
41+
The inline script now uses the same target as the application bundle.
42+
43+
To override the target only for inline scripts, or opt out entirely:
44+
45+
```ts
46+
Unhead({
47+
transformInlineScripts: { target: 'chrome77' },
48+
// transformInlineScripts: false,
49+
})
50+
```
51+
52+
Only static string and expression-free template literals can be transformed at
53+
build time. Dynamic strings remain unchanged. Scripts with a JavaScript MIME
54+
type, `type="module"`, an empty type, or no type are eligible. Other script types
55+
are data blocks and remain unchanged. If Vite cannot safely transform a script
56+
for the configured target, the build fails instead of shipping code that violates
57+
that target.
58+
59+
Nuxt's `app.head` configuration and HTML inserted from Nitro render hooks are
60+
created outside the Vite application module graph, so this transform cannot
61+
process them. Module authors should use `onPrehydrate` for pre-hydration code or
62+
ship script text that already supports their minimum browser target.
63+
64+
## Minification
1065

1166
The transform finds `innerHTML` and `textContent` properties on `script` and `style` objects inside `useHead()` calls, then minifies them at build time:
1267

@@ -36,10 +91,11 @@ useHead({
3691
::
3792

3893
The transform minifies `application/json`, `application/ld+json`, `speculationrules`, and `importmap` script types with Unhead's built-in JSON minifier. It ignores strings shorter than 20 characters and dynamic content, including template literals with expressions.
94+
JavaScript transpilation has no minimum length.
3995

4096
## Setup
4197

42-
The minify transform is disabled by default. Enable it by providing minifier functions:
98+
Minification is disabled by default. Enable it by providing minifier functions:
4399

44100
```ts
45101
import { Unhead } from '@unhead/vue/vite'

packages/bundler/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
## Features
1010

1111
- 🛠️ Build-time optimizations for Unhead
12+
- 🎯 Transpile static inline scripts to Vite's configured browser target
1213
- 🌲 Tree-shake server composables from client bundles
1314
- ⚡ Transform `useSeoMeta` calls for better performance
1415
- 📦 Support for Vite, Webpack, and other bundlers
@@ -53,6 +54,9 @@ interface UnpluginOptions {
5354

5455
// Transform useSeoMeta calls for better performance
5556
useSeoMetaTransform?: boolean | UseSeoMetaTransformOptions
57+
58+
// Vite: transpile static inline scripts to build.target (enabled by default)
59+
transformInlineScripts?: false | { target?: string | string[] | false }
5660
}
5761
```
5862

0 commit comments

Comments
 (0)