From 6c9e72e65585298319ee6a277d2a53d95f11d648 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Jun 2022 13:39:13 -0400 Subject: [PATCH 01/12] add failing test for #5137 --- .../load/dynamic-import-styles/_/Thing.svelte | 7 +++++++ .../load/dynamic-import-styles/index.svelte | 15 +++++++++++++++ packages/kit/test/apps/basics/test/test.js | 9 +++++++++ 3 files changed, 31 insertions(+) create mode 100644 packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/_/Thing.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/_/Thing.svelte b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/_/Thing.svelte new file mode 100644 index 000000000000..14923ae98276 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/_/Thing.svelte @@ -0,0 +1,7 @@ +

this text is red

+ + diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte new file mode 100644 index 000000000000..90167d792806 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/index.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index d5c7de9372c6..93bff4a0ecc6 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1532,6 +1532,15 @@ test.describe.parallel('Load', () => { ); } }); + + test('CSS for dynamically imported components is reflected in server render', async ({ + page + }) => { + await page.goto('/load/dynamic-import-styles'); + expect( + await page.evaluate(() => getComputedStyle(document.querySelector('#thing')).color) + ).toBe('rgb(255, 0, 0)'); + }); }); test.describe.parallel('Method overrides', () => { From ed36d49df4135d76c2fbe4b1bb93d01d8a8b191e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Jun 2022 13:52:11 -0400 Subject: [PATCH 02/12] partial fix for #5137 --- packages/kit/src/core/build/build_client.js | 2 +- packages/kit/src/core/build/build_server.js | 2 +- packages/kit/src/core/build/utils.js | 11 ++++++++--- packages/kit/src/core/dev/plugin.js | 4 ++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/core/build/build_client.js b/packages/kit/src/core/build/build_client.js index 9b8a793ec8bc..4fc1bbc10a10 100644 --- a/packages/kit/src/core/build/build_client.js +++ b/packages/kit/src/core/build/build_client.js @@ -59,7 +59,7 @@ export async function build_client(options) { const entry = posixify(client_entry_file); const entry_js = new Set(); const entry_css = new Set(); - find_deps(entry, vite_manifest, entry_js, entry_css); + find_deps(entry, vite_manifest, entry_js, entry_css, false); fs.writeFileSync( `${client_out_dir}/version.json`, diff --git a/packages/kit/src/core/build/build_server.js b/packages/kit/src/core/build/build_server.js index f1b50f5af18c..e78714f163ef 100644 --- a/packages/kit/src/core/build/build_server.js +++ b/packages/kit/src/core/build/build_server.js @@ -234,7 +234,7 @@ export async function build_server(options, client) { const js = new Set(); const css = new Set(); - find_deps(component, client.vite_manifest, js, css); + find_deps(component, client.vite_manifest, js, css, false); const imports = [`import * as module from '../${vite_manifest[component].file}';`]; diff --git a/packages/kit/src/core/build/utils.js b/packages/kit/src/core/build/utils.js index 6a0477b9dcc2..b363c43abcba 100644 --- a/packages/kit/src/core/build/utils.js +++ b/packages/kit/src/core/build/utils.js @@ -28,19 +28,24 @@ export async function create_build(config) { * @param {import('vite').Manifest} manifest * @param {Set} css * @param {Set} js + * @param {boolean} dynamic */ -export function find_deps(file, manifest, js, css) { +export function find_deps(file, manifest, js, css, dynamic) { const chunk = manifest[file]; if (js.has(chunk.file)) return; - js.add(chunk.file); + if (!dynamic) js.add(chunk.file); if (chunk.css) { chunk.css.forEach((file) => css.add(file)); } if (chunk.imports) { - chunk.imports.forEach((file) => find_deps(file, manifest, js, css)); + chunk.imports.forEach((file) => find_deps(file, manifest, js, css, dynamic)); + } + + if (chunk.dynamicImports) { + chunk.dynamicImports.forEach((file) => find_deps(file, manifest, js, css, true)); } } diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js index 94bc8bd4f631..08aa5da2b73b 100644 --- a/packages/kit/src/core/dev/plugin.js +++ b/packages/kit/src/core/dev/plugin.js @@ -492,6 +492,10 @@ async function find_deps(vite, node, deps) { if (node.ssrTransformResult.deps) { node.ssrTransformResult.deps.forEach((url) => branches.push(add_by_url(url))); } + + if (node.ssrTransformResult.dynamicDeps) { + node.ssrTransformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url))); + } } else { node.importedModules.forEach((node) => branches.push(add(node))); } From 7f0c63b9c99bfb2bacbb54fbf02c1c08161ffb07 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Jun 2022 14:58:45 -0400 Subject: [PATCH 03/12] defer gathering of styles --- packages/kit/src/core/build/build_server.js | 2 +- packages/kit/src/core/dev/plugin.js | 61 ++++++++++--------- .../kit/src/runtime/server/page/render.js | 6 +- packages/kit/types/internal.d.ts | 2 +- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/kit/src/core/build/build_server.js b/packages/kit/src/core/build/build_server.js index e78714f163ef..2504b08f683d 100644 --- a/packages/kit/src/core/build/build_server.js +++ b/packages/kit/src/core/build/build_server.js @@ -259,7 +259,7 @@ export async function build_server(options, client) { }); if (styles.length > 0) { - exports.push(`export const styles = {\n${styles.join(',\n')}\n};`); + exports.push(`export const styles = () => ({\n${styles.join(',\n')}\n});`); } fs.writeFileSync(file, `${imports.join('\n')}\n\n${exports.join('\n')}\n`); diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js index 08aa5da2b73b..f5e9943e2c22 100644 --- a/packages/kit/src/core/dev/plugin.js +++ b/packages/kit/src/core/dev/plugin.js @@ -116,34 +116,6 @@ export const sveltekit = function (svelte_config) { const module = /** @type {import('types').SSRComponent} */ ( await vite.ssrLoadModule(url, { fixStacktrace: false }) ); - const node = await vite.moduleGraph.getModuleByUrl(url); - - if (!node) throw new Error(`Could not find node for ${url}`); - - const deps = new Set(); - await find_deps(vite, node, deps); - - /** @type {Record} */ - const styles = {}; - - for (const dep of deps) { - const parsed = new URL(dep.url, 'http://localhost/'); - const query = parsed.searchParams; - - if ( - style_pattern.test(dep.file) || - (query.has('svelte') && query.get('type') === 'style') - ) { - try { - const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false }); - styles[dep.url] = mod.default; - } catch { - // this can happen with dynamically imported modules, I think - // because the Vite module graph doesn't distinguish between - // static and dynamic imports? TODO investigate, submit fix - } - } - } return { module, @@ -152,7 +124,38 @@ export const sveltekit = function (svelte_config) { css: [], js: [], // in dev we inline all styles to avoid FOUC - styles + async styles() { + const node = await vite.moduleGraph.getModuleByUrl(url); + + if (!node) throw new Error(`Could not find node for ${url}`); + + const deps = new Set(); + await find_deps(vite, node, deps); + + /** @type {Record} */ + const styles = {}; + + for (const dep of deps) { + const parsed = new URL(dep.url, 'http://localhost/'); + const query = parsed.searchParams; + + if ( + style_pattern.test(dep.file) || + (query.has('svelte') && query.get('type') === 'style') + ) { + try { + const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false }); + styles[dep.url] = mod.default; + } catch { + // this can happen with dynamically imported modules, I think + // because the Vite module graph doesn't distinguish between + // static and dynamic imports? TODO investigate, submit fix + } + } + } + + return styles; + } }; }; }), diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 1537d67658cb..33ab660466fc 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -71,10 +71,10 @@ export async function render_response({ } if (resolve_opts.ssr) { - branch.forEach(({ node, props, loaded, fetched, uses_credentials }) => { + for (const { node, props, loaded, fetched, uses_credentials } of branch) { if (node.css) node.css.forEach((url) => stylesheets.add(url)); if (node.js) node.js.forEach((url) => modulepreloads.add(url)); - if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v)); + if (node.styles) Object.entries(await node.styles()).forEach(([k, v]) => styles.set(k, v)); // TODO probably better if `fetched` wasn't populated unless `hydrate` if (fetched && page_config.hydrate) serialized_data.push(...fetched); @@ -82,7 +82,7 @@ export async function render_response({ cache = loaded?.cache; is_private = cache?.private ?? uses_credentials; - }); + } const session = writable($session); diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 7fa6a0c92048..5d445790aa4c 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -235,7 +235,7 @@ export interface SSRNode { /** external JS files */ js: string[]; /** inlined styles */ - styles?: Record; + styles?: () => Promise>; } export type SSRNodeLoader = () => Promise; From e3e590080351b16b4585fa64f15141f28dd49c01 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 18:20:39 -0400 Subject: [PATCH 04/12] fix typechecking --- packages/kit/src/vite/dev/index.js | 2 +- packages/kit/types/internal.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index 98ea61118503..3585d25dfe6f 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -95,7 +95,7 @@ export async function dev(vite, svelte_config) { css: [], js: [], // in dev we inline all styles to avoid FOUC - styles + styles: () => styles }; }; }), diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index a9dcb0093e01..2b325d4f357e 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -236,7 +236,7 @@ export interface SSRNode { /** external JS files */ js: string[]; /** inlined styles */ - styles?: () => Promise>; + styles?: () => MaybePromise>; } export type SSRNodeLoader = () => Promise; From 8a14094879270b1a091bd8a2e8e9c6a2b63e9a9c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 10:53:44 -0400 Subject: [PATCH 05/12] try and nudge turbo into not breaking everything --- turbo.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/turbo.json b/turbo.json index 1316f3c80926..12e61a0b762c 100644 --- a/turbo.json +++ b/turbo.json @@ -24,11 +24,18 @@ }, "check": { "dependsOn": ["sync"], - "inputs": ["src/**", "types/**", ".svelte-kit/types/**", ".custom-out-dir/types/**", ".svelte-kit/tsconfig.json"], + "inputs": [ + "src/**", + "types/**", + ".svelte-kit/types/**", + ".custom-out-dir/types/**", + ".svelte-kit/tsconfig.json" + ], "outputs": [] }, "format": {}, "sync": { + "inputs": ["src/**", "source/**"], "outputs": [".svelte-kit/types/**", ".custom-out-dir/types/**"] }, "test": { From 7c929689b4c9d32eb24f6700ec1fda79354e5bdd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 11:15:59 -0400 Subject: [PATCH 06/12] include dynamically imported styles during dev SSR --- packages/kit/src/vite/dev/index.js | 65 +++++++++++++++++------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index 3585d25dfe6f..b09e5b85a6c1 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -59,34 +59,6 @@ export async function dev(vite, svelte_config) { const module = /** @type {import('types').SSRComponent} */ ( await vite.ssrLoadModule(url, { fixStacktrace: false }) ); - const node = await vite.moduleGraph.getModuleByUrl(url); - - if (!node) throw new Error(`Could not find node for ${url}`); - - const deps = new Set(); - await find_deps(vite, node, deps); - - /** @type {Record} */ - const styles = {}; - - for (const dep of deps) { - const parsed = new URL(dep.url, 'http://localhost/'); - const query = parsed.searchParams; - - if ( - style_pattern.test(dep.file) || - (query.has('svelte') && query.get('type') === 'style') - ) { - try { - const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false }); - styles[dep.url] = mod.default; - } catch { - // this can happen with dynamically imported modules, I think - // because the Vite module graph doesn't distinguish between - // static and dynamic imports? TODO investigate, submit fix - } - } - } return { module, @@ -95,7 +67,38 @@ export async function dev(vite, svelte_config) { css: [], js: [], // in dev we inline all styles to avoid FOUC - styles: () => styles + styles: async () => { + const node = await vite.moduleGraph.getModuleByUrl(url); + + if (!node) throw new Error(`Could not find node for ${url}`); + + const deps = new Set(); + await find_deps(vite, node, deps); + + /** @type {Record} */ + const styles = {}; + + for (const dep of deps) { + const parsed = new URL(dep.url, 'http://localhost/'); + const query = parsed.searchParams; + + if ( + style_pattern.test(dep.file) || + (query.has('svelte') && query.get('type') === 'style') + ) { + try { + const mod = await vite.ssrLoadModule(dep.url, { fixStacktrace: false }); + styles[dep.url] = mod.default; + } catch { + // this can happen with dynamically imported modules, I think + // because the Vite module graph doesn't distinguish between + // static and dynamic imports? TODO investigate, submit fix + } + } + } + + return styles; + } }; }; }), @@ -429,6 +432,10 @@ async function find_deps(vite, node, deps) { if (node.ssrTransformResult.deps) { node.ssrTransformResult.deps.forEach((url) => branches.push(add_by_url(url))); } + + if (node.ssrTransformResult.dynamicDeps) { + node.ssrTransformResult.dynamicDeps.forEach((url) => branches.push(add_by_url(url))); + } } else { node.importedModules.forEach((node) => branches.push(add(node))); } From dd431fb01777b398380e42fbfc5cb0d753db8b94 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 11:16:29 -0400 Subject: [PATCH 07/12] changeset --- .changeset/wild-donuts-double.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wild-donuts-double.md diff --git a/.changeset/wild-donuts-double.md b/.changeset/wild-donuts-double.md new file mode 100644 index 000000000000..3ccb4fa2e35c --- /dev/null +++ b/.changeset/wild-donuts-double.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Include dynamically imported styles during SSR From 57b94ac2c985bf09dfe124ecef287686996cb9c1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 11:57:40 -0400 Subject: [PATCH 08/12] rename some stuff to make it clearer --- .../kit/src/runtime/server/page/load_node.js | 2 +- .../kit/src/runtime/server/page/render.js | 24 ++++++++++++------- packages/kit/src/vite/build/build_server.js | 8 +++---- packages/kit/src/vite/dev/index.js | 8 +++---- packages/kit/src/vite/index.js | 4 ++-- packages/kit/types/internal.d.ts | 14 +++++------ 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 262d021c8ff3..5ce5abd4d614 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -362,7 +362,7 @@ export async function load_node({ if (!loaded) { // TODO do we still want to enforce this now that there's no fallthrough? - throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`); + throw new Error(`load function must return a value${options.dev ? ` (${node.file})` : ''}`); } } else if (shadow.body) { loaded = { diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 7f215bd507cb..0085e1291558 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -51,10 +51,10 @@ export async function render_response({ } } - const stylesheets = new Set(options.manifest._.entry.css); + const linked_styles = new Set(options.manifest._.entry.css); const modulepreloads = new Set(options.manifest._.entry.js); /** @type {Map} */ - const styles = new Map(); + const inline_styles = new Map(); /** @type {Array} */ const serialized_data = []; @@ -73,9 +73,17 @@ export async function render_response({ if (resolve_opts.ssr) { for (const { node, props, loaded, fetched, uses_credentials } of branch) { - if (node.css) node.css.forEach((url) => stylesheets.add(url)); - if (node.js) node.js.forEach((url) => modulepreloads.add(url)); - if (node.styles) Object.entries(await node.styles()).forEach(([k, v]) => styles.set(k, v)); + if (node.imports) { + node.imports.forEach((url) => modulepreloads.add(url)); + } + + if (node.linked_styles) { + node.linked_styles.forEach((url) => linked_styles.add(url)); + } + + if (node.inline_styles) { + Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v)); + } // TODO probably better if `fetched` wasn't populated unless `hydrate` if (fetched && page_config.hydrate) serialized_data.push(...fetched); @@ -144,7 +152,7 @@ export async function render_response({ let { head, html: body } = rendered; - const inlined_style = Array.from(styles.values()).join('\n'); + const inlined_style = Array.from(inline_styles.values()).join('\n'); await csp_ready; const csp = new Csp(options.csp, { @@ -196,7 +204,7 @@ export async function render_response({ } // prettier-ignore - head += Array.from(stylesheets) + head += Array.from(linked_styles) .map((dep) => { const attributes = [ 'rel="stylesheet"', @@ -207,7 +215,7 @@ export async function render_response({ attributes.push(`nonce="${csp.nonce}"`); } - if (styles.has(dep)) { + if (inline_styles.has(dep)) { // don't load stylesheets that are already inlined // include them in disabled state so that Vite can detect them and doesn't try to add them attributes.push('disabled', 'media="(max-width: 0)"'); diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 3e989669060c..cb7cc72ea7c3 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -236,9 +236,9 @@ export async function build_server(vite_config, options, client) { const exports = [ 'export { module };', `export const index = ${i};`, - `export const entry = '${client.vite_manifest[component].file}';`, - `export const js = ${s(Array.from(js))};`, - `export const css = ${s(Array.from(css))};` + `export const file = '${client.vite_manifest[component].file}';`, + `export const imports = ${s(Array.from(js))};`, + `export const linked_styles = ${s(Array.from(css))};` ]; /** @type {string[]} */ @@ -254,7 +254,7 @@ export async function build_server(vite_config, options, client) { }); if (styles.length > 0) { - exports.push(`export const styles = () => ({\n${styles.join(',\n')}\n});`); + exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`); } fs.writeFileSync(file, `${imports.join('\n')}\n\n${exports.join('\n')}\n`); diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index b09e5b85a6c1..1193195e0fd6 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -63,11 +63,11 @@ export async function dev(vite, svelte_config) { return { module, index, - entry: url.endsWith('.svelte') ? url : url + '?import', - css: [], - js: [], + file: url.endsWith('.svelte') ? url : url + '?import', + imports: [], + linked_styles: [], // in dev we inline all styles to avoid FOUC - styles: async () => { + inline_styles: async () => { const node = await vite.moduleGraph.getModuleByUrl(url); if (!node) throw new Error(`Could not find node for ${url}`); diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index a44f66164c36..e38228713bb8 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -224,8 +224,8 @@ function kit() { chunks, entry: { file: vite_manifest[entry].file, - js: Array.from(entry_js), - css: Array.from(entry_css) + imports: Array.from(entry_js), + linked_styles: Array.from(entry_css) }, vite_manifest }; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 2b325d4f357e..50e6a1a505ae 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -51,8 +51,8 @@ export interface BuildData { chunks: OutputChunk[]; entry: { file: string; - js: string[]; - css: string[]; + imports: string[]; + linked_styles: string[]; }; vite_manifest: import('vite').Manifest; }; @@ -230,13 +230,13 @@ export interface SSRNode { /** index into the `components` array in client-manifest.js */ index: number; /** client-side module URL for this component */ - entry: string; - /** external CSS files */ - css: string[]; + file: string; /** external JS files */ - js: string[]; + imports: string[]; + /** external CSS files */ + linked_styles: string[]; /** inlined styles */ - styles?: () => MaybePromise>; + inline_styles?: () => MaybePromise>; } export type SSRNodeLoader = () => Promise; From a1700babced3e99d0b8e34feb3a6ed0c3bf1694c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 12:16:42 -0400 Subject: [PATCH 09/12] simplify, and dont follow dynamic imports from entry --- packages/kit/src/vite/build/build_server.js | 4 +- packages/kit/src/vite/build/utils.js | 43 +++++++++++++-------- packages/kit/src/vite/index.js | 4 +- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index cb7cc72ea7c3..4ee4687549a9 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -227,9 +227,7 @@ export async function build_server(vite_config, options, client) { manifest_data.components.forEach((component, i) => { const file = `${output_dir}/server/nodes/${i}.js`; - const js = new Set(); - const css = new Set(); - find_deps(component, client.vite_manifest, js, css, false); + const { js, css } = find_deps(client.vite_manifest, component, true); const imports = [`import * as module from '../${vite_manifest[component].file}';`]; diff --git a/packages/kit/src/vite/build/utils.js b/packages/kit/src/vite/build/utils.js index ffc055fbeec1..e3a9b2a4e55e 100644 --- a/packages/kit/src/vite/build/utils.js +++ b/packages/kit/src/vite/build/utils.js @@ -29,29 +29,40 @@ export async function create_build(config) { /** * Adds transitive JS and CSS dependencies to the js and css inputs. - * @param {string} file * @param {import('vite').Manifest} manifest - * @param {Set} css - * @param {Set} js - * @param {boolean} dynamic + * @param {string} entry + * @param {boolean} add_dynamic_css */ -export function find_deps(file, manifest, js, css, dynamic) { - const chunk = manifest[file]; +export function find_deps(manifest, entry, add_dynamic_css) { + const js = new Set(); + const css = new Set(); - if (js.has(chunk.file)) return; - if (!dynamic) js.add(chunk.file); + /** + * @param {string} file + * @param {boolean} add_js + */ + function traverse(file, add_js) { + const chunk = manifest[file]; - if (chunk.css) { - chunk.css.forEach((file) => css.add(file)); - } + if (js.has(chunk.file)) return; + if (add_js) js.add(chunk.file); - if (chunk.imports) { - chunk.imports.forEach((file) => find_deps(file, manifest, js, css, dynamic)); - } + if (chunk.css) { + chunk.css.forEach((file) => css.add(file)); + } + + if (chunk.imports) { + chunk.imports.forEach((file) => traverse(file, add_js)); + } - if (chunk.dynamicImports) { - chunk.dynamicImports.forEach((file) => find_deps(file, manifest, js, css, true)); + if (add_dynamic_css && chunk.dynamicImports) { + chunk.dynamicImports.forEach((file) => traverse(file, false)); + } } + + traverse(entry, true); + + return { js, css }; } /** diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index e38228713bb8..adf50e301290 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -211,9 +211,7 @@ function kit() { const entry = posixify( path.relative(cwd, `${get_runtime_path(svelte_config.kit)}/client/start.js`) ); - const entry_js = new Set(); - const entry_css = new Set(); - find_deps(entry, vite_manifest, entry_js, entry_css, false); + const { js: entry_js, css: entry_css } = find_deps(vite_manifest, entry, false); fs.writeFileSync( `${paths.client_out_dir}/version.json`, From c7169f586352766451d348b88c2bba1733d2a43e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 12:24:30 -0400 Subject: [PATCH 10/12] tidy up --- packages/kit/src/runtime/server/page/render.js | 14 ++++++++------ packages/kit/src/vite/dev/index.js | 6 +++--- packages/kit/src/vite/index.js | 2 +- packages/kit/types/index.d.ts | 4 ++-- packages/kit/types/internal.d.ts | 4 ++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 0085e1291558..98649d66e34f 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -51,10 +51,12 @@ export async function render_response({ } } - const linked_styles = new Set(options.manifest._.entry.css); - const modulepreloads = new Set(options.manifest._.entry.js); + const { entry } = options.manifest._; + + const linked_styles = new Set(entry.stylesheets); + const modulepreloads = new Set(entry.imports); /** @type {Map} */ - const inline_styles = new Map(); + const inline_styles = new Map(); // TODO include inline_styles from entry /** @type {Array} */ const serialized_data = []; @@ -77,8 +79,8 @@ export async function render_response({ node.imports.forEach((url) => modulepreloads.add(url)); } - if (node.linked_styles) { - node.linked_styles.forEach((url) => linked_styles.add(url)); + if (node.stylesheets) { + node.stylesheets.forEach((url) => linked_styles.add(url)); } if (node.inline_styles) { @@ -165,7 +167,7 @@ export async function render_response({ // prettier-ignore const init_app = ` - import { start } from ${s(options.prefix + options.manifest._.entry.file)}; + import { start } from ${s(options.prefix + entry.file)}; start({ target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode, paths: ${s(options.paths)}, diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index 1193195e0fd6..7a0105ce14c4 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -49,8 +49,8 @@ export async function dev(vite, svelte_config) { _: { entry: { file: `/@fs${runtime}/client/start.js`, - css: [], - js: [] + imports: [], + stylesheets: [] }, nodes: manifest_data.components.map((id, index) => { return async () => { @@ -65,7 +65,7 @@ export async function dev(vite, svelte_config) { index, file: url.endsWith('.svelte') ? url : url + '?import', imports: [], - linked_styles: [], + stylesheets: [], // in dev we inline all styles to avoid FOUC inline_styles: async () => { const node = await vite.moduleGraph.getModuleByUrl(url); diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index adf50e301290..e405c7381ff2 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -223,7 +223,7 @@ function kit() { entry: { file: vite_manifest[entry].file, imports: Array.from(entry_js), - linked_styles: Array.from(entry_css) + stylesheets: Array.from(entry_css) }, vite_manifest }; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 1aeed48b82ae..4d3da4335d38 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -284,8 +284,8 @@ export interface SSRManifest { _: { entry: { file: string; - js: string[]; - css: string[]; + imports: string[]; + stylesheets: string[]; }; nodes: SSRNodeLoader[]; routes: SSRRoute[]; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 50e6a1a505ae..c82407ce2194 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -52,7 +52,7 @@ export interface BuildData { entry: { file: string; imports: string[]; - linked_styles: string[]; + stylesheets: string[]; }; vite_manifest: import('vite').Manifest; }; @@ -234,7 +234,7 @@ export interface SSRNode { /** external JS files */ imports: string[]; /** external CSS files */ - linked_styles: string[]; + stylesheets: string[]; /** inlined styles */ inline_styles?: () => MaybePromise>; } From f1e41dd0b640a869a4c33d3fe79a7f55f47a70f8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 12:43:15 -0400 Subject: [PATCH 11/12] tidy up --- .../kit/src/runtime/server/page/render.js | 10 ++++++---- packages/kit/src/vite/build/build_server.js | 15 +++++++-------- packages/kit/src/vite/build/utils.js | 19 +++++++++++++------ packages/kit/src/vite/index.js | 18 +++++++----------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 98649d66e34f..185665e4492c 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -53,10 +53,12 @@ export async function render_response({ const { entry } = options.manifest._; - const linked_styles = new Set(entry.stylesheets); + const stylesheets = new Set(entry.stylesheets); const modulepreloads = new Set(entry.imports); + /** @type {Map} */ - const inline_styles = new Map(); // TODO include inline_styles from entry + // TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold + const inline_styles = new Map(); /** @type {Array} */ const serialized_data = []; @@ -80,7 +82,7 @@ export async function render_response({ } if (node.stylesheets) { - node.stylesheets.forEach((url) => linked_styles.add(url)); + node.stylesheets.forEach((url) => stylesheets.add(url)); } if (node.inline_styles) { @@ -206,7 +208,7 @@ export async function render_response({ } // prettier-ignore - head += Array.from(linked_styles) + head += Array.from(stylesheets) .map((dep) => { const attributes = [ 'rel="stylesheet"', diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 4ee4687549a9..9d4d41f44753 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -225,24 +225,22 @@ export async function build_server(vite_config, options, client) { }); manifest_data.components.forEach((component, i) => { - const file = `${output_dir}/server/nodes/${i}.js`; - - const { js, css } = find_deps(client.vite_manifest, component, true); + const entry = find_deps(client.vite_manifest, component, true); const imports = [`import * as module from '../${vite_manifest[component].file}';`]; const exports = [ 'export { module };', `export const index = ${i};`, - `export const file = '${client.vite_manifest[component].file}';`, - `export const imports = ${s(Array.from(js))};`, - `export const linked_styles = ${s(Array.from(css))};` + `export const file = '${entry.file}';`, + `export const imports = ${s(entry.imports)};`, + `export const stylesheets = ${s(entry.stylesheets)};` ]; /** @type {string[]} */ const styles = []; - css.forEach((file) => { + entry.stylesheets.forEach((file) => { if (stylesheet_lookup.has(file)) { const index = stylesheet_lookup.get(file); const name = `stylesheet_${index}`; @@ -255,7 +253,8 @@ export async function build_server(vite_config, options, client) { exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`); } - fs.writeFileSync(file, `${imports.join('\n')}\n\n${exports.join('\n')}\n`); + const out = `${output_dir}/server/nodes/${i}.js`; + fs.writeFileSync(out, `${imports.join('\n')}\n\n${exports.join('\n')}\n`); }); return { diff --git a/packages/kit/src/vite/build/utils.js b/packages/kit/src/vite/build/utils.js index e3a9b2a4e55e..ba27a79c7aef 100644 --- a/packages/kit/src/vite/build/utils.js +++ b/packages/kit/src/vite/build/utils.js @@ -34,8 +34,11 @@ export async function create_build(config) { * @param {boolean} add_dynamic_css */ export function find_deps(manifest, entry, add_dynamic_css) { - const js = new Set(); - const css = new Set(); + /** @type {Set} */ + const imports = new Set(); + + /** @type {Set} */ + const stylesheets = new Set(); /** * @param {string} file @@ -44,11 +47,11 @@ export function find_deps(manifest, entry, add_dynamic_css) { function traverse(file, add_js) { const chunk = manifest[file]; - if (js.has(chunk.file)) return; - if (add_js) js.add(chunk.file); + if (imports.has(chunk.file)) return; + if (add_js) imports.add(chunk.file); if (chunk.css) { - chunk.css.forEach((file) => css.add(file)); + chunk.css.forEach((file) => stylesheets.add(file)); } if (chunk.imports) { @@ -62,7 +65,11 @@ export function find_deps(manifest, entry, add_dynamic_css) { traverse(entry, true); - return { js, css }; + return { + file: manifest[entry].file, + imports: Array.from(imports), + stylesheets: Array.from(stylesheets) + }; } /** diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index e405c7381ff2..6e97cc23eda8 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -190,6 +190,11 @@ function kit() { async writeBundle(_options, bundle) { const log = logger({ verbose: !!process.env.VERBOSE }); + fs.writeFileSync( + `${paths.client_out_dir}/version.json`, + JSON.stringify({ version: process.env.VITE_SVELTEKIT_APP_VERSION }) + ); + /** @type {import('rollup').OutputChunk[]} */ const chunks = []; /** @type {import('rollup').OutputAsset[]} */ @@ -208,23 +213,14 @@ function kit() { fs.readFileSync(`${paths.client_out_dir}/immutable/manifest.json`, 'utf-8') ); - const entry = posixify( + const entry_id = posixify( path.relative(cwd, `${get_runtime_path(svelte_config.kit)}/client/start.js`) ); - const { js: entry_js, css: entry_css } = find_deps(vite_manifest, entry, false); - fs.writeFileSync( - `${paths.client_out_dir}/version.json`, - JSON.stringify({ version: process.env.VITE_SVELTEKIT_APP_VERSION }) - ); const client = { assets, chunks, - entry: { - file: vite_manifest[entry].file, - imports: Array.from(entry_js), - stylesheets: Array.from(entry_css) - }, + entry: find_deps(vite_manifest, entry_id, false), vite_manifest }; log.info(`Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets`); From 3bfd1fc804c4e34213e8d8a8beb2b88c61033da5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 30 Jun 2022 14:59:08 -0400 Subject: [PATCH 12/12] tidy up --- packages/kit/src/runtime/server/page/render.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 185665e4492c..d7976a6321b7 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -156,8 +156,6 @@ export async function render_response({ let { head, html: body } = rendered; - const inlined_style = Array.from(inline_styles.values()).join('\n'); - await csp_ready; const csp = new Csp(options.csp, { dev: options.dev, @@ -197,14 +195,16 @@ export async function render_response({ } `; - if (inlined_style) { + if (inline_styles.size > 0) { + const content = Array.from(inline_styles.values()).join('\n'); + const attributes = []; if (options.dev) attributes.push(' data-sveltekit'); if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`); - csp.add_style(inlined_style); + csp.add_style(content); - head += `\n\t${inlined_style}`; + head += `\n\t${content}`; } // prettier-ignore