diff --git a/.changeset/quiet-taxis-build.md b/.changeset/quiet-taxis-build.md new file mode 100644 index 000000000000..fa4aab10427b --- /dev/null +++ b/.changeset/quiet-taxis-build.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +chore: avoid bundling the server twice diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 02817381ba46..8c096829f619 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -1,15 +1,14 @@ -import { mkdirSync, readFileSync, rmSync } from 'node:fs'; -import { extname } from 'node:path'; +/** @import { TopLevelFilterExpression } from '@rolldown/pluginutils' */ +import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'; +import { dirname, extname, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { rolldown } from 'rolldown'; +import { and, id, importerId, include } from '@rolldown/pluginutils'; +import remapping from '@jridgewell/remapping'; +import MagicString from 'magic-string'; const files = fileURLToPath(new URL('./files', import.meta.url).href); - -/** @param {string} str */ -function escape_regex(str) { - // TODO replace with `RegExp.escape(str)` when we require Node >= 24 - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -} +const posixified_files = posixify(files); +const dir_id = posixify(`${files}/dir.js`); /** @type {typeof import('./index.js').default} */ export default function (opts = {}) { @@ -50,141 +49,250 @@ export default function (opts = {}) { written.map((file) => extname(file)).filter((ext) => ext && !compressed_extensions.has(ext)) ); - builder.log.minor('Building server'); - - const pkg = JSON.parse(readFileSync('package.json', 'utf8')); const server = builder.getServerDirectory(); + const manifest = JSON.parse(readFileSync(`${server}/.vite/manifest.json`, 'utf8')); + const adapter_index = find_entry(manifest, 'adapter-index'); + const handler = find_entry(manifest, 'handler'); - // Copy the prebuilt entrypoints into the build directory so that the - // adapter's own bundled dependencies resolve correctly, then bundle them - // together with the app's server code. Bundling everything in a single - // pass means shared modules (e.g. `SvelteKitError` from `@sveltejs/kit`) - // aren't duplicated. See https://github.com/sveltejs/kit/issues/15755 - const entries = posixify(`${tmp}/entries`); - builder.copy(files, entries); + builder.generateServerInstance(`${server}/server.js`); - const dir_id = `${entries}/dir.js`; + if (builder.hasServerInstrumentationFile()) { + builder.instrument({ + entrypoint: `${server}/${adapter_index.file}`, + instrumentation: `${server}/instrumentation.server.js`, + initializer: builder.createInstrumentationInitializer({ outputDirectory: server }), + module: { + exports: ['path', 'host', 'port', 'server'] + } + }); + } - /** @type {Record} */ - const input = { - index: `${entries}/index.js`, - 'adapter-env': `${entries}/adapter-env.js`, - env: `${server}/env.js`, - handler: `${entries}/handler.js` - }; + const output_server = `${out}/server`; + builder.copy(server, output_server); - if (builder.hasServerInstrumentationFile()) { - input.environment = builder.createInstrumentationInitializer({ outputDirectory: entries }); - input['instrumentation.server'] = `${server}/instrumentation.server.js`; + // `dir.js` must evaluate at the output root because it anchors the asset directories + renameSync(`${output_server}/dir.js`, `${out}/dir.js`); + if (existsSync(`${output_server}/dir.js.map`)) { + renameSync(`${output_server}/dir.js.map`, `${out}/dir.js.map`); } + writeFileSync(`${output_server}/dir.js`, `export * from '../dir.js';\n`); - builder.generateServerInstance(`${server}/server.js`); + // replace the stubs whose values are only known after the build + replace_stubs(output_server, handler.chunks, { + __SVELTEKIT_ADAPTER_NODE_UNCOMPRESSED_EXTENSIONS__: `new Set(${JSON.stringify([...uncompressed_extensions])})`, + __SVELTEKIT_ADAPTER_NODE_PRERENDERED__: `new Set(${JSON.stringify([...builder.prerendered.paths])})`, + __SVELTEKIT_ADAPTER_NODE_MIMETYPES__: JSON.stringify(builder.mimeTypes), + __SVELTEKIT_ADAPTER_NODE_SERVER__: (chunk) => { + const server = posixify(relative(dirname(chunk), 'server.js')); + return server.startsWith('.') ? server : `./${server}`; + } + }); + + writeFileSync(`${out}/index.js`, `export * from './server/${adapter_index.file}';\n`); + writeFileSync(`${out}/handler.js`, `export * from './server/${handler.file}';\n`); + }, + + supports: { + read: () => true, + instrumentation: () => true + }, + vite({ config }) { /** @type {Record} */ const defines = { - UNCOMPRESSED_EXTENSIONS: `new Set(${JSON.stringify([...uncompressed_extensions])})`, - BASE_PATH: JSON.stringify(builder.config.paths.base), - APP_PATH: JSON.stringify(builder.getAppPath()), - PRERENDERED: `new Set(${JSON.stringify(builder.prerendered.paths)})`, - MIME_TYPES: JSON.stringify(builder.mimeTypes), - ORIGIN: JSON.stringify(builder.config.paths.origin) || 'undefined', + // these get replaced later in the adapt step + UNCOMPRESSED_EXTENSIONS: '__SVELTEKIT_ADAPTER_NODE_UNCOMPRESSED_EXTENSIONS__', + PRERENDERED: '__SVELTEKIT_ADAPTER_NODE_PRERENDERED__', + MIME_TYPES: '__SVELTEKIT_ADAPTER_NODE_MIMETYPES__', + + BASE_PATH: JSON.stringify(config.paths.base), + APP_PATH: JSON.stringify( + `${config.paths.base.slice(1)}${config.paths.base ? '/' : ''}${config.appDir}` + ), + ORIGIN: JSON.stringify(config.paths.origin) || 'undefined', ENV_PREFIX: JSON.stringify(envPrefix), PRECOMPRESS: JSON.stringify(precompress) }; - // we bundle the Vite output so that deployments only need - // their production dependencies. Anything in devDependencies - // will get included in the bundled code - const bundle = await rolldown({ - input, - external: [ - // dependencies could have deep exports, so we need a regex - ...Object.keys(pkg.dependencies || {}).map((d) => new RegExp(`^${d}(\\/.*)?$`)), - // `@opentelemetry/api` is an optional peer dependency of `@sveltejs/kit`, - // so it's not in `pkg.dependencies` and wouldn't be matched by the regex above. - // It must stay external so that `instrumentation.server.js` and the SvelteKit - // runtime share a single instance — see https://github.com/sveltejs/kit/issues/16288 - /^@opentelemetry\/api(\/.*)?$/ - ], - platform: 'node', - resolve: { - conditionNames: ['node'] - }, - experimental: { - nativeMagicString: true - }, - plugins: [ - { - // resolve the app's server and manifest, generated above - name: 'adapter-node-resolve-app', - resolveId: { - filter: { id: /^SERVER$/ }, - handler() { - return `${server}/server.js`; - } - } - }, - { - // replace build-time constants in the adapter's own entrypoints - // only, so that identifiers in the app or its dependencies aren't - // accidentally replaced - name: 'adapter-node-replace-constants', - transform: { - filter: { id: new RegExp(escape_regex(entries)) }, - handler(_code, _id, { magicString }) { - if (!magicString) throw new Error('experimental.nativeMagicString is not enabled'); - - for (const [from, to] of Object.entries(defines)) { - // remove $& and $N substitutions by replacing every $ with $$ - const value = to.replace(/\$/g, '$$$$'); - magicString.replace(new RegExp(`\\b${from}\\b`, 'g'), value); - } + return { + plugins: { + post: [ + { + name: 'vite-plugin-sveltekit-adapter-node', + apply: 'build', + config() { + const pkg = JSON.parse(readFileSync('package.json', 'utf8')); return { - code: magicString, - map: magicString.generateMap().toString() + environments: { + ssr: { + build: { + rolldownOptions: { + // Copy the prebuilt entrypoints into the build directory so that the + // adapter's own bundled dependencies resolve correctly, then bundle them + // together with the app's server code. Bundling everything in a single + // pass means shared modules (e.g. `SvelteKitError` from `@sveltejs/kit`) + // aren't duplicated. See https://github.com/sveltejs/kit/issues/15755 + input: { + 'adapter-index': `${files}/index.js`, + 'adapter-env': `${files}/adapter-env.js`, + handler: `${files}/handler.js` + }, + output: { + chunkFileNames(chunk) { + if (chunk.name === 'dir') return '[name].js'; + return 'chunks/[name].js'; + }, + codeSplitting: { + groups: [ + { + name: 'dir', + test: dir_id + } + ] + } + }, + // deployments only need their production dependencies. + // Anything in devDependencies will get included in the + // bundled code + external: [ + // dependencies could have deep exports, so we need a regex + ...Object.keys(pkg.dependencies || {}).map( + (d) => new RegExp(`^${d}(\\/.*)?$`) + ) + ] + } + } + } + } }; - } - } - } - ] - }); + }, + applyToEnvironment(environment) { + return environment.name === 'ssr'; + }, + transform: { + filter: { id: new RegExp(`^${escape_regex(posixified_files)}/`) }, + handler(code) { + const s = new MagicString(code); - await bundle.write({ - dir: out, - format: 'esm', - sourcemap: true, - codeSplitting: { - groups: [ - { - name: 'dir', - test: dir_id + for (const [from, to] of Object.entries(defines)) { + for (const match of code.matchAll(new RegExp(`\\b${from}\\b`, 'g'))) { + s.overwrite(match.index, match.index + from.length, to); + } + } + + return { + code: s.toString(), + map: s.generateMap({ hires: 'boundary' }) + }; + } + }, + resolveId: { + // composable filters are not accepted type-wise but still work during build + // see https://github.com/vitejs/rolldown-vite/issues/605 + filter: /** @type {any} */ ( + /** @satisfies {TopLevelFilterExpression[]} */ ([ + include( + and( + importerId(new RegExp(`^${escape_regex(posixified_files)}/`)), + id(/^SERVER$/) + ) + ) + ]) + ), + handler() { + return { external: true, id: '__SVELTEKIT_ADAPTER_NODE_SERVER__' }; + } + } } ] - }, - chunkFileNames(chunk) { - if (chunk.name === 'dir') return '[name].js'; - return 'server/chunks/[name]-[hash].js'; } - }); + }; + } + }; +} - if (builder.hasServerInstrumentationFile()) { - builder.instrument({ - entrypoint: `${out}/index.js`, - instrumentation: `${out}/instrumentation.server.js`, - initializer: `${out}/environment.js`, - module: { - exports: ['path', 'host', 'port', 'server'] - } - }); - } - }, +/** + * @typedef {{ file: string; imports?: string[]; isEntry?: boolean; name?: string }} ManifestEntry + */ - supports: { - read: () => true, - instrumentation: () => true - } +/** + * @param {Record} manifest + * @param {string} name + */ +function find_entry(manifest, name) { + const match = Object.entries(manifest).find(([, entry]) => entry.isEntry && entry.name === name); + if (!match) throw new Error(`Could not find adapter-node entry ${name} in the Vite manifest`); + + /** @type {string[]} */ + const chunks = []; + const seen = new Set(); + + /** @param {string} key */ + const visit = (key) => { + if (seen.has(key)) return; + seen.add(key); + + const chunk = manifest[key]; + if (!chunk) throw new Error(`Could not find ${key} in the Vite manifest`); + + chunks.push(chunk.file); + for (const dependency of chunk.imports ?? []) visit(dependency); }; + + visit(match[0]); + return { file: match[1].file, chunks }; +} + +/** + * @param {string} dir + * @param {string[]} chunks + * @param {Record string)>} replacements + */ +function replace_stubs(dir, chunks, replacements) { + const pattern = new RegExp(`\\b(${Object.keys(replacements).join('|')})\\b`, 'g'); + const found = new Set(); + + for (const chunk of chunks) { + const file = `${dir}/${chunk}`; + const code = readFileSync(file, 'utf8'); + const matches = [...code.matchAll(pattern)]; + if (matches.length === 0) continue; + + const s = new MagicString(code); + + for (const match of matches) { + found.add(match[0]); + const replacement = replacements[match[0]]; + const value = typeof replacement === 'function' ? replacement(chunk) : replacement; + s.overwrite(match.index, match.index + match[0].length, value); + } + + writeFileSync(file, s.toString()); + + const map_file = `${file}.map`; + if (!existsSync(map_file)) continue; + + const map = remapping( + [ + JSON.parse(s.generateMap({ hires: 'boundary', source: chunk }).toString()), + JSON.parse(readFileSync(map_file, 'utf8')) + ], + () => null + ); + writeFileSync(map_file, map.toString()); + } + + const missing = Object.keys(replacements).filter((stub) => !found.has(stub)); + if (missing.length > 0) { + throw new Error(`Could not find adapter-node stubs: ${missing.join(', ')}`); + } +} + +/** @param {string} str */ +function escape_regex(str) { + // TODO replace with `RegExp.escape(str)` when we require Node >= 24 + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** @param {string} str */ diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json index d42ab3708086..6edd56f136cd 100644 --- a/packages/adapter-node/package.json +++ b/packages/adapter-node/package.json @@ -35,24 +35,28 @@ "scripts": { "dev": "rolldown -cw", "build": "rolldown -c", - "test": "vitest run", + "test": "pnpm test:unit && pnpm test:build", "test:unit": "vitest run", + "test:build": "pnpm build && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:build", "check": "tsc", "lint": "oxfmt --check .", "format": "oxfmt --write .", "prepublishOnly": "pnpm build" }, + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@rolldown/pluginutils": "^1.0.1", + "magic-string": "^1.2.3" + }, "devDependencies": { "@polka/url": "catalog:", "@sveltejs/kit": "workspace:^", "@types/node": "catalog:", + "rolldown": "catalog:", "sirv": "^3.0.2", "typescript": "catalog:typescript-native", "vitest": "catalog:" }, - "dependencies": { - "rolldown": "^1.2.3" - }, "peerDependencies": { "@sveltejs/kit": "^3.0.0-next.0" } diff --git a/packages/adapter-node/test/apps/basic/build.test.js b/packages/adapter-node/test/apps/basic/build.test.js new file mode 100644 index 000000000000..1067c144aa17 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/build.test.js @@ -0,0 +1,23 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { afterEach, expect, test, vi } from 'vitest'; + +afterEach(() => vi.unstubAllEnvs()); + +test('exports the handler', async () => { + vi.stubEnv('MY_CUSTOM_PORT', '5173'); + vi.stubEnv('INSTRUMENTATION_ENV', 'available'); + const { handler } = await import('./build/handler.js'); + expect(handler).toBeDefined(); +}); + +test('dependencies are not bundled', () => { + const build = path.resolve(import.meta.dirname, 'build'); + const marker = 'server-side-dep implementation'; + + for (const file of fs.readdirSync(build, { encoding: 'utf8', recursive: true })) { + if (file.endsWith('.js')) { + expect(fs.readFileSync(path.join(build, file), 'utf8')).not.toContain(marker); + } + } +}); diff --git a/packages/adapter-node/test/apps/basic/package.json b/packages/adapter-node/test/apps/basic/package.json index a84d96645a52..51556fca070e 100644 --- a/packages/adapter-node/test/apps/basic/package.json +++ b/packages/adapter-node/test/apps/basic/package.json @@ -6,6 +6,7 @@ "dev": "vite dev", "build": "MY_CUSTOM_PORT=5173 INSTRUMENTATION_ENV=available vite build", "preview": "MY_CUSTOM_PORT=5173 INSTRUMENTATION_ENV=available node build", + "test:build": "pnpm build && vitest run build.test.js", "test:platform": "playwright test" }, "devDependencies": { @@ -13,7 +14,11 @@ "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "catalog:", "svelte": "catalog:", - "vite": "catalog:" + "vite": "catalog:", + "vitest": "catalog:" + }, + "dependencies": { + "server-side-dep": "file:server-side-dep" }, "type": "module" } diff --git a/packages/adapter-node/test/apps/basic/server-side-dep/index.d.ts b/packages/adapter-node/test/apps/basic/server-side-dep/index.d.ts new file mode 100644 index 000000000000..8b5127f912f1 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/server-side-dep/index.d.ts @@ -0,0 +1 @@ +export function message(): string; diff --git a/packages/adapter-node/test/apps/basic/server-side-dep/index.js b/packages/adapter-node/test/apps/basic/server-side-dep/index.js new file mode 100644 index 000000000000..c282a9e45189 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/server-side-dep/index.js @@ -0,0 +1,4 @@ +/** @type {import('./index.js').message} */ +export function message() { + return 'server-side-dep implementation'; +} diff --git a/packages/adapter-node/test/apps/basic/server-side-dep/package.json b/packages/adapter-node/test/apps/basic/server-side-dep/package.json new file mode 100644 index 000000000000..5b26c9d1855a --- /dev/null +++ b/packages/adapter-node/test/apps/basic/server-side-dep/package.json @@ -0,0 +1,11 @@ +{ + "name": "server-side-dep", + "version": "0.0.1", + "type": "module", + "exports": { + ".": { + "default": "./index.js", + "types": "./index.d.ts" + } + } +} diff --git a/packages/adapter-node/test/apps/basic/src/routes/adapter-identifiers/+server.js b/packages/adapter-node/test/apps/basic/src/routes/adapter-identifiers/+server.js new file mode 100644 index 000000000000..de68a235327a --- /dev/null +++ b/packages/adapter-node/test/apps/basic/src/routes/adapter-identifiers/+server.js @@ -0,0 +1,11 @@ +// @ts-expect-error resolved by the Vite plugin in the test fixture +import { value as SERVER } from 'SERVER'; + +const BASE_PATH = 'user-base-path'; +const APP_PATH = 'user-app-path'; +const ENV_PREFIX = 'user-env-prefix'; +const PRECOMPRESS = 'user-precompress'; + +export function GET() { + return Response.json({ BASE_PATH, APP_PATH, ENV_PREFIX, PRECOMPRESS, SERVER }); +} diff --git a/packages/adapter-node/test/apps/basic/src/routes/external-dependency/+server.js b/packages/adapter-node/test/apps/basic/src/routes/external-dependency/+server.js new file mode 100644 index 000000000000..52df90bf8861 --- /dev/null +++ b/packages/adapter-node/test/apps/basic/src/routes/external-dependency/+server.js @@ -0,0 +1,5 @@ +import { message } from 'server-side-dep'; + +export function GET() { + return new Response(message()); +} diff --git a/packages/adapter-node/test/apps/basic/src/routes/stub/+server.js b/packages/adapter-node/test/apps/basic/src/routes/stub/+server.js new file mode 100644 index 000000000000..cc73dcaa34aa --- /dev/null +++ b/packages/adapter-node/test/apps/basic/src/routes/stub/+server.js @@ -0,0 +1,5 @@ +const stub = '__SVELTEKIT_ADAPTER_NODE_MIMETYPES__'; + +export function GET() { + return new Response(stub); +} diff --git a/packages/adapter-node/test/apps/basic/test/test.js b/packages/adapter-node/test/apps/basic/test/test.js index 81930bf52807..68f22a449044 100644 --- a/packages/adapter-node/test/apps/basic/test/test.js +++ b/packages/adapter-node/test/apps/basic/test/test.js @@ -12,6 +12,11 @@ test('CSR', async ({ page }) => { await expect(page.locator('button')).toContainText('Toggle: true'); }); +test('loads external dependencies', async ({ request }) => { + const response = await request.get('/external-dependency'); + expect(await response.text()).toBe('server-side-dep implementation'); +}); + test('sets X-Accel-Buffering header on text/event-stream responses', async ({ request }) => { const response = await request.get('/event-stream'); expect(response.headers()['content-type']).toContain('text/event-stream'); @@ -28,6 +33,17 @@ test('initializes dynamic env before instrumentation', async ({ request }) => { expect(await response.json()).toEqual({ value: 'available' }); }); +test('preserves similar user identifiers and imports', async ({ request }) => { + const response = await request.get('/adapter-identifiers'); + expect(await response.json()).toEqual({ + BASE_PATH: 'user-base-path', + APP_PATH: 'user-app-path', + ENV_PREFIX: 'user-env-prefix', + PRECOMPRESS: 'user-precompress', + SERVER: 'user-server' + }); +}); + test('sets Vary on assets that were precompressed', async ({ request }) => { const response = await request.get('/data.json'); expect(response.status()).toBe(200); @@ -76,3 +92,8 @@ test('serves static HTML with a charset', async ({ request }) => { expect(response.status()).toBe(200); expect(response.headers()['content-type']).toBe('text/html;charset=utf-8'); }); + +test('does not replace adapter stubs in application chunks', async ({ request }) => { + const response = await request.get('/stub'); + expect(await response.text()).toBe('__SVELTEKIT_ADAPTER_NODE_MIMETYPES__'); +}); diff --git a/packages/adapter-node/test/apps/basic/vite.config.js b/packages/adapter-node/test/apps/basic/vite.config.js index 71f440a54514..3c5c1848a5ac 100644 --- a/packages/adapter-node/test/apps/basic/vite.config.js +++ b/packages/adapter-node/test/apps/basic/vite.config.js @@ -11,7 +11,19 @@ const config = { adapter: adapter({ envPrefix: 'MY_CUSTOM_' }) - }) + }), + { + name: 'test-user-server-module', + enforce: 'post', + resolveId(id) { + if (id === 'SERVER') return '\0test-user-server-module'; + }, + load(id) { + if (id === '\0test-user-server-module') { + return `export const value = 'user-server'`; + } + } + } ] }; diff --git a/packages/adapter-node/vitest.config.js b/packages/adapter-node/vitest.config.js index a15b3a470a6d..074d6a223307 100644 --- a/packages/adapter-node/vitest.config.js +++ b/packages/adapter-node/vitest.config.js @@ -2,4 +2,8 @@ import { defineConfig } from 'vitest/config'; -export default defineConfig({}); +export default defineConfig({ + test: { + include: ['src/**/*.spec.{js,ts}'] + } +}); diff --git a/packages/enhanced-img/package.json b/packages/enhanced-img/package.json index 1def35f03450..83c8e7a1822d 100644 --- a/packages/enhanced-img/package.json +++ b/packages/enhanced-img/package.json @@ -38,7 +38,7 @@ }, "types": "types/index.d.ts", "dependencies": { - "magic-string": "^1.1.0", + "magic-string": "^1.2.3", "sharp": "^0.35.3", "vite-imagetools": "^12.0.0", "zimmerframe": "^1.1.4" diff --git a/packages/kit/package.json b/packages/kit/package.json index 61604dfed492..ae74ad3b7765 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -23,7 +23,7 @@ "cookie": "^2.0.1", "devalue": "^5.9.0", "esm-env": "^1.2.2", - "magic-string": "^1.1.0", + "magic-string": "^1.2.3", "mrmime": "^2.0.1", "sirv": "^3.0.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3b2f2d798a7..9a14efb8a4bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,6 +170,9 @@ catalogs: prettier-plugin-svelte: specifier: ^4.1.1 version: 4.1.1 + rolldown: + specifier: ^1.2.3 + version: 1.2.3 sirv-cli: specifier: ^3.0.1 version: 3.0.1 @@ -455,7 +458,13 @@ importers: packages/adapter-node: dependencies: - rolldown: + '@jridgewell/remapping': + specifier: ^2.3.5 + version: 2.3.5 + '@rolldown/pluginutils': + specifier: ^1.0.1 + version: 1.0.1 + magic-string: specifier: ^1.2.3 version: 1.2.3 devDependencies: @@ -468,6 +477,9 @@ importers: '@types/node': specifier: 'catalog:' version: 22.19.19 + rolldown: + specifier: 'catalog:' + version: 1.2.3 sirv: specifier: ^3.0.2 version: 3.0.2 @@ -479,6 +491,10 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.10)(jsdom@30.0.1)(vite@8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)) packages/adapter-node/test/apps/basic: + dependencies: + server-side-dep: + specifier: file:server-side-dep + version: file:packages/adapter-node/test/apps/basic/server-side-dep devDependencies: '@playwright/test': specifier: 'catalog:' @@ -495,6 +511,9 @@ importers: vite: specifier: 'catalog:' version: 8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0) + vitest: + specifier: 'catalog:' + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.19.19)(@vitest/browser-playwright@4.1.10)(jsdom@30.0.1)(vite@8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)) packages/adapter-static: devDependencies: @@ -617,8 +636,8 @@ importers: specifier: ^7.0.0 version: 7.3.0(svelte@5.57.0(@typescript-eslint/types@8.61.1))(vite@8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0)) magic-string: - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.3 + version: 1.2.3 sharp: specifier: ^0.35.3 version: 0.35.3(@types/node@22.19.19) @@ -684,8 +703,8 @@ importers: specifier: ^1.2.2 version: 1.2.2 magic-string: - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.3 + version: 1.2.3 mrmime: specifier: ^2.0.1 version: 2.0.1 @@ -4028,8 +4047,8 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@1.1.0: - resolution: {integrity: sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==} + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -4278,6 +4297,9 @@ packages: server-side-dep@file:packages/adapter-cloudflare/test/apps/workers/server-side-dep: resolution: {directory: packages/adapter-cloudflare/test/apps/workers/server-side-dep, type: directory} + server-side-dep@file:packages/adapter-node/test/apps/basic/server-side-dep: + resolution: {directory: packages/adapter-node/test/apps/basic/server-side-dep, type: directory} + sharp@0.35.2: resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} engines: {node: '>=20.9.0'} @@ -5793,7 +5815,7 @@ snapshots: '@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0(@typescript-eslint/types@8.61.1))(vite@8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0))': dependencies: deepmerge: 4.3.1 - magic-string: 1.1.0 + magic-string: 1.2.3 obug: 2.1.4 svelte: 5.57.0(@typescript-eslint/types@8.61.1) vite: 8.2.1(@types/node@22.19.19)(esbuild@0.28.1)(jiti@2.4.2)(yaml@2.9.0) @@ -6661,7 +6683,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magic-string@1.1.0: + magic-string@1.2.3: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6896,6 +6918,8 @@ snapshots: server-side-dep@file:packages/adapter-cloudflare/test/apps/workers/server-side-dep: {} + server-side-dep@file:packages/adapter-node/test/apps/basic/server-side-dep: {} + sharp@0.35.2: dependencies: '@img/colour': 1.1.0