Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove ":$" from virtual module ids to allow dev server to work with proxies #12157

Merged
merged 9 commits into from
Jan 13, 2025
5 changes: 5 additions & 0 deletions .changeset/eleven-colts-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: remove ":$" from virtual module ids to allow dev server to work with proxies
10 changes: 7 additions & 3 deletions packages/kit/src/exports/vite/build/build_service_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs';
import * as vite from 'vite';
import { dedent } from '../../../core/sync/utils.js';
import { s } from '../../../utils/misc.js';
import { get_config_aliases, strip_virtual_prefix, get_env } from '../utils.js';
import { get_config_aliases, strip_virtual_prefix, get_env, normalize_id } from '../utils.js';
import { create_static_module } from '../../../core/env.js';
import { env_static_public, service_worker } from '../module_ids.js';

Expand Down Expand Up @@ -68,7 +68,8 @@ export async function build_service_worker(
name: 'service-worker-build-virtual-modules',
resolveId(id) {
if (id.startsWith('$env/') || id.startsWith('$app/') || id === '$service-worker') {
return `\0virtual:${id}`;
// ids with :$ don't work with reverse proxies like nginx
return `\0virtual:${id.substring(1)}`;
dummdidumm marked this conversation as resolved.
Show resolved Hide resolved
}
},

Expand All @@ -83,7 +84,10 @@ export async function build_service_worker(
return create_static_module('$env/static/public', env.public);
}

const stripped = strip_virtual_prefix(id);
const normalized_cwd = vite.normalizePath(process.cwd());
const normalized_lib = vite.normalizePath(kit.files.lib);
const relative = normalize_id(id, normalized_lib, normalized_cwd);
const stripped = strip_virtual_prefix(relative);
throw new Error(
`Cannot import ${stripped} into service-worker code. Only the modules $service-worker and $env/static/public are available in service workers.`
);
Expand Down
24 changes: 1 addition & 23 deletions packages/kit/src/exports/vite/graph_analysis/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';
import { posixify } from '../../../utils/filesystem.js';
import { strip_virtual_prefix } from '../utils.js';
import { normalize_id, strip_virtual_prefix } from '../utils.js';
import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js';

const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]);
Expand Down Expand Up @@ -85,25 +85,3 @@ export function module_guard(context, { cwd, lib }) {
}
};
}

/**
* 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);
}

if (id === app_server) {
return '$app/server';
}

return posixify(id);
}
14 changes: 10 additions & 4 deletions packages/kit/src/exports/vite/graph_analysis/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assert, test } from 'vitest';
import { module_guard } from './index.js';
import * as module_ids from '../module_ids.js';

/**
*
Expand Down Expand Up @@ -44,7 +45,7 @@ test('throws an error when importing $env/static/private', () => {
importedIds: ['~/src/routes/+page.svelte']
},
'~/src/routes/+page.svelte': {
importedIds: ['\0virtual:$env/static/private']
importedIds: ['\0virtual:env/static/private']
}
},
`Cannot import $env/static/private into client-side code:
Expand All @@ -60,7 +61,7 @@ test('throws an error when dynamically importing $env/static/private', () => {
importedIds: ['~/src/routes/+page.svelte']
},
'~/src/routes/+page.svelte': {
dynamicallyImportedIds: ['\0virtual:$env/static/private']
dynamicallyImportedIds: ['\0virtual:env/static/private']
}
},
`Cannot import $env/static/private into client-side code:
Expand All @@ -76,7 +77,7 @@ test('throws an error when importing $env/dynamic/private', () => {
importedIds: ['~/src/routes/+page.svelte']
},
'~/src/routes/+page.svelte': {
importedIds: ['\0virtual:$env/dynamic/private']
importedIds: ['\0virtual:env/dynamic/private']
}
},
`Cannot import $env/dynamic/private into client-side code:
Expand All @@ -92,7 +93,7 @@ test('throws an error when dynamically importing $env/dynamic/private', () => {
importedIds: ['~/src/routes/+page.svelte']
},
'~/src/routes/+page.svelte': {
dynamicallyImportedIds: ['\0virtual:$env/dynamic/private']
dynamicallyImportedIds: ['\0virtual:env/dynamic/private']
}
},
`Cannot import $env/dynamic/private into client-side code:
Expand All @@ -101,6 +102,11 @@ test('throws an error when dynamically importing $env/dynamic/private', () => {
);
});

// nginx does not process URLs containing the sequence `:$`
test('":$" is not in virtual module ids', () => {
benmccann marked this conversation as resolved.
Show resolved Hide resolved
assert.notInclude(Object.values(module_ids).join(''), ':$');
});

test('throws an error when importing a .server.js module', () => {
check(
{
Expand Down
18 changes: 14 additions & 4 deletions packages/kit/src/exports/vite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { build_server_nodes } from './build/build_server.js';
import { build_service_worker } from './build/build_service_worker.js';
import { assets_base, find_deps } from './build/utils.js';
import { dev } from './dev/index.js';
import { is_illegal, module_guard, normalize_id } from './graph_analysis/index.js';
import { is_illegal, module_guard } from './graph_analysis/index.js';
import { preview } from './preview/index.js';
import { get_config_aliases, get_env, strip_virtual_prefix } from './utils.js';
import { get_config_aliases, get_env, normalize_id, strip_virtual_prefix } from './utils.js';
import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
import prerender from '../../core/postbuild/prerender.js';
import analyse from '../../core/postbuild/analyse.js';
Expand Down Expand Up @@ -376,16 +376,26 @@ async function kit({ svelte_config }) {
parsed_importer.name === parsed_service_worker.name;

if (importer_is_service_worker && id !== '$service-worker' && id !== '$env/static/public') {
const normalized_cwd = vite.normalizePath(cwd);
const normalized_lib = vite.normalizePath(kit.files.lib);
throw new Error(
`Cannot import ${id} into service-worker code. Only the modules $service-worker and $env/static/public are available in service workers.`
`Cannot import ${normalize_id(
id,
normalized_lib,
normalized_cwd
)} into service-worker code. Only the modules $service-worker and $env/static/public are available in service workers.`
);
}

import_map.set(id, importer);
}

// treat $env/static/[public|private] as virtual
if (id.startsWith('$env/') || id.startsWith('__sveltekit/') || id === '$service-worker') {
if (id.startsWith('$env/') || id === '$service-worker') {
// ids with :$ don't work with reverse proxies like nginx
return `\0virtual:${id.substring(1)}`;
}
if (id.startsWith('__sveltekit/')) {
return `\0virtual:${id}`;
}
},
Expand Down
10 changes: 5 additions & 5 deletions packages/kit/src/exports/vite/module_ids.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { fileURLToPath } from 'node:url';

export const env_static_private = '\0virtual:$env/static/private';
export const env_static_public = '\0virtual:$env/static/public';
export const env_dynamic_private = '\0virtual:$env/dynamic/private';
export const env_dynamic_public = '\0virtual:$env/dynamic/public';
export const env_static_private = '\0virtual:env/static/private';
export const env_static_public = '\0virtual:env/static/public';
export const env_dynamic_private = '\0virtual:env/dynamic/private';
export const env_dynamic_public = '\0virtual:env/dynamic/public';

export const service_worker = '\0virtual:$service-worker';
export const service_worker = '\0virtual:service-worker';

export const sveltekit_environment = '\0virtual:__sveltekit/environment';
export const sveltekit_paths = '\0virtual:__sveltekit/paths';
Expand Down
50 changes: 50 additions & 0 deletions packages/kit/src/exports/vite/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import { posixify } from '../../utils/filesystem.js';
import { negotiate } from '../../utils/http.js';
import { filter_private_env, filter_public_env } from '../../utils/env.js';
import { escape_html } from '../../utils/escape.js';
import {
app_server,
env_dynamic_private,
env_dynamic_public,
env_static_private,
env_static_public,
service_worker
} from './module_ids.js';

/**
* Transforms kit.alias to a valid vite.resolve.alias array.
Expand Down Expand Up @@ -105,4 +113,46 @@ export function not_found(req, res, base) {
}
}

/**
* 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);
}

if (id === app_server) {
return '$app/server';
}

if (id === env_static_private) {
return '$env/static/private';
}

if (id === env_static_public) {
return '$env/static/public';
}

if (id === env_dynamic_private) {
return '$env/dynamic/private';
}

if (id === env_dynamic_public) {
return '$env/dynamic/public';
}

if (id === service_worker) {
return '$service-worker';
}

return posixify(id);
}

export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
Loading