diff --git a/.changeset/perfect-rules-burn.md b/.changeset/perfect-rules-burn.md new file mode 100644 index 000000000000..1e4002f39ccb --- /dev/null +++ b/.changeset/perfect-rules-burn.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] Allow users to designate modules as server-only diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index c1caf2250cac..2812b2c195b2 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -13,6 +13,7 @@ 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, resolve_entry } from '../utils.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 @@ -24,10 +25,9 @@ const cwd = process.cwd(); * @param {import('vite').ViteDevServer} vite * @param {import('vite').ResolvedConfig} vite_config * @param {import('types').ValidatedConfig} svelte_config - * @param {Set} illegal_imports * @return {Promise void>>} */ -export async function dev(vite, vite_config, svelte_config, illegal_imports) { +export async function dev(vite, vite_config, svelte_config) { installPolyfills(); sync.init(svelte_config, vite_config.mode); @@ -90,7 +90,11 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { module_nodes.push(module_node); result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for? - prevent_illegal_vite_imports(module_node, illegal_imports, extensions); + prevent_illegal_vite_imports( + module_node, + normalizePath(svelte_config.kit.files.lib), + extensions + ); return module.default; }; @@ -103,7 +107,11 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { result.shared = module; - prevent_illegal_vite_imports(module_node, illegal_imports, extensions); + prevent_illegal_vite_imports( + module_node, + normalizePath(svelte_config.kit.files.lib), + extensions + ); } if (node.server) { diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index a613ecd89e52..87bff27433a5 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -103,9 +103,6 @@ function kit() { /** @type {import('types').BuildData} */ let build_data; - /** @type {Set} */ - let illegal_imports; - /** @type {string | undefined} */ let deferred_warning; @@ -212,13 +209,6 @@ function kit() { client_out_dir: `${svelte_config.kit.outDir}/output/client` }; - 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 - ]); - if (is_build) { manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; @@ -361,7 +351,7 @@ function kit() { prevent_illegal_rollup_imports( this.getModuleInfo.bind(this), module_node, - illegal_imports + vite.normalizePath(svelte_config.kit.files.lib) ); } }); @@ -517,7 +507,7 @@ function kit() { if (deferred_warning) console.error('\n' + deferred_warning); }; - return await dev(vite, vite_config, svelte_config, illegal_imports); + return await dev(vite, vite_config, svelte_config); }, /** diff --git a/packages/kit/src/exports/vite/utils.js b/packages/kit/src/exports/vite/utils.js index a08617f3d4e9..b3ea423bfef9 100644 --- a/packages/kit/src/exports/vite/utils.js +++ b/packages/kit/src/exports/vite/utils.js @@ -4,6 +4,26 @@ import { loadConfigFromFile, loadEnv, normalizePath } from 'vite'; import { runtime_directory } from '../../core/utils.js'; import { posixify } from '../../utils/filesystem.js'; +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 +]); + +/** @param {string} id */ +function is_illegal(id) { + if (illegal_imports.has(id)) return true; + + // files outside the project root are ignored + if (!id.startsWith(normalizePath(process.cwd()))) return false; + + // so are files inside node_modules + if (id.startsWith(normalizePath(node_modules_dir))) return false; + + return /.*\.server\..+/.test(path.basename(id)); +} + /** * @param {import('vite').ResolvedConfig} config * @param {import('vite').ConfigEnv} config_env @@ -183,8 +203,9 @@ function repeat(str, times) { /** * Create a formatted error for an illegal import. * @param {Array<{name: string, dynamic: boolean}>} stack + * @param {string} lib_dir */ -function format_illegal_import_chain(stack) { +function format_illegal_import_chain(stack, lib_dir) { const dev_virtual_prefix = '/@id/__x00__'; const prod_virtual_prefix = '\0'; @@ -195,6 +216,9 @@ function format_illegal_import_chain(stack) { if (file.name.startsWith(prod_virtual_prefix)) { return { ...file, name: file.name.replace(prod_virtual_prefix, '') }; } + if (file.name.startsWith(lib_dir)) { + return { ...file, name: file.name.replace(lib_dir, '$lib') }; + } return { ...file, name: path.relative(process.cwd(), file.name) }; }); @@ -211,6 +235,8 @@ function format_illegal_import_chain(stack) { return `Cannot import ${stack.at(-1)?.name} into client-side code:\n${pyramid}`; } +const node_modules_dir = path.resolve(process.cwd(), 'node_modules'); + /** * Load environment variables from process.env and .env files * @param {import('types').ValidatedKitConfig['env']} env_config @@ -228,11 +254,11 @@ export function get_env(env_config, mode) { /** * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter * @param {import('rollup').ModuleInfo} node - * @param {Set} illegal_imports Illegal module IDs -- be sure to call vite.normalizePath! + * @param {string} lib_dir */ -export function prevent_illegal_rollup_imports(node_getter, node, illegal_imports) { - const chain = find_illegal_rollup_imports(node_getter, node, false, illegal_imports); - if (chain) throw new Error(format_illegal_import_chain(chain)); +export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) { + const chain = find_illegal_rollup_imports(node_getter, node, false); + if (chain) throw new Error(format_illegal_import_chain(chain, lib_dir)); } const query_pattern = /\?.*$/s; @@ -246,36 +272,27 @@ function remove_query_from_path(path) { * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter * @param {import('rollup').ModuleInfo} node * @param {boolean} dynamic - * @param {Set} illegal_imports Illegal module IDs -- be sure to call vite.normalizePath! * @param {Set} seen * @returns {Array | null} */ -const find_illegal_rollup_imports = ( - node_getter, - node, - dynamic, - illegal_imports, - seen = new Set() -) => { +const find_illegal_rollup_imports = (node_getter, node, dynamic, seen = new Set()) => { const name = remove_query_from_path(normalizePath(node.id)); if (seen.has(name)) return null; seen.add(name); - if (illegal_imports.has(name)) { + if (is_illegal(name)) { return [{ name, dynamic }]; } for (const id of node.importedIds) { const child = node_getter(id); - const chain = - child && find_illegal_rollup_imports(node_getter, child, false, illegal_imports, seen); + const chain = child && find_illegal_rollup_imports(node_getter, child, false, seen); if (chain) return [{ name, dynamic }, ...chain]; } for (const id of node.dynamicallyImportedIds) { const child = node_getter(id); - const chain = - child && find_illegal_rollup_imports(node_getter, child, true, illegal_imports, seen); + const chain = child && find_illegal_rollup_imports(node_getter, child, true, seen); if (chain) return [{ name, dynamic }, ...chain]; } @@ -308,22 +325,21 @@ const get_module_types = (config_module_types) => { /** * Throw an error if a private module is imported from a client-side node. * @param {import('vite').ModuleNode} node - * @param {Set} illegal_imports Illegal module IDs -- be sure to call vite.normalizePath! + * @param {string} lib_dir * @param {Iterable} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc. */ -export function prevent_illegal_vite_imports(node, illegal_imports, module_types) { - const chain = find_illegal_vite_imports(node, illegal_imports, get_module_types(module_types)); - if (chain) throw new Error(format_illegal_import_chain(chain)); +export function prevent_illegal_vite_imports(node, lib_dir, module_types) { + const chain = find_illegal_vite_imports(node, get_module_types(module_types)); + if (chain) throw new Error(format_illegal_import_chain(chain, lib_dir)); } /** * @param {import('vite').ModuleNode} node - * @param {Set} illegal_imports Illegal module IDs -- be sure to call vite.normalizePath! * @param {Set} module_types File extensions to analyze: `.ts`, `.js`, etc. * @param {Set} seen * @returns {Array | null} */ -function find_illegal_vite_imports(node, illegal_imports, module_types, seen = new Set()) { +function find_illegal_vite_imports(node, module_types, seen = new Set()) { if (!node.id) return null; // TODO when does this happen? const name = remove_query_from_path(normalizePath(node.id)); @@ -332,12 +348,12 @@ function find_illegal_vite_imports(node, illegal_imports, module_types, seen = n } seen.add(name); - if (name && illegal_imports.has(name)) { + if (is_illegal(name)) { return [{ name, dynamic: false }]; } for (const child of node.importedModules) { - const chain = child && find_illegal_vite_imports(child, illegal_imports, module_types, seen); + const chain = child && find_illegal_vite_imports(child, module_types, seen); if (chain) return [{ name, dynamic: false }, ...chain]; } diff --git a/packages/kit/src/exports/vite/utils.spec.js b/packages/kit/src/exports/vite/utils.spec.js index 85b9b909d31d..558d5d563956 100644 --- a/packages/kit/src/exports/vite/utils.spec.js +++ b/packages/kit/src/exports/vite/utils.spec.js @@ -1,7 +1,6 @@ import path from 'path'; import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { normalizePath } from 'vite'; import { validate_config } from '../../core/config/index.js'; import { posixify } from '../../utils/filesystem.js'; import { @@ -12,6 +11,8 @@ import { prevent_illegal_vite_imports } from './utils.js'; +const illegal_id = '/@id/__x00__$env/dynamic/private'; + test('basic test no conflicts', async () => { const merged = deep_merge( { @@ -277,7 +278,7 @@ const rollup_node_getter = (id) => { }, '/statically-imports/bad/module.js': { id: '/statically-imports/bad/module.js', - importedIds: ['/illegal/boom.js'], + importedIds: [illegal_id], dynamicallyImportedIds: ['/test/path2.js'] }, '/bad/dynamic.js': { @@ -288,10 +289,10 @@ const rollup_node_getter = (id) => { '/dynamically-imports/bad/module.js': { id: '/dynamically-imports/bad/module.js', importedIds: ['/test/path5.js'], - dynamicallyImportedIds: ['/test/path2.js', '/illegal/boom.js'] + dynamicallyImportedIds: ['/test/path2.js', illegal_id] }, - '/illegal/boom.js': { - id: '/illegal/boom.js', + [illegal_id]: { + id: illegal_id, importedIds: [], dynamicallyImportedIds: [] } @@ -299,32 +300,39 @@ const rollup_node_getter = (id) => { return nodes[id] ?? null; }; -const illegal_imports = new Set([normalizePath('/illegal/boom.js')]); const ok_rollup_node = rollup_node_getter('/test/path1.js'); const bad_rollup_node_static = rollup_node_getter('/bad/static.js'); const bad_rollup_node_dynamic = rollup_node_getter('/bad/dynamic.js'); test('allows ok rollup imports', () => { assert.not.throws(() => { - // @ts-ignore - prevent_illegal_rollup_imports(rollup_node_getter, ok_rollup_node, illegal_imports, ''); + prevent_illegal_rollup_imports( + // @ts-expect-error + rollup_node_getter, + ok_rollup_node, + 'should_not_match_anything' + ); }); }); test('does not allow bad static rollup imports', () => { assert.throws(() => { - // @ts-ignore - prevent_illegal_rollup_imports(rollup_node_getter, bad_rollup_node_static, illegal_imports, ''); + prevent_illegal_rollup_imports( + // @ts-expect-error + rollup_node_getter, + bad_rollup_node_static, + 'should_not_match_anything' + ); }); }); test('does not allow bad dynamic rollup imports', () => { assert.throws(() => { prevent_illegal_rollup_imports( - // @ts-ignore + // @ts-expect-error rollup_node_getter, bad_rollup_node_dynamic, - illegal_imports + 'should_not_match_anything' ); }); }); @@ -359,7 +367,7 @@ const bad_vite_node = { id: '/test/path2.js', importedModules: new Set([ { - id: '/illegal/boom.js', + id: illegal_id, importedModules: new Set() } ]) @@ -372,15 +380,23 @@ const bad_vite_node = { test('allows ok vite imports', () => { assert.not.throws(() => { - // @ts-ignore - prevent_illegal_vite_imports(ok_vite_node, illegal_imports, ''); + prevent_illegal_vite_imports( + // @ts-expect-error + ok_vite_node, + 'should_not_match_anything', + [] + ); }); }); test('does not allow bad static rollup imports', () => { assert.throws(() => { - // @ts-ignore - prevent_illegal_vite_imports(bad_vite_node, illegal_imports, ''); + prevent_illegal_vite_imports( + // @ts-expect-error + bad_vite_node, + 'should_not_match_anything', + [] + ); }); }); 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/server-only-modules/dynamic-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/server-only-modules/dynamic-import/+page.svelte new file mode 100644 index 000000000000..ea20469dc4b2 --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/server-only-modules/dynamic-import/+page.svelte @@ -0,0 +1,7 @@ + + +{#await mod then resolved} +

{resolved.should_explode}

+{/await} diff --git a/packages/kit/test/apps/dev-only/src/routes/server-only-modules/static-import/+page.svelte b/packages/kit/test/apps/dev-only/src/routes/server-only-modules/static-import/+page.svelte new file mode 100644 index 000000000000..e0e7bff39ae1 --- /dev/null +++ b/packages/kit/test/apps/dev-only/src/routes/server-only-modules/static-import/+page.svelte @@ -0,0 +1,5 @@ + + +

{should_explode}

diff --git a/packages/kit/test/apps/dev-only/test/test.js b/packages/kit/test/apps/dev-only/test/test.js index 392b83ccb91f..c71d909fb58f 100644 --- a/packages/kit/test/apps/dev-only/test/test.js +++ b/packages/kit/test/apps/dev-only/test/test.js @@ -36,3 +36,18 @@ test.describe('$env', () => { ); }); }); + +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( + /.*Error: Cannot import \$lib\/test.server.js into client-side 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( + /.*Error: Cannot import \$lib\/test.server.js into client-side code:.*/gs + ); + }); +}); diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/package.json b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/package.json index 6dd1856f12ab..72d205023504 100644 --- a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/package.json +++ b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/package.json @@ -6,14 +6,14 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./jsconfig.json", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" }, "devDependencies": { "@sveltejs/kit": "workspace:*", "svelte": "^3.44.0", "svelte-check": "^2.7.1", - "typescript": "^4.7.4", + "typescript": "^4.8.2", "vite": "^3.1.0" }, "type": "module" diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/svelte.config.js b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/svelte.config.js new file mode 100644 index 000000000000..bded48544036 --- /dev/null +++ b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/svelte.config.js @@ -0,0 +1,4 @@ +/** @type {import('@sveltejs/kit').Config} */ +const config = {}; + +export default config; diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/jsconfig.json b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json similarity index 71% rename from packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/jsconfig.json rename to packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json index de37a5124d58..b5ab567d3c9d 100644 --- a/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/jsconfig.json +++ b/packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import/tsconfig.json @@ -9,8 +9,11 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, + "noEmit": true, "paths": { - "types": ["../../../../types/internal"] + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] } } } diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env/package.json b/packages/kit/test/build-errors/apps/private-dynamic-env/package.json index 6e482a9076cf..0ac9830cdc38 100644 --- a/packages/kit/test/build-errors/apps/private-dynamic-env/package.json +++ b/packages/kit/test/build-errors/apps/private-dynamic-env/package.json @@ -6,14 +6,14 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./jsconfig.json", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" }, "devDependencies": { "@sveltejs/kit": "workspace:*", "svelte": "^3.44.0", "svelte-check": "^2.7.1", - "typescript": "^4.7.4", + "typescript": "^4.8.2", "vite": "^3.1.0" }, "type": "module" diff --git a/packages/kit/test/build-errors/apps/private-dynamic-env/jsconfig.json b/packages/kit/test/build-errors/apps/private-dynamic-env/tsconfig.json similarity index 71% rename from packages/kit/test/build-errors/apps/private-dynamic-env/jsconfig.json rename to packages/kit/test/build-errors/apps/private-dynamic-env/tsconfig.json index de37a5124d58..b5ab567d3c9d 100644 --- a/packages/kit/test/build-errors/apps/private-dynamic-env/jsconfig.json +++ b/packages/kit/test/build-errors/apps/private-dynamic-env/tsconfig.json @@ -9,8 +9,11 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, + "noEmit": true, "paths": { - "types": ["../../../../types/internal"] + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] } } } diff --git a/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/package.json b/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/package.json index b09a47e886d6..66ad4845d066 100644 --- a/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/package.json +++ b/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/package.json @@ -6,14 +6,14 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./jsconfig.json", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" }, "devDependencies": { "@sveltejs/kit": "workspace:*", "svelte": "^3.44.0", "svelte-check": "^2.7.1", - "typescript": "^4.7.4", + "typescript": "^4.8.2", "vite": "^3.1.0" }, "type": "module" diff --git a/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/jsconfig.json b/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/tsconfig.json similarity index 71% rename from packages/kit/test/build-errors/apps/private-static-env-dynamic-import/jsconfig.json rename to packages/kit/test/build-errors/apps/private-static-env-dynamic-import/tsconfig.json index de37a5124d58..b5ab567d3c9d 100644 --- a/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/jsconfig.json +++ b/packages/kit/test/build-errors/apps/private-static-env-dynamic-import/tsconfig.json @@ -9,8 +9,11 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, + "noEmit": true, "paths": { - "types": ["../../../../types/internal"] + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] } } } diff --git a/packages/kit/test/build-errors/apps/private-static-env/package.json b/packages/kit/test/build-errors/apps/private-static-env/package.json index e98bf574ee72..99c139389a47 100644 --- a/packages/kit/test/build-errors/apps/private-static-env/package.json +++ b/packages/kit/test/build-errors/apps/private-static-env/package.json @@ -6,7 +6,7 @@ "dev": "vite dev", "build": "vite build", "preview": "vite preview", - "check": "svelte-check --tsconfig ./jsconfig.json", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" }, "devDependencies": { @@ -14,7 +14,7 @@ "cross-env": "^7.0.3", "svelte": "^3.44.0", "svelte-check": "^2.7.1", - "typescript": "^4.7.4", + "typescript": "^4.8.2", "vite": "^3.1.0" }, "type": "module" diff --git a/packages/kit/test/build-errors/apps/private-static-env/jsconfig.json b/packages/kit/test/build-errors/apps/private-static-env/tsconfig.json similarity index 71% rename from packages/kit/test/build-errors/apps/private-static-env/jsconfig.json rename to packages/kit/test/build-errors/apps/private-static-env/tsconfig.json index de37a5124d58..b5ab567d3c9d 100644 --- a/packages/kit/test/build-errors/apps/private-static-env/jsconfig.json +++ b/packages/kit/test/build-errors/apps/private-static-env/tsconfig.json @@ -9,8 +9,11 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, + "noEmit": true, "paths": { - "types": ["../../../../types/internal"] + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] } } } diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.gitignore b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.gitignore new file mode 100644 index 000000000000..cbee71f70e92 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env.* +!.env.example diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.npmrc b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.npmrc new file mode 100644 index 000000000000..b6f27f135954 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/package.json b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/package.json new file mode 100644 index 000000000000..819943cdec33 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/package.json @@ -0,0 +1,20 @@ +{ + "name": "server-only-module", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", + "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:*", + "svelte": "^3.44.0", + "svelte-check": "^2.7.1", + "typescript": "^4.8.2", + "vite": "^3.1.0-beta.1" + }, + "type": "module" +} diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.d.ts b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.d.ts new file mode 100644 index 000000000000..917a963999f3 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.d.ts @@ -0,0 +1,7 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Locals {} + // interface Platform {} +} diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.html b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.html new file mode 100644 index 000000000000..5b53ef7e3ae7 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/lib/test.server.js b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/lib/test.server.js new file mode 100644 index 000000000000..770268db581d --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/lib/test.server.js @@ -0,0 +1 @@ +export const should_explode = 'boom'; diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/routes/+page.svelte b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/routes/+page.svelte new file mode 100644 index 000000000000..ea20469dc4b2 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/src/routes/+page.svelte @@ -0,0 +1,7 @@ + + +{#await mod then resolved} +

{resolved.should_explode}

+{/await} diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/static/favicon.png b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/static/favicon.png new file mode 100644 index 000000000000..825b9e65af7c Binary files /dev/null and b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/static/favicon.png differ diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/svelte.config.js b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/svelte.config.js new file mode 100644 index 000000000000..bded48544036 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/svelte.config.js @@ -0,0 +1,4 @@ +/** @type {import('@sveltejs/kit').Config} */ +const config = {}; + +export default config; diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json new file mode 100644 index 000000000000..b5ab567d3c9d --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "noEmit": true, + "paths": { + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] + } + } +} diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/vite.config.js b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/vite.config.js new file mode 100644 index 000000000000..95a8fe5795c7 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/vite.config.js @@ -0,0 +1,14 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import path from 'path'; + +/** @type {import('vite').UserConfig} */ +const config = { + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../../src')] + } + } +}; + +export default config; diff --git a/packages/kit/test/build-errors/apps/server-only-module/.gitignore b/packages/kit/test/build-errors/apps/server-only-module/.gitignore new file mode 100644 index 000000000000..cbee71f70e92 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env.* +!.env.example diff --git a/packages/kit/test/build-errors/apps/server-only-module/.npmrc b/packages/kit/test/build-errors/apps/server-only-module/.npmrc new file mode 100644 index 000000000000..b6f27f135954 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/packages/kit/test/build-errors/apps/server-only-module/package.json b/packages/kit/test/build-errors/apps/server-only-module/package.json new file mode 100644 index 000000000000..819943cdec33 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/package.json @@ -0,0 +1,20 @@ +{ + "name": "server-only-module", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && tsc && svelte-check --tsconfig ./jsconfig.json", + "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:*", + "svelte": "^3.44.0", + "svelte-check": "^2.7.1", + "typescript": "^4.8.2", + "vite": "^3.1.0-beta.1" + }, + "type": "module" +} diff --git a/packages/kit/test/build-errors/apps/server-only-module/src/app.d.ts b/packages/kit/test/build-errors/apps/server-only-module/src/app.d.ts new file mode 100644 index 000000000000..917a963999f3 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/src/app.d.ts @@ -0,0 +1,7 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +// and what to do when importing types +declare namespace App { + // interface Locals {} + // interface Platform {} +} diff --git a/packages/kit/test/build-errors/apps/server-only-module/src/app.html b/packages/kit/test/build-errors/apps/server-only-module/src/app.html new file mode 100644 index 000000000000..5b53ef7e3ae7 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/build-errors/apps/server-only-module/src/lib/test.server.js b/packages/kit/test/build-errors/apps/server-only-module/src/lib/test.server.js new file mode 100644 index 000000000000..770268db581d --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/src/lib/test.server.js @@ -0,0 +1 @@ +export const should_explode = 'boom'; diff --git a/packages/kit/test/build-errors/apps/server-only-module/src/routes/+page.svelte b/packages/kit/test/build-errors/apps/server-only-module/src/routes/+page.svelte new file mode 100644 index 000000000000..e0e7bff39ae1 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/src/routes/+page.svelte @@ -0,0 +1,5 @@ + + +

{should_explode}

diff --git a/packages/kit/test/build-errors/apps/server-only-module/static/favicon.png b/packages/kit/test/build-errors/apps/server-only-module/static/favicon.png new file mode 100644 index 000000000000..825b9e65af7c Binary files /dev/null and b/packages/kit/test/build-errors/apps/server-only-module/static/favicon.png differ diff --git a/packages/kit/test/build-errors/apps/server-only-module/svelte.config.js b/packages/kit/test/build-errors/apps/server-only-module/svelte.config.js new file mode 100644 index 000000000000..bded48544036 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/svelte.config.js @@ -0,0 +1,4 @@ +/** @type {import('@sveltejs/kit').Config} */ +const config = {}; + +export default config; diff --git a/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json new file mode 100644 index 000000000000..b5ab567d3c9d --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "noEmit": true, + "paths": { + "types": ["../../../../types/internal"], + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"] + } + } +} diff --git a/packages/kit/test/build-errors/apps/server-only-module/vite.config.js b/packages/kit/test/build-errors/apps/server-only-module/vite.config.js new file mode 100644 index 000000000000..95a8fe5795c7 --- /dev/null +++ b/packages/kit/test/build-errors/apps/server-only-module/vite.config.js @@ -0,0 +1,14 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import path from 'path'; + +/** @type {import('vite').UserConfig} */ +const config = { + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../../src')] + } + } +}; + +export default config; diff --git a/packages/kit/test/build-errors/server-only.spec.js b/packages/kit/test/build-errors/server-only.spec.js new file mode 100644 index 000000000000..ee5383f61847 --- /dev/null +++ b/packages/kit/test/build-errors/server-only.spec.js @@ -0,0 +1,30 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +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 + }), + /.*Error: 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 + }), + /.*Error: Cannot import \$lib\/test.server.js into client-side code:.*/gs + ); +}); + +test.run(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b521796a0eef..e037be487cf4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -434,6 +434,98 @@ importers: typescript: 4.8.2 vite: 3.1.0 + packages/kit/test/build-errors: + specifiers: + uvu: ^0.5.6 + devDependencies: + uvu: 0.5.6 + + packages/kit/test/build-errors/apps/private-dynamic-env: + specifiers: + '@sveltejs/kit': workspace:* + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0 + devDependencies: + '@sveltejs/kit': link:../../../.. + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0 + + packages/kit/test/build-errors/apps/private-dynamic-env-dynamic-import: + specifiers: + '@sveltejs/kit': workspace:* + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0 + devDependencies: + '@sveltejs/kit': link:../../../.. + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0 + + packages/kit/test/build-errors/apps/private-static-env: + specifiers: + '@sveltejs/kit': workspace:* + cross-env: ^7.0.3 + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0 + devDependencies: + '@sveltejs/kit': link:../../../.. + cross-env: 7.0.3 + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0 + + packages/kit/test/build-errors/apps/private-static-env-dynamic-import: + specifiers: + '@sveltejs/kit': workspace:* + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0 + devDependencies: + '@sveltejs/kit': link:../../../.. + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0 + + packages/kit/test/build-errors/apps/server-only-module: + specifiers: + '@sveltejs/kit': workspace:* + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0-beta.1 + devDependencies: + '@sveltejs/kit': link:../../../.. + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0-beta.2 + + packages/kit/test/build-errors/apps/server-only-module-dynamic-import: + specifiers: + '@sveltejs/kit': workspace:* + svelte: ^3.44.0 + svelte-check: ^2.7.1 + typescript: ^4.8.2 + vite: ^3.1.0-beta.1 + devDependencies: + '@sveltejs/kit': link:../../../.. + svelte: 3.48.0 + svelte-check: 2.8.0_svelte@3.48.0 + typescript: 4.8.2 + vite: 3.1.0-beta.2 + packages/kit/test/prerendering/basics: specifiers: '@sveltejs/kit': workspace:* diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 869aecc434e8..7de1bd0a80cc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,7 @@ packages: - 'packages/adapter-static/test/apps/*' - 'packages/kit/test/apps/*' - 'packages/kit/test/prerendering/*' - - 'packages/kit/test/meta/**' + - 'packages/kit/test/build-errors/**' + - 'packages/kit/test/build-errors/apps/*' - 'packages/create-svelte/templates/*' - '!.test-tmp/**'