Skip to content

Commit f2b9271

Browse files
committed
feat: add Vite 7 Environment API support
- Upgrade to Vite 7.0.3 and add pnpm override to ensure consistent versions - Add sveltekit_vite_environment virtual module for dev-time manifest access - Add DevEnvironment type for sharing state between plugin and dev server - Add environment.js utilities for module resolution in Vite environments - Update dev server to populate and invalidate environment module - Support environments config for future isolated builds This is a minimal port of the Environment API changes from PR sveltejs#14008, enabling Vite 7 support while maintaining compatibility with existing adapters.
1 parent 85a57a0 commit f2b9271

File tree

8 files changed

+649
-178
lines changed

8 files changed

+649
-178
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
"svelte-preprocess",
4949
"unix-dgram",
5050
"workerd"
51-
]
51+
],
52+
"overrides": {
53+
"vite": "^7.0.3"
54+
}
5255
}
5356
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import path from 'node:path';
2+
3+
// `posixify` and `to_fs` are duplicated from utils/filesystem.js to avoid
4+
// the import from `node:fs` which isn't available in Cloudflare's workerd runtime
5+
6+
/** @param {string} str */
7+
export function posixify(str) {
8+
return str.replace(/\\/g, '/');
9+
}
10+
11+
/**
12+
* Prepend given path with `/@fs` prefix
13+
* @param {string} str
14+
*/
15+
export function to_fs(str) {
16+
str = posixify(str);
17+
return `/@fs${
18+
// Windows/Linux separation - Windows starts with a drive letter, we need a / in front there
19+
str.startsWith('/') ? '' : '/'
20+
}${str}`;
21+
}
22+
23+
/** @param {string} id */
24+
export async function resolve(id) {
25+
// TODO: doesn't work for files symlinked to kit package workspace
26+
const url = id.startsWith('..') ? to_fs(path.posix.resolve(id)) : `/${id}`;
27+
28+
const module = await loud_ssr_load_module(url);
29+
30+
// TODO: module_node
31+
// const module_node = await vite.moduleGraph.getModuleByUrl(url);
32+
// if (!module_node) throw new Error(`Could not find node for ${url}`);
33+
34+
// return { module, module_node, url };
35+
return { module, module_node: '', url };
36+
}
37+
38+
/**
39+
* @param {string} url
40+
* @returns {Promise<Record<string, any>>}
41+
*/
42+
export async function loud_ssr_load_module(url) {
43+
try {
44+
return await import(/* @vite-ignore */ url);
45+
} catch (/** @type {any} */ err) {
46+
// TODO: implement error logging
47+
// const msg = buildErrorMessage(err, [colors.red(`Internal server error: ${err.message}`)]);
48+
49+
// if (!vite.config.logger.hasErrorLogged(err)) {
50+
// vite.config.logger.error(msg, { error: err });
51+
// }
52+
53+
// vite.ws.send({
54+
// type: 'error',
55+
// err: {
56+
// ...err,
57+
// // these properties are non-enumerable and will
58+
// // not be serialized unless we explicitly include them
59+
// message: err.message,
60+
// stack: err.stack
61+
// }
62+
// });
63+
64+
throw err;
65+
}
66+
}

packages/kit/src/exports/vite/dev/index.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { SCHEME } from '../../../utils/url.js';
2020
import { check_feature } from '../../../utils/features.js';
2121
import { escape_html } from '../../../utils/escape.js';
2222
import { create_node_analyser } from '../static_analysis/index.js';
23+
import { sveltekit_vite_environment } from '../module_ids.js';
2324

2425
const cwd = process.cwd();
2526
// vite-specifc queries that we should skip handling for css urls
@@ -29,10 +30,11 @@ const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/;
2930
* @param {import('vite').ViteDevServer} vite
3031
* @param {import('vite').ResolvedConfig} vite_config
3132
* @param {import('types').ValidatedConfig} svelte_config
33+
* @param {import('types').DevEnvironment} dev_environment
3234
* @param {() => Array<{ hash: string, file: string }>} get_remotes
3335
* @return {Promise<Promise<() => void>>}
3436
*/
35-
export async function dev(vite, vite_config, svelte_config, get_remotes) {
37+
export async function dev(vite, vite_config, svelte_config, dev_environment, get_remotes) {
3638
installPolyfills();
3739

3840
const async_local_storage = new AsyncLocalStorage();
@@ -65,6 +67,20 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
6567
/** @type {Error | null} */
6668
let manifest_error = null;
6769

70+
function invalidate_vite_environment_module() {
71+
if (vite.environments) {
72+
for (const environment in vite.environments) {
73+
const module = vite.environments[environment].moduleGraph.getModuleById(
74+
sveltekit_vite_environment
75+
);
76+
77+
if (module) {
78+
vite.environments[environment].moduleGraph.invalidateModule(module);
79+
}
80+
}
81+
}
82+
}
83+
6884
/** @param {string} url */
6985
async function loud_ssr_load_module(url) {
7086
try {
@@ -110,6 +126,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
110126
try {
111127
({ manifest_data } = sync.create(svelte_config));
112128

129+
// Populate dev_environment for the virtual module
130+
dev_environment.manifest_data = manifest_data;
131+
invalidate_vite_environment_module();
132+
113133
if (manifest_error) {
114134
manifest_error = null;
115135
vite.ws.send({ type: 'full-reload' });
@@ -449,6 +469,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
449469
});
450470

451471
const env = loadEnv(vite_config.mode, svelte_config.kit.env.dir, '');
472+
dev_environment.env = env;
452473
const emulator = await svelte_config.kit.adapter?.emulate?.();
453474

454475
return () => {
@@ -461,6 +482,16 @@ export async function dev(vite, vite_config, svelte_config, get_remotes) {
461482
// serving routes with those names. See https://github.com/vitejs/vite/issues/7363
462483
remove_static_middlewares(vite.middlewares);
463484

485+
// Capture remote address for the virtual module
486+
vite.middlewares.stack.unshift({
487+
route: '',
488+
/** @type {import('vite').Connect.NextHandleFunction} */
489+
handle: (req, _, next) => {
490+
dev_environment.remote_address = req.socket.remoteAddress;
491+
next();
492+
}
493+
});
494+
464495
vite.middlewares.use(async (req, res) => {
465496
// Vite's base middleware strips out the base path. Restore it
466497
const original_url = req.url;

packages/kit/src/exports/vite/index.js

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { copy, mkdirp, posixify, read, resolve_entry, rimraf } from '../../utils
88
import { create_static_module, create_dynamic_module } from '../../core/env.js';
99
import * as sync from '../../core/sync/sync.js';
1010
import { create_assets } from '../../core/sync/create_manifest_data/index.js';
11-
import { runtime_directory, logger } from '../../core/utils.js';
11+
import { runtime_directory, logger, get_mime_lookup, runtime_base } from '../../core/utils.js';
12+
import { to_fs } from '../../utils/filesystem.js';
1213
import { load_config } from '../../core/config/index.js';
1314
import { generate_manifest } from '../../core/generate_manifest/index.js';
1415
import { build_server_nodes } from './build/build_server.js';
@@ -36,7 +37,8 @@ import {
3637
env_static_public,
3738
service_worker,
3839
sveltekit_environment,
39-
sveltekit_server
40+
sveltekit_server,
41+
sveltekit_vite_environment
4042
} from './module_ids.js';
4143
import { import_peer } from '../../utils/import.js';
4244
import { compact } from '../../utils/array.js';
@@ -175,6 +177,8 @@ let manifest_data;
175177
/** @type {import('types').ServerMetadata | undefined} only set at build time once analysis is finished */
176178
let build_metadata = undefined;
177179

180+
const dev_environment = /** @type {import('types').DevEnvironment} */ ({});
181+
178182
/**
179183
* Returns the SvelteKit Vite plugin. Vite executes Rollup hooks as well as some of its own.
180184
* Background reading is available at:
@@ -531,6 +535,97 @@ async function kit({ svelte_config }) {
531535
}
532536
`;
533537
}
538+
539+
case sveltekit_vite_environment: {
540+
const { manifest_data: md, env: dev_env, remote_address } = dev_environment;
541+
542+
if (!md) {
543+
return 'export const manifest = null; export const env = {}; export const remote_address = undefined; export const base_path = ""; export const prerendered = new Set();';
544+
}
545+
546+
const nodes_code = md.nodes.map((node, index) => {
547+
const component = s(node.component);
548+
return `async () => {
549+
const result = {
550+
index: ${s(index)},
551+
universal_id: ${s(node.universal)},
552+
server_id: ${s(node.server)},
553+
imports: [],
554+
stylesheets: [],
555+
fonts: []
556+
};
557+
if (${component}) {
558+
result.component = async () => {
559+
const { module } = await resolve(${component});
560+
return module.default;
561+
};
562+
}
563+
if (result.universal_id) {
564+
const { module } = await resolve(result.universal_id);
565+
result.universal = module;
566+
}
567+
if (result.server_id) {
568+
const { module } = await resolve(result.server_id);
569+
result.server = module;
570+
}
571+
result.inline_styles = async () => ({});
572+
return result;
573+
}`;
574+
}).join(',\n');
575+
576+
const routes_code = compact(md.routes.map((route) => {
577+
if (!route.page && !route.endpoint) return null;
578+
const endpoint = route.endpoint;
579+
return `{
580+
id: ${s(route.id)},
581+
pattern: /${route.pattern.source}/,
582+
params: ${s(route.params)},
583+
page: ${s(route.page)},
584+
endpoint: ${endpoint ? `async () => { const url = path.resolve(cwd, ${s(endpoint.file)}); return await loud_ssr_load_module(url); }` : 'null'},
585+
endpoint_id: ${s(endpoint?.file)}
586+
}`;
587+
})).join(',\n');
588+
589+
return `
590+
import path from "node:path";
591+
import { resolve, loud_ssr_load_module } from "${runtime_base}/../exports/vite/dev/environment.js";
592+
const cwd = ${s(cwd)};
593+
export const manifest = {
594+
appDir: ${s(svelte_config.kit.appDir)},
595+
appPath: ${s(svelte_config.kit.appDir)},
596+
assets: new Set(${s(md.assets.map((asset) => asset.file))}),
597+
mimeTypes: ${s(get_mime_lookup(md))},
598+
_: {
599+
client: {
600+
start: "${runtime_base}/client/entry.js",
601+
app: "${to_fs(svelte_config.kit.outDir)}/generated/client/app.js",
602+
imports: [],
603+
stylesheets: [],
604+
fonts: [],
605+
uses_env_dynamic_public: true
606+
},
607+
server_assets: new Proxy({}, { has: () => true, get: () => 1 }),
608+
nodes: [${nodes_code}],
609+
prerendered_routes: new Set(),
610+
routes: [${routes_code}],
611+
matchers: async () => {
612+
const matchers = {};
613+
for (const [key, file] of ${s(Object.entries(md.matchers))}) {
614+
const url = path.resolve(cwd, file);
615+
const module = await import(url);
616+
if (module.match) matchers[key] = module.match;
617+
else throw new Error(file + ' does not export a match function');
618+
}
619+
return matchers;
620+
}
621+
}
622+
};
623+
export const env = ${s(dev_env)};
624+
export const remote_address = ${s(remote_address)};
625+
export const base_path = ${s(svelte_config.kit.paths.base)};
626+
export const prerendered = manifest._.prerendered_routes;
627+
`;
628+
}
534629
}
535630
}
536631
};
@@ -964,7 +1059,7 @@ async function kit({ svelte_config }) {
9641059
* @see https://vitejs.dev/guide/api-plugin.html#configureserver
9651060
*/
9661061
async configureServer(vite) {
967-
return await dev(vite, vite_config, svelte_config, () => remotes);
1062+
return await dev(vite, vite_config, svelte_config, dev_environment, () => remotes);
9681063
},
9691064

9701065
/**

packages/kit/src/exports/vite/module_ids.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ export const env_dynamic_public = '\0virtual:env/dynamic/public';
99
export const service_worker = '\0virtual:service-worker';
1010

1111
export const sveltekit_environment = '\0virtual:__sveltekit/environment';
12+
export const sveltekit_paths = '\0virtual:__sveltekit/paths';
1213
export const sveltekit_server = '\0virtual:__sveltekit/server';
14+
export const sveltekit_vite_environment = '\0virtual:__sveltekit/vite-environment';
1315

1416
export const app_server = posixify(
1517
fileURLToPath(new URL('../../runtime/app/server/index.js', import.meta.url))

packages/kit/src/types/internal.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,5 +627,11 @@ export interface RequestStore {
627627
state: RequestState;
628628
}
629629

630+
export interface DevEnvironment {
631+
manifest_data: ManifestData;
632+
env: Record<string, string>;
633+
remote_address: string | undefined;
634+
}
635+
630636
export * from '../exports/index.js';
631637
export * from './private.js';

0 commit comments

Comments
 (0)