-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
feat(nuxt,vite): use importmap to increase chunk stability #33075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
|
|
Warning Rate limit exceeded@danielroe has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 18 minutes and 52 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughAdds an experimental feature to stabilise the client entry chunk using an import map. Introduces StableEntryPlugin for Vite client builds to capture the final hashed entry filename and rewrite entry imports to a stable alias ( Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes ✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (9)
packages/schema/src/types/schema.ts (1)
1447-1451: Clarify scope in JSDoc (Vite-only, prod-only).Minor doc enhancement to prevent misconfiguration when using non‑Vite builders or dev mode.
/** - * Whether to improve chunk stability by using an import map to resolve the entry chunk of the bundle. + * Whether to improve chunk stability by using an import map to resolve the entry chunk of the bundle. + * Applies to the Vite client build in production when hashed entry filenames are used. + * Ignored in development and by other builders. */ entryImportMap: booleanpackages/vite/src/client.ts (1)
136-138: Consider a fallback for browsers without native import maps.StableEntryPlugin rewrites imports to
#entry. On browsers lacking import map support, this will fail at runtime. Two options:
- Document the requirement and recommend
es-module-shimsfor legacy targets.- Or conditionally inject a shim when
experimental.entryImportMapis enabled and the project targets such browsers.Would you like me to open a follow-up proposing conditional shim injection and a docs note on supported browsers?
docs/2.guide/3.going-further/1.experimental-features.md (1)
641-662: Add builder scope and browser support note.Call out that this is a Vite client build feature and that native import maps are required (or a shim).
## entryImportMap -By default, Nuxt improves chunk stability by using an import map to resolve the entry chunk of the bundle. +By default, Nuxt (Vite builder) improves chunk stability by using an import map to resolve the entry chunk of the bundle. + +::note +This requires native import map support (e.g. Chrome 89+, Firefox 108+, Safari 16.4+) or a polyfill such as `es-module-shims` if you target older browsers. +::packages/nuxt/src/core/nitro.ts (1)
133-134: Virtual module placeholder is sensible; minor robustness considerationThe placeholder export works and will be overridden by the Vite plugin. If you want to make the intent explicit for TS consumers and future readers, consider exporting a typed value (string | undefined) and a JSDoc explaining that Vite replaces this at build time. Not required, just a small clarity win.
Example:
- '#internal/entry-chunk.mjs': () => `export const entryFileName = undefined`, + '#internal/entry-chunk.mjs': () => [ + '/** Resolved at client build time by StableEntryPlugin */', + 'export const entryFileName /*: string | undefined*/ = undefined', + ].join('\n'),packages/vite/src/plugins/stable-entry.ts (5)
16-20: Avoid duplicating assignment; centralise virtual registrationYou’re assigning both options.virtual and options._config.virtual to the same factory. Fine, but a tiny helper avoids drift and makes intent clearer.
- nitro.options.virtual ||= {} - nitro.options._config.virtual ||= {} - - nitro.options._config.virtual['#internal/entry-chunk.mjs'] = nitro.options.virtual['#internal/entry-chunk.mjs'] = () => `export const entryFileName = ${JSON.stringify(entryFileName)}` + const setVirtual = (key: string, fn: () => string) => { + nitro.options.virtual ||= {} + nitro.options._config.virtual ||= {} + nitro.options.virtual[key] = fn + nitro.options._config.virtual[key] = fn + } + setVirtual('#internal/entry-chunk.mjs', () => `export const entryFileName = ${JSON.stringify(entryFileName)}`)
27-33: Plugin gating is good; consider guarding for non-string patternsNice check to only run when hashed entry names are enabled. Add a defensive branch for functions/arrays to avoid silent false negatives if users customise entryFileNames as a function.
- return toArray(config.build?.rollupOptions?.output).some(output => typeof output?.entryFileNames === 'string' && output?.entryFileNames.includes('[hash]')) + return toArray(config.build?.rollupOptions?.output).some((output) => { + const pattern = output?.entryFileNames + if (typeof pattern === 'string') { return pattern.includes('[hash]') } + // if a function is used assume hashing may be applied + return typeof pattern === 'function' + })
40-43: Regex is safe from ReDoS but too narrow for absolute pathsThe basename is escaped (good), but
[\./]*won’t match path segments like/_nuxt/. Use a broader class bounded by quotes.- const filename = new RegExp(`(?<=['"])[\\./]*${escapeStringRegexp(basename(entry))}`, 'g') + const filename = new RegExp(String.raw`(?<=['"])[^'"]*?${escapeStringRegexp(basename(entry))}(?=['"])`, 'g')Note: This also quietens false positives from static analysers without weakening safety.
51-58: Prefix trimming: ensure trailing slash for consistencyWhen
buildAssetsDiris customised without a trailing slash,startsWith(prefix)may not match. Normalise to include a trailing slash before compare/slice.- const prefix = withoutLeadingSlash(nuxt.options.app.buildAssetsDir) + const prefix = withoutLeadingSlash(nuxt.options.app.buildAssetsDir).replace(/\/?$/, '/')
10-60: Add a minimal test to lock behaviourConsider a unit/integration test that builds a tiny project twice with a one-line change in a component and asserts:
- Only the entry filename changes.
- All imports of the entry are rewritten to '#entry'.
- The virtual module exports the final
entryFileName.I can provide a fixture and pnpm script if helpful.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
docs/2.guide/3.going-further/1.experimental-features.md(1 hunks)packages/nuxt/src/core/nitro.ts(1 hunks)packages/nuxt/src/core/runtime/nitro/handlers/renderer.ts(2 hunks)packages/schema/src/config/experimental.ts(1 hunks)packages/schema/src/types/schema.ts(1 hunks)packages/vite/src/client.ts(2 hunks)packages/vite/src/plugins/stable-entry.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/schema/src/types/schema.tspackages/vite/src/client.tspackages/vite/src/plugins/stable-entry.tspackages/schema/src/config/experimental.tspackages/nuxt/src/core/nitro.tspackages/nuxt/src/core/runtime/nitro/handlers/renderer.ts
🧠 Learnings (1)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/nitro.ts
🧬 Code graph analysis (2)
packages/vite/src/client.ts (1)
packages/vite/src/plugins/stable-entry.ts (1)
StableEntryPlugin(10-60)
packages/nuxt/src/core/runtime/nitro/handlers/renderer.ts (1)
packages/nuxt/src/core/runtime/nitro/utils/paths.ts (1)
buildAssetsURL(14-16)
🪛 ast-grep (0.38.6)
packages/vite/src/plugins/stable-entry.ts
[warning] 39-39: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=['"])[\\./]*${escapeStringRegexp(basename(entry))}, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🪛 LanguageTool
docs/2.guide/3.going-further/1.experimental-features.md
[uncategorized] ~653-~653: Possible missing comma found.
Context: ...unchanged. If you need to disable this feature you can do so: ```ts twoslash [nuxt.co...
(AI_HYDRA_LEO_MISSING_COMMA)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: test-fixtures (windows-latest, built, webpack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, built, rspack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
- GitHub Check: release-pkg-pr-new
- GitHub Check: test-benchmark
- GitHub Check: test-size
- GitHub Check: typecheck (ubuntu-latest, bundler)
- GitHub Check: typecheck (windows-latest, bundler)
- GitHub Check: code
🔇 Additional comments (5)
packages/vite/src/client.ts (1)
23-23: LGTM: plugin import is correct.Import path and placement look good.
packages/schema/src/config/experimental.ts (1)
219-219: Defaulting totrue: confirm builder compatibility.This enables the feature for all builders, though only Vite consumes it. That’s fine, but please double‑check no unintended side effects occur for
webpack/rspack.If you want stricter scoping, I can propose a resolver that enables it by default only when
builder === 'vite'.packages/nuxt/src/core/runtime/nitro/handlers/renderer.ts (2)
31-32: LGTM: virtual import for entry filename.Importing from the virtual module is appropriate and aligns with the Vite plugin’s
writeBundlehook.
187-197: Ensure CSP nonce on inline import map and verify SSRContext fieldIt looks like the head runtime already supports a
nonceattribute for script tags (seepackages/nuxt/src/head/runtime/components.tsdefiningnonce: String). Before merging this patch, please confirm which property on your SSR context holds the CSP nonce (e.g.ssrContext.cspNonceor simplyssrContext.nonce).Key points to address:
- Add the script tag’s
nonceonly when the SSR context exposes it.- Retain
tagPriority: -2to guarantee earliest insertion before module scripts.- Update the spread logic once you’ve confirmed the exact property name.
Suggested diff (adjust property name as needed):
if (entryFileName && !NO_SCRIPTS) { ssrContext.head.push({ script: [{ tagPosition: 'head', tagPriority: -2, type: 'importmap', innerHTML: JSON.stringify({ imports: { '#entry': buildAssetsURL(entryFileName) } }), - // Ensure CSP compatibility where nonces are used - ...(ssrContext as any).cspNonce ? { nonce: (ssrContext as any).cspNonce } : {}, + // Inject CSP nonce if available on the SSR context + ...((ssrContext as any).cspNonce ?? (ssrContext as any).nonce + ? { nonce: (ssrContext as any).cspNonce ?? (ssrContext as any).nonce } + : {}), }], }, headEntryOptions) }packages/vite/src/plugins/stable-entry.ts (1)
35-50: ClarifyrenderChunkvsgenerateBundleusage and improve the entry-chunk regex
meta.chunksinrenderChunkis part of Rollup’s public API, but itsfileNamevalues contain hash placeholders rather than the actual hashes (rollupjs.org).- If you need to rewrite code using the final hashed filenames, use the
generateBundlehook (which receivesbundle: { [fileName]: OutputAsset | OutputChunk }) to access realfileNames (rollupjs.org).- You can still use
renderChunk, but be aware of placeholder names (guardmeta?.chunks) and that placeholders only get resolved later.- In either hook, update the regex to match nested paths (e.g.
/_nuxt/entry-*.js), not just./or/prefixes.Suggested diffs:
Option A: Continue in
renderChunkwith improved regex and a guardrenderChunk(code: string, chunk: RenderedChunk, _options, meta) { - const entry = Object.values(meta.chunks).find(c => c.isEntry && c.name === 'entry')?.fileName + if (!meta?.chunks) { return null } + const entry = Object.values(meta.chunks).find(c => c.isEntry && c.name === 'entry')?.fileName if (!entry || !chunk.imports.includes(entry)) { return } - const filename = new RegExp(`(?<=['"])[\\./]*${escapeStringRegexp(basename(entry))}`, 'g') + const filenameRE = new RegExp(String.raw`(?<=['"])[^'"]*?${escapeStringRegexp(basename(entry))}(?=['"])`, 'g') const s = new MagicString(code) - s.replaceAll(filename, '#entry') + s.replaceAll(filenameRE, '#entry') …Option B: Switch to
generateBundleto work with actual filenames- renderChunk (code, chunk, _options, meta) { … }, + generateBundle(_options: OutputOptions, bundle) { + const entryFile = Object.values(bundle).find( + (c): c is OutputChunk => c.type === 'chunk' && c.isEntry && c.name === 'entry' + )?.fileName + if (!entryFile) { return } + const base = escapeStringRegexp(basename(entryFile)) + const filenameRE = new RegExp(String.raw`(?<=['"])[^'"]*?${base}(?=['"])`, 'g') + + for (const chunk of Object.values(bundle)) { + if (chunk.type !== 'chunk' || !chunk.imports?.includes(entryFile)) { continue } + const s = new MagicString(chunk.code) + s.replaceAll(filenameRE, '#entry') + if (s.hasChanged()) { + chunk.code = s.toString() + if (sourcemap) { chunk.map = s.generateMap({ hires: true }) } + } + } + },Likely an incorrect or invalid review comment.
CodSpeed Performance ReportMerging #33075 will not alter performanceComparing Summary
|
@nuxt/kit
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
packages/vite/src/plugins/stable-entry.ts (8)
16-19: Factor out the virtual module generator and avoid chained assignment for clarityThis reduces cognitive load and makes it easier to extend later (e.g., more exports).
Apply this diff:
- nitro.options.virtual ||= {} - nitro.options._config.virtual ||= {} - - nitro.options._config.virtual['#internal/entry-chunk.mjs'] = nitro.options.virtual['#internal/entry-chunk.mjs'] = () => `export const entryFileName = ${JSON.stringify(entryFileName)}` + nitro.options.virtual ||= {} + nitro.options._config.virtual ||= {} + const renderEntryChunkVirtual = () => `export const entryFileName = ${JSON.stringify(entryFileName)}` + nitro.options.virtual['#internal/entry-chunk.mjs'] = renderEntryChunkVirtual + nitro.options._config.virtual['#internal/entry-chunk.mjs'] = renderEntryChunkVirtual
26-39: Prefer the full Viteapply(config, env)signature and short-circuit non-build commandsThis makes intent explicit and avoids any type friction with Vite’s
Plugin['apply']overloads.Apply this diff:
- apply (config) { + apply (config, env) { + if (env?.command !== 'build') { return false } if (nuxt.options.dev || !nuxt.options.experimental.entryImportMap) { return false } if (config.build?.target) { const targets = toArray(config.build.target) if (!targets.every(isSupported)) { return false } } // only apply plugin if the entry file name is hashed return toArray(config.build?.rollupOptions?.output).some(output => typeof output?.entryFileNames === 'string' && output?.entryFileNames.includes('[hash]')) },
40-44: Avoid shadowingchunkfor readabilityMinor readability: don’t shadow the outer
chunkvariable in thefindcallback.Apply this diff:
- const entry = Object.values(meta.chunks).find(chunk => chunk.isEntry && chunk.name === 'entry')?.fileName + const entry = Object.values(meta.chunks).find(c => c.isEntry && c.name === 'entry')?.fileName
46-49: Make the replacement regex more robust (nested paths and querystrings) while staying safeCurrent pattern misses cases like "/_nuxt/entry/XYZ.js" or specifiers with query strings. Using
escapeStringRegexpis good; extend the non-greedy path segment and optional query handling.Apply this diff:
- const filename = new RegExp(`(?<=['"])[\\./]*${escapeStringRegexp(basename(entry))}`, 'g') + const entryBase = escapeStringRegexp(basename(entry)) + // Match optional path segments and optional query, bounded by quotes + const filename = new RegExp(`(?<=['"])(?:[^"'\\n]*/)?${entryBase}(?:\\?[^'"]*)?(?=['"])`, 'g')Note: Static analysis warning about variable-driven regex is mitigated by
escapeStringRegexp; the rest is simple character classes, so ReDoS risk is negligible.
41-49: Guard against false negatives by relaxing the import check
RenderedChunk.importscontains chunk fileNames; in some outputs it may include path segments. Checking onlyincludes(entry)could skip valid chunks. Consider suffix match.Apply this diff:
- if (!entry || !chunk.imports.includes(entry)) { + if (!entry || !chunk.imports.some(i => i === entry || i.endsWith('/' + basename(entry)))) { return }
57-64: NormaliseentryFileNameconsistently withbuildAssetsDirIf Rollup prefixes fileNames with
assets/, keep it; if the fileName is absolute, stripbuildAssetsDirwith or without leading slash for safety.Apply this diff:
- const prefix = withoutLeadingSlash(nuxt.options.app.buildAssetsDir) - if (entry?.startsWith(prefix)) { - entry = entry.slice(prefix.length) - } + const prefix = withoutLeadingSlash(nuxt.options.app.buildAssetsDir) + if (entry?.startsWith('/' + prefix)) { + entry = entry.slice(prefix.length + 1) + } else if (entry?.startsWith(prefix)) { + entry = entry.slice(prefix.length) + }
69-87: Consider expanding target parsing and documenting unsupported engines
build.targetoften includeses20xxor mixed entries. Your fallback is sensible; adding a short comment and supporting common aliases (e.g.,ios_saf) could reduce surprises.Apply this diff:
-const supportedEnvironments = { +// Minimal set; unknown engines are treated as supported to avoid over-restricting. +const supportedEnvironments: Record<string, number> = { chrome: 89, edge: 89, firefox: 108, ie: Infinity, ios: 16.4, opera: 75, safari: 16.4, + // Common esbuild aliases (mapped to Safari iOS engine) + ios_saf: 16.4, }
1-88: Add targeted tests for entry rewrite and import-map gatingRecommend unit/integration tests to lock behaviour:
- renderChunk rewrites imports to
#entryonly when the entry is imported.- writeBundle exposes the correct
entryFileNamerelative tobuildAssetsDir.- apply gating respects
experimental.entryImportMap, dev mode, and unsupported targets.I can draft a minimal test harness using a synthetic Rollup bundle and verify
renderChunk/writeBundleoutputs if helpful.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/vite/src/plugins/stable-entry.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/vite/src/plugins/stable-entry.ts
🪛 ast-grep (0.38.6)
packages/vite/src/plugins/stable-entry.ts
[warning] 45-45: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=['"])[\\./]*${escapeStringRegexp(basename(entry))}, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: code
🔇 Additional comments (1)
packages/vite/src/plugins/stable-entry.ts (1)
21-65: Overall plugin logic LGTM; nice gating and minimal surface areaGood use of
applyToEnvironment, hashed-entry gating, and virtual module exposure. The approach should materially improve chunk stability with low overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
docs/2.guide/3.going-further/1.experimental-features.md (3)
651-655: Add a blank line before the admonition for correct rendering.Keeps formatting consistent with other sections and avoids parser hiccups.
Apply this diff:
-Within the script chunks emitted by Vite, imports will be from `#entry`. This means that changes to the entry will not invalidate chunks which are otherwise unchanged. -::note +Within the script chunks emitted by Vite, imports will reference `#entry` instead of the hashed entry filename. This prevents otherwise-unchanged chunks from being invalidated when only the entry changes. + +::note
647-649: Format the import map example for readability.Multiline JSON improves scanability and aligns with other docs samples.
Apply this diff:
-```html -<script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script> -``` +```html +<script type="importmap"> +{ "imports": { "#entry": "/_nuxt/DC5HVSK5.js" } } +</script> +```
641-666: Clarify scope (client build only) and add references for consistency with other sections.Most experimental sections include default/scope notes and a read-more link.
Apply this diff:
## entryImportMap -By default, Nuxt improves chunk stability by using an import map to resolve the entry chunk of the bundle. +By default, Nuxt improves chunk stability by using an import map to resolve the entry chunk of the bundle. + +*Enabled by default for client builds.*Optionally add a read-more block after the section:
+::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/pull/33075" target="_blank"} +See PR #33075 for implementation details and discussion. +::
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
docs/2.guide/3.going-further/1.experimental-features.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/2.guide/3.going-further/1.experimental-features.md
[uncategorized] ~652-~652: Loose punctuation mark.
Context: ... chunks which are otherwise unchanged. ::note Nuxt smartly disables this feature...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~654-~654: Loose punctuation mark.
Context: ...a value that does not include [hash]. :: If you need to disable this feature y...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~657-~657: Possible missing comma found.
Context: ...hash]`. :: If you need to disable this feature you can do so: ```ts twoslash [nuxt.co...
(AI_HYDRA_LEO_MISSING_COMMA)
|
I guess this could also have an impact on #32105 |
|
Is this only for Nuxt version 4? Can it be used for Nuxt version 3? @danielroe |
|
@fikryrmdhna features are still backported to Nuxt 3. See https://github.com/nuxt/nuxt/releases/tag/v3.19.0 |
🔗 Linked issue
resolves #27863
related: #26565 and #29624
📚 Description
Background
By default, JS chunks emitted in a vite build are hashed. This means they can be cached immutably because their content never changes without a file name change.
However, there are some very significant consequences. Namely, a change to a single component in a Nuxt build can cause every hash to be invalidated, massively increasing the chance of 404s.
Here's an example:
In a Nuxt app almost every file will import the entry, so it is particularly prone to this.
Other projects, like Vitepress, use a static hashmap which is embedded in the HTML and allows mapping every chunk.
Approach
This PR improves chunk stability by using an import map to resolve the entry chunk of the bundle.
This injects an import map at the top of your
<head>tag:Within the script chunks emitted by Vite, imports will be from
#entry. This means that changes to the entry will not invalidate chunks which are otherwise unchanged. In the example above, steps 1, 2 and 3 still occur, but at that point a 'boundary' is in place and the rest of the project does not have its hashes invalidated.Importantly, although we are rewriting imports to use
#entrythey are still treated as hashed assets by the browser so this should not impact (desired) cache invalidation.This is more lightweight than the Vitepress approach of embedding a hashmap of the entire project.
If you need to disable this feature (to support Safari <16.4, for example - cross-browser lack of support for import maps is at 1.75%) you can do so: