From 5eab07a98b95bcb7d61a2df431e88eea0c9f0276 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 16:38:04 -0400 Subject: [PATCH 01/30] prevent loading of illegal modules in the browser --- packages/kit/src/exports/vite/dev/index.js | 16 ----- .../src/exports/vite/graph_analysis/index.js | 72 +------------------ .../exports/vite/graph_analysis/index.spec.js | 6 +- .../src/exports/vite/graph_analysis/utils.js | 24 ------- packages/kit/src/exports/vite/index.js | 14 +++- 5 files changed, 17 insertions(+), 115 deletions(-) diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 938acd639a74..a00afc67cf66 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -11,9 +11,7 @@ import { load_error_page, load_template } from '../../../core/config/index.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import * as sync from '../../../core/sync/sync.js'; import { get_mime_lookup, runtime_base, runtime_prefix } from '../../../core/utils.js'; -import { prevent_illegal_vite_imports } from '../graph_analysis/index.js'; import { compact } from '../../../utils/array.js'; -import { normalizePath } from 'vite'; // Vite doesn't expose this so we just copy the list for now // https://github.com/vitejs/vite/blob/3edd1af56e980aef56641a5a51cf2932bb580d41/packages/vite/src/node/plugins/css.ts#L96 @@ -43,8 +41,6 @@ export async function dev(vite, vite_config, svelte_config) { /** @type {import('types').SSRManifest} */ let manifest; - const extensions = [...svelte_config.extensions, ...svelte_config.kit.moduleExtensions]; - /** @param {string} id */ async function resolve(id) { const url = id.startsWith('..') ? `/@fs${path.posix.resolve(id)}` : `/${id}`; @@ -94,12 +90,6 @@ export async function dev(vite, vite_config, svelte_config) { module_nodes.push(module_node); result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for? - prevent_illegal_vite_imports( - module_node, - normalizePath(svelte_config.kit.files.lib), - extensions - ); - return module.default; }; } @@ -110,12 +100,6 @@ export async function dev(vite, vite_config, svelte_config) { module_nodes.push(module_node); result.shared = module; - - prevent_illegal_vite_imports( - module_node, - normalizePath(svelte_config.kit.files.lib), - extensions - ); } if (node.server) { diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 6c40503268c1..86ebd8d668df 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 'path'; import { normalizePath } from 'vite'; -import { remove_query_from_id, get_module_types } from './utils.js'; +import { remove_query_from_id } from './utils.js'; /** @typedef {import('./types').ImportGraph} ImportGraph */ @@ -193,63 +193,6 @@ export class RollupImportGraph { } } -/** @implements {ImportGraph} */ -export class ViteImportGraph { - /** @type {Set} */ - #module_types; - - /** @type {import('vite').ModuleNode} */ - #module_info; - - /** @type {string} */ - id; - - /** @type {Set} */ - #seen; - - /** - * @param {Set} module_types Module types to analyze, eg '.js', '.ts', etc. - * @param {import('vite').ModuleNode} node - */ - constructor(module_types, node) { - this.#module_types = module_types; - this.#module_info = node; - this.id = remove_query_from_id(normalizePath(node.id ?? '')); - this.#seen = new Set(); - } - - /** - * @param {Set} module_types Module types to analyze, eg '.js', '.ts', etc. - * @param {import('vite').ModuleNode} node - * @param {Set} seen - * @returns {ViteImportGraph} - */ - static #new_internal(module_types, node, seen) { - const instance = new ViteImportGraph(module_types, node); - instance.#seen = seen; - return instance; - } - - get dynamic() { - return false; - } - - get children() { - return this.#children(); - } - - *#children() { - if (this.#seen.has(this.id)) return; - this.#seen.add(this.id); - for (const child of this.#module_info.importedModules) { - if (!this.#module_types.has(path.extname(this.id))) { - continue; - } - yield ViteImportGraph.#new_internal(this.#module_types, child, this.#seen); - } - } -} - /** * Throw an error if a private module is imported from a client-side node. * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter @@ -262,16 +205,3 @@ export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) { const guard = new IllegalModuleGuard(lib_dir); guard.assert_legal(graph); } - -/** - * Throw an error if a private module is imported from a client-side node. - * @param {import('vite').ModuleNode} node - * @param {string} lib_dir - * @param {Iterable} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc. - * @returns {void} - */ -export function prevent_illegal_vite_imports(node, lib_dir, module_types) { - const graph = new ViteImportGraph(get_module_types(module_types), node); - const guard = new IllegalModuleGuard(lib_dir); - guard.assert_legal(graph); -} 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 1b00de5a804a..e130a9338e50 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -184,8 +184,8 @@ describe('IllegalImportGuard', (test) => { }); /* -We don't have a great way to mock Vite and Rollup's implementations of module graphs, so unit testing -ViteImportGraph and RollupImportGraph is kind of an exercise in "code coverage hubris" -- they're covered by -the integration tests, where Vite and Rollup can provide a useful graph implementation. If, in the future, we can find +We don't have a great way to mock Rollup's implementation of a module graph, so unit testing +RollupImportGraph is kind of an exercise in "code coverage hubris" -- it's covered by the integration +tests, where Rollup can provide a useful graph implementation. If, in the future, we can find a reason to unit test them, we can add those below. */ diff --git a/packages/kit/src/exports/vite/graph_analysis/utils.js b/packages/kit/src/exports/vite/graph_analysis/utils.js index f2caa3e04026..bdfa7a42b058 100644 --- a/packages/kit/src/exports/vite/graph_analysis/utils.js +++ b/packages/kit/src/exports/vite/graph_analysis/utils.js @@ -4,27 +4,3 @@ const query_pattern = /\?.*$/s; export function remove_query_from_id(path) { return path.replace(query_pattern, ''); } - -/** - * Vite does some weird things with import trees in dev - * for example, a Tailwind app.css will appear to import - * every file in the project. This isn't a problem for - * Rollup during build. - * @param {Iterable} config_module_types - */ -export const get_module_types = (config_module_types) => { - return new Set([ - '', - '.ts', - '.js', - '.svelte', - '.mts', - '.mjs', - '.cts', - '.cjs', - '.svelte.md', - '.svx', - '.md', - ...config_module_types - ]); -}; diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 941a87568309..fd058d2d67fc 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -292,7 +292,19 @@ function kit() { if (id.startsWith('$env/')) return `\0${id}`; }, - async load(id) { + async load(id, { ssr }) { + if (!ssr) { + // TODO this is incomplete — we need to replicate the #is_illegal logic + if ( + id.endsWith('.server.js') || + id === '\0$env/static/private' || + id === '\0$env/dynamic/private' + ) { + const relative = id.startsWith(process.cwd()) ? path.relative('.', id) : id; + throw new Error(`Cannot import ${relative} into client-side code`); + } + } + switch (id) { case '\0$env/static/private': return create_static_module('$env/static/private', env.private); From 204d862f172de199cac6e0e16b20dcd2372cdf9e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 16:58:45 -0400 Subject: [PATCH 02/30] update test --- .../exports/vite/graph_analysis/utils.spec.js | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/utils.spec.js b/packages/kit/src/exports/vite/graph_analysis/utils.spec.js index 7aff1b9998a4..8b1feb7882a4 100644 --- a/packages/kit/src/exports/vite/graph_analysis/utils.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/utils.spec.js @@ -1,6 +1,6 @@ import { describe } from '../../../utils/unit_test.js'; import * as assert from 'uvu/assert'; -import { remove_query_from_id, get_module_types } from './utils.js'; +import { remove_query_from_id } from './utils.js'; describe('remove_query_string_from_path', (test) => { const module_ids = [ @@ -25,34 +25,3 @@ describe('remove_query_string_from_path', (test) => { }); }); }); - -describe('get_module_types', (test) => { - const base_expected_extensions = [ - '', - '.ts', - '.js', - '.svelte', - '.mts', - '.mjs', - '.cts', - '.cjs', - '.svelte.md', - '.svx', - '.md' - ]; - - test('returns correct base extensions', () => { - const module_types = get_module_types([]); - base_expected_extensions.forEach((extension) => { - assert.equal(module_types.has(extension), true); - }); - }); - - test('correctly extends base extensions', () => { - const additional_extensions = ['.foo', '.bar', '.baz']; - const module_types = get_module_types(additional_extensions); - base_expected_extensions.concat(additional_extensions).forEach((extension) => { - assert.equal(module_types.has(extension), true); - }); - }); -}); From 526102138ce28029e2750ca023e509424e88db0b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 17:00:16 -0400 Subject: [PATCH 03/30] rename function --- packages/kit/src/exports/vite/graph_analysis/index.js | 9 ++------- packages/kit/src/exports/vite/index.js | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 86ebd8d668df..6de2d8fd1202 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -6,12 +6,7 @@ import { remove_query_from_id } from './utils.js'; const CWD_ID = normalizePath(process.cwd()); const NODE_MODULES_ID = normalizePath(path.resolve(process.cwd(), 'node_modules')); -const ILLEGAL_IMPORTS = new Set([ - '/@id/__x00__$env/dynamic/private', //dev - '\0$env/dynamic/private', // prod - '/@id/__x00__$env/static/private', // dev - '\0$env/static/private' // prod -]); +const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/private']); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; export class IllegalModuleGuard { @@ -200,7 +195,7 @@ export class RollupImportGraph { * @param {string} lib_dir * @returns {void} */ -export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) { +export function prevent_illegal_imports(node_getter, node, lib_dir) { const graph = new RollupImportGraph(node_getter, node); const guard = new IllegalModuleGuard(lib_dir); guard.assert_legal(graph); diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index fd058d2d67fc..f0aa1b19c867 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -15,7 +15,7 @@ import { runtime_directory, logger } from '../../core/utils.js'; import { find_deps, get_default_build_config } from './build/utils.js'; import { preview } from './preview/index.js'; import { get_aliases, get_env } from './utils.js'; -import { prevent_illegal_rollup_imports } from './graph_analysis/index.js'; +import { prevent_illegal_imports } from './graph_analysis/index.js'; import { fileURLToPath } from 'node:url'; import { create_static_module, create_dynamic_module } from '../../core/env.js'; @@ -370,7 +370,7 @@ function kit() { const module_node = this.getModuleInfo(id); if (module_node) { - prevent_illegal_rollup_imports( + prevent_illegal_imports( this.getModuleInfo.bind(this), module_node, vite.normalizePath(svelte_config.kit.files.lib) From b0c2282b7c4c0a53c0a1ae25a8f4a6933cedcb4c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 17:09:16 -0400 Subject: [PATCH 04/30] fix --- packages/kit/src/exports/vite/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index f0aa1b19c867..8f67996b0e02 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -292,8 +292,8 @@ function kit() { if (id.startsWith('$env/')) return `\0${id}`; }, - async load(id, { ssr }) { - if (!ssr) { + async load(id, options) { + if (options?.ssr === false) { // TODO this is incomplete — we need to replicate the #is_illegal logic if ( id.endsWith('.server.js') || From b6d8a2a07f54b5e160acd68e90a924c78719f46b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 17:16:00 -0400 Subject: [PATCH 05/30] remove dev test --- .../exports/vite/graph_analysis/index.spec.js | 24 ------------------- 1 file changed, 24 deletions(-) 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 e130a9338e50..37bc0eacbeaf 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -88,18 +88,6 @@ describe('IllegalImportGuard', (test) => { assert.not.throws(() => guard.assert_legal(get_module_graph())); }); - test('assert throws an error when importing $env/static/private in dev', () => { - const module_graph = get_module_graph({ - id: DEV_VIRTUAL_STATIC_ID, - dynamic: false, - children: generator_from_array([]) - }); - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs - ); - }); - test('assert throws an error when importing $env/static/private in prod', () => { const module_graph = get_module_graph({ id: PROD_VIRTUAL_STATIC_ID, @@ -112,18 +100,6 @@ describe('IllegalImportGuard', (test) => { ); }); - test('assert throws an error when importing $env/dynamic/private in dev', () => { - const module_graph = get_module_graph({ - id: DEV_VIRTUAL_DYNAMIC_ID, - dynamic: false, - children: generator_from_array([]) - }); - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs - ); - }); - test('assert throws an error when importing $env/dynamic/private in prod', () => { const module_graph = get_module_graph({ id: PROD_VIRTUAL_DYNAMIC_ID, From 1e3256fcf686fb241be354a9d4a9dada0916639f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 17:17:15 -0400 Subject: [PATCH 06/30] remove unused consts --- packages/kit/src/exports/vite/graph_analysis/index.spec.js | 2 -- 1 file changed, 2 deletions(-) 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 37bc0eacbeaf..00635258ea1f 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -6,9 +6,7 @@ import { normalizePath } from 'vite'; const CWD = process.cwd(); const FAKE_LIB_DIR = normalizePath(path.join(CWD, 'lib')); -const DEV_VIRTUAL_DYNAMIC_ID = '/@id/__x00__$env/dynamic/private'; const PROD_VIRTUAL_DYNAMIC_ID = '\0$env/dynamic/private'; -const DEV_VIRTUAL_STATIC_ID = '/@id/__x00__$env/static/private'; const PROD_VIRTUAL_STATIC_ID = '\0$env/static/private'; const USER_SERVER_ID = normalizePath(path.join(FAKE_LIB_DIR, 'test.server.js')); const USER_SERVER_ID_NODE_MODULES = normalizePath(path.join(CWD, 'node_modules', 'test.server.js')); From 9132a6686a40e0c4abdb71fe3de9ca4f23209434 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 4 Nov 2022 18:04:33 -0400 Subject: [PATCH 07/30] simpler checking code --- .../src/exports/vite/graph_analysis/index.js | 225 ++++-------------- .../exports/vite/graph_analysis/index.spec.js | 165 ------------- packages/kit/src/exports/vite/index.js | 14 +- 3 files changed, 56 insertions(+), 348 deletions(-) delete mode 100644 packages/kit/src/exports/vite/graph_analysis/index.spec.js diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 6de2d8fd1202..2c4f707ebfa3 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,202 +1,81 @@ import path from 'path'; import { normalizePath } from 'vite'; -import { remove_query_from_id } from './utils.js'; -/** @typedef {import('./types').ImportGraph} ImportGraph */ - -const CWD_ID = normalizePath(process.cwd()); -const NODE_MODULES_ID = normalizePath(path.resolve(process.cwd(), 'node_modules')); +const cwd = normalizePath(process.cwd()); +const node_modules = normalizePath(path.resolve(process.cwd(), 'node_modules')); const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/private']); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; -export class IllegalModuleGuard { - /** @type {string} */ - #lib_dir; - - /** @type {string} */ - #server_dir; - - /** @type {Array} */ - #chain = []; - - /** - * @param {string} lib_dir - */ - constructor(lib_dir) { - this.#lib_dir = normalizePath(lib_dir); - this.#server_dir = normalizePath(path.resolve(lib_dir, 'server')); - } +/** + * @param {import('rollup').PluginContext} context + * @param {string} lib + */ +export function module_guard(context, lib) { + /** @type {Set} */ + const safe = new Set(); - /** - * Assert that a node imports no illegal modules. - * @param {ImportGraph} node - * @returns {void} - */ - assert_legal(node) { - this.#chain.push(node); - for (const child of node.children) { - if (this.#is_illegal(child.id)) { - this.#chain.push(child); - const error = this.#format_illegal_import_chain(this.#chain); - this.#chain = []; // Reset the chain in case we want to reuse this guard - throw new Error(error); - } - this.assert_legal(child); - } - this.#chain.pop(); - } + const lib_dir = normalizePath(lib); + const server_dir = normalizePath(path.resolve(lib, 'server')); /** - * `true` if the provided ID represents a server-only module, else `false`. - * @param {string} module_id - * @returns {boolean} + * @param {string} id + * @param {Array<{ id: string, dynamic: boolean }>} chain */ - #is_illegal(module_id) { - if (this.#is_kit_illegal(module_id) || this.#is_user_illegal(module_id)) return true; - return false; - } + function follow(id, chain = []) { + if (safe.has(id)) return; - /** - * `true` if the provided ID represents a Kit-defined server-only module, else `false`. - * @param {string} module_id - * @returns {boolean} - */ - #is_kit_illegal(module_id) { - return ILLEGAL_IMPORTS.has(module_id); - } + if ( + ILLEGAL_IMPORTS.has(id) || + id.startsWith(server_dir) || + (ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) && + id.startsWith(cwd) && + !id.startsWith(node_modules)) + ) { + chain.shift(); // discard the entry point - /** - * `true` if the provided ID represents a user-defined server-only module, else `false`. - * @param {string} module_id - * @returns {boolean} - */ - #is_user_illegal(module_id) { - if (module_id.startsWith(this.#server_dir)) return true; + if (id.startsWith(lib_dir)) id = id.replace(lib_dir, '$lib'); + if (id.startsWith(cwd)) id = path.relative(cwd, id); - // files outside the project root are ignored - if (!module_id.startsWith(CWD_ID)) return false; + const pyramid = + chain.map(({ id, dynamic }, i) => { + if (id.startsWith(lib_dir)) id = id.replace(lib_dir, '$lib'); + if (id.startsWith(cwd)) id = path.relative(cwd, id); - // so are files inside node_modules - if (module_id.startsWith(NODE_MODULES_ID)) return false; + return `${repeat(' ', i * 2)}- ${id} ${dynamic ? 'dynamically imports' : 'imports'}\n`; + }) + `${repeat(' ', chain.length)}- ${id}`; - return ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(module_id)); - } + const message = `Cannot import ${id} into public-facing code:\n${pyramid}`; - /** - * @param {string} str - * @param {number} times - */ - #repeat(str, times) { - return new Array(times + 1).join(str); - } + throw new Error(message); + } - /** - * Create a formatted error for an illegal import. - * @param {Array} stack - */ - #format_illegal_import_chain(stack) { - const dev_virtual_prefix = '/@id/__x00__'; - const prod_virtual_prefix = '\0'; + const module = context.getModuleInfo(id); - stack = stack.map((graph) => { - if (graph.id.startsWith(dev_virtual_prefix)) { - return { ...graph, id: graph.id.replace(dev_virtual_prefix, '') }; - } - if (graph.id.startsWith(prod_virtual_prefix)) { - return { ...graph, id: graph.id.replace(prod_virtual_prefix, '') }; - } - if (graph.id.startsWith(this.#lib_dir)) { - return { ...graph, id: graph.id.replace(this.#lib_dir, '$lib') }; + if (module) { + for (const child of module.importedIds) { + follow(child, [...chain, { id, dynamic: false }]); } - return { ...graph, id: path.relative(process.cwd(), graph.id) }; - }); - - const pyramid = stack - .map( - (file, i) => - `${this.#repeat(' ', i * 2)}- ${file.id} ${ - file.dynamic ? '(imported by parent dynamically)' : '' - }` - ) - .join('\n'); - - return `Cannot import ${stack.at(-1)?.id} into public-facing code:\n${pyramid}`; - } -} - -/** @implements {ImportGraph} */ -export class RollupImportGraph { - /** @type {(id: string) => import('rollup').ModuleInfo | null} */ - #node_getter; - - /** @type {import('rollup').ModuleInfo} */ - #module_info; - - /** @type {string} */ - id; - - /** @type {boolean} */ - dynamic; - - /** @type {Set} */ - #seen; - - /** - * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter - * @param {import('rollup').ModuleInfo} node - */ - constructor(node_getter, node) { - this.#node_getter = node_getter; - this.#module_info = node; - this.id = remove_query_from_id(normalizePath(node.id)); - this.dynamic = false; - this.#seen = new Set(); - } - - /** - * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter - * @param {import('rollup').ModuleInfo} node - * @param {boolean} dynamic - * @param {Set} seen; - * @returns {RollupImportGraph} - */ - static #new_internal(node_getter, node, dynamic, seen) { - const instance = new RollupImportGraph(node_getter, node); - instance.dynamic = dynamic; - instance.#seen = seen; - return instance; - } + for (const child of module.dynamicallyImportedIds) { + follow(child, [...chain, { id, dynamic: true }]); + } + } - get children() { - return this.#children(); + safe.add(id); } - *#children() { - if (this.#seen.has(this.id)) return; - this.#seen.add(this.id); - for (const id of this.#module_info.importedIds) { - const child = this.#node_getter(id); - if (child === null) return; - yield RollupImportGraph.#new_internal(this.#node_getter, child, false, this.#seen); - } - for (const id of this.#module_info.dynamicallyImportedIds) { - const child = this.#node_getter(id); - if (child === null) return; - yield RollupImportGraph.#new_internal(this.#node_getter, child, true, this.#seen); + return { + /** @param {string} id */ + check: (id) => { + follow(id, []); } - } + }; } /** - * Throw an error if a private module is imported from a client-side node. - * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter - * @param {import('rollup').ModuleInfo} node - * @param {string} lib_dir - * @returns {void} + * @param {string} str + * @param {number} times */ -export function prevent_illegal_imports(node_getter, node, lib_dir) { - const graph = new RollupImportGraph(node_getter, node); - const guard = new IllegalModuleGuard(lib_dir); - guard.assert_legal(graph); +function repeat(str, times) { + return new Array(times + 1).join(str); } 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 00635258ea1f..000000000000 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ /dev/null @@ -1,165 +0,0 @@ -import { describe } from '../../../utils/unit_test.js'; -import * as assert from 'uvu/assert'; -import { IllegalModuleGuard } from './index.js'; -import path from 'path'; -import { normalizePath } from 'vite'; - -const CWD = process.cwd(); -const FAKE_LIB_DIR = normalizePath(path.join(CWD, 'lib')); -const PROD_VIRTUAL_DYNAMIC_ID = '\0$env/dynamic/private'; -const PROD_VIRTUAL_STATIC_ID = '\0$env/static/private'; -const USER_SERVER_ID = normalizePath(path.join(FAKE_LIB_DIR, 'test.server.js')); -const USER_SERVER_ID_NODE_MODULES = normalizePath(path.join(CWD, 'node_modules', 'test.server.js')); -const USER_SERVER_ID_OUTSIDE_ROOT = normalizePath(path.join(CWD, '..', 'test.server.js')); -const USER_SERVER_FOLDER_ID = normalizePath(path.join(FAKE_LIB_DIR, '/server/some/nested/path.js')); - -/** - * @template {any} T - * @param {Array} arr - * @returns {Generator} - */ -function* generator_from_array(arr) { - for (const item of arr) { - yield item; - } -} - -/** - * @param {Array} nodes_to_insert - * @returns {import('./types').ImportGraph} - */ -function get_module_graph(...nodes_to_insert) { - return { - id: 'test.svelte', - dynamic: false, - children: generator_from_array([ - { - id: 'fine.js', - dynamic: false, - children: generator_from_array([ - { - id: 'also_fine.js', - dynamic: false, - children: generator_from_array([ - { - id: 'erstwhile.css', - dynamic: false, - children: generator_from_array([]) - }, - { - id: 'gruntled.js', - dynamic: false, - children: generator_from_array([]) - } - ]) - }, - { - id: 'somewhat_neat.js', - dynamic: false, - children: generator_from_array([]) - }, - { - id: 'blah.ts', - dynamic: false, - children: generator_from_array([]) - } - ]) - }, - { - id: 'something.svelte', - dynamic: false, - children: generator_from_array(nodes_to_insert) - }, - { - id: 'im_not_creative.hamburger', - dynamic: false, - children: generator_from_array([]) - } - ]) - }; -} - -describe('IllegalImportGuard', (test) => { - const guard = new IllegalModuleGuard(FAKE_LIB_DIR); - - test('assert succeeds for a graph with no illegal imports', () => { - assert.not.throws(() => guard.assert_legal(get_module_graph())); - }); - - test('assert throws an error when importing $env/static/private in prod', () => { - const module_graph = get_module_graph({ - id: PROD_VIRTUAL_STATIC_ID, - dynamic: false, - children: generator_from_array([]) - }); - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs - ); - }); - - test('assert throws an error when importing $env/dynamic/private in prod', () => { - const module_graph = get_module_graph({ - id: PROD_VIRTUAL_DYNAMIC_ID, - dynamic: false, - children: generator_from_array([]) - }); - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs - ); - }); - - test('assert throws an error when importing a single server-only module', () => { - const module_graph = get_module_graph({ - id: USER_SERVER_ID, - dynamic: false, - children: generator_from_array([]) - }); - - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs - ); - }); - - test('assert throws an error when importing a module in the server-only folder', () => { - const module_graph = get_module_graph({ - id: USER_SERVER_FOLDER_ID, - dynamic: false, - children: generator_from_array([]) - }); - - assert.throws( - () => guard.assert_legal(module_graph), - /.*Cannot import \$lib\/server\/some\/nested\/path.js into public-facing code:.*/gs - ); - }); - - test('assert ignores illegal server-only modules in node_modules', () => { - const module_graph = get_module_graph({ - id: USER_SERVER_ID_NODE_MODULES, - dynamic: false, - children: generator_from_array([]) - }); - - assert.not.throws(() => guard.assert_legal(module_graph)); - }); - - test('assert ignores illegal server-only modules outside the project root', () => { - const module_graph = get_module_graph({ - id: USER_SERVER_ID_OUTSIDE_ROOT, - dynamic: false, - children: generator_from_array([]) - }); - - assert.not.throws(() => guard.assert_legal(module_graph)); - }); -}); - -/* -We don't have a great way to mock Rollup's implementation of a module graph, so unit testing -RollupImportGraph is kind of an exercise in "code coverage hubris" -- it's covered by the integration -tests, where Rollup can provide a useful graph implementation. If, in the future, we can find -a reason to unit test them, we can add those below. -*/ diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 8f67996b0e02..889f4cff6b8c 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -15,9 +15,9 @@ import { runtime_directory, logger } from '../../core/utils.js'; import { find_deps, get_default_build_config } from './build/utils.js'; import { preview } from './preview/index.js'; import { get_aliases, get_env } from './utils.js'; -import { prevent_illegal_imports } from './graph_analysis/index.js'; import { fileURLToPath } from 'node:url'; import { create_static_module, create_dynamic_module } from '../../core/env.js'; +import { module_guard } from './graph_analysis/index.js'; const cwd = process.cwd(); @@ -362,20 +362,14 @@ function kit() { return; } + const guard = module_guard(this, vite.normalizePath(svelte_config.kit.files.lib)); + manifest_data.nodes.forEach((_node, i) => { const id = vite.normalizePath( path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) ); - const module_node = this.getModuleInfo(id); - - if (module_node) { - prevent_illegal_imports( - this.getModuleInfo.bind(this), - module_node, - vite.normalizePath(svelte_config.kit.files.lib) - ); - } + guard.check(id); }); const verbose = vite_config.logLevel === 'info'; From b847be2a0eebc2a9eb763a1d278124fdc6180a11 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 Nov 2022 09:27:34 -0500 Subject: [PATCH 08/30] move tests (still failing) --- .../src/lib/server/blah/test.js | 0 .../+page.svelte | 0 .../env/dynamic-private/+page.svelte | 0 .../+page.svelte | 0 .../env/static-private/+page.svelte | 0 .../dynamic-import/+page.svelte | 0 .../static-import/+page.svelte | 0 .../dynamic-import/+page.svelte | 0 .../static-import/+page.svelte | 0 .../kit/test/apps/basics/test/client.test.js | 58 +++++++++++++++ packages/kit/test/apps/dev-only/.env | 1 - packages/kit/test/apps/dev-only/.gitignore | 2 - packages/kit/test/apps/dev-only/package.json | 22 ------ .../test/apps/dev-only/playwright.config.js | 1 - packages/kit/test/apps/dev-only/src/app.html | 16 ----- .../test/apps/dev-only/src/hooks.server.js | 4 -- .../test/apps/dev-only/src/lib/test.server.js | 1 - .../apps/dev-only/src/routes/+error.svelte | 5 -- .../apps/dev-only/src/routes/+layout.svelte | 7 -- .../kit/test/apps/dev-only/static/favicon.png | Bin 1571 -> 0 bytes .../kit/test/apps/dev-only/svelte.config.js | 4 -- packages/kit/test/apps/dev-only/test/test.js | 68 ------------------ packages/kit/test/apps/dev-only/tsconfig.json | 16 ----- .../kit/test/apps/dev-only/vite.config.js | 23 ------ 24 files changed, 58 insertions(+), 170 deletions(-) rename packages/kit/test/apps/{dev-only => basics}/src/lib/server/blah/test.js (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/env/dynamic-private-dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/env/dynamic-private/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/env/static-private-dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/env/static-private/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/server-only-folder/dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/server-only-folder/static-import/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/server-only-modules/dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{dev-only/src/routes => basics/src/routes/illegal-imports}/server-only-modules/static-import/+page.svelte (100%) delete mode 100644 packages/kit/test/apps/dev-only/.env delete mode 100644 packages/kit/test/apps/dev-only/.gitignore delete mode 100644 packages/kit/test/apps/dev-only/package.json delete mode 100644 packages/kit/test/apps/dev-only/playwright.config.js delete mode 100644 packages/kit/test/apps/dev-only/src/app.html delete mode 100644 packages/kit/test/apps/dev-only/src/hooks.server.js delete mode 100644 packages/kit/test/apps/dev-only/src/lib/test.server.js delete mode 100644 packages/kit/test/apps/dev-only/src/routes/+error.svelte delete mode 100644 packages/kit/test/apps/dev-only/src/routes/+layout.svelte delete mode 100644 packages/kit/test/apps/dev-only/static/favicon.png delete mode 100644 packages/kit/test/apps/dev-only/svelte.config.js delete mode 100644 packages/kit/test/apps/dev-only/test/test.js delete mode 100644 packages/kit/test/apps/dev-only/tsconfig.json delete mode 100644 packages/kit/test/apps/dev-only/vite.config.js diff --git a/packages/kit/test/apps/dev-only/src/lib/server/blah/test.js b/packages/kit/test/apps/basics/src/lib/server/blah/test.js similarity index 100% rename from packages/kit/test/apps/dev-only/src/lib/server/blah/test.js rename to packages/kit/test/apps/basics/src/lib/server/blah/test.js diff --git a/packages/kit/test/apps/dev-only/src/routes/env/dynamic-private-dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/env/dynamic-private-dynamic-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/env/dynamic-private/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/env/dynamic-private/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/env/static-private-dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/env/static-private-dynamic-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/env/static-private/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/env/static-private/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/server-only-folder/dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/server-only-folder/dynamic-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/server-only-folder/static-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/server-only-folder/static-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/server-only-modules/dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/server-only-modules/dynamic-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/server-only-modules/static-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/dev-only/src/routes/server-only-modules/static-import/+page.svelte rename to packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 9d82bfccff96..b5739be603ec 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -987,3 +987,61 @@ test.describe('cookies', () => { await expect(page.locator('p')).toHaveText('foo=bar'); }); }); + +test.describe.only('Illegal imports', () => { + test.skip(() => !process.env.DEV); + + test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/dynamic-private'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + ); + }); + + test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/dynamic-private-dynamic-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + ); + }); + + test('$env/static/private is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/static-private'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + ); + }); + + test('$env/static/private is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/static-private-dynamic-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + ); + }); + + test('server-only module is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-modules/static-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + ); + }); + test('server-only module is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-modules/dynamic-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + ); + }); + + test('server-only folder is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-folder/static-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs + ); + }); + test('server-only folder is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-folder/dynamic-import'); + expect(await page.innerHTML('body')).toMatch( + /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs + ); + }); +}); diff --git a/packages/kit/test/apps/dev-only/.env b/packages/kit/test/apps/dev-only/.env deleted file mode 100644 index 752bad1e78a0..000000000000 --- a/packages/kit/test/apps/dev-only/.env +++ /dev/null @@ -1 +0,0 @@ -SHOULD_EXPLODE=boom \ No newline at end of file diff --git a/packages/kit/test/apps/dev-only/.gitignore b/packages/kit/test/apps/dev-only/.gitignore deleted file mode 100644 index fa2f5a579236..000000000000 --- a/packages/kit/test/apps/dev-only/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/test/errors.json -!/.env diff --git a/packages/kit/test/apps/dev-only/package.json b/packages/kit/test/apps/dev-only/package.json deleted file mode 100644 index b45841ba2a68..000000000000 --- a/packages/kit/test/apps/dev-only/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "test-basics", - "private": true, - "version": "0.0.2-next.0", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && tsc && svelte-check", - "test": "cross-env DEV=true playwright test" - }, - "devDependencies": { - "@sveltejs/kit": "workspace:*", - "cross-env": "^7.0.3", - "rimraf": "^3.0.2", - "svelte": "^3.52.0", - "svelte-check": "^2.9.2", - "typescript": "^4.8.4", - "vite": "^3.2.1" - }, - "type": "module" -} diff --git a/packages/kit/test/apps/dev-only/playwright.config.js b/packages/kit/test/apps/dev-only/playwright.config.js deleted file mode 100644 index 33d36b651014..000000000000 --- a/packages/kit/test/apps/dev-only/playwright.config.js +++ /dev/null @@ -1 +0,0 @@ -export { config as default } from '../../utils.js'; diff --git a/packages/kit/test/apps/dev-only/src/app.html b/packages/kit/test/apps/dev-only/src/app.html deleted file mode 100644 index bbc550f6a1b8..000000000000 --- a/packages/kit/test/apps/dev-only/src/app.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - %sveltekit.head% - - - %sveltekit.body% - - diff --git a/packages/kit/test/apps/dev-only/src/hooks.server.js b/packages/kit/test/apps/dev-only/src/hooks.server.js deleted file mode 100644 index b9bd53a8148a..000000000000 --- a/packages/kit/test/apps/dev-only/src/hooks.server.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import("@sveltejs/kit").HandleServerError} */ -export function handleError({ error }) { - return { message: /**@type{any}*/ (error).message }; -} diff --git a/packages/kit/test/apps/dev-only/src/lib/test.server.js b/packages/kit/test/apps/dev-only/src/lib/test.server.js deleted file mode 100644 index 770268db581d..000000000000 --- a/packages/kit/test/apps/dev-only/src/lib/test.server.js +++ /dev/null @@ -1 +0,0 @@ -export const should_explode = 'boom'; diff --git a/packages/kit/test/apps/dev-only/src/routes/+error.svelte b/packages/kit/test/apps/dev-only/src/routes/+error.svelte deleted file mode 100644 index 830415220588..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/+error.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -
{$page.error.message}
diff --git a/packages/kit/test/apps/dev-only/src/routes/+layout.svelte b/packages/kit/test/apps/dev-only/src/routes/+layout.svelte deleted file mode 100644 index 5e1f1fed86c2..000000000000 --- a/packages/kit/test/apps/dev-only/src/routes/+layout.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/packages/kit/test/apps/dev-only/static/favicon.png b/packages/kit/test/apps/dev-only/static/favicon.png deleted file mode 100644 index 825b9e65af7c104cfb07089bb28659393b4f2097..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH { - test('$env/dynamic/private is not statically importable from the client', async ({ request }) => { - const resp = await request.get('/env/dynamic-private'); - expect(await resp.text()).toMatch( - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs - ); - }); - - test('$env/dynamic/private is not dynamically importable from the client', async ({ - request - }) => { - const resp = await request.get('/env/dynamic-private-dynamic-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs - ); - }); - - test('$env/static/private is not statically importable from the client', async ({ request }) => { - const resp = await request.get('/env/static-private'); - expect(await resp.text()).toMatch( - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs - ); - }); - - test('$env/static/private is not dynamically importable from the client', async ({ request }) => { - const resp = await request.get('/env/static-private-dynamic-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs - ); - }); -}); - -test.describe('server-only modules', () => { - test('server-only module is not statically importable from the client', async ({ request }) => { - const resp = await request.get('/server-only-modules/static-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs - ); - }); - test('server-only module is not dynamically importable from the client', async ({ request }) => { - const resp = await request.get('/server-only-modules/dynamic-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs - ); - }); -}); - -test.describe('server-only folder', () => { - test('server-only folder is not statically importable from the client', async ({ request }) => { - const resp = await request.get('/server-only-folder/static-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs - ); - }); - test('server-only folder is not dynamically importable from the client', async ({ request }) => { - const resp = await request.get('/server-only-folder/dynamic-import'); - expect(await resp.text()).toMatch( - /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs - ); - }); -}); diff --git a/packages/kit/test/apps/dev-only/tsconfig.json b/packages/kit/test/apps/dev-only/tsconfig.json deleted file mode 100644 index a1e1e2da2142..000000000000 --- a/packages/kit/test/apps/dev-only/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "noEmit": true, - "paths": { - "@sveltejs/kit": ["../../../types"], - "$lib": ["./src/lib"], - "$lib/*": ["./src/lib/*"], - "types": ["../../../types/internal"] - }, - "resolveJsonModule": true - }, - "extends": "./.svelte-kit/tsconfig.json" -} diff --git a/packages/kit/test/apps/dev-only/vite.config.js b/packages/kit/test/apps/dev-only/vite.config.js deleted file mode 100644 index 1938615ca91c..000000000000 --- a/packages/kit/test/apps/dev-only/vite.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import * as path from 'path'; -import { sveltekit } from '@sveltejs/kit/vite'; - -/** @type {import('vite').UserConfig} */ -const config = { - build: { - minify: false - }, - clearScreen: false, - optimizeDeps: { - // for CI, we need to explicitly prebundle deps, since - // the reload confuses Playwright - include: ['cookie', 'marked'] - }, - plugins: [sveltekit()], - server: { - fs: { - allow: [path.resolve('../../../src')] - } - } -}; - -export default config; From dde14a101396d2c20b00a07fd3a7339b4930de88 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 Nov 2022 17:44:03 -0500 Subject: [PATCH 09/30] fix tests --- packages/kit/test/apps/basics/src/app.html | 5 +-- .../dynamic-import/+page.svelte | 2 +- .../static-import/+page.svelte | 2 +- .../server-only-modules/test.server.js | 1 + .../kit/test/apps/basics/test/client.test.js | 32 +++++++++---------- pnpm-lock.yaml | 18 ----------- 6 files changed, 20 insertions(+), 40 deletions(-) create mode 100644 packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js diff --git a/packages/kit/test/apps/basics/src/app.html b/packages/kit/test/apps/basics/src/app.html index bbc550f6a1b8..0d5ade6e2f3e 100644 --- a/packages/kit/test/apps/basics/src/app.html +++ b/packages/kit/test/apps/basics/src/app.html @@ -5,12 +5,9 @@ - - %sveltekit.head% - %sveltekit.body% +
%sveltekit.body%
diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte index ea20469dc4b2..05c167ac1d61 100644 --- a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte @@ -1,5 +1,5 @@ {#await mod then resolved} diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte index e0e7bff39ae1..d7eff254925a 100644 --- a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte @@ -1,5 +1,5 @@

{should_explode}

diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js new file mode 100644 index 000000000000..770268db581d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js @@ -0,0 +1 @@ +export const should_explode = 'boom'; diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index b5739be603ec..fd62c6e2fa5c 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -993,55 +993,55 @@ test.describe.only('Illegal imports', () => { test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/dynamic-private'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/dynamic/private into client-side code' ); }); test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/dynamic-private-dynamic-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/dynamic/private into client-side code' ); }); test('$env/static/private is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/static-private'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/static/private into client-side code' ); }); test('$env/static/private is not dynamically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/static-private-dynamic-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/static/private into client-side code' ); }); test('server-only module is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/static-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/test.server.js into client-side code' ); }); test('server-only module is not dynamically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/dynamic-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/test.server.js into client-side code' ); }); test('server-only folder is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-folder/static-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/server/blah/test.js into client-side code' ); }); test('server-only folder is not dynamically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-folder/dynamic-import'); - expect(await page.innerHTML('body')).toMatch( - /.*Cannot import \$lib\/server\/blah\/test.js into public-facing code:.*/gs + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/server/blah/test.js into client-side code' ); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 925598f66fa5..ca76908eb516 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -358,24 +358,6 @@ importers: typescript: 4.8.4 vite: 3.2.1 - packages/kit/test/apps/dev-only: - specifiers: - '@sveltejs/kit': workspace:* - cross-env: ^7.0.3 - rimraf: ^3.0.2 - svelte: ^3.52.0 - svelte-check: ^2.9.2 - typescript: ^4.8.4 - vite: ^3.2.1 - devDependencies: - '@sveltejs/kit': link:../../.. - cross-env: 7.0.3 - rimraf: 3.0.2 - svelte: 3.52.0 - svelte-check: 2.9.2_svelte@3.52.0 - typescript: 4.8.4 - vite: 3.2.1 - packages/kit/test/apps/options: specifiers: '@sveltejs/kit': workspace:* From 9737b044d12bfda4eeff89d2867a4df37f3d9428 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 Nov 2022 17:46:08 -0500 Subject: [PATCH 10/30] remove only --- packages/kit/test/apps/basics/test/client.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index fd62c6e2fa5c..75bf26fd6ea1 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -988,7 +988,7 @@ test.describe('cookies', () => { }); }); -test.describe.only('Illegal imports', () => { +test.describe('Illegal imports', () => { test.skip(() => !process.env.DEV); test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { From bca015d467f6a239a32285d9e527ca0f7f3fe25d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 7 Nov 2022 17:54:20 -0500 Subject: [PATCH 11/30] fix --- .../server-only-modules/dynamic-import/+page.svelte | 2 +- .../{test.server.js => illegal.server.js} | 0 .../server-only-modules/static-import/+page.svelte | 2 +- packages/kit/test/apps/basics/test/client.test.js | 6 +++--- 4 files changed, 5 insertions(+), 5 deletions(-) rename packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/{test.server.js => illegal.server.js} (100%) diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte index 05c167ac1d61..5e763368e26f 100644 --- a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte @@ -1,5 +1,5 @@ {#await mod then resolved} diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/illegal.server.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/test.server.js rename to packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/illegal.server.js diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte index d7eff254925a..43bb66e92f6d 100644 --- a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte @@ -1,5 +1,5 @@

{should_explode}

diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 75bf26fd6ea1..34f6688fba4c 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -988,7 +988,7 @@ test.describe('cookies', () => { }); }); -test.describe('Illegal imports', () => { +test.describe.serial('Illegal imports', () => { test.skip(() => !process.env.DEV); test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { @@ -1022,13 +1022,13 @@ test.describe('Illegal imports', () => { test('server-only module is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/static-import'); expect(await page.textContent('.message-body')).toBe( - 'Cannot import $lib/test.server.js into client-side code' + 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' ); }); test('server-only module is not dynamically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/server-only-modules/dynamic-import'); expect(await page.textContent('.message-body')).toBe( - 'Cannot import $lib/test.server.js into client-side code' + 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' ); }); From ca9c8eb29bca12a30f2abad03a07bf477a2b176c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 14:13:49 -0500 Subject: [PATCH 12/30] remove unnecessary default parameter --- packages/kit/src/exports/vite/graph_analysis/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 2c4f707ebfa3..375520039838 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -21,7 +21,7 @@ export function module_guard(context, lib) { * @param {string} id * @param {Array<{ id: string, dynamic: boolean }>} chain */ - function follow(id, chain = []) { + function follow(id, chain) { if (safe.has(id)) return; if ( From caa38c43e1f474a0afc092bb4a069f2df1559632 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 14:28:02 -0500 Subject: [PATCH 13/30] move path normalization out of graph_analysis --- .../kit/src/exports/vite/graph_analysis/index.js | 15 ++++++--------- packages/kit/src/exports/vite/index.js | 5 ++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 375520039838..cb4e35165a6e 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,21 +1,18 @@ import path from 'path'; -import { normalizePath } from 'vite'; -const cwd = normalizePath(process.cwd()); -const node_modules = normalizePath(path.resolve(process.cwd(), 'node_modules')); const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/private']); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; /** * @param {import('rollup').PluginContext} context - * @param {string} lib + * @param {{ cwd: string, lib: string }} paths */ -export function module_guard(context, lib) { +export function module_guard(context, { cwd, lib }) { /** @type {Set} */ const safe = new Set(); - const lib_dir = normalizePath(lib); - const server_dir = normalizePath(path.resolve(lib, 'server')); + const node_modules = path.join(cwd, 'node_modules'); + const server_dir = path.join(lib, 'server'); /** * @param {string} id @@ -33,12 +30,12 @@ export function module_guard(context, lib) { ) { chain.shift(); // discard the entry point - if (id.startsWith(lib_dir)) id = id.replace(lib_dir, '$lib'); + if (id.startsWith(lib)) id = id.replace(lib, '$lib'); if (id.startsWith(cwd)) id = path.relative(cwd, id); const pyramid = chain.map(({ id, dynamic }, i) => { - if (id.startsWith(lib_dir)) id = id.replace(lib_dir, '$lib'); + if (id.startsWith(lib)) id = id.replace(lib, '$lib'); if (id.startsWith(cwd)) id = path.relative(cwd, id); return `${repeat(' ', i * 2)}- ${id} ${dynamic ? 'dynamically imports' : 'imports'}\n`; diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 87f95f23dec0..27ebda30b336 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -362,7 +362,10 @@ function kit() { return; } - const guard = module_guard(this, vite.normalizePath(svelte_config.kit.files.lib)); + const guard = module_guard(this, { + cwd: vite.normalizePath(process.cwd()), + lib: vite.normalizePath(svelte_config.kit.files.lib) + }); manifest_data.nodes.forEach((_node, i) => { const id = vite.normalizePath( From 72989f4735dcbcbf54f3b092bce2e7c0c34b7611 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 15:07:31 -0500 Subject: [PATCH 14/30] reinstate tests --- .../exports/vite/graph_analysis/index.spec.js | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 packages/kit/src/exports/vite/graph_analysis/index.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 new file mode 100644 index 000000000000..740d2bda2fa6 --- /dev/null +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -0,0 +1,162 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { module_guard } from './index.js'; + +/** + * + * @param {Record} graph + * @param {string} [expected_error] + */ +function check(graph, expected_error) { + // @ts-expect-error + const context = /** @type {import('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) { + 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: ['\0$env/static/private'] + } + }, + `Cannot import \0$env/static/private into public-facing code: + - src/routes/+page.svelte imports + - \0$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: ['\0$env/static/private'] + } + }, + `Cannot import \0$env/static/private into public-facing code: + - src/routes/+page.svelte dynamically imports + - \0$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: ['\0$env/dynamic/private'] + } + }, + `Cannot import \0$env/dynamic/private into public-facing code: + - src/routes/+page.svelte imports + - \0$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: ['\0$env/dynamic/private'] + } + }, + `Cannot import \0$env/dynamic/private into public-facing code: + - src/routes/+page.svelte dynamically imports + - \0$env/dynamic/private` + ); +}); + +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 public-facing 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 public-facing 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': {} + }); +}); + +test.run(); From 6776dd90f980a42f4d8fee057e3a7f1083193d97 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 15:09:39 -0500 Subject: [PATCH 15/30] ugh --- packages/kit/src/exports/vite/graph_analysis/index.spec.js | 1 + 1 file changed, 1 insertion(+) 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 740d2bda2fa6..29bd6e163d76 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -30,6 +30,7 @@ function check(graph, expected_error) { 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 { From 72bc5263fd38f6769307169d9478972f3795b042 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 15:52:15 -0500 Subject: [PATCH 16/30] doh, we need the dev-only test app --- .../src/exports/vite/graph_analysis/index.js | 42 +++++++---- packages/kit/src/exports/vite/index.js | 17 +++-- packages/kit/src/runtime/client/client.js | 2 + .../env/static-private/+page.svelte | 5 -- .../kit/test/apps/basics/test/client.test.js | 58 --------------- packages/kit/test/apps/dev-only/.env | 1 + packages/kit/test/apps/dev-only/.gitignore | 2 + packages/kit/test/apps/dev-only/package.json | 22 ++++++ .../test/apps/dev-only/playwright.config.js | 1 + packages/kit/test/apps/dev-only/src/app.html | 16 +++++ .../test/apps/dev-only/src/hooks.server.js | 4 ++ .../src/lib/server/blah/test.js} | 0 .../test/apps/dev-only/src/lib/test.server.js | 1 + .../apps/dev-only/src/routes/+error.svelte | 5 ++ .../apps/dev-only/src/routes/+layout.svelte | 7 ++ .../+page.svelte | 0 .../env/dynamic-private/+page.svelte | 0 .../+page.svelte | 0 .../env/static-private/+page.svelte | 5 ++ .../dynamic-import/+page.svelte | 0 .../static-import/+page.svelte | 0 .../dynamic-import/+page.svelte | 0 .../server-only-modules/illegal.server.js | 1 + .../static-import/+page.svelte | 0 .../kit/test/apps/dev-only/static/favicon.png | Bin 0 -> 1571 bytes .../kit/test/apps/dev-only/svelte.config.js | 4 ++ packages/kit/test/apps/dev-only/test/test.js | 68 ++++++++++++++++++ packages/kit/test/apps/dev-only/tsconfig.json | 16 +++++ .../kit/test/apps/dev-only/vite.config.js | 23 ++++++ pnpm-lock.yaml | 18 +++++ 30 files changed, 235 insertions(+), 83 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte create mode 100644 packages/kit/test/apps/dev-only/.env create mode 100644 packages/kit/test/apps/dev-only/.gitignore create mode 100644 packages/kit/test/apps/dev-only/package.json create mode 100644 packages/kit/test/apps/dev-only/playwright.config.js create mode 100644 packages/kit/test/apps/dev-only/src/app.html create mode 100644 packages/kit/test/apps/dev-only/src/hooks.server.js rename packages/kit/test/apps/{basics/src/routes/illegal-imports/server-only-modules/illegal.server.js => dev-only/src/lib/server/blah/test.js} (100%) create mode 100644 packages/kit/test/apps/dev-only/src/lib/test.server.js create mode 100644 packages/kit/test/apps/dev-only/src/routes/+error.svelte create mode 100644 packages/kit/test/apps/dev-only/src/routes/+layout.svelte rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/env/dynamic-private/+page.svelte (100%) rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte (100%) create mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte (100%) rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte (100%) rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte (100%) create mode 100644 packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/illegal.server.js rename packages/kit/test/apps/{basics => dev-only}/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte (100%) create mode 100644 packages/kit/test/apps/dev-only/static/favicon.png create mode 100644 packages/kit/test/apps/dev-only/svelte.config.js create mode 100644 packages/kit/test/apps/dev-only/test/test.js create mode 100644 packages/kit/test/apps/dev-only/tsconfig.json create mode 100644 packages/kit/test/apps/dev-only/vite.config.js diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index cb4e35165a6e..d32b56ff11fc 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -3,31 +3,47 @@ import path from 'path'; const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/private']); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; +/** + * @param {string} id + * @param {{ + * cwd: string; + * node_modules: string; + * server: string; + * }} dirs + */ +export function is_illegal(id, dirs) { + if (!id.startsWith(dirs.cwd) || id.startsWith(dirs.node_modules)) return false; + + return ( + ILLEGAL_IMPORTS.has(id) || + ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) || + id.startsWith(dirs.server) + ); +} + /** * @param {import('rollup').PluginContext} context * @param {{ cwd: string, lib: string }} paths */ export function module_guard(context, { cwd, lib }) { /** @type {Set} */ - const safe = new Set(); + const seen = new Set(); - const node_modules = path.join(cwd, 'node_modules'); - const server_dir = path.join(lib, 'server'); + const dirs = { + cwd, + node_modules: path.join(cwd, 'node_modules'), + server: path.join(lib, 'server') + }; /** * @param {string} id * @param {Array<{ id: string, dynamic: boolean }>} chain */ function follow(id, chain) { - if (safe.has(id)) return; - - if ( - ILLEGAL_IMPORTS.has(id) || - id.startsWith(server_dir) || - (ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) && - id.startsWith(cwd) && - !id.startsWith(node_modules)) - ) { + if (seen.has(id)) return; + seen.add(id); + + if (is_illegal(id, dirs)) { chain.shift(); // discard the entry point if (id.startsWith(lib)) id = id.replace(lib, '$lib'); @@ -57,8 +73,6 @@ export function module_guard(context, { cwd, lib }) { follow(child, [...chain, { id, dynamic: true }]); } } - - safe.add(id); } return { diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 27ebda30b336..dd54cc307ab8 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -17,7 +17,7 @@ import { preview } from './preview/index.js'; import { get_config_aliases, get_app_aliases, get_env } from './utils.js'; import { fileURLToPath } from 'node:url'; import { create_static_module, create_dynamic_module } from '../../core/env.js'; -import { module_guard } from './graph_analysis/index.js'; +import { is_illegal, module_guard } from './graph_analysis/index.js'; const cwd = process.cwd(); @@ -294,13 +294,18 @@ function kit() { async load(id, options) { if (options?.ssr === false) { - // TODO this is incomplete — we need to replicate the #is_illegal logic if ( - id.endsWith('.server.js') || - id === '\0$env/static/private' || - id === '\0$env/dynamic/private' + is_illegal(id, { + cwd: vite.normalizePath(cwd), + node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), + server: vite.normalizePath(path.join(svelte_config.kit.files.lib, 'server')) + }) ) { - const relative = id.startsWith(process.cwd()) ? path.relative('.', id) : id; + const relative = id.startsWith(svelte_config.kit.files.lib) + ? id.replace(svelte_config.kit.files.lib, '$lib') + : id.startsWith(cwd) + ? path.relative('.', id) + : id; throw new Error(`Cannot import ${relative} into client-side code`); } } diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 0e7f5bb51cd1..73f0aed7f434 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -373,6 +373,8 @@ export function create_client({ target, base, trailing_slash }) { /** @param {import('./types').NavigationFinished} result */ function initialize(result) { + if (__SVELTEKIT_DEV__ && document.querySelector('vite-error-overlay')) return; + current = result.state; const style = document.querySelector('style[data-sveltekit]'); diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte b/packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte deleted file mode 100644 index 72546ac594c3..000000000000 --- a/packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private/+page.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

{SHOULD_EXPLODE}

diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 6dcca642bd5b..b0a5b5f5f2d0 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -999,61 +999,3 @@ test.describe('cookies', () => { await expect(page.locator('p')).toHaveText('foo=bar'); }); }); - -test.describe.serial('Illegal imports', () => { - test.skip(() => !process.env.DEV); - - test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/dynamic-private'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import \0$env/dynamic/private into client-side code' - ); - }); - - test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/dynamic-private-dynamic-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import \0$env/dynamic/private into client-side code' - ); - }); - - test('$env/static/private is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/static-private'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import \0$env/static/private into client-side code' - ); - }); - - test('$env/static/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/static-private-dynamic-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import \0$env/static/private into client-side code' - ); - }); - - test('server-only module is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-modules/static-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' - ); - }); - test('server-only module is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-modules/dynamic-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' - ); - }); - - test('server-only folder is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-folder/static-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import $lib/server/blah/test.js into client-side code' - ); - }); - test('server-only folder is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-folder/dynamic-import'); - expect(await page.textContent('.message-body')).toBe( - 'Cannot import $lib/server/blah/test.js into client-side code' - ); - }); -}); diff --git a/packages/kit/test/apps/dev-only/.env b/packages/kit/test/apps/dev-only/.env new file mode 100644 index 000000000000..752bad1e78a0 --- /dev/null +++ b/packages/kit/test/apps/dev-only/.env @@ -0,0 +1 @@ +SHOULD_EXPLODE=boom \ No newline at end of file diff --git a/packages/kit/test/apps/dev-only/.gitignore b/packages/kit/test/apps/dev-only/.gitignore new file mode 100644 index 000000000000..fa2f5a579236 --- /dev/null +++ b/packages/kit/test/apps/dev-only/.gitignore @@ -0,0 +1,2 @@ +/test/errors.json +!/.env diff --git a/packages/kit/test/apps/dev-only/package.json b/packages/kit/test/apps/dev-only/package.json new file mode 100644 index 000000000000..b45841ba2a68 --- /dev/null +++ b/packages/kit/test/apps/dev-only/package.json @@ -0,0 +1,22 @@ +{ + "name": "test-basics", + "private": true, + "version": "0.0.2-next.0", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && tsc && svelte-check", + "test": "cross-env DEV=true playwright test" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:*", + "cross-env": "^7.0.3", + "rimraf": "^3.0.2", + "svelte": "^3.52.0", + "svelte-check": "^2.9.2", + "typescript": "^4.8.4", + "vite": "^3.2.1" + }, + "type": "module" +} diff --git a/packages/kit/test/apps/dev-only/playwright.config.js b/packages/kit/test/apps/dev-only/playwright.config.js new file mode 100644 index 000000000000..33d36b651014 --- /dev/null +++ b/packages/kit/test/apps/dev-only/playwright.config.js @@ -0,0 +1 @@ +export { config as default } from '../../utils.js'; diff --git a/packages/kit/test/apps/dev-only/src/app.html b/packages/kit/test/apps/dev-only/src/app.html new file mode 100644 index 000000000000..653bf20d1133 --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/app.html @@ -0,0 +1,16 @@ + + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/apps/dev-only/src/hooks.server.js b/packages/kit/test/apps/dev-only/src/hooks.server.js new file mode 100644 index 000000000000..b9bd53a8148a --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/hooks.server.js @@ -0,0 +1,4 @@ +/** @type {import("@sveltejs/kit").HandleServerError} */ +export function handleError({ error }) { + return { message: /**@type{any}*/ (error).message }; +} diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/illegal.server.js b/packages/kit/test/apps/dev-only/src/lib/server/blah/test.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/illegal.server.js rename to packages/kit/test/apps/dev-only/src/lib/server/blah/test.js diff --git a/packages/kit/test/apps/dev-only/src/lib/test.server.js b/packages/kit/test/apps/dev-only/src/lib/test.server.js new file mode 100644 index 000000000000..770268db581d --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/lib/test.server.js @@ -0,0 +1 @@ +export const should_explode = 'boom'; diff --git a/packages/kit/test/apps/dev-only/src/routes/+error.svelte b/packages/kit/test/apps/dev-only/src/routes/+error.svelte new file mode 100644 index 000000000000..830415220588 --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/+error.svelte @@ -0,0 +1,5 @@ + + +
{$page.error.message}
diff --git a/packages/kit/test/apps/dev-only/src/routes/+layout.svelte b/packages/kit/test/apps/dev-only/src/routes/+layout.svelte new file mode 100644 index 000000000000..5e1f1fed86c2 --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/+layout.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/kit/test/apps/basics/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 similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private-dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/env/dynamic-private/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/dynamic-private/+page.svelte diff --git a/packages/kit/test/apps/basics/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 similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private-dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte new file mode 100644 index 000000000000..e22f1bd4b71d --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte @@ -0,0 +1,5 @@ + + +

{PRIVATE_STATIC}

diff --git a/packages/kit/test/apps/basics/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 similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-folder/static-import/+page.svelte diff --git a/packages/kit/test/apps/basics/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 similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/dynamic-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/illegal.server.js b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/illegal.server.js new file mode 100644 index 000000000000..770268db581d --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/illegal.server.js @@ -0,0 +1 @@ +export const should_explode = 'boom'; diff --git a/packages/kit/test/apps/basics/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 similarity index 100% rename from packages/kit/test/apps/basics/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte rename to packages/kit/test/apps/dev-only/src/routes/illegal-imports/server-only-modules/static-import/+page.svelte diff --git a/packages/kit/test/apps/dev-only/static/favicon.png b/packages/kit/test/apps/dev-only/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH { + test.skip(() => !process.env.DEV); + + test.only('$env/dynamic/private is not statically importable from the client', async ({ + page + }) => { + await page.goto('/illegal-imports/env/dynamic-private'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/dynamic/private into client-side code' + ); + }); + + test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/dynamic-private-dynamic-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/dynamic/private into client-side code' + ); + }); + + test('$env/static/private is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/static-private'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/static/private into client-side code' + ); + }); + + test('$env/static/private is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/env/static-private-dynamic-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import \0$env/static/private into client-side code' + ); + }); + + test('server-only module is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-modules/static-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' + ); + }); + + test('server-only module is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-modules/dynamic-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import src/routes/illegal-imports/server-only-modules/illegal.server.js into client-side code' + ); + }); + + test('server-only folder is not statically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-folder/static-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/server/blah/test.js into client-side code' + ); + }); + + test('server-only folder is not dynamically importable from the client', async ({ page }) => { + await page.goto('/illegal-imports/server-only-folder/dynamic-import'); + expect(await page.textContent('.message-body')).toBe( + 'Cannot import $lib/server/blah/test.js into client-side code' + ); + }); +}); diff --git a/packages/kit/test/apps/dev-only/tsconfig.json b/packages/kit/test/apps/dev-only/tsconfig.json new file mode 100644 index 000000000000..a1e1e2da2142 --- /dev/null +++ b/packages/kit/test/apps/dev-only/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "noEmit": true, + "paths": { + "@sveltejs/kit": ["../../../types"], + "$lib": ["./src/lib"], + "$lib/*": ["./src/lib/*"], + "types": ["../../../types/internal"] + }, + "resolveJsonModule": true + }, + "extends": "./.svelte-kit/tsconfig.json" +} diff --git a/packages/kit/test/apps/dev-only/vite.config.js b/packages/kit/test/apps/dev-only/vite.config.js new file mode 100644 index 000000000000..1938615ca91c --- /dev/null +++ b/packages/kit/test/apps/dev-only/vite.config.js @@ -0,0 +1,23 @@ +import * as path from 'path'; +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + build: { + minify: false + }, + clearScreen: false, + optimizeDeps: { + // for CI, we need to explicitly prebundle deps, since + // the reload confuses Playwright + include: ['cookie', 'marked'] + }, + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../src')] + } + } +}; + +export default config; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca76908eb516..925598f66fa5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -358,6 +358,24 @@ importers: typescript: 4.8.4 vite: 3.2.1 + packages/kit/test/apps/dev-only: + specifiers: + '@sveltejs/kit': workspace:* + cross-env: ^7.0.3 + rimraf: ^3.0.2 + svelte: ^3.52.0 + svelte-check: ^2.9.2 + typescript: ^4.8.4 + vite: ^3.2.1 + devDependencies: + '@sveltejs/kit': link:../../.. + cross-env: 7.0.3 + rimraf: 3.0.2 + svelte: 3.52.0 + svelte-check: 2.9.2_svelte@3.52.0 + typescript: 4.8.4 + vite: 3.2.1 + packages/kit/test/apps/options: specifiers: '@sveltejs/kit': workspace:* From 96b7a376a772dad843577428a65d4ca644d906ec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 15:54:07 -0500 Subject: [PATCH 17/30] no longer necessary --- packages/kit/test/apps/basics/src/app.html | 2 +- packages/kit/test/apps/basics/src/lib/server/blah/test.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/lib/server/blah/test.js diff --git a/packages/kit/test/apps/basics/src/app.html b/packages/kit/test/apps/basics/src/app.html index 0d5ade6e2f3e..dd9910661cc3 100644 --- a/packages/kit/test/apps/basics/src/app.html +++ b/packages/kit/test/apps/basics/src/app.html @@ -8,6 +8,6 @@ %sveltekit.head% -
%sveltekit.body%
+ %sveltekit.body% diff --git a/packages/kit/test/apps/basics/src/lib/server/blah/test.js b/packages/kit/test/apps/basics/src/lib/server/blah/test.js deleted file mode 100644 index 770268db581d..000000000000 --- a/packages/kit/test/apps/basics/src/lib/server/blah/test.js +++ /dev/null @@ -1 +0,0 @@ -export const should_explode = 'boom'; From 86400f7719d645b70ad45e86b4c44b723fc7a1f4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 15:57:29 -0500 Subject: [PATCH 18/30] fix --- .../routes/illegal-imports/env/static-private/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte index e22f1bd4b71d..72546ac594c3 100644 --- a/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte +++ b/packages/kit/test/apps/dev-only/src/routes/illegal-imports/env/static-private/+page.svelte @@ -1,5 +1,5 @@ -

{PRIVATE_STATIC}

+

{SHOULD_EXPLODE}

From 6d10cefeb337c7c3b74efeab9657c49286061b46 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 16:19:48 -0500 Subject: [PATCH 19/30] fixes --- packages/kit/src/exports/vite/graph_analysis/index.js | 8 ++------ packages/kit/test/apps/dev-only/src/app.html | 5 +---- packages/kit/test/apps/dev-only/svelte.config.js | 4 +++- packages/kit/test/apps/dev-only/test/test.js | 10 +++------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index d32b56ff11fc..877110344582 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -12,13 +12,9 @@ const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; * }} 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_IMPORTS.has(id) || - ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) || - id.startsWith(dirs.server) - ); + return ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(id)) || id.startsWith(dirs.server); } /** diff --git a/packages/kit/test/apps/dev-only/src/app.html b/packages/kit/test/apps/dev-only/src/app.html index 653bf20d1133..d169577acb70 100644 --- a/packages/kit/test/apps/dev-only/src/app.html +++ b/packages/kit/test/apps/dev-only/src/app.html @@ -5,12 +5,9 @@ - - %sveltekit.head% - +
%sveltekit.body%
diff --git a/packages/kit/test/apps/dev-only/svelte.config.js b/packages/kit/test/apps/dev-only/svelte.config.js index bded48544036..821c14379ec8 100644 --- a/packages/kit/test/apps/dev-only/svelte.config.js +++ b/packages/kit/test/apps/dev-only/svelte.config.js @@ -1,4 +1,6 @@ /** @type {import('@sveltejs/kit').Config} */ -const config = {}; +const config = { + kit: {} +}; export default config; diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 69fc9f86094d..556a53584295 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -3,14 +3,10 @@ import { test } from '../../../utils.js'; /** @typedef {import('@playwright/test').Response} Response */ -test.describe.configure({ mode: 'parallel' }); +test.describe.serial('Illegal imports', () => { + test.skip(({ javaScriptEnabled }) => !process.env.DEV || !javaScriptEnabled); -test.describe.serial.only('Illegal imports', () => { - test.skip(() => !process.env.DEV); - - test.only('$env/dynamic/private is not statically importable from the client', async ({ - page - }) => { + test('$env/dynamic/private is not statically importable from the client', async ({ page }) => { await page.goto('/illegal-imports/env/dynamic-private'); expect(await page.textContent('.message-body')).toBe( 'Cannot import \0$env/dynamic/private into client-side code' From d18047d9e5977d9a7dfa25b077eefcb4712f8225 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 16:25:18 -0500 Subject: [PATCH 20/30] slow clap for windows --- packages/kit/src/exports/vite/graph_analysis/index.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 29bd6e163d76..7f73ab17598e 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -31,7 +31,7 @@ function check(graph, expected_error) { throw new Error('Expected an error'); } catch (e) { // @ts-expect-error - assert.equal(e.message, expected_error.replace(/^\t+/gm, '')); + assert.equal(e.message, expected_error.replace(/^\t+/gm, '').replace(/\//g, path.sep)); } } else { guard.check('~/src/entry'); From 0c6cd04e538cd9ec714aae03648cef353e87dea8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 8 Nov 2022 16:27:32 -0500 Subject: [PATCH 21/30] ah come on --- packages/kit/src/exports/vite/graph_analysis/index.spec.js | 1 + 1 file changed, 1 insertion(+) 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 7f73ab17598e..debb2132cfe4 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -1,3 +1,4 @@ +import path from 'path'; import { test } from 'uvu'; import * as assert from 'uvu/assert'; import { module_guard } from './index.js'; From 7d83036365356dbcbc53a014eb8eba7fd05c9d2b Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 9 Nov 2022 10:36:11 +0100 Subject: [PATCH 22/30] get tests passing + windows path shenanigans --- packages/kit/src/exports/vite/index.js | 14 +++++---- packages/kit/test/apps/dev-only/test/test.js | 32 +++++++++++++++----- packages/kit/test/utils.d.ts | 6 ++++ packages/kit/test/utils.js | 2 +- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index dd54cc307ab8..79e14c83044a 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -294,17 +294,19 @@ function kit() { async load(id, options) { if (options?.ssr === false) { + const normalized_cwd = vite.normalizePath(cwd); + const normalized_lib = vite.normalizePath(svelte_config.kit.files.lib); if ( is_illegal(id, { - cwd: vite.normalizePath(cwd), + cwd: normalized_cwd, node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), - server: vite.normalizePath(path.join(svelte_config.kit.files.lib, 'server')) + server: vite.normalizePath(path.join(normalized_lib, 'server')) }) ) { - const relative = id.startsWith(svelte_config.kit.files.lib) - ? id.replace(svelte_config.kit.files.lib, '$lib') - : id.startsWith(cwd) - ? path.relative('.', id) + const relative = id.startsWith(normalized_lib) + ? id.replace(normalized_lib, '$lib') + : id.startsWith(normalized_cwd) + ? vite.normalizePath(path.relative('.', id)) : id; throw new Error(`Cannot import ${relative} into client-side code`); } diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 556a53584295..cc8f615f7e2c 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -7,56 +7,72 @@ 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 }) => { - await page.goto('/illegal-imports/env/dynamic-private'); + await page.goto('/illegal-imports/env/dynamic-private', { + wait_for_started: false + }); expect(await page.textContent('.message-body')).toBe( 'Cannot import \0$env/dynamic/private into client-side code' ); }); test('$env/dynamic/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/dynamic-private-dynamic-import'); + await page.goto('/illegal-imports/env/dynamic-private-dynamic-import', { + wait_for_started: false + }); expect(await page.textContent('.message-body')).toBe( 'Cannot import \0$env/dynamic/private into client-side code' ); }); test('$env/static/private is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/static-private'); + await page.goto('/illegal-imports/env/static-private', { + wait_for_started: false + }); expect(await page.textContent('.message-body')).toBe( 'Cannot import \0$env/static/private into client-side code' ); }); test('$env/static/private is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/env/static-private-dynamic-import'); + await page.goto('/illegal-imports/env/static-private-dynamic-import', { + wait_for_started: false + }); expect(await page.textContent('.message-body')).toBe( 'Cannot import \0$env/static/private into client-side code' ); }); test('server-only module is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-modules/static-import'); + 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' ); }); test('server-only module is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-modules/dynamic-import'); + 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' ); }); test('server-only folder is not statically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-folder/static-import'); + 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/test.js into client-side code' ); }); test('server-only folder is not dynamically importable from the client', async ({ page }) => { - await page.goto('/illegal-imports/server-only-folder/dynamic-import'); + 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/test.js into client-side code' ); diff --git a/packages/kit/test/utils.d.ts b/packages/kit/test/utils.d.ts index a1958487e261..31d83058f9e4 100644 --- a/packages/kit/test/utils.d.ts +++ b/packages/kit/test/utils.d.ts @@ -26,6 +26,12 @@ export const test: TestType< * `handleError` defines the shape */ read_errors(href: string): Record; + page: PlaywrightTestArgs['page'] & { + goto: ( + url: string, + opts?: Parameters[1] & { wait_for_started?: boolean } + ) => ReturnType; + }; }, PlaywrightWorkerArgs & PlaywrightWorkerOptions >; diff --git a/packages/kit/test/utils.js b/packages/kit/test/utils.js index a60ec8e56e5d..4bfc9ebdc94e 100644 --- a/packages/kit/test/utils.js +++ b/packages/kit/test/utils.js @@ -90,7 +90,7 @@ export const test = base.extend({ // @ts-expect-error page[fn] = async function (...args) { const res = await page_fn.call(page, ...args); - if (javaScriptEnabled) { + if (javaScriptEnabled && args[1]?.wait_for_started !== false) { await page.waitForSelector('body.started', { timeout: 5000 }); } return res; From b3982437f7199de18640b8ffc06f0fffcc2a16af Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 9 Nov 2022 11:18:50 +0100 Subject: [PATCH 23/30] fix build tests --- packages/kit/test/build-errors/env.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/kit/test/build-errors/env.spec.js b/packages/kit/test/build-errors/env.spec.js index 1dba0a958ec4..1480c06c0c19 100644 --- a/packages/kit/test/build-errors/env.spec.js +++ b/packages/kit/test/build-errors/env.spec.js @@ -11,7 +11,7 @@ test('$env/dynamic/private is not statically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/dynamic\/private into public-facing code:.*/gs ); }); @@ -23,7 +23,7 @@ test('$env/dynamic/private is not dynamically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$env\/dynamic\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/dynamic\/private into public-facing code:.*/gs ); }); @@ -35,7 +35,7 @@ test('$env/static/private is not statically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/static\/private into public-facing code:.*/gs ); }); @@ -47,7 +47,7 @@ test('$env/static/private is not dynamically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$env\/static\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/static\/private into public-facing code:.*/gs ); }); From 922ff31afb1aad8d232b131272843af810f03728 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 9 Nov 2022 12:01:00 +0100 Subject: [PATCH 24/30] more crossplatform posixify stuff --- .../src/exports/vite/graph_analysis/index.js | 37 ++++++++++++++----- .../exports/vite/graph_analysis/index.spec.js | 3 +- packages/kit/src/exports/vite/index.js | 12 ++---- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 877110344582..54abc736e741 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -1,9 +1,11 @@ import path from 'path'; +import { posixify } from '../../../utils/filesystem.js'; const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/private']); const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; /** + * Checks if given id imports a module that is not allowed to be imported into public-facing code. * @param {string} id * @param {{ * cwd: string; @@ -18,6 +20,7 @@ export function is_illegal(id, dirs) { } /** + * Creates a guard that checks that no id imports a module that is not allowed to be imported into public-facing code. * @param {import('rollup').PluginContext} context * @param {{ cwd: string, lib: string }} paths */ @@ -26,9 +29,10 @@ export function module_guard(context, { cwd, lib }) { const seen = new Set(); const dirs = { - cwd, - node_modules: path.join(cwd, 'node_modules'), - server: path.join(lib, 'server') + // 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')) }; /** @@ -41,14 +45,11 @@ export function module_guard(context, { cwd, lib }) { if (is_illegal(id, dirs)) { chain.shift(); // discard the entry point - - if (id.startsWith(lib)) id = id.replace(lib, '$lib'); - if (id.startsWith(cwd)) id = path.relative(cwd, id); + id = normalize_id(id, lib, cwd); const pyramid = chain.map(({ id, dynamic }, i) => { - if (id.startsWith(lib)) id = id.replace(lib, '$lib'); - if (id.startsWith(cwd)) id = path.relative(cwd, id); + id = normalize_id(id, lib, cwd); return `${repeat(' ', i * 2)}- ${id} ${dynamic ? 'dynamically imports' : 'imports'}\n`; }) + `${repeat(' ', chain.length)}- ${id}`; @@ -72,13 +73,31 @@ export function module_guard(context, { cwd, lib }) { } return { - /** @param {string} id */ + /** @param {string} id should be posixified */ check: (id) => { follow(id, []); } }; } +/** + * Removes cwd/lib path from the start of the id + * @param {string} id + * @param {string} lib + * @param {string} cwd + */ +export function normalize_id(id, lib, cwd) { + if (id.startsWith(lib)) { + id = id.replace(lib, '$lib'); + } + + if (id.startsWith(cwd)) { + id = path.relative(cwd, id); + } + + return posixify(id); +} + /** * @param {string} str * @param {number} times 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 debb2132cfe4..29bd6e163d76 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -1,4 +1,3 @@ -import path from 'path'; import { test } from 'uvu'; import * as assert from 'uvu/assert'; import { module_guard } from './index.js'; @@ -32,7 +31,7 @@ function check(graph, expected_error) { throw new Error('Expected an error'); } catch (e) { // @ts-expect-error - assert.equal(e.message, expected_error.replace(/^\t+/gm, '').replace(/\//g, path.sep)); + assert.equal(e.message, expected_error.replace(/^\t+/gm, '')); } } else { guard.check('~/src/entry'); diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 79e14c83044a..6ff7e6e66980 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -17,7 +17,7 @@ import { preview } from './preview/index.js'; import { get_config_aliases, get_app_aliases, get_env } from './utils.js'; import { fileURLToPath } from 'node:url'; import { create_static_module, create_dynamic_module } from '../../core/env.js'; -import { is_illegal, module_guard } from './graph_analysis/index.js'; +import { is_illegal, module_guard, normalize_id } from './graph_analysis/index.js'; const cwd = process.cwd(); @@ -303,11 +303,7 @@ function kit() { server: vite.normalizePath(path.join(normalized_lib, 'server')) }) ) { - const relative = id.startsWith(normalized_lib) - ? id.replace(normalized_lib, '$lib') - : id.startsWith(normalized_cwd) - ? vite.normalizePath(path.relative('.', id)) - : id; + const relative = normalize_id(id, normalized_lib, normalized_cwd); throw new Error(`Cannot import ${relative} into client-side code`); } } @@ -370,8 +366,8 @@ function kit() { } const guard = module_guard(this, { - cwd: vite.normalizePath(process.cwd()), - lib: vite.normalizePath(svelte_config.kit.files.lib) + cwd: process.cwd(), + lib: svelte_config.kit.files.lib }); manifest_data.nodes.forEach((_node, i) => { From 6223832167c2c4bcc3e63b5d9dfa1c0f7255119a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Nov 2022 10:55:33 -0500 Subject: [PATCH 25/30] use consistent terminology between dev and build --- .../scripts/special-types/$env+dynamic+private.md | 2 +- .../kit/scripts/special-types/$env+static+private.md | 2 +- packages/kit/scripts/special-types/$lib.md | 2 +- .../kit/src/exports/vite/graph_analysis/index.js | 6 +++--- .../src/exports/vite/graph_analysis/index.spec.js | 12 ++++++------ packages/kit/test/build-errors/env.spec.js | 8 ++++---- packages/kit/test/build-errors/server-only.spec.js | 8 ++++---- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/kit/scripts/special-types/$env+dynamic+private.md b/packages/kit/scripts/special-types/$env+dynamic+private.md index b4b63a9df45d..689e8e879202 100644 --- a/packages/kit/scripts/special-types/$env+dynamic+private.md +++ b/packages/kit/scripts/special-types/$env+dynamic+private.md @@ -1,6 +1,6 @@ This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env). -This module cannot be imported into public-facing code. +This module cannot be imported into client-side code. ```ts import { env } from '$env/dynamic/private'; diff --git a/packages/kit/scripts/special-types/$env+static+private.md b/packages/kit/scripts/special-types/$env+static+private.md index cf2454a30596..536ce64de9c0 100644 --- a/packages/kit/scripts/special-types/$env+static+private.md +++ b/packages/kit/scripts/special-types/$env+static+private.md @@ -1,4 +1,4 @@ -Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into public-facing code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env). +Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env). _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination. diff --git a/packages/kit/scripts/special-types/$lib.md b/packages/kit/scripts/special-types/$lib.md index 444a39cab62c..56a1d7489e80 100644 --- a/packages/kit/scripts/special-types/$lib.md +++ b/packages/kit/scripts/special-types/$lib.md @@ -2,4 +2,4 @@ This is a simple alias to `src/lib`, or whatever directory is specified as [`con #### `$lib/server` -A subdirectory of `$lib`. SvelteKit will prevent you from importing any modules in `$lib/server` into public-facing code. See [server-only modules](/docs/server-only-modules). +A subdirectory of `$lib`. SvelteKit will prevent you from importing any modules in `$lib/server` into client-side code. See [server-only modules](/docs/server-only-modules). diff --git a/packages/kit/src/exports/vite/graph_analysis/index.js b/packages/kit/src/exports/vite/graph_analysis/index.js index 54abc736e741..f74376203892 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.js @@ -5,7 +5,7 @@ const ILLEGAL_IMPORTS = new Set(['\0$env/dynamic/private', '\0$env/static/privat const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/; /** - * Checks if given id imports a module that is not allowed to be imported into public-facing code. + * Checks if given id imports a module that is not allowed to be imported into client-side code. * @param {string} id * @param {{ * cwd: string; @@ -20,7 +20,7 @@ export function is_illegal(id, dirs) { } /** - * Creates a guard that checks that no id imports a module that is not allowed to be imported into public-facing code. + * Creates a guard that checks that no id imports a module that is not allowed to be imported into client-side code. * @param {import('rollup').PluginContext} context * @param {{ cwd: string, lib: string }} paths */ @@ -54,7 +54,7 @@ export function module_guard(context, { cwd, lib }) { return `${repeat(' ', i * 2)}- ${id} ${dynamic ? 'dynamically imports' : 'imports'}\n`; }) + `${repeat(' ', chain.length)}- ${id}`; - const message = `Cannot import ${id} into public-facing code:\n${pyramid}`; + const message = `Cannot import ${id} into client-side code:\n${pyramid}`; throw new Error(message); } 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 29bd6e163d76..2a3ee77382a5 100644 --- a/packages/kit/src/exports/vite/graph_analysis/index.spec.js +++ b/packages/kit/src/exports/vite/graph_analysis/index.spec.js @@ -48,7 +48,7 @@ test('throws an error when importing $env/static/private', () => { importedIds: ['\0$env/static/private'] } }, - `Cannot import \0$env/static/private into public-facing code: + `Cannot import \0$env/static/private into client-side code: - src/routes/+page.svelte imports - \0$env/static/private` ); @@ -64,7 +64,7 @@ test('throws an error when dynamically importing $env/static/private', () => { dynamicallyImportedIds: ['\0$env/static/private'] } }, - `Cannot import \0$env/static/private into public-facing code: + `Cannot import \0$env/static/private into client-side code: - src/routes/+page.svelte dynamically imports - \0$env/static/private` ); @@ -80,7 +80,7 @@ test('throws an error when importing $env/dynamic/private', () => { importedIds: ['\0$env/dynamic/private'] } }, - `Cannot import \0$env/dynamic/private into public-facing code: + `Cannot import \0$env/dynamic/private into client-side code: - src/routes/+page.svelte imports - \0$env/dynamic/private` ); @@ -96,7 +96,7 @@ test('throws an error when dynamically importing $env/dynamic/private', () => { dynamicallyImportedIds: ['\0$env/dynamic/private'] } }, - `Cannot import \0$env/dynamic/private into public-facing code: + `Cannot import \0$env/dynamic/private into client-side code: - src/routes/+page.svelte dynamically imports - \0$env/dynamic/private` ); @@ -113,7 +113,7 @@ test('throws an error when importing a .server.js module', () => { }, '~/src/routes/illegal.server.js': {} }, - `Cannot import src/routes/illegal.server.js into public-facing code: + `Cannot import src/routes/illegal.server.js into client-side code: - src/routes/+page.svelte imports - src/routes/illegal.server.js` ); @@ -130,7 +130,7 @@ 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 public-facing code: + `Cannot import $lib/server/some/module.js into client-side code: - src/routes/+page.svelte imports - $lib/server/some/module.js` ); diff --git a/packages/kit/test/build-errors/env.spec.js b/packages/kit/test/build-errors/env.spec.js index 1480c06c0c19..c0fec16a4ecd 100644 --- a/packages/kit/test/build-errors/env.spec.js +++ b/packages/kit/test/build-errors/env.spec.js @@ -11,7 +11,7 @@ test('$env/dynamic/private is not statically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \0\$env\/dynamic\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/dynamic\/private into client-side code:.*/gs ); }); @@ -23,7 +23,7 @@ test('$env/dynamic/private is not dynamically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \0\$env\/dynamic\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/dynamic\/private into client-side code:.*/gs ); }); @@ -35,7 +35,7 @@ test('$env/static/private is not statically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \0\$env\/static\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/static\/private into client-side code:.*/gs ); }); @@ -47,7 +47,7 @@ test('$env/static/private is not dynamically importable from the client', () => stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \0\$env\/static\/private into public-facing code:.*/gs + /.*Cannot import \0\$env\/static\/private into client-side 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 f11b33beb36a..7e620820eb5b 100644 --- a/packages/kit/test/build-errors/server-only.spec.js +++ b/packages/kit/test/build-errors/server-only.spec.js @@ -11,7 +11,7 @@ test('$lib/*.server.* is not statically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs ); }); @@ -23,7 +23,7 @@ test('$lib/*.server.* is not dynamically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$lib\/test.server.js into public-facing code:.*/gs + /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs ); }); @@ -35,7 +35,7 @@ test('$lib/server/* is not statically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$lib\/server\/something\/test.js into public-facing code:.*/gs + /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs ); }); @@ -47,7 +47,7 @@ test('$lib/server/* is not dynamically importable from the client', () => { stdio: 'pipe', timeout: 15000 }), - /.*Cannot import \$lib\/server\/something\/test.js into public-facing code:.*/gs + /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs ); }); From b7f65ccfc63c851c708e2f22ba95a6a06be60957 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Nov 2022 11:13:44 -0500 Subject: [PATCH 26/30] ugh. print error messages so we can see why windows is being a dick --- .../kit/test/build-errors/server-only.spec.js | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/kit/test/build-errors/server-only.spec.js b/packages/kit/test/build-errors/server-only.spec.js index 7e620820eb5b..2c1cad711429 100644 --- a/packages/kit/test/build-errors/server-only.spec.js +++ b/packages/kit/test/build-errors/server-only.spec.js @@ -4,51 +4,51 @@ import { execSync } from 'node:child_process'; import path from 'node:path'; test('$lib/*.server.* is not statically importable from the client', () => { - assert.throws( - () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module'), - stdio: 'pipe', - timeout: 15000 - }), - /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs - ); + // assert.throws( + // () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module'), + stdio: 'pipe', + timeout: 15000 + }); + // /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs + // ); }); test('$lib/*.server.* is not dynamically importable from the client', () => { - assert.throws( - () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), - stdio: 'pipe', - timeout: 15000 - }), - /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs - ); + // assert.throws( + // () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), + stdio: 'pipe', + timeout: 15000 + }); + // /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs + // ); }); test('$lib/server/* is not statically importable from the client', () => { - assert.throws( - () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder'), - stdio: 'pipe', - timeout: 15000 - }), - /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs - ); + // assert.throws( + // () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder'), + stdio: 'pipe', + timeout: 15000 + }); + // /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs + // ); }); test('$lib/server/* is not dynamically importable from the client', () => { - assert.throws( - () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), - stdio: 'pipe', - timeout: 15000 - }), - /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs - ); + // assert.throws( + // () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), + stdio: 'pipe', + timeout: 15000 + }); + // /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs + // ); }); test.run(); From c537d2130457eddc2e9faed47746cec746141194 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Nov 2022 11:39:13 -0500 Subject: [PATCH 27/30] fml. can everyone just stop using fucking windows --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d7f72981f48..862b1e6574ac 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "monorepo for @sveltejs/kit and friends", "private": true, "scripts": { - "test": "turbo run test --filter=./packages/* --force", + "test": "turbo run test --filter=test-build-errors --force", "check": "turbo run check", "lint": "turbo run lint", "format": "turbo run format", From 269c4561a44f9b5196740f09b7185b2e9c2e18a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Nov 2022 12:01:49 -0500 Subject: [PATCH 28/30] try this --- packages/kit/src/exports/vite/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 6ff7e6e66980..951d8de681f3 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -366,8 +366,8 @@ function kit() { } const guard = module_guard(this, { - cwd: process.cwd(), - lib: svelte_config.kit.files.lib + cwd: vite.normalizePath(process.cwd()), + lib: vite.normalizePath(svelte_config.kit.files.lib) }); manifest_data.nodes.forEach((_node, i) => { From 2cea0233b3d377f10abbd2baeb724fa35c9160e3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 9 Nov 2022 12:07:01 -0500 Subject: [PATCH 29/30] ok turn everything back on --- package.json | 2 +- .../kit/test/build-errors/server-only.spec.js | 72 +++++++++---------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 862b1e6574ac..3d7f72981f48 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "monorepo for @sveltejs/kit and friends", "private": true, "scripts": { - "test": "turbo run test --filter=test-build-errors --force", + "test": "turbo run test --filter=./packages/* --force", "check": "turbo run check", "lint": "turbo run lint", "format": "turbo run format", diff --git a/packages/kit/test/build-errors/server-only.spec.js b/packages/kit/test/build-errors/server-only.spec.js index 2c1cad711429..7e620820eb5b 100644 --- a/packages/kit/test/build-errors/server-only.spec.js +++ b/packages/kit/test/build-errors/server-only.spec.js @@ -4,51 +4,51 @@ import { execSync } from 'node:child_process'; import path from 'node:path'; test('$lib/*.server.* is not statically importable from the client', () => { - // assert.throws( - // () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module'), - stdio: 'pipe', - timeout: 15000 - }); - // /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs - // ); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module'), + stdio: 'pipe', + timeout: 15000 + }), + /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs + ); }); test('$lib/*.server.* is not dynamically importable from the client', () => { - // assert.throws( - // () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), - stdio: 'pipe', - timeout: 15000 - }); - // /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs - // ); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-module-dynamic-import'), + stdio: 'pipe', + timeout: 15000 + }), + /.*Cannot import \$lib\/test.server.js into client-side code:.*/gs + ); }); test('$lib/server/* is not statically importable from the client', () => { - // assert.throws( - // () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder'), - stdio: 'pipe', - timeout: 15000 - }); - // /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs - // ); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder'), + stdio: 'pipe', + timeout: 15000 + }), + /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs + ); }); test('$lib/server/* is not dynamically importable from the client', () => { - // assert.throws( - // () => - execSync('pnpm build', { - cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), - stdio: 'pipe', - timeout: 15000 - }); - // /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs - // ); + assert.throws( + () => + execSync('pnpm build', { + cwd: path.join(process.cwd(), 'apps/server-only-folder-dynamic-import'), + stdio: 'pipe', + timeout: 15000 + }), + /.*Cannot import \$lib\/server\/something\/test.js into client-side code:.*/gs + ); }); test.run(); From d79cc59d0bca11ce86322a29262d4d51f0934f75 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 10 Nov 2022 09:52:26 -0500 Subject: [PATCH 30/30] Create soft-gorillas-hear.md --- .changeset/soft-gorillas-hear.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/soft-gorillas-hear.md diff --git a/.changeset/soft-gorillas-hear.md b/.changeset/soft-gorillas-hear.md new file mode 100644 index 000000000000..6b3d8affef90 --- /dev/null +++ b/.changeset/soft-gorillas-hear.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +prevent loading of illegal modules in the browser, rather than during SSR