@sveltejs/kit 3.0.0-next.24 → 3.0.0-next.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -10
- package/src/constants.js +6 -0
- package/src/core/adapt/builder.js +128 -33
- package/src/core/adapt/index.js +3 -0
- package/src/core/config/index.js +4 -7
- package/src/core/env.js +39 -14
- package/src/core/generate_manifest/index.js +42 -43
- package/src/core/postbuild/analyse.js +4 -4
- package/src/core/postbuild/fallback.js +1 -1
- package/src/core/postbuild/prerender.js +4 -4
- package/src/core/sync/create_manifest_data/index.js +0 -1
- package/src/core/sync/write_app_manifest.js +3 -2
- package/src/core/sync/write_server.js +3 -17
- package/src/core/utils.js +10 -0
- package/src/exports/adapter.js +22 -0
- package/src/exports/public.d.ts +101 -49
- package/src/exports/vite/build/index.js +1173 -0
- package/src/exports/vite/build/remote.js +4 -4
- package/src/exports/vite/build/service-worker.js +98 -0
- package/src/exports/vite/dev/generate_manifest.js +308 -0
- package/src/exports/vite/dev/index.js +40 -282
- package/src/exports/vite/index.js +156 -1717
- package/src/exports/vite/plugins/env-vars.js +53 -34
- package/src/exports/vite/plugins/guard.js +217 -0
- package/src/exports/vite/plugins/remote.js +237 -0
- package/src/exports/vite/preview/index.js +8 -8
- package/src/exports/vite/static_analysis/index.js +15 -15
- package/src/exports/vite/utils.js +98 -1
- package/src/runner.js +4 -3
- package/src/runtime/app/paths/server.js +2 -2
- package/src/runtime/app/server/index.js +3 -3
- package/src/runtime/app/server/public.d.ts +114 -57
- package/src/runtime/app/server/remote/query.js +2 -2
- package/src/runtime/app/server/remote/requested.js +31 -15
- package/src/runtime/app/state/client.svelte.js +3 -1
- package/src/runtime/client/client.js +43 -198
- package/src/runtime/client/fetcher.js +6 -6
- package/src/runtime/client/focus.js +120 -0
- package/src/runtime/client/remote-functions/command.svelte.js +20 -9
- package/src/runtime/client/remote-functions/form.svelte.js +12 -7
- package/src/runtime/client/remote-functions/query/instance.svelte.js +19 -5
- package/src/runtime/client/remote-functions/shared.svelte.js +22 -1
- package/src/runtime/client/scroll.js +39 -0
- package/src/runtime/client/utils.js +28 -0
- package/src/runtime/form-utils.js +254 -264
- package/src/runtime/server/data/index.js +14 -36
- package/src/runtime/server/errors.js +7 -10
- package/src/runtime/server/fetch.js +21 -25
- package/src/runtime/server/index.js +35 -31
- package/src/runtime/server/internal.js +34 -5
- package/src/runtime/server/page/actions.js +6 -8
- package/src/runtime/server/page/data_serializer.js +6 -10
- package/src/runtime/server/page/index.js +18 -28
- package/src/runtime/server/page/load_data.js +2 -2
- package/src/runtime/server/page/render.js +22 -40
- package/src/runtime/server/page/respond_with_error.js +10 -13
- package/src/runtime/server/page/server_routing.js +21 -27
- package/src/runtime/server/remote-functions.js +136 -136
- package/src/runtime/server/respond.js +66 -64
- package/src/runtime/server/state.js +2 -0
- package/src/runtime/server/utils.js +4 -11
- package/src/runtime/shared.js +9 -0
- package/src/runtime/utils.js +41 -0
- package/src/types/ambient-private.d.ts +1 -2
- package/src/types/global-private.d.ts +8 -0
- package/src/types/internal.d.ts +56 -17
- package/src/utils/filesystem.js +44 -28
- package/src/utils/streaming.js +5 -15
- package/src/version.js +1 -1
- package/types/index.d.ts +235 -93
- package/types/index.d.ts.map +9 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { EnvVarConfig } from '@sveltejs/kit/env' */
|
|
3
3
|
/** @import { ValidatedConfig } from 'types' */
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { exactRegex } from '@rolldown/pluginutils';
|
|
5
6
|
import * as sync from '../../../core/sync/sync.js';
|
|
6
7
|
import { create_env_modules, resolve_env_entry } from '../../../core/env.js';
|
|
7
8
|
import { import_peer } from '../../../utils/import.js';
|
|
@@ -26,9 +27,6 @@ export function plugin_env_vars(config, callback) {
|
|
|
26
27
|
/** @type {ResolvedConfig} */
|
|
27
28
|
let resolved_config;
|
|
28
29
|
|
|
29
|
-
/** @type {string | null} */
|
|
30
|
-
let resolved_entry = null;
|
|
31
|
-
|
|
32
30
|
/** @type {Set<string>} */
|
|
33
31
|
let deps = new Set();
|
|
34
32
|
|
|
@@ -38,12 +36,8 @@ export function plugin_env_vars(config, callback) {
|
|
|
38
36
|
let generated;
|
|
39
37
|
|
|
40
38
|
async function generate() {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
resolved_entry,
|
|
44
|
-
resolved_config.root,
|
|
45
|
-
resolved_config.mode
|
|
46
|
-
);
|
|
39
|
+
const entry = resolve_env_entry(config, resolved_config.root);
|
|
40
|
+
const synced = await sync.env(config, entry, resolved_config.root, resolved_config.mode);
|
|
47
41
|
|
|
48
42
|
deps = synced.deps;
|
|
49
43
|
|
|
@@ -55,7 +49,7 @@ export function plugin_env_vars(config, callback) {
|
|
|
55
49
|
vars,
|
|
56
50
|
env,
|
|
57
51
|
dir,
|
|
58
|
-
|
|
52
|
+
entry && posixify(path.relative(dir, entry)),
|
|
59
53
|
!is_build
|
|
60
54
|
);
|
|
61
55
|
|
|
@@ -84,34 +78,59 @@ export function plugin_env_vars(config, callback) {
|
|
|
84
78
|
// runs once via the memo — per-process, whichever environment starts first
|
|
85
79
|
// (environment names vary by adapter), and never in the postbuild forks,
|
|
86
80
|
// which resolve the config without building
|
|
87
|
-
await (generated ??= (
|
|
88
|
-
resolved_entry = resolve_env_entry(config, resolved_config.root);
|
|
89
|
-
await generate();
|
|
90
|
-
})());
|
|
81
|
+
await (generated ??= generate());
|
|
91
82
|
},
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const resolved = resolve_env_entry(config, resolved_config.root);
|
|
100
|
-
|
|
101
|
-
if (file === resolved_entry || file === resolved) {
|
|
102
|
-
resolved_entry = resolved;
|
|
103
|
-
await generate();
|
|
104
|
-
server.hot.send({ type: 'full-reload' });
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
server.watcher.on('add', on_entry_add_unlink);
|
|
109
|
-
server.watcher.on('unlink', on_entry_add_unlink);
|
|
110
|
-
},
|
|
84
|
+
async hotUpdate({ type, file }) {
|
|
85
|
+
// runs for every environment; the generated modules are shared, so do the work once
|
|
86
|
+
if (this.environment.name !== 'client') return;
|
|
87
|
+
|
|
88
|
+
const created = type === 'create' && file === resolve_env_entry(config, resolved_config.root);
|
|
89
|
+
if (!deps.has(file) && !created) return;
|
|
111
90
|
|
|
112
|
-
async handleHotUpdate(update) {
|
|
113
|
-
if (!deps.has(update.file)) return;
|
|
114
91
|
await generate();
|
|
92
|
+
if (type !== 'update') this.environment.hot.send({ type: 'full-reload' });
|
|
115
93
|
}
|
|
116
94
|
};
|
|
117
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {() => string | null} get_service_worker_entry_file
|
|
99
|
+
* @returns {Plugin}
|
|
100
|
+
*/
|
|
101
|
+
export function plugin_service_worker_env_vars(get_service_worker_entry_file) {
|
|
102
|
+
/** @type {string | null} */
|
|
103
|
+
let service_worker_entry_file;
|
|
104
|
+
|
|
105
|
+
/** @satisfies {Plugin} */
|
|
106
|
+
const plugin = {
|
|
107
|
+
name: 'vite-plugin-sveltekit-service-worker-env',
|
|
108
|
+
configResolved() {
|
|
109
|
+
service_worker_entry_file = get_service_worker_entry_file();
|
|
110
|
+
|
|
111
|
+
if (service_worker_entry_file) {
|
|
112
|
+
plugin.transform.filter = {
|
|
113
|
+
id: exactRegex(service_worker_entry_file)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
applyToEnvironment(environment) {
|
|
118
|
+
return !!service_worker_entry_file && environment.config.consumer === 'client';
|
|
119
|
+
},
|
|
120
|
+
transform: {
|
|
121
|
+
filter: {},
|
|
122
|
+
handler(code) {
|
|
123
|
+
// prepend the service worker with an import that configures
|
|
124
|
+
// `env`, in case `$app/env/public` is imported. In production
|
|
125
|
+
// this is required: dynamic public env vars aren't known at
|
|
126
|
+
// build time, so `env.js` is loaded at runtime. In dev, the
|
|
127
|
+
// imported module just inlines the current values instead.
|
|
128
|
+
return {
|
|
129
|
+
code: `import '<sveltekit:generated>/env/service-worker.js';\n${code}`
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return plugin;
|
|
136
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/** @import { TopLevelFilterExpression } from '@rolldown/pluginutils' */
|
|
2
|
+
/** @import { ManifestData, ValidatedConfig } from 'types' */
|
|
3
|
+
/** @import { Plugin } from 'vite' */
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { and, exactRegex, importerId, include, not } from '@rolldown/pluginutils';
|
|
6
|
+
import { app_env_private, app_server } from '../module_ids.js';
|
|
7
|
+
import {
|
|
8
|
+
error_for_missing_config,
|
|
9
|
+
normalize_id,
|
|
10
|
+
remote_module_pattern,
|
|
11
|
+
server_only_directory_pattern,
|
|
12
|
+
server_only_module_pattern
|
|
13
|
+
} from '../utils.js';
|
|
14
|
+
import { stackless } from '../../../utils/error.js';
|
|
15
|
+
import { posixify } from '../../../utils/os.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ensures that client-side code can't accidentally import server-side code,
|
|
19
|
+
* whether in `*.server.js` files, `$app/server`, any `/server/` directory, or `$app/env/private`
|
|
20
|
+
* @param {ValidatedConfig} kit
|
|
21
|
+
* @param {() => { vite: typeof import('vite'); root: string; normalized_aliases: Array<{ alias: string, path: string }>; service_worker_entry_file: string | null; }} get_config
|
|
22
|
+
* @param {() => ManifestData} get_manifest_data
|
|
23
|
+
* @returns {Plugin}
|
|
24
|
+
*/
|
|
25
|
+
export function plugin_guard(kit, get_config, get_manifest_data) {
|
|
26
|
+
/** @type {string} */
|
|
27
|
+
let root;
|
|
28
|
+
|
|
29
|
+
/** @type {string} */
|
|
30
|
+
let normalized_cwd;
|
|
31
|
+
/** @type {Array<{ alias: string, path: string }>} */
|
|
32
|
+
let normalized_aliases;
|
|
33
|
+
/** @type {string} */
|
|
34
|
+
let normalized_node_modules;
|
|
35
|
+
/** @type {string} */
|
|
36
|
+
let normalized_routes;
|
|
37
|
+
/** @type {string} */
|
|
38
|
+
let normalized_assets;
|
|
39
|
+
|
|
40
|
+
/** @type {string | null} */
|
|
41
|
+
let service_worker_entry_file;
|
|
42
|
+
|
|
43
|
+
/** @type {Map<string, Set<string>>} */
|
|
44
|
+
const import_map = new Map();
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
name: 'vite-plugin-sveltekit-guard',
|
|
48
|
+
|
|
49
|
+
// Run this plugin before built-in resolution, so that relative imports
|
|
50
|
+
// are added to the module graph
|
|
51
|
+
enforce: 'pre',
|
|
52
|
+
|
|
53
|
+
configResolved() {
|
|
54
|
+
/** @type {typeof import('vite')} */
|
|
55
|
+
let vite;
|
|
56
|
+
({ vite, root, normalized_aliases, service_worker_entry_file } = get_config());
|
|
57
|
+
|
|
58
|
+
normalized_cwd = vite.normalizePath(root);
|
|
59
|
+
normalized_node_modules = vite.normalizePath(path.resolve(root, 'node_modules'));
|
|
60
|
+
normalized_routes = vite.normalizePath(path.resolve(root, kit.files.routes));
|
|
61
|
+
normalized_assets = vite.normalizePath(path.resolve(root, kit.files.assets));
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
applyToEnvironment(environment) {
|
|
65
|
+
// the import map is only read for client-side violations in `load`, so skip other environments
|
|
66
|
+
return environment.config.consumer === 'client';
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
resolveId: {
|
|
70
|
+
// composable filters are not accepted type-wise but still work during build
|
|
71
|
+
// see https://github.com/vitejs/rolldown-vite/issues/605
|
|
72
|
+
filter: /** @type {any} */ (
|
|
73
|
+
/** @satisfies {TopLevelFilterExpression[]} */ ([
|
|
74
|
+
include(and(importerId(/.+/), not(importerId(/index\.html$/))))
|
|
75
|
+
])
|
|
76
|
+
),
|
|
77
|
+
async handler(id, importer, options) {
|
|
78
|
+
// composable filters only work during build so we still need this guard for dev
|
|
79
|
+
// see https://github.com/vitejs/rolldown-vite/issues/605
|
|
80
|
+
if (importer && !importer.endsWith('index.html')) {
|
|
81
|
+
const resolved = await this.resolve(id, importer, options);
|
|
82
|
+
|
|
83
|
+
if (resolved) {
|
|
84
|
+
const normalized = normalize_id(resolved.id, normalized_aliases, normalized_cwd);
|
|
85
|
+
|
|
86
|
+
let importers = import_map.get(normalized);
|
|
87
|
+
|
|
88
|
+
if (!importers) {
|
|
89
|
+
importers = new Set();
|
|
90
|
+
import_map.set(normalized, importers);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
importers.add(normalize_id(importer, normalized_aliases, normalized_cwd));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
load: {
|
|
100
|
+
filter: {
|
|
101
|
+
id: [
|
|
102
|
+
exactRegex(app_server),
|
|
103
|
+
exactRegex(app_env_private),
|
|
104
|
+
server_only_module_pattern,
|
|
105
|
+
server_only_directory_pattern
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
handler(id) {
|
|
109
|
+
const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
|
|
110
|
+
|
|
111
|
+
let is_server_only = normalized === '$app/env/private' || normalized === '$app/server';
|
|
112
|
+
|
|
113
|
+
// skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
|
|
114
|
+
if (id.startsWith(normalized_cwd + '/') && !id.startsWith(normalized_node_modules + '/')) {
|
|
115
|
+
// e.g. `server.ts` or `foo.server.ts`
|
|
116
|
+
is_server_only ||= server_only_module_pattern.test(id);
|
|
117
|
+
|
|
118
|
+
// e.g. `server/foo.ts`, unless in `src/routes` or `static`
|
|
119
|
+
is_server_only ||=
|
|
120
|
+
server_only_directory_pattern.test(id) &&
|
|
121
|
+
!id.startsWith(normalized_routes + '/') &&
|
|
122
|
+
!id.startsWith(normalized_assets + '/');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!is_server_only) return;
|
|
126
|
+
|
|
127
|
+
const manifest_data = get_manifest_data();
|
|
128
|
+
|
|
129
|
+
/** @type {Set<string>} */
|
|
130
|
+
const entrypoints = new Set();
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Entrypoints must be normalized like the import map keys, or files
|
|
134
|
+
* outside the project root (e.g. hooks) would never match an importer
|
|
135
|
+
* @param {string} file - absolute, or relative to the project root
|
|
136
|
+
*/
|
|
137
|
+
const add_entrypoint = (file) => {
|
|
138
|
+
entrypoints.add(
|
|
139
|
+
normalize_id(posixify(path.resolve(root, file)), normalized_aliases, normalized_cwd)
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (const node of manifest_data.nodes) {
|
|
144
|
+
if (node.component) add_entrypoint(node.component);
|
|
145
|
+
if (node.universal) add_entrypoint(node.universal);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (manifest_data.hooks.client) add_entrypoint(manifest_data.hooks.client);
|
|
149
|
+
if (manifest_data.hooks.universal) add_entrypoint(manifest_data.hooks.universal);
|
|
150
|
+
|
|
151
|
+
if (service_worker_entry_file) {
|
|
152
|
+
add_entrypoint(service_worker_entry_file);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Walk up the import graph from the server-only module, looking for a chain
|
|
156
|
+
// that leads back to a client entrypoint. We search all candidates (not just
|
|
157
|
+
// the first) because a module can be imported by both server and client code,
|
|
158
|
+
// and a greedy first-match could follow a server-only branch that never
|
|
159
|
+
// reaches an entrypoint — see https://github.com/sveltejs/kit/issues/16232
|
|
160
|
+
/** @type {Set<string>} */
|
|
161
|
+
const visited = new Set([normalized]);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {string} current
|
|
165
|
+
* @param {string[]} chain
|
|
166
|
+
* @returns {string[] | null}
|
|
167
|
+
*/
|
|
168
|
+
function find_chain(current, chain) {
|
|
169
|
+
const importers = import_map.get(current);
|
|
170
|
+
if (!importers) return null;
|
|
171
|
+
|
|
172
|
+
for (const importer of importers) {
|
|
173
|
+
if (visited.has(importer)) continue;
|
|
174
|
+
visited.add(importer);
|
|
175
|
+
|
|
176
|
+
const next_chain = [...chain, importer];
|
|
177
|
+
if (entrypoints.has(importer)) {
|
|
178
|
+
return next_chain;
|
|
179
|
+
}
|
|
180
|
+
const result = find_chain(importer, next_chain);
|
|
181
|
+
if (result) return result;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const chain = find_chain(normalized, [normalized]);
|
|
187
|
+
|
|
188
|
+
if (chain) {
|
|
189
|
+
if (chain.some((id) => remote_module_pattern.test(id))) {
|
|
190
|
+
error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const pyramid = chain
|
|
194
|
+
.reverse()
|
|
195
|
+
.map((id, i) => {
|
|
196
|
+
return `${' '.repeat(i + 1)}${id}`;
|
|
197
|
+
})
|
|
198
|
+
.join(' imports\n');
|
|
199
|
+
|
|
200
|
+
let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
|
|
201
|
+
message += `\n\n${pyramid}`;
|
|
202
|
+
message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
|
|
203
|
+
|
|
204
|
+
throw stackless(message);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// No chain from this server-only module to a client entrypoint was found —
|
|
208
|
+
// the module is only imported from server code, which is valid.
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// avoid watch mode rebuilds using stale import map data
|
|
213
|
+
buildEnd() {
|
|
214
|
+
import_map.clear();
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/** @import { RemoteChunk, RemoteInternals, ServerMetadata, ValidatedConfig } from 'types' */
|
|
2
|
+
/** @import { Plugin, ViteDevServer } from 'vite' */
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { prefixRegex } from '@rolldown/pluginutils';
|
|
5
|
+
import MagicString from 'magic-string';
|
|
6
|
+
import { error_for_missing_config, is_remote_module, remote_module_pattern } from '../utils.js';
|
|
7
|
+
import { create_exported_declarations } from '../../../core/env.js';
|
|
8
|
+
import { dedent } from '../../../core/sync/utils.js';
|
|
9
|
+
import { runtime_directory } from '../../../core/utils.js';
|
|
10
|
+
import { get_runner } from '../../../runner.js';
|
|
11
|
+
import { hash } from '../../../utils/hash.js';
|
|
12
|
+
import { s } from '../../../utils/misc.js';
|
|
13
|
+
import { posixify } from '../../../utils/os.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {ValidatedConfig} svelte_config
|
|
17
|
+
* @param {() => { root: string; vite: typeof import('vite'); }} get_config
|
|
18
|
+
* @param {() => ServerMetadata | null} get_build_metadata
|
|
19
|
+
* @param {(remote_metadata: {remotes: RemoteChunk[]; remote_original_by_hash: Map<string, string>}) => void} set_remote_metadata
|
|
20
|
+
* @returns {Plugin}
|
|
21
|
+
*/
|
|
22
|
+
export function plugin_remote(svelte_config, get_config, get_build_metadata, set_remote_metadata) {
|
|
23
|
+
/** @type {string} */
|
|
24
|
+
let root;
|
|
25
|
+
/** @type {typeof import('vite')} */
|
|
26
|
+
let vite;
|
|
27
|
+
|
|
28
|
+
/** @type {ViteDevServer} */
|
|
29
|
+
let dev_server;
|
|
30
|
+
|
|
31
|
+
/** @type {ServerMetadata | null} */
|
|
32
|
+
let build_metadata;
|
|
33
|
+
|
|
34
|
+
/** @type {RemoteChunk[]} */
|
|
35
|
+
let remotes = [];
|
|
36
|
+
|
|
37
|
+
/** @type {Map<string, string>} Maps remote hash -> original module id */
|
|
38
|
+
const remote_original_by_hash = new Map();
|
|
39
|
+
/** @type {Set<string>} Track which remote hashes have already been emitted */
|
|
40
|
+
const emitted_remote_hashes = new Set();
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name: 'vite-plugin-sveltekit-remote',
|
|
44
|
+
|
|
45
|
+
configResolved() {
|
|
46
|
+
({ root, vite } = get_config());
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
configureServer(_dev_server) {
|
|
50
|
+
dev_server = _dev_server;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
applyToEnvironment(environment) {
|
|
54
|
+
return svelte_config.experimental.remoteFunctions && environment.name !== 'serviceWorker';
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
perEnvironmentStartEndDuringDev: true,
|
|
58
|
+
|
|
59
|
+
buildStart() {
|
|
60
|
+
// avoid stale data when building with watch mode
|
|
61
|
+
if (this.meta.watchMode && this.environment.config.consumer === 'server') {
|
|
62
|
+
remotes = [];
|
|
63
|
+
remote_original_by_hash.clear();
|
|
64
|
+
emitted_remote_hashes.clear();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
build_metadata = get_build_metadata();
|
|
68
|
+
set_remote_metadata({ remotes, remote_original_by_hash });
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// prevent other plugins from resolving our remote virtual module
|
|
72
|
+
resolveId: {
|
|
73
|
+
filter: {
|
|
74
|
+
id: prefixRegex('\0sveltekit-remote:')
|
|
75
|
+
},
|
|
76
|
+
handler(id) {
|
|
77
|
+
return id;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
load: {
|
|
82
|
+
filter: {
|
|
83
|
+
id: prefixRegex('\0sveltekit-remote:')
|
|
84
|
+
},
|
|
85
|
+
handler(id) {
|
|
86
|
+
// On-the-fly generated entry point for remote file just forwards the original module
|
|
87
|
+
// We're not using manualChunks because it can cause problems with circular dependencies
|
|
88
|
+
// (e.g. https://github.com/sveltejs/kit/issues/14679) and module ordering in general
|
|
89
|
+
// (e.g. https://github.com/sveltejs/kit/issues/14590).
|
|
90
|
+
const hash_id = id.slice('\0sveltekit-remote:'.length);
|
|
91
|
+
const original = remote_original_by_hash.get(hash_id);
|
|
92
|
+
if (!original) throw new Error(`Expected to find metadata for remote file ${id}`);
|
|
93
|
+
return `import * as m from ${s(original)};\nexport default m;`;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
transform: {
|
|
98
|
+
filter: {
|
|
99
|
+
id: remote_module_pattern
|
|
100
|
+
},
|
|
101
|
+
async handler(code, id) {
|
|
102
|
+
if (!is_remote_module(id)) return;
|
|
103
|
+
|
|
104
|
+
const file = posixify(path.relative(root, id));
|
|
105
|
+
const remote = {
|
|
106
|
+
hash: hash(file),
|
|
107
|
+
file
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
if (this.environment.config.consumer === 'server') {
|
|
111
|
+
remotes.push(remote);
|
|
112
|
+
|
|
113
|
+
// we need to add an `await Promise.resolve()` because if the user imports this function
|
|
114
|
+
// on the client AND in a load function when loading the client module we will trigger
|
|
115
|
+
// an import during dev. During a link preload, the module can be mistakenly
|
|
116
|
+
// loaded and transformed twice and the first time all its exports would be undefined
|
|
117
|
+
// triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
|
|
118
|
+
const ms = new MagicString(code);
|
|
119
|
+
|
|
120
|
+
// Extra newlines to prevent syntax errors around missing semicolons or comments
|
|
121
|
+
ms.append(
|
|
122
|
+
'\n\n' +
|
|
123
|
+
dedent`
|
|
124
|
+
import * as $$_self_$$ from './${path.basename(id)}';
|
|
125
|
+
import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
|
|
126
|
+
|
|
127
|
+
${dev_server ? 'await Promise.resolve()' : ''}
|
|
128
|
+
|
|
129
|
+
$$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)});
|
|
130
|
+
|
|
131
|
+
for (const [name, fn] of Object.entries($$_self_$$)) {
|
|
132
|
+
fn.__.id = ${s(remote.hash)} + '/' + name;
|
|
133
|
+
fn.__.name = name;
|
|
134
|
+
}
|
|
135
|
+
`
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Emit a dedicated entry chunk for this remote in SSR builds (prod only)
|
|
139
|
+
if (!dev_server) {
|
|
140
|
+
remote_original_by_hash.set(remote.hash, id);
|
|
141
|
+
|
|
142
|
+
if (!emitted_remote_hashes.has(remote.hash)) {
|
|
143
|
+
this.emitFile({
|
|
144
|
+
type: 'chunk',
|
|
145
|
+
id: `\0sveltekit-remote:${remote.hash}`,
|
|
146
|
+
name: `remote-${remote.hash}`
|
|
147
|
+
});
|
|
148
|
+
emitted_remote_hashes.add(remote.hash);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
code: ms.toString(),
|
|
154
|
+
map: ms.generateMap({ hires: 'boundary' })
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
|
|
159
|
+
|
|
160
|
+
/** @type {Map<string, RemoteInternals['type']>} */
|
|
161
|
+
const map = new Map();
|
|
162
|
+
|
|
163
|
+
// in dev, load the server module here (which will result in this hook
|
|
164
|
+
// being called again with `opts.ssr === true` if the module isn't
|
|
165
|
+
// already loaded) so we can determine what it exports
|
|
166
|
+
if (dev_server) {
|
|
167
|
+
const module = await get_runner(vite, dev_server).import(id);
|
|
168
|
+
|
|
169
|
+
for (const [name, value] of Object.entries(module)) {
|
|
170
|
+
const type = value?.__?.type;
|
|
171
|
+
if (type) map.set(name, type);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// in prod, we already built and analysed the server code before
|
|
175
|
+
// building the client code, so `remotes` is populated
|
|
176
|
+
else if (build_metadata?.remotes) {
|
|
177
|
+
const exports = build_metadata.remotes.get(remote.hash);
|
|
178
|
+
if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
|
|
179
|
+
|
|
180
|
+
for (const [name, value] of exports) {
|
|
181
|
+
map.set(name, value.type);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { namespace, declarations, reexports } = create_exported_declarations(
|
|
186
|
+
map.keys(),
|
|
187
|
+
(name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
|
|
188
|
+
'__remote'
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const relative = posixify(
|
|
192
|
+
path.relative(path.dirname(id), `${runtime_directory}/client/remote-functions/index.js`)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
let result = `import * as ${namespace} from '${relative}';\n\n${declarations.join('\n')}`;
|
|
196
|
+
if (reexports.length > 0) {
|
|
197
|
+
result += `\nexport { ${reexports.join(', ')} };`;
|
|
198
|
+
}
|
|
199
|
+
result += '\n';
|
|
200
|
+
|
|
201
|
+
if (dev_server) {
|
|
202
|
+
result += `\nimport.meta.hot?.accept();\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
code: result,
|
|
207
|
+
map: null
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {ValidatedConfig} svelte_config
|
|
216
|
+
* @returns {Plugin}
|
|
217
|
+
*/
|
|
218
|
+
export function plugin_remote_guard(svelte_config) {
|
|
219
|
+
return {
|
|
220
|
+
name: 'vite-plugin-sveltekit-remote-guard',
|
|
221
|
+
|
|
222
|
+
applyToEnvironment() {
|
|
223
|
+
return !svelte_config.experimental.remoteFunctions;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
transform: {
|
|
227
|
+
filter: {
|
|
228
|
+
id: new RegExp(
|
|
229
|
+
`.remote(${svelte_config.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
|
|
230
|
+
)
|
|
231
|
+
},
|
|
232
|
+
handler() {
|
|
233
|
+
error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @import { NextHandleFunction } from 'connect' */
|
|
2
|
-
/** @import { PreviewServer
|
|
2
|
+
/** @import { PreviewServer } from 'vite' */
|
|
3
3
|
/** @import { ValidatedConfig, ServerInternalModule, ServerModule } from 'types' */
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
@@ -15,15 +15,14 @@ import { stackless } from '../../../utils/error.js';
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {PreviewServer} vite
|
|
18
|
-
* @param {ResolvedConfig} vite_config
|
|
19
18
|
* @param {ValidatedConfig} svelte_config
|
|
20
19
|
*/
|
|
21
|
-
export async function preview(vite,
|
|
20
|
+
export async function preview(vite, svelte_config) {
|
|
22
21
|
const { paths } = svelte_config;
|
|
23
22
|
const base = paths.base;
|
|
24
23
|
const assets = paths.assets ? SVELTE_KIT_ASSETS : paths.base;
|
|
25
24
|
|
|
26
|
-
const protocol =
|
|
25
|
+
const protocol = vite.config.preview.https ? 'https' : 'http';
|
|
27
26
|
|
|
28
27
|
const etag = `"${Date.now()}"`;
|
|
29
28
|
|
|
@@ -44,6 +43,7 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
44
43
|
/** @type {ServerModule} */
|
|
45
44
|
const { Server } = await import(pathToFileURL(join(dir, 'index.js')).href);
|
|
46
45
|
|
|
46
|
+
/** @type {{ manifest: import('types').SSRManifest }} */
|
|
47
47
|
const { manifest } = await import(pathToFileURL(join(dir, 'manifest.js')).href);
|
|
48
48
|
|
|
49
49
|
set_assets(assets);
|
|
@@ -52,7 +52,7 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
52
52
|
|
|
53
53
|
try {
|
|
54
54
|
await server.init({
|
|
55
|
-
env: loadEnv(
|
|
55
|
+
env: loadEnv(vite.config.mode, svelte_config.env.dir, ''),
|
|
56
56
|
read: (file) => createReadableStream(`${dir}/${file}`)
|
|
57
57
|
});
|
|
58
58
|
} catch (error) {
|
|
@@ -205,13 +205,13 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
205
205
|
vite.middlewares.use(async (req, res) => {
|
|
206
206
|
const host = req.headers[':authority'] || req.headers.host;
|
|
207
207
|
|
|
208
|
-
const request = getRequest({
|
|
208
|
+
const request = (svelte_config.adapter?.vite?.getRequest ?? getRequest)({
|
|
209
209
|
base: `${protocol}://${host}`,
|
|
210
210
|
request: req,
|
|
211
211
|
response: res
|
|
212
212
|
});
|
|
213
213
|
|
|
214
|
-
setResponse(
|
|
214
|
+
(svelte_config.adapter?.vite?.setResponse ?? setResponse)(
|
|
215
215
|
res,
|
|
216
216
|
await server.respond(request, {
|
|
217
217
|
getClientAddress: () => {
|
|
@@ -220,7 +220,7 @@ export async function preview(vite, vite_config, svelte_config) {
|
|
|
220
220
|
throw new Error('Could not determine clientAddress');
|
|
221
221
|
},
|
|
222
222
|
read: (file) => {
|
|
223
|
-
if (file in manifest.
|
|
223
|
+
if (file in manifest.server_assets) {
|
|
224
224
|
return fs.readFileSync(join(dir, file));
|
|
225
225
|
}
|
|
226
226
|
|