Skip to content

Commit 3591411

Browse files
authored
fix: replace version in generateBundle (#12700)
In #8957 one of the desired outcomes was that the framework code would have its own chunk with a stable hash for better caching, but that didn't really happen because the client imports something that references version, through __sveltekit/environment for created_updated_store and version hash through __sveltekit/path, which meant that when the developer changes their app code and increments/changes the version it'd also change the above mentioned modules and subsequently most chunks with the framework code In this PR the version is injected in generateBundle once the chunk hashes have already been calculated so that just changing the version doesn't affect the hashes for chunks without any actual code changes fixes #12260
1 parent 7fb1f5c commit 3591411

File tree

3 files changed

+77
-6
lines changed

3 files changed

+77
-6
lines changed

.changeset/moody-zoos-compare.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: ensure a changing `version` doesn't affect the hashes for chunks without any actual code changes

packages/kit/src/constants.js

+12
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,15 @@ export const GENERATED_COMMENT = '// this file is generated — do not edit it\n
99
export const ENDPOINT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'];
1010

1111
export const PAGE_METHODS = ['GET', 'POST', 'HEAD'];
12+
13+
/**
14+
* Placeholders for the hash of the app version.
15+
* Later replaced in the generateBundle hook to avoid affecting the chunk hash.
16+
*/
17+
export const APP_VERSION_HASH_PLACEHOLDER_BASE = '__SVELTEKIT_APP_VERSION_HASH__';
18+
19+
/**
20+
* Placeholder for the app version.
21+
* Later replaced in the generateBundle hook to avoid affecting the chunk hash.
22+
*/
23+
export const APP_VERSION_PLACEHOLDER_BASE = '__SVELTEKIT_APP_VERSION__';

packages/kit/src/exports/vite/index.js

+60-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import {
3535
sveltekit_server
3636
} from './module_ids.js';
3737
import { resolve_peer_dependency } from '../../utils/import.js';
38+
import {
39+
APP_VERSION_PLACEHOLDER_BASE,
40+
APP_VERSION_HASH_PLACEHOLDER_BASE
41+
} from '../../constants.js';
3842

3943
const cwd = process.cwd();
4044

@@ -184,7 +188,16 @@ async function kit({ svelte_config }) {
184188
const { kit } = svelte_config;
185189
const out = `${kit.outDir}/output`;
186190

187-
const version_hash = hash(kit.version.name);
191+
const app_version = kit.version.name;
192+
const version_hash = hash(app_version);
193+
194+
// if the app version or hash is longer than the placeholder, we need to pad it to avoid
195+
// source map damage
196+
const app_version_placeholder = APP_VERSION_PLACEHOLDER_BASE.padEnd(app_version.length, '_');
197+
const app_version_hash_placeholder = APP_VERSION_HASH_PLACEHOLDER_BASE.padEnd(
198+
version_hash.length,
199+
'_'
200+
);
188201

189202
/** @type {import('vite').ResolvedConfig} */
190203
let vite_config;
@@ -384,7 +397,7 @@ async function kit({ svelte_config }) {
384397
const browser = !options?.ssr;
385398

386399
const global = is_build
387-
? `globalThis.__sveltekit_${version_hash}`
400+
? `globalThis.__sveltekit_${browser ? app_version_hash_placeholder : version_hash}`
388401
: 'globalThis.__sveltekit_dev';
389402

390403
if (options?.ssr === false && process.env.TEST !== 'true') {
@@ -470,10 +483,8 @@ async function kit({ svelte_config }) {
470483
}
471484

472485
case sveltekit_environment: {
473-
const { version } = svelte_config.kit;
474-
475486
return dedent`
476-
export const version = ${s(version.name)};
487+
export const version = ${is_build && browser ? app_version_placeholder : s(kit.version.name)};
477488
export let building = false;
478489
export let prerendering = false;
479490
@@ -923,7 +934,50 @@ async function kit({ svelte_config }) {
923934
}
924935
};
925936

926-
return [plugin_setup, plugin_virtual_modules, plugin_guard, plugin_compile];
937+
/** @type {import('vite').Plugin} */
938+
const plugin_replace_version_and_hash = {
939+
name: 'vite-plugin-svelte-replace-version-and-hash',
940+
enforce: 'post',
941+
942+
generateBundle(_, bundle, __) {
943+
if (vite_config.build.ssr) return;
944+
945+
for (const file in bundle) {
946+
if (bundle[file].type !== 'chunk') continue;
947+
const chunk = /** @type {import('rollup').OutputChunk} */ (bundle[file]);
948+
let code = chunk.code;
949+
if (
950+
!(code.includes(app_version_placeholder) || code.includes(app_version_hash_placeholder))
951+
)
952+
continue;
953+
954+
// replace the version and version after the chunk hash has already been calculated
955+
// to avoid affecting the chunk hash
956+
const substitutions = [
957+
[app_version_hash_placeholder, version_hash],
958+
[app_version_placeholder, JSON.stringify(kit.version.name)]
959+
];
960+
961+
for (const [placeholder, replacement] of substitutions) {
962+
code = code.replaceAll(
963+
placeholder,
964+
// pad the replacement to mitigate source map changes
965+
replacement.padEnd(placeholder.length, ' ')
966+
);
967+
}
968+
969+
chunk.code = code;
970+
}
971+
}
972+
};
973+
974+
return [
975+
plugin_setup,
976+
plugin_virtual_modules,
977+
plugin_guard,
978+
plugin_compile,
979+
plugin_replace_version_and_hash
980+
];
927981
}
928982

929983
/**

0 commit comments

Comments
 (0)