From 26704c1e16abc9971a34f0f8c4f5b45e1312b8c2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 16:37:16 -0400 Subject: [PATCH 01/24] hide useless stack --- .../kit/src/exports/vite/graph_analysis/index.js | 4 ++-- packages/kit/src/exports/vite/index.js | 10 ++++++++-- packages/kit/src/exports/vite/utils.js | 12 ++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index ecf7ee146769..b2094473ce01 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,6 +1,6 @@ import path from 'node:path'; import { posixify } from '../../../utils/filesystem.js'; -import { normalize_id, strip_virtual_prefix } from '../utils.js'; +import { normalize_id, stackless, strip_virtual_prefix } from '../utils.js'; import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js'; const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]); @@ -62,7 +62,7 @@ export function module_guard(context, { cwd, lib }) { id )} into client-side code:\n${pyramid}`; - throw new Error(message); + throw stackless(message); } const module = context.getModuleInfo(id); diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 31a79718fcb1..a9704b675e98 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -17,7 +17,13 @@ import { assets_base, find_deps, resolve_symlinks } from './build/utils.js'; import { dev } from './dev/index.js'; import { is_illegal, module_guard } from './graph_analysis/index.js'; import { preview } from './preview/index.js'; -import { get_config_aliases, get_env, normalize_id, strip_virtual_prefix } from './utils.js'; +import { + get_config_aliases, + get_env, + normalize_id, + stackless, + strip_virtual_prefix +} from './utils.js'; import { write_client_manifest } from '../../core/sync/write_client_manifest.js'; import prerender from '../../core/postbuild/prerender.js'; import analyse from '../../core/postbuild/analyse.js'; @@ -456,7 +462,7 @@ Tips: throw new Error(`${error_prefix}\nImported by: ${importer}.${error_suffix}`); } - throw new Error(`${error_prefix}${error_suffix}`); + throw stackless(`${error_prefix}${error_suffix}`); } } diff --git a/packages/kit/src/exports/vite/utils.js b/packages/kit/src/exports/vite/utils.js index 02916e4d85c5..b764da12d45e 100644 --- a/packages/kit/src/exports/vite/utils.js +++ b/packages/kit/src/exports/vite/utils.js @@ -155,4 +155,16 @@ export function normalize_id(id, lib, cwd) { return posixify(id); } +/** + * For times when you need to throw an error, but without + * displaying a useless stack trace (since the developer + * can't do anything useful with it) + * @param {string} message + */ +export function stackless(message) { + const error = new Error(message); + error.stack = ''; + throw error; +} + export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', ''); From 6db5dcb8697f5592bc2e64c43ddc9398959d0f6d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 16:37:39 -0400 Subject: [PATCH 02/24] defer to graph_analysis in build, so the more useful error appears --- packages/kit/src/exports/vite/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index a9704b675e98..bbae88d8c117 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -435,7 +435,7 @@ async function kit({ svelte_config }) { ? `globalThis.__sveltekit_${version_hash}` : 'globalThis.__sveltekit_dev'; - if (options?.ssr === false && process.env.TEST !== 'true') { + if (!is_build && options?.ssr === false && process.env.TEST !== 'true') { if ( is_illegal(id, { cwd: normalized_cwd, From 703d3e8a12294ffd86bf14ee49c8f433911892b5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 16:37:50 -0400 Subject: [PATCH 03/24] tweak error message --- packages/kit/src/exports/vite/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index bbae88d8c117..002a77d8e226 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -452,7 +452,7 @@ async function kit({ svelte_config }) { Tips: - To resolve this error, ensure that no exports from ${illegal_module} are used, even transitively, in client-side code. - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`; + - If you're not sure which module is causing this, try building your app to see the import chain.`; if (import_map.has(illegal_module)) { const importer = path.relative( From 4024d7ce2c8f888e1d1d09dc240895684e271a8a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 16:38:05 -0400 Subject: [PATCH 04/24] various fixes --- .../kit/src/exports/vite/graph_analysis/index.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index b2094473ce01..2c4a7e7e23d8 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -50,13 +50,15 @@ export function module_guard(context, { cwd, lib }) { id = normalize_id(id, lib, cwd); const pyramid = - chain.map(({ id, dynamic }, i) => { - id = normalize_id(id, lib, cwd); + chain + .map(({ id, dynamic }, i) => { + id = normalize_id(id, lib, cwd); - return `${' '.repeat(i * 2)}- ${strip_virtual_prefix(id)} ${ - dynamic ? 'dynamically imports' : 'imports' - }\n`; - }) + `${' '.repeat(chain.length)}- ${strip_virtual_prefix(id)}`; + return `${' '.repeat(i)}- ${strip_virtual_prefix(id)} ${ + dynamic ? 'dynamically imports' : 'imports' + }\n`; + }) + .join('') + `${' '.repeat(chain.length)}- ${strip_virtual_prefix(id)}`; const message = `Cannot import ${strip_virtual_prefix( id From 49c126f5af2b10fe1d283dd09dc055ea6a3f7195 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 16:56:56 -0400 Subject: [PATCH 05/24] prevent double-logging --- packages/kit/src/exports/vite/index.js | 47 ++++++++++++++++---------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 002a77d8e226..9c1caa5087b0 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -962,23 +962,36 @@ Tips: secondary_build_started = true; - const { output: client_chunks } = /** @type {import('vite').Rollup.RollupOutput} */ ( - await vite.build({ - configFile: vite_config.configFile, - // CLI args - mode: vite_config_env.mode, - logLevel: vite_config.logLevel, - clearScreen: vite_config.clearScreen, - build: { - minify: initial_config.build?.minify, - assetsInlineLimit: vite_config.build.assetsInlineLimit, - sourcemap: vite_config.build.sourcemap - }, - optimizeDeps: { - force: vite_config.optimizeDeps.force - } - }) - ); + let client_chunks; + + try { + const bundle = /** @type {import('vite').Rollup.RollupOutput} */ ( + await vite.build({ + configFile: vite_config.configFile, + // CLI args + mode: vite_config_env.mode, + logLevel: vite_config.logLevel, + clearScreen: vite_config.clearScreen, + build: { + minify: initial_config.build?.minify, + assetsInlineLimit: vite_config.build.assetsInlineLimit, + sourcemap: vite_config.build.sourcemap + }, + optimizeDeps: { + force: vite_config.optimizeDeps.force + } + }) + ); + + client_chunks = bundle.output; + } catch (e) { + const error = + e instanceof Error ? e : new Error(/** @type {any} */ (e).message ?? e ?? ''); + + // without this, errors that occur during the secondary build + // will be logged twice + throw stackless(error.stack ?? error.message); + } copy( `${out}/server/${kit.appDir}/immutable/assets`, From a0365e7e16d48f86558f8d4046942afbecefba90 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 17:08:28 -0400 Subject: [PATCH 06/24] tidy up a lil --- .../src/exports/vite/graph_analysis/index.js | 4 ++-- .../exports/vite/graph_analysis/index.spec.js | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 2c4a7e7e23d8..3923427a2703 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -54,11 +54,11 @@ export function module_guard(context, { cwd, lib }) { .map(({ id, dynamic }, i) => { id = normalize_id(id, lib, cwd); - return `${' '.repeat(i)}- ${strip_virtual_prefix(id)} ${ + return `${' '.repeat(i + 1)}${strip_virtual_prefix(id)} ${ dynamic ? 'dynamically imports' : 'imports' }\n`; }) - .join('') + `${' '.repeat(chain.length)}- ${strip_virtual_prefix(id)}`; + .join('') + `${' '.repeat(chain.length + 1)}${strip_virtual_prefix(id)}`; const message = `Cannot import ${strip_virtual_prefix( id diff --git a/packages/kit/src/exports/vite/graph_analysis/index.spec.js b/packages/kit/src/exports/vite/graph_analysis/index.spec.js index 35ecd31feb7b..76192a62f535 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -49,8 +49,8 @@ test('throws an error when importing $env/static/private', () => { } }, `Cannot import $env/static/private into client-side code: - - src/routes/+page.svelte imports - - $env/static/private` + src/routes/+page.svelte imports + $env/static/private` ); }); @@ -65,8 +65,8 @@ test('throws an error when dynamically importing $env/static/private', () => { } }, `Cannot import $env/static/private into client-side code: - - src/routes/+page.svelte dynamically imports - - $env/static/private` + src/routes/+page.svelte dynamically imports + $env/static/private` ); }); @@ -81,8 +81,8 @@ test('throws an error when importing $env/dynamic/private', () => { } }, `Cannot import $env/dynamic/private into client-side code: - - src/routes/+page.svelte imports - - $env/dynamic/private` + src/routes/+page.svelte imports + $env/dynamic/private` ); }); @@ -97,8 +97,8 @@ test('throws an error when dynamically importing $env/dynamic/private', () => { } }, `Cannot import $env/dynamic/private into client-side code: - - src/routes/+page.svelte dynamically imports - - $env/dynamic/private` + src/routes/+page.svelte dynamically imports + $env/dynamic/private` ); }); @@ -119,8 +119,8 @@ test('throws an error when importing a .server.js module', () => { '~/src/routes/illegal.server.js': {} }, `Cannot import src/routes/illegal.server.js into client-side code: - - src/routes/+page.svelte imports - - src/routes/illegal.server.js` + src/routes/+page.svelte imports + src/routes/illegal.server.js` ); }); @@ -136,8 +136,8 @@ test('throws an error when importing a $lib/server/**/*.js module', () => { '~/src/lib/server/some/module.js': {} }, `Cannot import $lib/server/some/module.js into client-side code: - - src/routes/+page.svelte imports - - $lib/server/some/module.js` + src/routes/+page.svelte imports + $lib/server/some/module.js` ); }); From 3aec108c22e481118bfc4705f1e0985129b64576 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 8 Aug 2025 17:10:35 -0400 Subject: [PATCH 07/24] changeset --- .changeset/chubby-shirts-run.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chubby-shirts-run.md diff --git a/.changeset/chubby-shirts-run.md b/.changeset/chubby-shirts-run.md new file mode 100644 index 000000000000..a6f8c628f839 --- /dev/null +++ b/.changeset/chubby-shirts-run.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: make illegal server-only import errors actually useful From 966e02103a6b3f9ab47b28ee2d87c74180e8b77e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 08:55:45 -0400 Subject: [PATCH 08/24] unify --- .../src/exports/vite/graph_analysis/index.js | 69 --------- packages/kit/src/exports/vite/index.js | 136 ++++++++++-------- 2 files changed, 75 insertions(+), 130 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 3923427a2703..1129ffb28b0f 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,6 +1,4 @@ import path from 'node:path'; -import { posixify } from '../../../utils/filesystem.js'; -import { normalize_id, stackless, strip_virtual_prefix } from '../utils.js'; import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js'; const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]); @@ -20,70 +18,3 @@ export function is_illegal(id, dirs) { if (!id.startsWith(dirs.cwd) || id.startsWith(dirs.node_modules)) return false; return ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) || id.startsWith(dirs.server); } - -/** - * Creates a guard that checks that no id imports a module that is not allowed to be imported into client-side code. - * @param {import('vite').Rollup.PluginContext} context - * @param {{ cwd: string; lib: string }} paths - */ -export function module_guard(context, { cwd, lib }) { - /** @type {Set} */ - const seen = new Set(); - - const dirs = { - // ids will be posixified, so we need to posixify these, too - cwd: posixify(cwd), - node_modules: posixify(path.join(cwd, 'node_modules')), - server: posixify(path.join(lib, 'server')) - }; - - /** - * @param {string} id - * @param {Array<{ id: string; dynamic: boolean }>} chain - */ - function follow(id, chain) { - if (seen.has(id)) return; - seen.add(id); - - if (is_illegal(id, dirs)) { - chain.shift(); // discard the entry point - id = normalize_id(id, lib, cwd); - - const pyramid = - chain - .map(({ id, dynamic }, i) => { - id = normalize_id(id, lib, cwd); - - return `${' '.repeat(i + 1)}${strip_virtual_prefix(id)} ${ - dynamic ? 'dynamically imports' : 'imports' - }\n`; - }) - .join('') + `${' '.repeat(chain.length + 1)}${strip_virtual_prefix(id)}`; - - const message = `Cannot import ${strip_virtual_prefix( - id - )} into client-side code:\n${pyramid}`; - - throw stackless(message); - } - - const module = context.getModuleInfo(id); - - if (module) { - for (const child of module.importedIds) { - follow(child, [...chain, { id, dynamic: false }]); - } - - for (const child of module.dynamicallyImportedIds) { - follow(child, [...chain, { id, dynamic: true }]); - } - } - } - - return { - /** @param {string} id should be posixified */ - check: (id) => { - follow(id, []); - } - }; -} diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 9c1caa5087b0..67db96ee50f7 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -15,15 +15,9 @@ import { build_server_nodes } from './build/build_server.js'; import { build_service_worker } from './build/build_service_worker.js'; import { assets_base, find_deps, resolve_symlinks } from './build/utils.js'; import { dev } from './dev/index.js'; -import { is_illegal, module_guard } from './graph_analysis/index.js'; +import { is_illegal } from './graph_analysis/index.js'; import { preview } from './preview/index.js'; -import { - get_config_aliases, - get_env, - normalize_id, - stackless, - strip_virtual_prefix -} from './utils.js'; +import { get_config_aliases, get_env, normalize_id, stackless } from './utils.js'; import { write_client_manifest } from '../../core/sync/write_client_manifest.js'; import prerender from '../../core/postbuild/prerender.js'; import analyse from '../../core/postbuild/analyse.js'; @@ -240,6 +234,12 @@ async function kit({ svelte_config }) { const plugin_setup = { name: 'vite-plugin-sveltekit-setup', + enforce: 'pre', + + configureServer(_dev_server) { + dev_server = _dev_server; + }, + /** * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. * @see https://vitejs.dev/guide/api-plugin.html#config @@ -380,14 +380,18 @@ async function kit({ svelte_config }) { } }; - /** @type {Map} */ + /** @type {Map>} */ const import_map = new Map(); /** @type {import('vite').Plugin} */ const plugin_virtual_modules = { name: 'vite-plugin-sveltekit-virtual-modules', - resolveId(id, importer) { + // Run this plugin before built-in resolution, so that relative imports + // are added to the module graph + enforce: 'pre', + + async resolveId(id, importer) { if (id === '__sveltekit/manifest') { return `${kit.outDir}/generated/client-optimized/app.js`; } @@ -395,6 +399,7 @@ async function kit({ svelte_config }) { // If importing from a service-worker, only allow $service-worker & $env/static/public, but none of the other virtual modules. // This check won't catch transitive imports, but it will warn when the import comes from a service-worker directly. // Transitive imports will be caught during the build. + // TODO move this logic to plugin_guard if (importer) { const parsed_importer = path.parse(importer); @@ -411,8 +416,6 @@ async function kit({ svelte_config }) { )} into service-worker code. Only the modules $service-worker and $env/static/public are available in service workers.` ); } - - import_map.set(id, importer); } // treat $env/static/[public|private] as virtual @@ -426,6 +429,21 @@ async function kit({ svelte_config }) { if (id.startsWith('__sveltekit/')) { return `\0virtual:${id}`; } + + if (importer && !importer.endsWith('index.html')) { + const resolved = await this.resolve(id, importer, { skipSelf: true }); + + if (resolved) { + let importers = import_map.get(resolved.id); + + if (!importers) { + importers = new Set(); + import_map.set(resolved.id, importers); + } + + importers.add(importer); + } + } }, load(id, options) { @@ -435,37 +453,6 @@ async function kit({ svelte_config }) { ? `globalThis.__sveltekit_${version_hash}` : 'globalThis.__sveltekit_dev'; - if (!is_build && options?.ssr === false && process.env.TEST !== 'true') { - if ( - is_illegal(id, { - cwd: normalized_cwd, - node_modules: vite.normalizePath(path.resolve('node_modules')), - server: vite.normalizePath(path.join(normalized_lib, 'server')) - }) - ) { - const relative = normalize_id(id, normalized_lib, normalized_cwd); - - const illegal_module = strip_virtual_prefix(relative); - - const error_prefix = `Cannot import ${illegal_module} into client-side code. This could leak sensitive information.`; - const error_suffix = ` -Tips: - - To resolve this error, ensure that no exports from ${illegal_module} are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app to see the import chain.`; - - if (import_map.has(illegal_module)) { - const importer = path.relative( - cwd, - /** @type {string} */ (import_map.get(illegal_module)) - ); - throw new Error(`${error_prefix}\nImported by: ${importer}.${error_suffix}`); - } - - throw stackless(`${error_prefix}${error_suffix}`); - } - } - switch (id) { case env_static_private: return create_static_module('$env/static/private', env.private); @@ -580,23 +567,54 @@ Tips: const plugin_guard = { name: 'vite-plugin-sveltekit-guard', - writeBundle: { - sequential: true, - handler(_options) { - if (vite_config.build.ssr) return; + load(id, options) { + if (options?.ssr === false && process.env.TEST !== 'true') { + if ( + is_illegal(id, { + cwd: normalized_cwd, + node_modules: vite.normalizePath(path.resolve('node_modules')), + server: vite.normalizePath(path.join(normalized_lib, 'server')) + }) + ) { + // in dev, this doesn't exist, so we need to create it + manifest_data ??= sync.all(svelte_config, vite_config_env.mode).manifest_data; + + /** @type {Set} */ + const entrypoints = new Set(); + for (const node of manifest_data.nodes) { + if (node.component) entrypoints.add(node.component); + if (node.universal) entrypoints.add(node.universal); + } - const guard = module_guard(this, { - cwd: vite.normalizePath(process.cwd()), - lib: vite.normalizePath(kit.files.lib) - }); + const chain = [id]; + let current = id; - manifest_data.nodes.forEach((_node, i) => { - const id = vite.normalizePath( - path.resolve(kit.outDir, `generated/client-optimized/nodes/${i}.js`) - ); + while (true) { + const importers = import_map.get(current); + if (!importers) break; - guard.check(id); - }); + const candidates = Array.from(importers).filter((id) => !chain.includes(id)); + if (candidates.length === 0) break; + + chain.push((current = candidates[0])); + + if (entrypoints.has(path.relative(cwd, current))) { + let message = `Cannot import ${normalize_id(id, kit.files.lib, cwd)} into client-side code. This could leak sensitive information.`; + + const pyramid = chain + .reverse() + .map((id, i) => { + return `${' '.repeat(i + 1)}${normalize_id(id, kit.files.lib, cwd)}`; + }) + .join(' imports\n'); + + message += `\n\n${pyramid}`; + message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`; + + throw stackless(message); + } + } + } } } }; @@ -608,10 +626,6 @@ Tips: const plugin_remote = { name: 'vite-plugin-sveltekit-remote', - configureServer(_dev_server) { - dev_server = _dev_server; - }, - async transform(code, id, opts) { if (!svelte_config.kit.moduleExtensions.some((ext) => id.endsWith(`.remote${ext}`))) { return; From 628c604c47445d5a253854843990f31cead84dd3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 09:15:08 -0400 Subject: [PATCH 09/24] tidy up --- packages/kit/src/exports/vite/index.js | 74 +++++++++++++------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 67db96ee50f7..7b75d86b7e22 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -568,51 +568,51 @@ async function kit({ svelte_config }) { name: 'vite-plugin-sveltekit-guard', load(id, options) { - if (options?.ssr === false && process.env.TEST !== 'true') { - if ( - is_illegal(id, { - cwd: normalized_cwd, - node_modules: vite.normalizePath(path.resolve('node_modules')), - server: vite.normalizePath(path.join(normalized_lib, 'server')) - }) - ) { - // in dev, this doesn't exist, so we need to create it - manifest_data ??= sync.all(svelte_config, vite_config_env.mode).manifest_data; - - /** @type {Set} */ - const entrypoints = new Set(); - for (const node of manifest_data.nodes) { - if (node.component) entrypoints.add(node.component); - if (node.universal) entrypoints.add(node.universal); - } + if (options?.ssr === true || process.env.TEST === 'true') return; + + if ( + is_illegal(id, { + cwd: normalized_cwd, + node_modules: vite.normalizePath(path.resolve('node_modules')), + server: vite.normalizePath(path.join(normalized_lib, 'server')) + }) + ) { + // in dev, this doesn't exist, so we need to create it + manifest_data ??= sync.all(svelte_config, vite_config_env.mode).manifest_data; + + /** @type {Set} */ + const entrypoints = new Set(); + for (const node of manifest_data.nodes) { + if (node.component) entrypoints.add(node.component); + if (node.universal) entrypoints.add(node.universal); + } - const chain = [id]; - let current = id; + const chain = [id]; + let current = id; - while (true) { - const importers = import_map.get(current); - if (!importers) break; + while (true) { + const importers = import_map.get(current); + if (!importers) break; - const candidates = Array.from(importers).filter((id) => !chain.includes(id)); - if (candidates.length === 0) break; + const candidates = Array.from(importers).filter((id) => !chain.includes(id)); + if (candidates.length === 0) break; - chain.push((current = candidates[0])); + chain.push((current = candidates[0])); - if (entrypoints.has(path.relative(cwd, current))) { - let message = `Cannot import ${normalize_id(id, kit.files.lib, cwd)} into client-side code. This could leak sensitive information.`; + if (entrypoints.has(path.relative(cwd, current))) { + let message = `Cannot import ${normalize_id(id, kit.files.lib, cwd)} into code that runs in the browser, as this could leak sensitive information.`; - const pyramid = chain - .reverse() - .map((id, i) => { - return `${' '.repeat(i + 1)}${normalize_id(id, kit.files.lib, cwd)}`; - }) - .join(' imports\n'); + const pyramid = chain + .reverse() + .map((id, i) => { + return `${' '.repeat(i + 1)}${normalize_id(id, kit.files.lib, cwd)}`; + }) + .join(' imports\n'); - message += `\n\n${pyramid}`; - message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`; + message += `\n\n${pyramid}`; + message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`; - throw stackless(message); - } + throw stackless(message); } } } From 8fb34f6bcdb998ace13530875863f5c0c0617dea Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 09:20:25 -0400 Subject: [PATCH 10/24] tweak --- packages/kit/src/exports/vite/index.js | 54 +++++++++++++------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 7b75d86b7e22..2f9d60cd3d1e 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -234,12 +234,6 @@ async function kit({ svelte_config }) { const plugin_setup = { name: 'vite-plugin-sveltekit-setup', - enforce: 'pre', - - configureServer(_dev_server) { - dev_server = _dev_server; - }, - /** * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. * @see https://vitejs.dev/guide/api-plugin.html#config @@ -387,11 +381,7 @@ async function kit({ svelte_config }) { const plugin_virtual_modules = { name: 'vite-plugin-sveltekit-virtual-modules', - // Run this plugin before built-in resolution, so that relative imports - // are added to the module graph - enforce: 'pre', - - async resolveId(id, importer) { + resolveId(id, importer) { if (id === '__sveltekit/manifest') { return `${kit.outDir}/generated/client-optimized/app.js`; } @@ -423,27 +413,14 @@ async function kit({ svelte_config }) { // ids with :$ don't work with reverse proxies like nginx return `\0virtual:${id.substring(1)}`; } + if (id === '__sveltekit/remote') { return `${runtime_directory}/client/remote-functions/index.js`; } + if (id.startsWith('__sveltekit/')) { return `\0virtual:${id}`; } - - if (importer && !importer.endsWith('index.html')) { - const resolved = await this.resolve(id, importer, { skipSelf: true }); - - if (resolved) { - let importers = import_map.get(resolved.id); - - if (!importers) { - importers = new Set(); - import_map.set(resolved.id, importers); - } - - importers.add(importer); - } - } }, load(id, options) { @@ -567,6 +544,27 @@ async function kit({ svelte_config }) { const plugin_guard = { name: 'vite-plugin-sveltekit-guard', + // Run this plugin before built-in resolution, so that relative imports + // are added to the module graph + enforce: 'pre', + + async resolveId(id, importer) { + if (importer && !importer.endsWith('index.html')) { + const resolved = await this.resolve(id, importer, { skipSelf: true }); + + if (resolved) { + let importers = import_map.get(resolved.id); + + if (!importers) { + importers = new Set(); + import_map.set(resolved.id, importers); + } + + importers.add(importer); + } + } + }, + load(id, options) { if (options?.ssr === true || process.env.TEST === 'true') return; @@ -626,6 +624,10 @@ async function kit({ svelte_config }) { const plugin_remote = { name: 'vite-plugin-sveltekit-remote', + configureServer(_dev_server) { + dev_server = _dev_server; + }, + async transform(code, id, opts) { if (!svelte_config.kit.moduleExtensions.some((ext) => id.endsWith(`.remote${ext}`))) { return; From 4b92500f9f879164b52322ff8bf2075078ad533f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 09:21:59 -0400 Subject: [PATCH 11/24] unused --- .../exports/vite/graph_analysis/index.spec.js | 166 ------------------ .../src/exports/vite/graph_analysis/utils.js | 6 - .../exports/vite/graph_analysis/utils.spec.js | 26 --- 3 files changed, 198 deletions(-) delete mode 100644 packages/kit/src/exports/vite/graph_analysis/index.spec.js delete mode 100644 packages/kit/src/exports/vite/graph_analysis/utils.js delete mode 100644 packages/kit/src/exports/vite/graph_analysis/utils.spec.js diff --git a/packages/kit/src/exports/vite/graph_analysis/index.spec.js b/packages/kit/src/exports/vite/graph_analysis/index.spec.js deleted file mode 100644 index 76192a62f535..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ /dev/null @@ -1,166 +0,0 @@ -import { assert, test } from 'vitest'; -import { module_guard } from './index.js'; -import * as module_ids from '../module_ids.js'; - -/** - * - * @param {Record} graph - * @param {string} [expected_error] - */ -function check(graph, expected_error) { - // @ts-expect-error - const context = /** @type {import('vite').Rollup.PluginContext} */ ({ - /** @param {string} id */ - getModuleInfo(id) { - return { - importedIds: [], - dynamicallyImportedIds: [], - ...graph[id] - }; - } - }); - - const guard = module_guard(context, { - cwd: '~', - lib: '~/src/lib' - }); - - if (expected_error) { - try { - guard.check('~/src/entry'); - throw new Error('Expected an error'); - } catch (e) { - // @ts-expect-error - assert.equal(e.message, expected_error.replace(/^\t+/gm, '')); - } - } else { - guard.check('~/src/entry'); - } -} - -test('throws an error when importing $env/static/private', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['\0virtual:env/static/private'] - } - }, - `Cannot import $env/static/private into client-side code: - src/routes/+page.svelte imports - $env/static/private` - ); -}); - -test('throws an error when dynamically importing $env/static/private', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - dynamicallyImportedIds: ['\0virtual:env/static/private'] - } - }, - `Cannot import $env/static/private into client-side code: - src/routes/+page.svelte dynamically imports - $env/static/private` - ); -}); - -test('throws an error when importing $env/dynamic/private', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['\0virtual:env/dynamic/private'] - } - }, - `Cannot import $env/dynamic/private into client-side code: - src/routes/+page.svelte imports - $env/dynamic/private` - ); -}); - -test('throws an error when dynamically importing $env/dynamic/private', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - dynamicallyImportedIds: ['\0virtual:env/dynamic/private'] - } - }, - `Cannot import $env/dynamic/private into client-side code: - src/routes/+page.svelte dynamically imports - $env/dynamic/private` - ); -}); - -// nginx does not process URLs containing the sequence `:$` -test('":$" is not in virtual module ids', () => { - assert.notInclude(Object.values(module_ids).join(''), ':$'); -}); - -test('throws an error when importing a .server.js module', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['~/src/routes/illegal.server.js'] - }, - '~/src/routes/illegal.server.js': {} - }, - `Cannot import src/routes/illegal.server.js into client-side code: - src/routes/+page.svelte imports - src/routes/illegal.server.js` - ); -}); - -test('throws an error when importing a $lib/server/**/*.js module', () => { - check( - { - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['~/src/lib/server/some/module.js'] - }, - '~/src/lib/server/some/module.js': {} - }, - `Cannot import $lib/server/some/module.js into client-side code: - src/routes/+page.svelte imports - $lib/server/some/module.js` - ); -}); - -test('ignores .server.js files in node_modules', () => { - check({ - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['~/node_modules/illegal.server.js'] - }, - '~/node_modules/illegal.server.js': {} - }); -}); - -test('ignores .server.js files outside the project root', () => { - check({ - '~/src/entry': { - importedIds: ['~/src/routes/+page.svelte'] - }, - '~/src/routes/+page.svelte': { - importedIds: ['/illegal.server.js'] - }, - '/illegal.server.js': {} - }); -}); diff --git a/packages/kit/src/exports/vite/graph_analysis/utils.js b/packages/kit/src/exports/vite/graph_analysis/utils.js deleted file mode 100644 index bdfa7a42b058..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/utils.js +++ /dev/null @@ -1,6 +0,0 @@ -const query_pattern = /\?.*$/s; - -/** @param {string} path */ -export function remove_query_from_id(path) { - return path.replace(query_pattern, ''); -} diff --git a/packages/kit/src/exports/vite/graph_analysis/utils.spec.js b/packages/kit/src/exports/vite/graph_analysis/utils.spec.js deleted file mode 100644 index f4b609c03ab7..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/utils.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -import { assert, describe } from 'vitest'; -import { remove_query_from_id } from './utils.js'; - -describe('remove_query_string_from_path', (test) => { - const module_ids = [ - '$env/static/private', - 'some-normal-js-module.js', - 'c:\\\\some\\stupid\\windows\\path.js', - '/some/normal/linux/path.js' - ]; - const query_module_ids = module_ids.map((module_id) => `${module_id}?hello=world,something=else`); - - test('does nothing to valid IDs', () => { - module_ids.forEach((id) => { - const query_stringless = remove_query_from_id(id); - assert.equal(query_stringless, id); - }); - }); - - test('removes querystring from paths with querystrings at the end', () => { - query_module_ids.forEach((id, i) => { - const query_stringless = remove_query_from_id(id); - assert.equal(query_stringless, module_ids[i]); - }); - }); -}); From f039ed540c5f90220fcffa81ad13fa699752e4ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 11:25:04 -0400 Subject: [PATCH 12/24] simplify, update tests --- packages/kit/src/exports/vite/index.js | 4 +- packages/kit/src/exports/vite/utils.js | 4 + .../+page.svelte | 7 -- .../+page.svelte | 7 -- .../dynamic-import/+page.svelte | 7 -- .../dynamic-import/+page.svelte | 7 -- packages/kit/test/apps/dev-only/test/test.js | 111 +++++------------- 7 files changed, 39 insertions(+), 108 deletions(-) delete mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte delete mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte delete mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte delete mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 2f9d60cd3d1e..b3df3b69d733 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -597,7 +597,9 @@ async function kit({ svelte_config }) { chain.push((current = candidates[0])); - if (entrypoints.has(path.relative(cwd, current))) { + const normalized = normalize_id(current, kit.files.lib, cwd); + + if (entrypoints.has(normalized)) { let message = `Cannot import ${normalize_id(id, kit.files.lib, cwd)} into code that runs in the browser, as this could leak sensitive information.`; const pyramid = chain diff --git a/packages/kit/src/exports/vite/utils.js b/packages/kit/src/exports/vite/utils.js index b764da12d45e..89dafac6fcf1 100644 --- a/packages/kit/src/exports/vite/utils.js +++ b/packages/kit/src/exports/vite/utils.js @@ -113,6 +113,8 @@ export function not_found(req, res, base) { } } +const query_pattern = /\?.*$/s; + /** * Removes cwd/lib path from the start of the id * @param {string} id @@ -120,6 +122,8 @@ export function not_found(req, res, base) { * @param {string} cwd */ export function normalize_id(id, lib, cwd) { + id = id.replace(query_pattern, ''); + if (id.startsWith(lib)) { id = id.replace(lib, '$lib'); } diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte deleted file mode 100644 index d5a365067afe..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -{#await p then envModule} -

{envModule.env.SHOULD_EXPLODE}

-{/await} diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte deleted file mode 100644 index 5b5bcda6a886..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -{#await p then envModule} -

{envModule.SHOULD_EXPLODE}

-{/await} diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte deleted file mode 100644 index 5fa759e0272f..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -{#await mod then resolved} -

{resolved.should_explode}

-{/await} diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte deleted file mode 100644 index 5e763368e26f..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -{#await mod then resolved} -

{resolved.should_explode}

-{/await} diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 8d296daf8406..7f3bc584c3d6 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -12,116 +12,69 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); test.describe.serial('Illegal imports', () => { test.skip(({ javaScriptEnabled }) => !process.env.DEV || !javaScriptEnabled); - test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { + test('$env/dynamic/private is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/dynamic-private', { - wait_for_started: false + // wait_for_started: false }); expect(await page.textContent('.message-body')) - .toBe(`Cannot import $env/dynamic/private into client-side code. This could leak sensitive information. -Imported by: src/routes/illegal-imports/env/dynamic-private/+page.svelte. -Tips: - - To resolve this error, ensure that no exports from $env/dynamic/private are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); - }); + .toBe(`Cannot import $env/dynamic/private into code that runs in the browser, as this could leak sensitive information. - test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/dynamic-private-dynamic-import', { - wait_for_started: false - }); - expect(await page.textContent('.message-body')) - .toBe(`Cannot import $env/dynamic/private into client-side code. This could leak sensitive information. -Imported by: src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte. -Tips: - - To resolve this error, ensure that no exports from $env/dynamic/private are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); + src/routes/illegal-imports/env/dynamic-private/+page.svelte imports + $env/dynamic/private + +If you're only using the import as a type, change it to \`import type\`.`); }); - test('$env/static/private is not statically importable from the client', async ({ page }) => { + test('$env/static/private is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/static-private', { wait_for_started: false }); expect(await page.textContent('.message-body')) - .toBe(`Cannot import $env/static/private into client-side code. This could leak sensitive information. -Imported by: src/routes/illegal-imports/env/static-private/+page.svelte. -Tips: - - To resolve this error, ensure that no exports from $env/static/private are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); - }); + .toBe(`Cannot import $env/static/private into code that runs in the browser, as this could leak sensitive information. - test('$env/static/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/static-private-dynamic-import', { - wait_for_started: false - }); - expect(await page.textContent('.message-body')) - .toBe(`Cannot import $env/static/private into client-side code. This could leak sensitive information. -Imported by: src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte. -Tips: - - To resolve this error, ensure that no exports from $env/static/private are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); + src/routes/illegal-imports/env/static-private/+page.svelte imports + $env/static/private + +If you're only using the import as a type, change it to \`import type\`.`); }); - test('server-only module is not statically importable from the client', async ({ page }) => { + test('server-only module is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/static-import', { wait_for_started: false }); expect(await page.textContent('.message-body')) - .toBe(`Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code. This could leak sensitive information. -Tips: - - To resolve this error, ensure that no exports from src/routes/illegal-imports/server-only-modules/illegal.server.js are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); + .toBe(`Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into code that runs in the browser, as this could leak sensitive information. + + src/routes/illegal-imports/server-only-modules/static-import/+page.svelte imports + src/routes/illegal-imports/server-only-modules/illegal.server.js + +If you're only using the import as a type, change it to \`import type\`.`); }); - test('$app/server module is not statically importable from the client', async ({ page }) => { + test('$app/server module is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/static-import-2', { wait_for_started: false }); expect(await page.textContent('.message-body')) - .toBe(`Cannot import $app/server into client-side code. This could leak sensitive information. -Tips: - - To resolve this error, ensure that no exports from $app/server are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); - }); + .toBe(`Cannot import $app/server into code that runs in the browser, as this could leak sensitive information. - test('server-only module is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-modules/dynamic-import', { - wait_for_started: false - }); - expect(await page.textContent('.message-body')) - .toBe(`Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code. This could leak sensitive information. -Tips: - - To resolve this error, ensure that no exports from src/routes/illegal-imports/server-only-modules/illegal.server.js are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); + src/routes/illegal-imports/server-only-modules/static-import-2/+page.svelte imports + $app/server + +If you're only using the import as a type, change it to \`import type\`.`); }); - test('server-only folder is not statically importable from the client', async ({ page }) => { + test('server-only folder is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-folder/static-import', { wait_for_started: false }); expect(await page.textContent('.message-body')) - .toBe(`Cannot import $lib/server/blah/private.js into client-side code. This could leak sensitive information. -Tips: - - To resolve this error, ensure that no exports from $lib/server/blah/private.js are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); - }); + .toBe(`Cannot import $lib/server/blah/private.js into code that runs in the browser, as this could leak sensitive information. - test('server-only folder is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-folder/dynamic-import', { - wait_for_started: false - }); - expect(await page.textContent('.message-body')) - .toBe(`Cannot import $lib/server/blah/private.js into client-side code. This could leak sensitive information. -Tips: - - To resolve this error, ensure that no exports from $lib/server/blah/private.js are used, even transitively, in client-side code. - - If you're only using the import as a type, change it to \`import type\`. - - If you're not sure which module is causing this, try building your app -- it will create a more helpful error.`); + src/routes/illegal-imports/server-only-folder/static-import/+page.svelte imports + $lib/server/blah/private.js + +If you're only using the import as a type, change it to \`import type\`.`); }); }); From 95316ff7bdd4d455d2960b2bb31eb19706e0ed10 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 11:32:23 -0400 Subject: [PATCH 13/24] simplify --- packages/kit/src/exports/vite/index.js | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index b3df3b69d733..f549cbc2ca4b 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -374,9 +374,6 @@ async function kit({ svelte_config }) { } }; - /** @type {Map>} */ - const import_map = new Map(); - /** @type {import('vite').Plugin} */ const plugin_virtual_modules = { name: 'vite-plugin-sveltekit-virtual-modules', @@ -536,6 +533,9 @@ async function kit({ svelte_config }) { } }; + /** @type {Map>} */ + const import_map = new Map(); + /** * Ensures that client-side code can't accidentally import server-side code, * whether in `*.server.js` files, `$app/server`, `$lib/server`, or `$env/[static|dynamic]/private` @@ -553,14 +553,16 @@ async function kit({ svelte_config }) { const resolved = await this.resolve(id, importer, { skipSelf: true }); if (resolved) { - let importers = import_map.get(resolved.id); + const normalized = normalize_id(resolved.id, kit.files.lib, cwd); + + let importers = import_map.get(normalized); if (!importers) { importers = new Set(); - import_map.set(resolved.id, importers); + import_map.set(normalized, importers); } - importers.add(importer); + importers.add(normalize_id(importer, kit.files.lib, cwd)); } } }, @@ -585,27 +587,27 @@ async function kit({ svelte_config }) { if (node.universal) entrypoints.add(node.universal); } - const chain = [id]; - let current = id; + const normalized = normalize_id(id, kit.files.lib, cwd); + const chain = [normalized]; + + let current = normalized; while (true) { const importers = import_map.get(current); if (!importers) break; - const candidates = Array.from(importers).filter((id) => !chain.includes(id)); + const candidates = Array.from(importers).filter((importer) => !chain.includes(importer)); if (candidates.length === 0) break; chain.push((current = candidates[0])); - const normalized = normalize_id(current, kit.files.lib, cwd); - - if (entrypoints.has(normalized)) { - let message = `Cannot import ${normalize_id(id, kit.files.lib, cwd)} into code that runs in the browser, as this could leak sensitive information.`; + if (entrypoints.has(current)) { + let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`; const pyramid = chain .reverse() .map((id, i) => { - return `${' '.repeat(i + 1)}${normalize_id(id, kit.files.lib, cwd)}`; + return `${' '.repeat(i + 1)}${id}`; }) .join(' imports\n'); From fd093fb00310ed68249dd3517abb3ab8ffe9fcfa Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 11:51:09 -0400 Subject: [PATCH 14/24] simplify/fix --- .../src/exports/vite/graph_analysis/index.js | 20 ------------- .../exports/vite/graph_analysis/types.d.ts | 5 ---- packages/kit/src/exports/vite/index.js | 30 ++++++++++++------- 3 files changed, 20 insertions(+), 35 deletions(-) delete mode 100644 packages/kit/src/exports/vite/graph_analysis/index.js delete mode 100644 packages/kit/src/exports/vite/graph_analysis/types.d.ts diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js deleted file mode 100644 index 1129ffb28b0f..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import path from 'node:path'; -import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js'; - -const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]); -const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; - -/** - * Checks if given id imports a module that is not allowed to be imported into client-side code. - * @param {string} id - * @param {{ - * cwd: string; - * node_modules: string; - * server: string; - * }} dirs - */ -export function is_illegal(id, dirs) { - if (ILLEGAL_IMPORTS.has(id)) return true; - if (!id.startsWith(dirs.cwd) || id.startsWith(dirs.node_modules)) return false; - return ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) || id.startsWith(dirs.server); -} diff --git a/packages/kit/src/exports/vite/graph_analysis/types.d.ts b/packages/kit/src/exports/vite/graph_analysis/types.d.ts deleted file mode 100644 index 1239fec437e1..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/types.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface ImportGraph { - readonly id: string; - readonly dynamic: boolean; - readonly children: Generator; -} diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index f549cbc2ca4b..d696593f93d1 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -15,7 +15,6 @@ import { build_server_nodes } from './build/build_server.js'; import { build_service_worker } from './build/build_service_worker.js'; import { assets_base, find_deps, resolve_symlinks } from './build/utils.js'; import { dev } from './dev/index.js'; -import { is_illegal } from './graph_analysis/index.js'; import { preview } from './preview/index.js'; import { get_config_aliases, get_env, normalize_id, stackless } from './utils.js'; import { write_client_manifest } from '../../core/sync/write_client_manifest.js'; @@ -535,6 +534,7 @@ async function kit({ svelte_config }) { /** @type {Map>} */ const import_map = new Map(); + const server_only_pattern = /.*\.server\..+/; /** * Ensures that client-side code can't accidentally import server-side code, @@ -568,15 +568,25 @@ async function kit({ svelte_config }) { }, load(id, options) { - if (options?.ssr === true || process.env.TEST === 'true') return; - - if ( - is_illegal(id, { - cwd: normalized_cwd, - node_modules: vite.normalizePath(path.resolve('node_modules')), - server: vite.normalizePath(path.join(normalized_lib, 'server')) - }) - ) { + if (options?.ssr === true || process.env.TEST === 'true') { + return; + } + + // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context + const is_internal = + id.startsWith(normalized_cwd) && + !id.startsWith(vite.normalizePath(path.resolve('node_modules'))); + + const normalized = normalize_id(id, kit.files.lib, cwd); + + const is_server_only = + normalized === '$env/static/private' || + normalized === '$env/dynamic/private' || + normalized === '$app/server' || + normalized.startsWith('$lib/server') || + (is_internal && server_only_pattern.test(path.basename(id))); + + if (is_server_only) { // in dev, this doesn't exist, so we need to create it manifest_data ??= sync.all(svelte_config, vite_config_env.mode).manifest_data; From 0f0269d9a4d13013c89d3920a1845a0f426ca4e9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 11:52:24 -0400 Subject: [PATCH 15/24] tweak --- packages/kit/src/exports/vite/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index d696593f93d1..332de97a12a3 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -218,6 +218,7 @@ async function kit({ svelte_config }) { const normalized_cwd = vite.normalizePath(cwd); const normalized_lib = vite.normalizePath(kit.files.lib); + const normalized_node_modules = vite.normalizePath(path.resolve('node_modules')); /** * A map showing which features (such as `$app/server:read`) are defined @@ -573,9 +574,7 @@ async function kit({ svelte_config }) { } // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context - const is_internal = - id.startsWith(normalized_cwd) && - !id.startsWith(vite.normalizePath(path.resolve('node_modules'))); + const is_internal = id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules); const normalized = normalize_id(id, kit.files.lib, cwd); From 96264cc992acb5ed75edebd5d2df9c51f89bb1e3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 12:19:37 -0400 Subject: [PATCH 16/24] fix tests --- packages/kit/test/build-errors/env.spec.js | 30 +++-- .../kit/test/build-errors/server-only.spec.js | 104 ++++++++---------- 2 files changed, 64 insertions(+), 70 deletions(-) diff --git a/packages/kit/test/build-errors/env.spec.js b/packages/kit/test/build-errors/env.spec.js index 64d21150f85a..aa41d4db6545 100644 --- a/packages/kit/test/build-errors/env.spec.js +++ b/packages/kit/test/build-errors/env.spec.js @@ -5,15 +5,20 @@ import process from 'node:process'; const timeout = 60_000; +// ordinarily server-only modules are allowed during testing, since Vitest can't differentiate +/** @type {Record} */ +const env = { ...process.env, TEST: false }; + test('$env/dynamic/private is not statically importable from the client', { timeout }, () => { assert.throws( () => execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/private-dynamic-env'), stdio: 'pipe', - timeout + timeout, + env }), - /.*Cannot import \$env\/dynamic\/private into client-side code:.*/gs + /.*Cannot import \$env\/dynamic\/private into code that runs in the browser.*/gs ); }); @@ -23,9 +28,10 @@ test('$env/dynamic/private is not dynamically importable from the client', { tim execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/private-dynamic-env-dynamic-import'), stdio: 'pipe', - timeout + timeout, + env }), - /.*Cannot import \$env\/dynamic\/private into client-side code:.*/gs + /.*Cannot import \$env\/dynamic\/private into code that runs in the browser.*/gs ); }); @@ -35,9 +41,10 @@ test('$env/static/private is not statically importable from the client', { timeo execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/private-static-env'), stdio: 'pipe', - timeout + timeout, + env }), - /.*Cannot import \$env\/static\/private into client-side code:.*/gs + /.*Cannot import \$env\/static\/private into code that runs in the browser.*/gs ); }); @@ -47,9 +54,10 @@ test('$env/static/private is not dynamically importable from the client', { time execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/private-static-env-dynamic-import'), stdio: 'pipe', - timeout + timeout, + env }), - /.*Cannot import \$env\/static\/private into client-side code:.*/gs + /.*Cannot import \$env\/static\/private into code that runs in the browser.*/gs ); }); @@ -59,7 +67,8 @@ test('$env/dynamic/private is not importable from the service worker', { timeout execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/service-worker-private-env'), stdio: 'pipe', - timeout: 60000 + timeout, + env }), /.*Cannot import \$env\/dynamic\/private into service-worker code.*/gs ); @@ -71,7 +80,8 @@ test('$env/dynamic/public is not importable from the service worker', { timeout execSync('pnpm build', { cwd: path.join(process.cwd(), 'apps/service-worker-dynamic-public-env'), stdio: 'pipe', - timeout + timeout, + env }), /.*Cannot import \$env\/dynamic\/public into service-worker code.*/gs ); diff --git a/packages/kit/test/build-errors/server-only.spec.js b/packages/kit/test/build-errors/server-only.spec.js index 3275bb50e46c..67bccd96afd3 100644 --- a/packages/kit/test/build-errors/server-only.spec.js +++ b/packages/kit/test/build-errors/server-only.spec.js @@ -5,74 +5,58 @@ import process from 'node:process'; const timeout = 60_000; +// ordinarily server-only modules are allowed during testing, since Vitest can't differentiate +/** @type {Record} */ +const env = { ...process.env, TEST: false }; + test('$lib/*.server.* is not statically importable from the client', { timeout }, () => { - try { - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module'), - stdio: 'pipe', - timeout - }); - } catch (err) { - const message = /** @type {Error} */ (err).message; - assert.ok( - message.includes('Cannot import $lib/test.server.js into client-side code'), - `received unexpected exception message ${message}` - ); - return; - } - throw new Error(); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module'), + stdio: 'pipe', + timeout, + env + }), + /.*Cannot import \$lib\/test.server.js into code that runs in the browser.*/gs + ); }); test('$lib/*.server.* is not dynamically importable from the client', { timeout }, () => { - try { - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), - stdio: 'pipe', - timeout - }); - } catch (err) { - const message = /** @type {Error} */ (err).message; - assert.ok( - message.includes('Cannot import $lib/test.server.js into client-side code'), - `received unexpected exception message ${message}` - ); - return; - } - throw new Error(); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), + stdio: 'pipe', + timeout, + env + }), + /.*Cannot import \$lib\/test.server.js into code that runs in the browser.*/gs + ); }); test('$lib/server/* is not statically importable from the client', { timeout }, () => { - try { - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder'), - stdio: 'pipe', - timeout - }); - } catch (err) { - const message = /** @type {Error} */ (err).message; - assert.ok( - message.includes('Cannot import $lib/server/something/private.js into client-side code'), - `received unexpected exception message ${message}` - ); - return; - } - throw new Error(); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder'), + stdio: 'pipe', + timeout, + env + }), + /.*Cannot import \$lib\/server\/something\/private.js into code that runs in the browser.*/gs + ); }); test('$lib/server/* is not dynamically importable from the client', { timeout }, () => { - try { - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), - stdio: 'pipe', - timeout - }); - } catch (err) { - const message = /** @type {Error} */ (err).message; - assert.ok( - message.includes('Cannot import $lib/server/something/private.js into client-side code'), - `received unexpected exception message ${message}` - ); - return; - } - throw new Error(); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), + stdio: 'pipe', + timeout, + env + }), + /.*Cannot import \$lib\/server\/something\/private.js into code that runs in the browser.*/gs + ); }); From 87ca7d05208c85138cf1ae2311048262857edd1b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 12:39:02 -0400 Subject: [PATCH 17/24] try this --- packages/kit/src/exports/vite/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 332de97a12a3..fa4dbf89c923 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -626,6 +626,8 @@ async function kit({ svelte_config }) { throw stackless(message); } } + + throw new Error('An impossible situation occurred'); } } }; From e2194e2d61a9f9d10c088b48e89ea16dda20dd5b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 12:39:37 -0400 Subject: [PATCH 18/24] while we're here --- packages/kit/src/exports/vite/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index fa4dbf89c923..11d4f4df5723 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -627,6 +627,8 @@ async function kit({ svelte_config }) { } } + console.error({ chain, entrypoints }); + throw new Error('An impossible situation occurred'); } } From 157faa4a0cbd71f27304a420233970dfe8b1e123 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 13:05:22 -0400 Subject: [PATCH 19/24] getting closer --- packages/kit/src/exports/vite/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 11d4f4df5723..6c2f00be7981 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -627,7 +627,7 @@ async function kit({ svelte_config }) { } } - console.error({ chain, entrypoints }); + console.error({ id, cwd, normalized, chain, entrypoints }); throw new Error('An impossible situation occurred'); } From 191a35fefcfb71e23559ec6248f520b5c1dad988 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 13:14:41 -0400 Subject: [PATCH 20/24] ahhh i think this is it --- packages/kit/src/exports/vite/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 6c2f00be7981..a96cffc56539 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -554,7 +554,7 @@ async function kit({ svelte_config }) { const resolved = await this.resolve(id, importer, { skipSelf: true }); if (resolved) { - const normalized = normalize_id(resolved.id, kit.files.lib, cwd); + const normalized = normalize_id(resolved.id, kit.files.lib, normalized_cwd); let importers = import_map.get(normalized); @@ -563,7 +563,7 @@ async function kit({ svelte_config }) { import_map.set(normalized, importers); } - importers.add(normalize_id(importer, kit.files.lib, cwd)); + importers.add(normalize_id(importer, kit.files.lib, normalized_cwd)); } } }, @@ -596,7 +596,7 @@ async function kit({ svelte_config }) { if (node.universal) entrypoints.add(node.universal); } - const normalized = normalize_id(id, kit.files.lib, cwd); + const normalized = normalize_id(id, kit.files.lib, normalized_cwd); const chain = [normalized]; let current = normalized; From 49d5fbb20a8fac0297c70c3ebc72d85998c55929 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 9 Aug 2025 13:26:11 -0400 Subject: [PATCH 21/24] last bit --- packages/kit/src/exports/vite/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index a96cffc56539..352a8705fb62 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -554,7 +554,7 @@ async function kit({ svelte_config }) { const resolved = await this.resolve(id, importer, { skipSelf: true }); if (resolved) { - const normalized = normalize_id(resolved.id, kit.files.lib, normalized_cwd); + const normalized = normalize_id(resolved.id, normalized_lib, normalized_cwd); let importers = import_map.get(normalized); @@ -563,7 +563,7 @@ async function kit({ svelte_config }) { import_map.set(normalized, importers); } - importers.add(normalize_id(importer, kit.files.lib, normalized_cwd)); + importers.add(normalize_id(importer, normalized_lib, normalized_cwd)); } } }, @@ -576,7 +576,7 @@ async function kit({ svelte_config }) { // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context const is_internal = id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules); - const normalized = normalize_id(id, kit.files.lib, cwd); + const normalized = normalize_id(id, normalized_lib, normalized_cwd); const is_server_only = normalized === '$env/static/private' || @@ -596,7 +596,7 @@ async function kit({ svelte_config }) { if (node.universal) entrypoints.add(node.universal); } - const normalized = normalize_id(id, kit.files.lib, normalized_cwd); + const normalized = normalize_id(id, normalized_lib, normalized_cwd); const chain = [normalized]; let current = normalized; @@ -627,8 +627,6 @@ async function kit({ svelte_config }) { } } - console.error({ id, cwd, normalized, chain, entrypoints }); - throw new Error('An impossible situation occurred'); } } From 92a18254ec3cd8377ce2416bd4d851b1200da951 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 12 Aug 2025 12:02:01 -0400 Subject: [PATCH 22/24] Update packages/kit/src/exports/vite/utils.js --- packages/kit/src/exports/vite/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/utils.js b/packages/kit/src/exports/vite/utils.js index 89dafac6fcf1..cf6223e6c632 100644 --- a/packages/kit/src/exports/vite/utils.js +++ b/packages/kit/src/exports/vite/utils.js @@ -168,7 +168,7 @@ export function normalize_id(id, lib, cwd) { export function stackless(message) { const error = new Error(message); error.stack = ''; - throw error; + return error; } export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', ''); From 21650f4f71908a0b454be28830573c09ecdde239 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 12 Aug 2025 12:13:19 -0400 Subject: [PATCH 23/24] Update packages/kit/test/apps/dev-only/test/test.js --- packages/kit/test/apps/dev-only/test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 7f3bc584c3d6..6cca2bef4d0f 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -14,7 +14,7 @@ test.describe.serial('Illegal imports', () => { test('$env/dynamic/private is not importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/dynamic-private', { - // wait_for_started: false + wait_for_started: false }); expect(await page.textContent('.message-body')) .toBe(`Cannot import $env/dynamic/private into code that runs in the browser, as this could leak sensitive information. From 8cd0bba079290d08c5f8b0ddb0cd774f8a3d6f9e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 12 Aug 2025 12:36:58 -0400 Subject: [PATCH 24/24] test for indirect import --- .../server-only-modules/static-import/+page.svelte | 2 +- .../illegal-imports/server-only-modules/static-import/foo.js | 1 + packages/kit/test/apps/dev-only/test/test.js | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/foo.js diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte index 43bb66e92f6d..eb91c6f3058f 100644 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte +++ b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte @@ -1,5 +1,5 @@

{should_explode}

diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/foo.js b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/foo.js new file mode 100644 index 000000000000..ac86daab308e --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/foo.js @@ -0,0 +1 @@ +export { should_explode } from '../illegal.server.js'; diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 6cca2bef4d0f..07daa6ac2482 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -46,7 +46,8 @@ If you're only using the import as a type, change it to \`import type\`.`); .toBe(`Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into code that runs in the browser, as this could leak sensitive information. src/routes/illegal-imports/server-only-modules/static-import/+page.svelte imports - src/routes/illegal-imports/server-only-modules/illegal.server.js + src/routes/illegal-imports/server-only-modules/static-import/foo.js imports + src/routes/illegal-imports/server-only-modules/illegal.server.js If you're only using the import as a type, change it to \`import type\`.`); });