@sveltejs/kit 1.0.0-next.480 → 1.0.0-next.482

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.480",
3
+ "version": "1.0.0-next.482",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -1,6 +1,6 @@
1
- This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix).
1
+ This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env).
2
2
 
3
- This module cannot be imported into client-side code.
3
+ This module cannot be imported into public-facing code.
4
4
 
5
5
  ```ts
6
6
  import { env } from '$env/dynamic/private';
@@ -1,4 +1,4 @@
1
- Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
1
+ Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
2
2
 
3
3
  Note that public dynamic environment variables must all be sent from the server to the client, causing larger network requests — when possible, use `$env/static/public` instead.
4
4
 
@@ -1,4 +1,4 @@
1
- Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix).
1
+ Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into public-facing code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env).
2
2
 
3
3
  _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination.
4
4
 
@@ -1,4 +1,4 @@
1
- Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#kit-env-publicprefix) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
1
+ Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
2
2
 
3
3
  Values are replaced statically at build time.
4
4
 
@@ -1 +1,5 @@
1
1
  This is a simple alias to `src/lib`, or whatever directory is specified as [`config.kit.files.lib`](https://kit.svelte.dev/docs/configuration#files). It allows you to access common components and utility modules without `../../../../` nonsense.
2
+
3
+ #### `$lib/server`
4
+
5
+ A subdirectory of `$lib`. SvelteKit will prevent you from importing any modules in `$lib/server` into public-facing code. See [server-only modules](/docs/server-only-modules).
@@ -11,23 +11,22 @@ function get_raw_body(req, body_size_limit) {
11
11
  return null;
12
12
  }
13
13
 
14
- const length = Number(h['content-length']);
14
+ const content_length = Number(h['content-length']);
15
15
 
16
16
  // check if no request body
17
17
  if (
18
- (req.httpVersionMajor === 1 && isNaN(length) && h['transfer-encoding'] == null) ||
19
- length === 0
18
+ (req.httpVersionMajor === 1 && isNaN(content_length) && h['transfer-encoding'] == null) ||
19
+ content_length === 0
20
20
  ) {
21
21
  return null;
22
22
  }
23
23
 
24
+ let length = content_length;
25
+
24
26
  if (body_size_limit) {
25
27
  if (!length) {
26
- throw new Error(
27
- `Received content-length of ${length}. content-length must be provided when body size limit is specified.`
28
- );
29
- }
30
- if (length > body_size_limit) {
28
+ length = body_size_limit;
29
+ } else if (length > body_size_limit) {
31
30
  throw new Error(
32
31
  `Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`
33
32
  );
@@ -59,7 +58,13 @@ function get_raw_body(req, body_size_limit) {
59
58
 
60
59
  size += chunk.length;
61
60
  if (size > length) {
62
- controller.error(new Error('content-length exceeded'));
61
+ req.destroy(
62
+ new Error(
63
+ `request body size exceeded ${
64
+ content_length ? "'content-length'" : 'BODY_SIZE_LIMIT'
65
+ } of ${length}`
66
+ )
67
+ );
63
68
  return;
64
69
  }
65
70
 
@@ -147,7 +147,7 @@ export function get_default_build_config({ config, input, ssr, outDir }) {
147
147
  * @returns {string}
148
148
  */
149
149
  export function assets_base(config) {
150
- return config.paths.assets + '/' || config.paths.base + '/' || './';
150
+ return (config.paths.assets || config.paths.base || '.') + '/';
151
151
  }
152
152
 
153
153
  const method_names = new Set(['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']);
@@ -11,7 +11,7 @@ import { load_error_page, load_template } from '../../../core/config/index.js';
11
11
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
12
12
  import * as sync from '../../../core/sync/sync.js';
13
13
  import { get_mime_lookup, runtime_base, runtime_prefix } from '../../../core/utils.js';
14
- import { prevent_illegal_vite_imports } from '../utils.js';
14
+ import { prevent_illegal_vite_imports } from '../graph_analysis/index.js';
15
15
  import { compact } from '../../../utils/array.js';
16
16
  import { normalizePath } from 'vite';
17
17
 
@@ -0,0 +1,277 @@
1
+ import path from 'path';
2
+ import { normalizePath } from 'vite';
3
+ import { remove_query_from_id, get_module_types } from './utils.js';
4
+
5
+ /** @typedef {import('./types').ImportGraph} ImportGraph */
6
+
7
+ const CWD_ID = normalizePath(process.cwd());
8
+ const NODE_MODULES_ID = normalizePath(path.resolve(process.cwd(), 'node_modules'));
9
+ const ILLEGAL_IMPORTS = new Set([
10
+ '/@id/__x00__$env/dynamic/private', //dev
11
+ '\0$env/dynamic/private', // prod
12
+ '/@id/__x00__$env/static/private', // dev
13
+ '\0$env/static/private' // prod
14
+ ]);
15
+ const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/;
16
+
17
+ export class IllegalModuleGuard {
18
+ /** @type {string} */
19
+ #lib_dir;
20
+
21
+ /** @type {string} */
22
+ #server_dir;
23
+
24
+ /** @type {Array<ImportGraph>} */
25
+ #chain = [];
26
+
27
+ /**
28
+ * @param {string} lib_dir
29
+ */
30
+ constructor(lib_dir) {
31
+ this.#lib_dir = normalizePath(lib_dir);
32
+ this.#server_dir = normalizePath(path.resolve(lib_dir, 'server'));
33
+ }
34
+
35
+ /**
36
+ * Assert that a node imports no illegal modules.
37
+ * @param {ImportGraph} node
38
+ * @returns {void}
39
+ */
40
+ assert_legal(node) {
41
+ this.#chain.push(node);
42
+ for (const child of node.children) {
43
+ if (this.#is_illegal(child.id)) {
44
+ this.#chain.push(child);
45
+ const error = this.#format_illegal_import_chain(this.#chain);
46
+ this.#chain = []; // Reset the chain in case we want to reuse this guard
47
+ throw new Error(error);
48
+ }
49
+ this.assert_legal(child);
50
+ }
51
+ this.#chain.pop();
52
+ }
53
+
54
+ /**
55
+ * `true` if the provided ID represents a server-only module, else `false`.
56
+ * @param {string} module_id
57
+ * @returns {boolean}
58
+ */
59
+ #is_illegal(module_id) {
60
+ if (this.#is_kit_illegal(module_id) || this.#is_user_illegal(module_id)) return true;
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * `true` if the provided ID represents a Kit-defined server-only module, else `false`.
66
+ * @param {string} module_id
67
+ * @returns {boolean}
68
+ */
69
+ #is_kit_illegal(module_id) {
70
+ return ILLEGAL_IMPORTS.has(module_id);
71
+ }
72
+
73
+ /**
74
+ * `true` if the provided ID represents a user-defined server-only module, else `false`.
75
+ * @param {string} module_id
76
+ * @returns {boolean}
77
+ */
78
+ #is_user_illegal(module_id) {
79
+ if (module_id.startsWith(this.#server_dir)) return true;
80
+
81
+ // files outside the project root are ignored
82
+ if (!module_id.startsWith(CWD_ID)) return false;
83
+
84
+ // so are files inside node_modules
85
+ if (module_id.startsWith(NODE_MODULES_ID)) return false;
86
+
87
+ return ILLEGAL_MODULE_NAME_PATTERN.test(path.basename(module_id));
88
+ }
89
+
90
+ /**
91
+ * @param {string} str
92
+ * @param {number} times
93
+ */
94
+ #repeat(str, times) {
95
+ return new Array(times + 1).join(str);
96
+ }
97
+
98
+ /**
99
+ * Create a formatted error for an illegal import.
100
+ * @param {Array<ImportGraph>} stack
101
+ */
102
+ #format_illegal_import_chain(stack) {
103
+ const dev_virtual_prefix = '/@id/__x00__';
104
+ const prod_virtual_prefix = '\0';
105
+
106
+ stack = stack.map((graph) => {
107
+ if (graph.id.startsWith(dev_virtual_prefix)) {
108
+ return { ...graph, id: graph.id.replace(dev_virtual_prefix, '') };
109
+ }
110
+ if (graph.id.startsWith(prod_virtual_prefix)) {
111
+ return { ...graph, id: graph.id.replace(prod_virtual_prefix, '') };
112
+ }
113
+ if (graph.id.startsWith(this.#lib_dir)) {
114
+ return { ...graph, id: graph.id.replace(this.#lib_dir, '$lib') };
115
+ }
116
+
117
+ return { ...graph, id: path.relative(process.cwd(), graph.id) };
118
+ });
119
+
120
+ const pyramid = stack
121
+ .map(
122
+ (file, i) =>
123
+ `${this.#repeat(' ', i * 2)}- ${file.id} ${
124
+ file.dynamic ? '(imported by parent dynamically)' : ''
125
+ }`
126
+ )
127
+ .join('\n');
128
+
129
+ return `Cannot import ${stack.at(-1)?.id} into public-facing code:\n${pyramid}`;
130
+ }
131
+ }
132
+
133
+ /** @implements {ImportGraph} */
134
+ export class RollupImportGraph {
135
+ /** @type {(id: string) => import('rollup').ModuleInfo | null} */
136
+ #node_getter;
137
+
138
+ /** @type {import('rollup').ModuleInfo} */
139
+ #module_info;
140
+
141
+ /** @type {string} */
142
+ id;
143
+
144
+ /** @type {boolean} */
145
+ dynamic;
146
+
147
+ /** @type {Set<string>} */
148
+ #seen;
149
+
150
+ /**
151
+ * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
152
+ * @param {import('rollup').ModuleInfo} node
153
+ */
154
+ constructor(node_getter, node) {
155
+ this.#node_getter = node_getter;
156
+ this.#module_info = node;
157
+ this.id = remove_query_from_id(normalizePath(node.id));
158
+ this.dynamic = false;
159
+ this.#seen = new Set();
160
+ }
161
+
162
+ /**
163
+ * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
164
+ * @param {import('rollup').ModuleInfo} node
165
+ * @param {boolean} dynamic
166
+ * @param {Set<string>} seen;
167
+ * @returns {RollupImportGraph}
168
+ */
169
+ static #new_internal(node_getter, node, dynamic, seen) {
170
+ const instance = new RollupImportGraph(node_getter, node);
171
+ instance.dynamic = dynamic;
172
+ instance.#seen = seen;
173
+ return instance;
174
+ }
175
+
176
+ get children() {
177
+ return this.#children();
178
+ }
179
+
180
+ *#children() {
181
+ if (this.#seen.has(this.id)) return;
182
+ this.#seen.add(this.id);
183
+ for (const id of this.#module_info.importedIds) {
184
+ const child = this.#node_getter(id);
185
+ if (child === null) return;
186
+ yield RollupImportGraph.#new_internal(this.#node_getter, child, false, this.#seen);
187
+ }
188
+ for (const id of this.#module_info.dynamicallyImportedIds) {
189
+ const child = this.#node_getter(id);
190
+ if (child === null) return;
191
+ yield RollupImportGraph.#new_internal(this.#node_getter, child, true, this.#seen);
192
+ }
193
+ }
194
+ }
195
+
196
+ /** @implements {ImportGraph} */
197
+ export class ViteImportGraph {
198
+ /** @type {Set<string>} */
199
+ #module_types;
200
+
201
+ /** @type {import('vite').ModuleNode} */
202
+ #module_info;
203
+
204
+ /** @type {string} */
205
+ id;
206
+
207
+ /** @type {Set<string>} */
208
+ #seen;
209
+
210
+ /**
211
+ * @param {Set<string>} module_types Module types to analyze, eg '.js', '.ts', etc.
212
+ * @param {import('vite').ModuleNode} node
213
+ */
214
+ constructor(module_types, node) {
215
+ this.#module_types = module_types;
216
+ this.#module_info = node;
217
+ this.id = remove_query_from_id(normalizePath(node.id ?? ''));
218
+ this.#seen = new Set();
219
+ }
220
+
221
+ /**
222
+ * @param {Set<string>} module_types Module types to analyze, eg '.js', '.ts', etc.
223
+ * @param {import('vite').ModuleNode} node
224
+ * @param {Set<string>} seen
225
+ * @returns {ViteImportGraph}
226
+ */
227
+ static #new_internal(module_types, node, seen) {
228
+ const instance = new ViteImportGraph(module_types, node);
229
+ instance.#seen = seen;
230
+ return instance;
231
+ }
232
+
233
+ get dynamic() {
234
+ return false;
235
+ }
236
+
237
+ get children() {
238
+ return this.#children();
239
+ }
240
+
241
+ *#children() {
242
+ if (this.#seen.has(this.id)) return;
243
+ this.#seen.add(this.id);
244
+ for (const child of this.#module_info.importedModules) {
245
+ if (!this.#module_types.has(path.extname(this.id))) {
246
+ continue;
247
+ }
248
+ yield ViteImportGraph.#new_internal(this.#module_types, child, this.#seen);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Throw an error if a private module is imported from a client-side node.
255
+ * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
256
+ * @param {import('rollup').ModuleInfo} node
257
+ * @param {string} lib_dir
258
+ * @returns {void}
259
+ */
260
+ export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) {
261
+ const graph = new RollupImportGraph(node_getter, node);
262
+ const guard = new IllegalModuleGuard(lib_dir);
263
+ guard.assert_legal(graph);
264
+ }
265
+
266
+ /**
267
+ * Throw an error if a private module is imported from a client-side node.
268
+ * @param {import('vite').ModuleNode} node
269
+ * @param {string} lib_dir
270
+ * @param {Iterable<string>} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc.
271
+ * @returns {void}
272
+ */
273
+ export function prevent_illegal_vite_imports(node, lib_dir, module_types) {
274
+ const graph = new ViteImportGraph(get_module_types(module_types), node);
275
+ const guard = new IllegalModuleGuard(lib_dir);
276
+ guard.assert_legal(graph);
277
+ }
@@ -0,0 +1,5 @@
1
+ export interface ImportGraph {
2
+ readonly id: string;
3
+ readonly dynamic: boolean;
4
+ readonly children: Generator<ImportGraph>;
5
+ }
@@ -0,0 +1,30 @@
1
+ const query_pattern = /\?.*$/s;
2
+
3
+ /** @param {string} path */
4
+ export function remove_query_from_id(path) {
5
+ return path.replace(query_pattern, '');
6
+ }
7
+
8
+ /**
9
+ * Vite does some weird things with import trees in dev
10
+ * for example, a Tailwind app.css will appear to import
11
+ * every file in the project. This isn't a problem for
12
+ * Rollup during build.
13
+ * @param {Iterable<string>} config_module_types
14
+ */
15
+ export const get_module_types = (config_module_types) => {
16
+ return new Set([
17
+ '',
18
+ '.ts',
19
+ '.js',
20
+ '.svelte',
21
+ '.mts',
22
+ '.mjs',
23
+ '.cts',
24
+ '.cjs',
25
+ '.svelte.md',
26
+ '.svx',
27
+ '.md',
28
+ ...config_module_types
29
+ ]);
30
+ };
@@ -14,7 +14,8 @@ import { generate_manifest } from '../../core/generate_manifest/index.js';
14
14
  import { runtime_directory, logger } from '../../core/utils.js';
15
15
  import { find_deps, get_default_build_config } from './build/utils.js';
16
16
  import { preview } from './preview/index.js';
17
- import { get_aliases, prevent_illegal_rollup_imports, get_env } from './utils.js';
17
+ import { get_aliases, get_env } from './utils.js';
18
+ import { prevent_illegal_rollup_imports } from './graph_analysis/index.js';
18
19
  import { fileURLToPath } from 'node:url';
19
20
  import { create_static_module, create_dynamic_module } from '../../core/env.js';
20
21
 
@@ -1,232 +1,8 @@
1
1
  import path from 'path';
2
- import { loadConfigFromFile, loadEnv, normalizePath } from 'vite';
2
+ import { loadConfigFromFile, loadEnv } from 'vite';
3
3
  import { runtime_directory } from '../../core/utils.js';
4
4
  import { posixify } from '../../utils/filesystem.js';
5
5
 
6
- class IllegalModuleGuard {
7
- /** @type {string} */
8
- #lib_dir;
9
-
10
- /** @type {string} */
11
- #server_dir;
12
-
13
- /** @type {string} */
14
- #node_modules_dir = normalizePath(path.resolve(process.cwd(), 'node_modules'));
15
-
16
- /** @type {string} */
17
- #cwd = normalizePath(process.cwd());
18
-
19
- /** @type {Set<string>} */
20
- #illegal_imports = new Set([
21
- '/@id/__x00__$env/dynamic/private', //dev
22
- '\0$env/dynamic/private', // prod
23
- '/@id/__x00__$env/static/private', // dev
24
- '\0$env/static/private' // prod
25
- ]);
26
-
27
- /** @type {Array<import('types').ImportNode>} */
28
- #chain = [];
29
-
30
- /**
31
- * @param {string} lib_dir
32
- */
33
- constructor(lib_dir) {
34
- this.#lib_dir = normalizePath(lib_dir);
35
- this.#server_dir = normalizePath(path.resolve(lib_dir, 'server'));
36
- }
37
-
38
- /**
39
- * @param {import('types').ImportNode} node
40
- * @returns {void}
41
- */
42
- assert_legal(node) {
43
- this.#chain.push(node);
44
- for (const child of node.children) {
45
- if (this.#is_illegal(child.name)) {
46
- this.#chain.push(child);
47
- throw new Error(this.#format_illegal_import_chain(this.#chain));
48
- }
49
- this.assert_legal(child);
50
- }
51
- this.#chain.pop();
52
- }
53
-
54
- /**
55
- * `true` if the provided ID represents a server-only module, else `false`.
56
- * @param {string} module_id
57
- * @returns {boolean}
58
- */
59
- #is_illegal(module_id) {
60
- if (this.#is_kit_illegal(module_id) || this.#is_user_illegal(module_id)) return true;
61
- return false;
62
- }
63
-
64
- /**
65
- * `true` if the provided ID represents a Kit-defined server-only module, else `false`.
66
- * @param {string} module_id
67
- * @returns {boolean}
68
- */
69
- #is_kit_illegal(module_id) {
70
- return this.#illegal_imports.has(module_id);
71
- }
72
-
73
- /**
74
- * `true` if the provided ID represents a user-defined server-only module, else `false`.
75
- * @param {string} module_id
76
- * @returns {boolean}
77
- */
78
- #is_user_illegal(module_id) {
79
- if (module_id.startsWith(this.#server_dir)) return true;
80
-
81
- // files outside the project root are ignored
82
- if (!module_id.startsWith(this.#cwd)) return false;
83
-
84
- // so are files inside node_modules
85
- if (module_id.startsWith(this.#node_modules_dir)) return false;
86
-
87
- return /.*\.server\..+/.test(path.basename(module_id));
88
- }
89
-
90
- /**
91
- * @param {string} str
92
- * @param {number} times
93
- */
94
- #repeat(str, times) {
95
- return new Array(times + 1).join(str);
96
- }
97
-
98
- /**
99
- * Create a formatted error for an illegal import.
100
- * @param {Array<{name: string, dynamic: boolean}>} stack
101
- */
102
- #format_illegal_import_chain(stack) {
103
- const dev_virtual_prefix = '/@id/__x00__';
104
- const prod_virtual_prefix = '\0';
105
-
106
- stack = stack.map((file) => {
107
- if (file.name.startsWith(dev_virtual_prefix)) {
108
- return { ...file, name: file.name.replace(dev_virtual_prefix, '') };
109
- }
110
- if (file.name.startsWith(prod_virtual_prefix)) {
111
- return { ...file, name: file.name.replace(prod_virtual_prefix, '') };
112
- }
113
- if (file.name.startsWith(this.#lib_dir)) {
114
- return { ...file, name: file.name.replace(this.#lib_dir, '$lib') };
115
- }
116
-
117
- return { ...file, name: path.relative(process.cwd(), file.name) };
118
- });
119
-
120
- const pyramid = stack
121
- .map(
122
- (file, i) =>
123
- `${this.#repeat(' ', i * 2)}- ${file.name} ${
124
- file.dynamic ? '(imported by parent dynamically)' : ''
125
- }`
126
- )
127
- .join('\n');
128
-
129
- return `Cannot import ${stack.at(-1)?.name} into client-side code:\n${pyramid}`;
130
- }
131
- }
132
-
133
- class RollupImportGraph {
134
- /** @type {(id: string) => import('rollup').ModuleInfo | null} */
135
- #node_getter;
136
-
137
- /** @type {import('rollup').ModuleInfo} */
138
- #module_info;
139
-
140
- /** @type {string} */
141
- name;
142
-
143
- /** @type {boolean} */
144
- dynamic;
145
-
146
- /** @type {Set<string>} */
147
- #seen;
148
-
149
- /**
150
- * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
151
- * @param {import('rollup').ModuleInfo} node
152
- * @param {boolean} dynamic
153
- * @param {Set<string>} seen
154
- */
155
- constructor(node_getter, node, dynamic = false, seen = new Set()) {
156
- this.#node_getter = node_getter;
157
- this.#module_info = node;
158
- this.name = remove_query_from_path(normalizePath(node.id));
159
- this.dynamic = dynamic;
160
- this.#seen = seen;
161
- void (/** @type {import('types').ImportNode} */ (this));
162
- }
163
-
164
- get children() {
165
- return this.#children();
166
- }
167
-
168
- *#children() {
169
- if (this.#seen.has(this.name)) return;
170
- this.#seen.add(this.name);
171
- for (const id of this.#module_info.importedIds) {
172
- const child = this.#node_getter(id);
173
- if (child === null) return;
174
- yield new RollupImportGraph(this.#node_getter, child, false, this.#seen);
175
- }
176
- for (const id of this.#module_info.dynamicallyImportedIds) {
177
- const child = this.#node_getter(id);
178
- if (child === null) return;
179
- yield new RollupImportGraph(this.#node_getter, child, true, this.#seen);
180
- }
181
- }
182
- }
183
-
184
- class ViteImportGraph {
185
- /** @type {Set<string>} */
186
- #module_types;
187
-
188
- /** @type {import('vite').ModuleNode} */
189
- #module_info;
190
-
191
- /** @type {string} */
192
- name;
193
-
194
- /** @type {Set<string>} */
195
- #seen;
196
-
197
- /**
198
- * @param {Set<string>} module_types Module types to analyze, eg '.js', '.ts', etc.
199
- * @param {import('vite').ModuleNode} node
200
- * @param {Set<string>} seen
201
- */
202
- constructor(module_types, node, seen = new Set()) {
203
- this.#module_types = module_types;
204
- this.#module_info = node;
205
- this.name = remove_query_from_path(normalizePath(node.id ?? ''));
206
- this.#seen = seen;
207
- void (/** @type {import('types').ImportNode} */ (this));
208
- }
209
-
210
- get dynamic() {
211
- return false;
212
- }
213
-
214
- get children() {
215
- return this.#children();
216
- }
217
-
218
- *#children() {
219
- if (this.#seen.has(this.name)) return;
220
- this.#seen.add(this.name);
221
- for (const child of this.#module_info.importedModules) {
222
- if (!this.#module_types.has(path.extname(this.name))) {
223
- continue;
224
- }
225
- yield new ViteImportGraph(this.#module_types, child, this.#seen);
226
- }
227
- }
228
- }
229
-
230
6
  /**
231
7
  * @param {import('vite').ResolvedConfig} config
232
8
  * @param {import('vite').ConfigEnv} config_env
@@ -379,60 +155,3 @@ export function get_env(env_config, mode) {
379
155
  private: Object.fromEntries(entries.filter(([k]) => !k.startsWith(env_config.publicPrefix)))
380
156
  };
381
157
  }
382
-
383
- const query_pattern = /\?.*$/s;
384
-
385
- /** @param {string} path */
386
- function remove_query_from_path(path) {
387
- return path.replace(query_pattern, '');
388
- }
389
-
390
- /**
391
- * Vite does some weird things with import trees in dev
392
- * for example, a Tailwind app.css will appear to import
393
- * every file in the project. This isn't a problem for
394
- * Rollup during build.
395
- * @param {Iterable<string>} config_module_types
396
- */
397
- const get_module_types = (config_module_types) => {
398
- return new Set([
399
- '',
400
- '.ts',
401
- '.js',
402
- '.svelte',
403
- '.mts',
404
- '.mjs',
405
- '.cts',
406
- '.cjs',
407
- '.svelte.md',
408
- '.svx',
409
- '.md',
410
- ...config_module_types
411
- ]);
412
- };
413
-
414
- /**
415
- * Throw an error if a private module is imported from a client-side node.
416
- * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
417
- * @param {import('rollup').ModuleInfo} node
418
- * @param {string} lib_dir
419
- * @returns {void}
420
- */
421
- export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) {
422
- const graph = new RollupImportGraph(node_getter, node);
423
- const guard = new IllegalModuleGuard(lib_dir);
424
- guard.assert_legal(graph);
425
- }
426
-
427
- /**
428
- * Throw an error if a private module is imported from a client-side node.
429
- * @param {import('vite').ModuleNode} node
430
- * @param {string} lib_dir
431
- * @param {Iterable<string>} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc.
432
- * @returns {void}
433
- */
434
- export function prevent_illegal_vite_imports(node, lib_dir, module_types) {
435
- const graph = new ViteImportGraph(get_module_types(module_types), node);
436
- const guard = new IllegalModuleGuard(lib_dir);
437
- guard.assert_legal(graph);
438
- }
@@ -43,6 +43,7 @@ function update_scroll_positions(index) {
43
43
  scroll_positions[index] = scroll_state();
44
44
  }
45
45
 
46
+ // TODO remove for 1.0
46
47
  /** @type {Record<string, true>} */
47
48
  let warned_about_attributes = {};
48
49
 
@@ -742,12 +743,11 @@ export function create_client({ target, base, trailing_slash }) {
742
743
  /** @type {import('./types').BranchNode | undefined} */
743
744
  const previous = current.branch[i];
744
745
 
745
- const server_data_node = server_data_nodes?.[i] ?? null;
746
+ const server_data_node = server_data_nodes?.[i];
746
747
 
747
- const can_reuse_server_data = !server_data_node || server_data_node.type === 'skip';
748
748
  // re-use data from previous load if it's still valid
749
749
  const valid =
750
- can_reuse_server_data &&
750
+ (!server_data_node || server_data_node.type === 'skip') &&
751
751
  loader[1] === previous?.loader &&
752
752
  !has_changed(changed, parent_changed, previous.shared?.uses);
753
753
  if (valid) return previous;
@@ -771,7 +771,12 @@ export function create_client({ target, base, trailing_slash }) {
771
771
  }
772
772
  return data;
773
773
  },
774
- server_data_node: create_data_node(server_data_node, previous?.server)
774
+ server_data_node: create_data_node(
775
+ // server_data_node is undefined if it wasn't reloaded from the server;
776
+ // and if current loader uses server data, we want to reuse previous data.
777
+ server_data_node === undefined && loader[0] ? { type: 'skip' } : server_data_node ?? null,
778
+ previous?.server
779
+ )
775
780
  });
776
781
  });
777
782
 
@@ -1321,10 +1326,12 @@ export function create_client({ target, base, trailing_slash }) {
1321
1326
  if (hash !== undefined && base === location.href.split('#')[0]) {
1322
1327
  // set this flag to distinguish between navigations triggered by
1323
1328
  // clicking a hash link and those triggered by popstate
1329
+ // TODO why not update history here directly?
1324
1330
  hash_navigating = true;
1325
1331
 
1326
1332
  update_scroll_positions(current_history_index);
1327
1333
 
1334
+ current.url = url;
1328
1335
  stores.page.set({ ...page, url });
1329
1336
  stores.page.notify();
1330
1337
 
@@ -1545,25 +1552,26 @@ function add_url_properties(type, target) {
1545
1552
 
1546
1553
  function pre_update() {
1547
1554
  if (__SVELTEKIT_DEV__) {
1548
- // Nasty hack to silence harmless warnings the user can do nothing about
1549
- const warn = console.warn;
1550
- console.warn = (...args) => {
1551
- if (
1552
- args.length === 1 &&
1553
- /<(Layout|Page)(_[\w$]+)?> was created (with unknown|without expected) prop '(data|form)'/.test(
1554
- args[0]
1555
- )
1556
- ) {
1557
- return;
1558
- }
1559
- warn(...args);
1560
- };
1561
-
1562
1555
  return () => {
1563
- tick().then(() => (console.warn = warn));
1564
1556
  check_for_removed_attributes();
1565
1557
  };
1566
1558
  }
1567
1559
 
1568
1560
  return () => {};
1569
1561
  }
1562
+
1563
+ if (__SVELTEKIT_DEV__) {
1564
+ // Nasty hack to silence harmless warnings the user can do nothing about
1565
+ const warn = console.warn;
1566
+ console.warn = (...args) => {
1567
+ if (
1568
+ args.length === 1 &&
1569
+ /<(Layout|Page)(_[\w$]+)?> was created (with unknown|without expected) prop '(data|form)'/.test(
1570
+ args[0]
1571
+ )
1572
+ ) {
1573
+ return;
1574
+ }
1575
+ warn(...args);
1576
+ };
1577
+ }
@@ -0,0 +1,11 @@
1
+ import { suite } from 'uvu';
2
+
3
+ /**
4
+ * @param {string} name
5
+ * @param {(suite: import('uvu').Test<import('uvu').Context>) => void} fn
6
+ */
7
+ export function describe(name, fn) {
8
+ const s = suite(name);
9
+ fn(s);
10
+ s.run();
11
+ }
package/types/index.d.ts CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  TrailingSlash
17
17
  } from './private.js';
18
18
  import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js';
19
- import { HttpError, Redirect, ValidationError } from '../src/runtime/control.js';
19
+ import { HttpError, Redirect } from '../src/runtime/control.js';
20
20
 
21
21
  export { PrerenderOption } from './private.js';
22
22
 
@@ -39,6 +39,12 @@ export type AwaitedActions<T extends Record<string, (...args: any) => any>> = {
39
39
  [Key in keyof T]: UnpackValidationError<Awaited<ReturnType<T[Key]>>>;
40
40
  }[keyof T];
41
41
 
42
+ // Needs to be here, else ActionData will be resolved to unknown - probably because of "d.ts file imports .js file" in combination with allowJs
43
+ interface ValidationError<T extends Record<string, unknown> | undefined = undefined> {
44
+ status: number;
45
+ data: T;
46
+ }
47
+
42
48
  type UnpackValidationError<T> = T extends ValidationError<infer X> ? X : T;
43
49
 
44
50
  export interface Builder {
@@ -292,7 +298,7 @@ export interface RequestEvent<
292
298
  /**
293
299
  * A `(event: RequestEvent) => Response` function exported from a `+server.js` file that corresponds to an HTTP verb (`GET`, `PUT`, `PATCH`, etc) and handles requests with that method.
294
300
  *
295
- * It receives `Params` as the first generic argument, which you can skip by using [generated types](/docs/types#generated-types) instead.
301
+ * It receives `Params` as the first generic argument, which you can skip by using [generated types](https://kit.svelte.dev/docs/types#generated-types) instead.
296
302
  */
297
303
  export interface RequestHandler<
298
304
  Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
@@ -100,12 +100,6 @@ export interface ClientHooks {
100
100
  handleError: HandleClientError;
101
101
  }
102
102
 
103
- export interface ImportNode {
104
- readonly name: string;
105
- readonly dynamic: boolean;
106
- readonly children: Generator<ImportNode>;
107
- }
108
-
109
103
  export class InternalServer extends Server {
110
104
  init(options: ServerInitOptions): Promise<void>;
111
105
  respond(