From 9975cd45fa3d88fca55ac3aaf6ba92e6096f8627 Mon Sep 17 00:00:00 2001 From: svelte-triage-bot Date: Tue, 1 Sep 2026 00:37:57 +0000 Subject: [PATCH] fix: externalize linked env dependencies --- .changeset/tame-loaders-resolve.md | 5 ++ packages/kit/src/core/env.js | 1 + packages/kit/src/core/sync/sync.spec.js | 102 ++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 .changeset/tame-loaders-resolve.md diff --git a/.changeset/tame-loaders-resolve.md b/.changeset/tame-loaders-resolve.md new file mode 100644 index 000000000000..c80dede7f083 --- /dev/null +++ b/.changeset/tame-loaders-resolve.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: externalize linked dependencies when loading explicit environment variables diff --git a/packages/kit/src/core/env.js b/packages/kit/src/core/env.js index e76c1d5476d2..ec10662eefd6 100644 --- a/packages/kit/src/core/env.js +++ b/packages/kit/src/core/env.js @@ -47,6 +47,7 @@ export async function load_explicit_env(kit, file, root, mode) { const server = await vite.createServer({ configFile: false, + ssr: { external: true }, logLevel: 'silent', mode, define: { diff --git a/packages/kit/src/core/sync/sync.spec.js b/packages/kit/src/core/sync/sync.spec.js index 74d70fb26583..6ee6d4f436bc 100644 --- a/packages/kit/src/core/sync/sync.spec.js +++ b/packages/kit/src/core/sync/sync.spec.js @@ -1,6 +1,9 @@ +import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; +import process from 'node:process'; +import { pathToFileURL } from 'node:url'; import { expect, test } from 'vitest'; import { process_config, validate_config } from '../config/index.js'; import { relative_path } from '../../utils/filesystem.js'; @@ -98,3 +101,102 @@ test('requests a manifest rebuild if type generation encounters a missing route fs.rmSync(root, { recursive: true, force: true }); } }); + +test('externalizes linked dependencies when loading explicit environment variables', () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'svelte-kit-env-')); + const dependency = path.join(root, 'packages/linked-env-dependency'); + const loader = path.join(root, 'loader'); + const node_modules = path.join(root, 'node_modules'); + + fs.mkdirSync(path.join(root, 'src'), { recursive: true }); + fs.mkdirSync(dependency, { recursive: true }); + fs.mkdirSync(loader, { recursive: true }); + fs.mkdirSync(node_modules, { recursive: true }); + + fs.writeFileSync(path.join(root, 'package.json'), '{"type":"module"}'); + fs.writeFileSync( + path.join(root, 'src/env.js'), + `import { marker } from 'linked-env-dependency'; + +export const variables = { + TEST: { description: marker } +}; +` + ); + fs.writeFileSync( + path.join(dependency, 'package.json'), + '{"name":"linked-env-dependency","type":"module","exports":"./index.js"}' + ); + fs.writeFileSync( + path.join(dependency, 'index.js'), + `import { marker } from 'loader-target'; + +export { marker }; +` + ); + fs.writeFileSync(path.join(loader, 'target.js'), `export const marker = 'resolved by loader';\n`); + fs.writeFileSync( + path.join(loader, 'hooks.js'), + `export async function resolve(specifier, context, nextResolve) { + if (specifier === 'loader-target') { + return { + url: new URL('./target.js', import.meta.url).href, + shortCircuit: true + }; + } + + return nextResolve(specifier, context); +} +` + ); + fs.writeFileSync( + path.join(loader, 'register.js'), + `import { register } from 'node:module'; + +register('./hooks.js', import.meta.url); +` + ); + + const config_url = pathToFileURL(path.resolve(import.meta.dirname, '../config/index.js')).href; + const env_url = pathToFileURL(path.resolve(import.meta.dirname, '../env.js')).href; + fs.writeFileSync( + path.join(root, 'run.js'), + `import { fileURLToPath } from 'node:url'; +import { process_config, validate_config } from ${JSON.stringify(config_url)}; +import { load_explicit_env } from ${JSON.stringify(env_url)}; + +const config = process_config(validate_config({}), import.meta.dirname); +const file = fileURLToPath(new URL('./src/env.js', import.meta.url)); +const { variables } = await load_explicit_env(config, file, import.meta.dirname, 'development'); + +if (variables.TEST.description !== 'resolved by loader') { + throw new Error('Node loader was not used'); +} +` + ); + + const link_type = process.platform === 'win32' ? 'junction' : 'dir'; + fs.symlinkSync(dependency, path.join(node_modules, 'linked-env-dependency'), link_type); + fs.symlinkSync( + path.resolve(import.meta.dirname, '../../../node_modules/vite'), + path.join(node_modules, 'vite'), + link_type + ); + + try { + execFileSync(process.execPath, [path.join(root, 'run.js')], { + cwd: root, + env: { + ...process.env, + NODE_OPTIONS: [ + process.env.NODE_OPTIONS, + `--import=${pathToFileURL(path.join(loader, 'register.js')).href}` + ] + .filter(Boolean) + .join(' ') + } + }); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +});