Skip to content

Commit 5121479

Browse files
fix: avoid client modules while traversing dependencies to prevent FOUC during dev (#14577)
* fix: avoid client modules while traversing dependencies to prevent FOUC during dev This tweaks the way we're traversing dependencies to avoid a flash of unstyled content during dev. Previously we were using `getModuleByURL` which loads both the client and server version of the module. This means that server-only dependencies can end up being loaded into the client module graph, which is more likely to happen with remote modules. Example: +page.svelte -> foo.remote.ts -> {client: nothing, server: server-only-dependency } -> getModuleByURL for both client and server of server-only dependency -> crash I'm not totally sure if this may have been a somewhat hidden breaking change from Vite 5 -> 6, but either way this ensures that Vite 5-7 now only load the ssr variant. Fixes #14519 * snake_case --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>
1 parent cde1458 commit 5121479

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

.changeset/late-banks-clean.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: avoid client modules while traversing dependencies to prevent FOUC during dev

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
234234
// in dev we inline all styles to avoid FOUC. this gets populated lazily so that
235235
// components/stylesheets loaded via import() during `load` are included
236236
result.inline_styles = async () => {
237-
/** @type {Set<import('vite').ModuleNode>} */
237+
/** @type {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} */
238238
const deps = new Set();
239239

240240
for (const module_node of module_nodes) {
@@ -610,16 +610,16 @@ function remove_static_middlewares(server) {
610610

611611
/**
612612
* @param {import('vite').ViteDevServer} vite
613-
* @param {import('vite').ModuleNode} node
614-
* @param {Set<import('vite').ModuleNode>} deps
613+
* @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node
614+
* @param {Set<import('vite').ModuleNode | import('vite').EnvironmentModuleNode>} deps
615615
*/
616616
async function find_deps(vite, node, deps) {
617617
// since `ssrTransformResult.deps` contains URLs instead of `ModuleNode`s, this process is asynchronous.
618618
// instead of using `await`, we resolve all branches in parallel.
619619
/** @type {Promise<void>[]} */
620620
const branches = [];
621621

622-
/** @param {import('vite').ModuleNode} node */
622+
/** @param {import('vite').ModuleNode | import('vite').EnvironmentModuleNode} node */
623623
async function add(node) {
624624
if (!deps.has(node)) {
625625
deps.add(node);
@@ -629,20 +629,23 @@ async function find_deps(vite, node, deps) {
629629

630630
/** @param {string} url */
631631
async function add_by_url(url) {
632-
const node = await vite.moduleGraph.getModuleByUrl(url);
632+
const node = await get_server_module_by_url(vite, url);
633633

634634
if (node) {
635635
await add(node);
636636
}
637637
}
638638

639-
if (node.ssrTransformResult) {
640-
if (node.ssrTransformResult.deps) {
641-
node.ssrTransformResult.deps.forEach((url) => branches.push(add_by_url(url)));
639+
const transform_result =
640+
/** @type {import('vite').ModuleNode} */ (node).ssrTransformResult || node.transformResult;
641+
642+
if (transform_result) {
643+
if (transform_result.deps) {
644+
transform_result.deps.forEach((url) => branches.push(add_by_url(url)));
642645
}
643646

644-
if (node.ssrTransformResult.dynamicDeps) {
645-
node.ssrTransformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
647+
if (transform_result.dynamicDeps) {
648+
transform_result.dynamicDeps.forEach((url) => branches.push(add_by_url(url)));
646649
}
647650
} else {
648651
node.importedModules.forEach((node) => branches.push(add(node)));
@@ -651,6 +654,16 @@ async function find_deps(vite, node, deps) {
651654
await Promise.all(branches);
652655
}
653656

657+
/**
658+
* @param {import('vite').ViteDevServer} vite
659+
* @param {string} url
660+
*/
661+
function get_server_module_by_url(vite, url) {
662+
return vite.environments
663+
? vite.environments.ssr.moduleGraph.getModuleByUrl(url)
664+
: vite.moduleGraph.getModuleByUrl(url, true);
665+
}
666+
654667
/**
655668
* Determine if a file is being requested with the correct case,
656669
* to ensure consistent behaviour between dev and prod and across

0 commit comments

Comments
 (0)