@sveltejs/kit 1.16.2 → 1.17.0

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.16.2",
3
+ "version": "1.17.0",
4
4
  "description": "The fastest way to build Svelte apps",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "@sveltejs/vite-plugin-svelte": "^2.1.1",
15
15
  "@types/cookie": "^0.5.1",
16
16
  "cookie": "^0.5.0",
17
- "devalue": "^4.3.0",
17
+ "devalue": "^4.3.1",
18
18
  "esm-env": "^1.0.0",
19
19
  "kleur": "^4.1.5",
20
20
  "magic-string": "^0.30.0",
@@ -38,8 +38,8 @@
38
38
  "svelte": "^3.56.0",
39
39
  "svelte-preprocess": "^5.0.3",
40
40
  "typescript": "^4.9.4",
41
- "uvu": "^0.5.6",
42
- "vite": "^4.3.0"
41
+ "vite": "^4.3.6",
42
+ "vitest": "^0.31.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "svelte": "^3.54.0",
@@ -89,7 +89,7 @@
89
89
  "test:integration": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test",
90
90
  "test:cross-platform:dev": "pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:dev",
91
91
  "test:cross-platform:build": "pnpm test:unit && pnpm -r --workspace-concurrency 1 --filter=\"./test/**\" test:cross-platform:build",
92
- "test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
92
+ "test:unit": "vitest --config kit.vitest.config.js run",
93
93
  "postinstall": "node postinstall.js"
94
94
  }
95
95
  }
@@ -13,6 +13,20 @@ const ATTRIBUTE_NAME = /[^\t\n\f />"'=]/;
13
13
 
14
14
  const WHITESPACE = /[\s\n\r]/;
15
15
 
16
+ const CRAWLABLE_META_NAME_ATTRS = new Set([
17
+ 'og:url',
18
+ 'og:image',
19
+ 'og:image:url',
20
+ 'og:image:secure_url',
21
+ 'og:video',
22
+ 'og:video:url',
23
+ 'og:video:secure_url',
24
+ 'og:audio',
25
+ 'og:audio:url',
26
+ 'og:audio:secure_url',
27
+ 'twitter:image'
28
+ ]);
29
+
16
30
  /**
17
31
  * @param {string} html
18
32
  * @param {string} base
@@ -81,6 +95,9 @@ export function crawl(html, base) {
81
95
 
82
96
  const tag = html.slice(start, i).toUpperCase();
83
97
 
98
+ /** @type {Record<string, string>} */
99
+ const attributes = {};
100
+
84
101
  if (tag === 'SCRIPT' || tag === 'STYLE') {
85
102
  while (i < html.length) {
86
103
  if (
@@ -95,9 +112,6 @@ export function crawl(html, base) {
95
112
  }
96
113
  }
97
114
 
98
- let href = '';
99
- let rel = '';
100
-
101
115
  while (i < html.length) {
102
116
  const start = i;
103
117
 
@@ -159,44 +173,7 @@ export function crawl(html, base) {
159
173
  }
160
174
 
161
175
  value = decode(value);
162
-
163
- if (name === 'href') {
164
- if (tag === 'BASE') {
165
- base = resolve(base, value);
166
- } else {
167
- href = resolve(base, value);
168
- }
169
- } else if (name === 'id') {
170
- ids.push(value);
171
- } else if (name === 'name') {
172
- if (tag === 'A') ids.push(value);
173
- } else if (name === 'rel') {
174
- rel = value;
175
- } else if (name === 'src') {
176
- if (value) hrefs.push(resolve(base, value));
177
- } else if (name === 'srcset') {
178
- const candidates = [];
179
- let insideURL = true;
180
- value = value.trim();
181
- for (let i = 0; i < value.length; i++) {
182
- if (
183
- value[i] === ',' &&
184
- (!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))
185
- ) {
186
- candidates.push(value.slice(0, i));
187
- value = value.substring(i + 1).trim();
188
- i = 0;
189
- insideURL = true;
190
- } else if (WHITESPACE.test(value[i])) {
191
- insideURL = false;
192
- }
193
- }
194
- candidates.push(value);
195
- for (const candidate of candidates) {
196
- const src = candidate.split(WHITESPACE)[0];
197
- if (src) hrefs.push(resolve(base, src));
198
- }
199
- }
176
+ attributes[name] = value;
200
177
  } else {
201
178
  i -= 1;
202
179
  }
@@ -205,8 +182,56 @@ export function crawl(html, base) {
205
182
  i += 1;
206
183
  }
207
184
 
208
- if (href && !/\bexternal\b/i.test(rel)) {
209
- hrefs.push(resolve(base, href));
185
+ const { href, id, name, property, rel, src, srcset, content } = attributes;
186
+
187
+ if (href) {
188
+ if (tag === 'BASE') {
189
+ base = resolve(base, href);
190
+ } else if (!rel || !/\bexternal\b/i.test(rel)) {
191
+ hrefs.push(resolve(base, href));
192
+ }
193
+ }
194
+
195
+ if (id) {
196
+ ids.push(id);
197
+ }
198
+
199
+ if (name && tag === 'A') {
200
+ ids.push(name);
201
+ }
202
+
203
+ if (src) {
204
+ hrefs.push(resolve(base, src));
205
+ }
206
+
207
+ if (srcset) {
208
+ let value = srcset;
209
+ const candidates = [];
210
+ let insideURL = true;
211
+ value = value.trim();
212
+ for (let i = 0; i < value.length; i++) {
213
+ if (value[i] === ',' && (!insideURL || (insideURL && WHITESPACE.test(value[i + 1])))) {
214
+ candidates.push(value.slice(0, i));
215
+ value = value.substring(i + 1).trim();
216
+ i = 0;
217
+ insideURL = true;
218
+ } else if (WHITESPACE.test(value[i])) {
219
+ insideURL = false;
220
+ }
221
+ }
222
+ candidates.push(value);
223
+ for (const candidate of candidates) {
224
+ const src = candidate.split(WHITESPACE)[0];
225
+ if (src) hrefs.push(resolve(base, src));
226
+ }
227
+ }
228
+
229
+ if (tag === 'META' && content) {
230
+ const attr = name ?? property;
231
+
232
+ if (attr && CRAWLABLE_META_NAME_ATTRS.has(attr)) {
233
+ hrefs.push(resolve(base, content));
234
+ }
210
235
  }
211
236
  }
212
237
  }
@@ -12,6 +12,7 @@ import { get_route_segments } from '../../utils/routing.js';
12
12
  import { queue } from './queue.js';
13
13
  import { crawl } from './crawl.js';
14
14
  import { forked } from '../../utils/fork.js';
15
+ import * as devalue from 'devalue';
15
16
 
16
17
  export default forked(import.meta.url, prerender);
17
18
 
@@ -340,7 +341,11 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
340
341
 
341
342
  writeFileSync(
342
343
  dest,
343
- `<meta http-equiv="refresh" content=${escape_html_attr(`0;url=${location}`)}>`
344
+ `<script>location.href=${devalue.uneval(
345
+ location
346
+ )};</script><meta http-equiv="refresh" content=${escape_html_attr(
347
+ `0;url=${location}`
348
+ )}>`
344
349
  );
345
350
 
346
351
  written.add(file);
@@ -176,20 +176,17 @@ export async function dev(vite, vite_config, svelte_config) {
176
176
  const styles = {};
177
177
 
178
178
  for (const dep of deps) {
179
- const url = new URL(dep.url, 'http://localhost/');
179
+ const url = new URL(dep.url, 'dummy:/');
180
180
  const query = url.searchParams;
181
181
 
182
182
  if (
183
- isCSSRequest(dep.file) ||
184
- (query.has('svelte') && query.get('type') === 'style')
183
+ (isCSSRequest(dep.file) ||
184
+ (query.has('svelte') && query.get('type') === 'style')) &&
185
+ !(query.has('raw') || query.has('url') || query.has('inline'))
185
186
  ) {
186
- // setting `?inline` to load CSS modules as css string
187
- query.set('inline', '');
188
-
189
187
  try {
190
- const mod = await loud_ssr_load_module(
191
- `${url.pathname}${url.search}${url.hash}`
192
- );
188
+ query.set('inline', '');
189
+ const mod = await vite.ssrLoadModule(`${url.pathname}${url.search}${url.hash}`);
193
190
  styles[dep.url] = mod.default;
194
191
  } catch {
195
192
  // this can happen with dynamically imported modules, I think
@@ -14,12 +14,26 @@ export function deserialize(result) {
14
14
  return parsed;
15
15
  }
16
16
 
17
+ /**
18
+ * @param {string} old_name
19
+ * @param {string} new_name
20
+ * @param {string} call_location
21
+ * @returns void
22
+ */
23
+ function warn_on_access(old_name, new_name, call_location) {
24
+ if (!DEV) return;
25
+ // TODO 2.0: Remove this code
26
+ console.warn(
27
+ `\`${old_name}\` has been deprecated in favor of \`${new_name}\`. \`${old_name}\` will be removed in a future version. (Called from ${call_location})`
28
+ );
29
+ }
30
+
17
31
  /** @type {import('$app/forms').enhance} */
18
- export function enhance(form, submit = () => {}) {
32
+ export function enhance(form_element, submit = () => {}) {
19
33
  if (
20
34
  DEV &&
21
- /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form)).method !==
22
- 'post'
35
+ /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form_element))
36
+ .method !== 'post'
23
37
  ) {
24
38
  throw new Error('use:enhance can only be used on <form> fields with method="POST"');
25
39
  }
@@ -35,7 +49,7 @@ export function enhance(form, submit = () => {}) {
35
49
  if (result.type === 'success') {
36
50
  if (reset !== false) {
37
51
  // We call reset from the prototype to avoid DOM clobbering
38
- HTMLFormElement.prototype.reset.call(form);
52
+ HTMLFormElement.prototype.reset.call(form_element);
39
53
  }
40
54
  await invalidateAll();
41
55
  }
@@ -60,14 +74,15 @@ export function enhance(form, submit = () => {}) {
60
74
  // We do cloneNode for avoid DOM clobbering - https://github.com/sveltejs/kit/issues/7593
61
75
  event.submitter?.hasAttribute('formaction')
62
76
  ? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
63
- : /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form)).action
77
+ : /** @type {HTMLFormElement} */ (HTMLFormElement.prototype.cloneNode.call(form_element))
78
+ .action
64
79
  );
65
80
 
66
- const data = new FormData(form);
81
+ const form_data = new FormData(form_element);
67
82
 
68
83
  const submitter_name = event.submitter?.getAttribute('name');
69
84
  if (submitter_name) {
70
- data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
85
+ form_data.append(submitter_name, event.submitter?.getAttribute('value') ?? '');
71
86
  }
72
87
 
73
88
  const controller = new AbortController();
@@ -75,13 +90,22 @@ export function enhance(form, submit = () => {}) {
75
90
  let cancelled = false;
76
91
  const cancel = () => (cancelled = true);
77
92
 
93
+ // TODO 2.0: Remove `data` and `form`
78
94
  const callback =
79
95
  (await submit({
80
96
  action,
81
97
  cancel,
82
98
  controller,
83
- data,
84
- form,
99
+ get data() {
100
+ warn_on_access('data', 'formData', 'use:enhance submit function');
101
+ return form_data;
102
+ },
103
+ formData: form_data,
104
+ get form() {
105
+ warn_on_access('form', 'formElement', 'use:enhance submit function');
106
+ return form_element;
107
+ },
108
+ formElement: form_element,
85
109
  submitter: event.submitter
86
110
  })) ?? fallback_callback;
87
111
  if (cancelled) return;
@@ -97,7 +121,7 @@ export function enhance(form, submit = () => {}) {
97
121
  'x-sveltekit-action': 'true'
98
122
  },
99
123
  cache: 'no-store',
100
- body: data,
124
+ body: form_data,
101
125
  signal: controller.signal
102
126
  });
103
127
 
@@ -110,8 +134,16 @@ export function enhance(form, submit = () => {}) {
110
134
 
111
135
  callback({
112
136
  action,
113
- data,
114
- form,
137
+ get data() {
138
+ warn_on_access('data', 'formData', 'callback returned from use:enhance submit function');
139
+ return form_data;
140
+ },
141
+ formData: form_data,
142
+ get form() {
143
+ warn_on_access('form', 'formElement', 'callback returned from use:enhance submit function');
144
+ return form_element;
145
+ },
146
+ formElement: form_element,
115
147
  update: (opts) => fallback_callback({ action, result, reset: opts?.reset }),
116
148
  // @ts-expect-error generic constraints stuff we don't care about
117
149
  result
@@ -119,12 +151,12 @@ export function enhance(form, submit = () => {}) {
119
151
  }
120
152
 
121
153
  // @ts-expect-error
122
- HTMLFormElement.prototype.addEventListener.call(form, 'submit', handle_submit);
154
+ HTMLFormElement.prototype.addEventListener.call(form_element, 'submit', handle_submit);
123
155
 
124
156
  return {
125
157
  destroy() {
126
158
  // @ts-expect-error
127
- HTMLFormElement.prototype.removeEventListener.call(form, 'submit', handle_submit);
159
+ HTMLFormElement.prototype.removeEventListener.call(form_element, 'submit', handle_submit);
128
160
  }
129
161
  };
130
162
  }
@@ -272,7 +272,7 @@ export function create_client(app, target) {
272
272
 
273
273
  /** @param {import('./types').NavigationFinished} result */
274
274
  function initialize(result) {
275
- if (DEV && document.querySelector('vite-error-overlay')) return;
275
+ if (DEV && result.state.error && document.querySelector('vite-error-overlay')) return;
276
276
 
277
277
  current = result.state;
278
278
 
@@ -1908,7 +1908,8 @@ function reset_focus() {
1908
1908
  const tabindex = root.getAttribute('tabindex');
1909
1909
 
1910
1910
  root.tabIndex = -1;
1911
- root.focus({ preventScroll: true });
1911
+ // @ts-expect-error
1912
+ root.focus({ preventScroll: true, focusVisible: false });
1912
1913
 
1913
1914
  // restore `tabindex` as to prevent `root` from stealing input from elements
1914
1915
  if (tabindex !== null) {
@@ -107,32 +107,7 @@ export function get_cookies(request, url, trailing_slash) {
107
107
  * @param {import('cookie').CookieSerializeOptions} opts
108
108
  */
109
109
  set(name, value, opts = {}) {
110
- let path = opts.path ?? default_path;
111
-
112
- new_cookies[name] = {
113
- name,
114
- value,
115
- options: {
116
- ...defaults,
117
- ...opts,
118
- path
119
- }
120
- };
121
-
122
- if (__SVELTEKIT_DEV__) {
123
- const serialized = serialize(name, value, new_cookies[name].options);
124
- if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
125
- throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
126
- }
127
-
128
- cookie_paths[name] ??= new Set();
129
-
130
- if (!value) {
131
- cookie_paths[name].delete(path);
132
- } else {
133
- cookie_paths[name].add(path);
134
- }
135
- }
110
+ set_internal(name, value, { ...defaults, ...opts });
136
111
  },
137
112
 
138
113
  /**
@@ -193,7 +168,40 @@ export function get_cookies(request, url, trailing_slash) {
193
168
  .join('; ');
194
169
  }
195
170
 
196
- return { cookies, new_cookies, get_cookie_header };
171
+ /**
172
+ * @param {string} name
173
+ * @param {string} value
174
+ * @param {import('cookie').CookieSerializeOptions} opts
175
+ */
176
+ function set_internal(name, value, opts) {
177
+ let path = opts.path ?? default_path;
178
+
179
+ new_cookies[name] = {
180
+ name,
181
+ value,
182
+ options: {
183
+ ...opts,
184
+ path
185
+ }
186
+ };
187
+
188
+ if (__SVELTEKIT_DEV__) {
189
+ const serialized = serialize(name, value, new_cookies[name].options);
190
+ if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
191
+ throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
192
+ }
193
+
194
+ cookie_paths[name] ??= new Set();
195
+
196
+ if (!value) {
197
+ cookie_paths[name].delete(path);
198
+ } else {
199
+ cookie_paths[name].add(path);
200
+ }
201
+ }
202
+ }
203
+
204
+ return { cookies, new_cookies, get_cookie_header, set_internal };
197
205
  }
198
206
 
199
207
  /**
@@ -9,10 +9,11 @@ import * as paths from '__sveltekit/paths';
9
9
  * manifest: import('types').SSRManifest;
10
10
  * state: import('types').SSRState;
11
11
  * get_cookie_header: (url: URL, header: string | null) => string;
12
+ * set_internal: (name: string, value: string, opts: import('cookie').CookieSerializeOptions) => void;
12
13
  * }} opts
13
14
  * @returns {typeof fetch}
14
15
  */
15
- export function create_fetch({ event, options, manifest, state, get_cookie_header }) {
16
+ export function create_fetch({ event, options, manifest, state, get_cookie_header, set_internal }) {
16
17
  return async (info, init) => {
17
18
  const original_request = normalize_fetch_input(info, init, event.url);
18
19
 
@@ -131,7 +132,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
131
132
  const { name, value, ...options } = set_cookie_parser.parseString(str);
132
133
 
133
134
  // options.sameSite is string, something more specific is required - type cast is safe
134
- event.cookies.set(
135
+ set_internal(
135
136
  name,
136
137
  value,
137
138
  /** @type {import('cookie').CookieSerializeOptions} */ (options)
@@ -244,7 +244,7 @@ export async function respond(request, options, manifest, state) {
244
244
  }
245
245
  }
246
246
 
247
- const { cookies, new_cookies, get_cookie_header } = get_cookies(
247
+ const { cookies, new_cookies, get_cookie_header, set_internal } = get_cookies(
248
248
  request,
249
249
  url,
250
250
  trailing_slash ?? 'never'
@@ -252,7 +252,14 @@ export async function respond(request, options, manifest, state) {
252
252
 
253
253
  cookies_to_add = new_cookies;
254
254
  event.cookies = cookies;
255
- event.fetch = create_fetch({ event, options, manifest, state, get_cookie_header });
255
+ event.fetch = create_fetch({
256
+ event,
257
+ options,
258
+ manifest,
259
+ state,
260
+ get_cookie_header,
261
+ set_internal
262
+ });
256
263
 
257
264
  if (state.prerendering && !state.prerendering.fallback) disable_search(url);
258
265
 
package/src/utils/fork.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { fileURLToPath } from 'node:url';
2
- import child_process from 'node:child_process';
2
+ import { Worker, parentPort } from 'node:worker_threads';
3
3
 
4
4
  /**
5
5
  * Runs a task in a subprocess so any dangling stuff gets killed upon completion.
@@ -11,23 +11,21 @@ import child_process from 'node:child_process';
11
11
  * @returns {(opts: T) => Promise<U>} A function that when called starts the subprocess
12
12
  */
13
13
  export function forked(module, callback) {
14
- if (process.env.SVELTEKIT_FORK && process.send) {
15
- process.send({ type: 'ready', module });
16
-
17
- process.on(
14
+ if (process.env.SVELTEKIT_FORK && parentPort) {
15
+ parentPort.on(
18
16
  'message',
19
17
  /** @param {any} data */ async (data) => {
20
18
  if (data?.type === 'args' && data.module === module) {
21
- if (process.send) {
22
- process.send({
23
- type: 'result',
24
- module,
25
- payload: await callback(data.payload)
26
- });
27
- }
19
+ parentPort?.postMessage({
20
+ type: 'result',
21
+ module,
22
+ payload: await callback(data.payload)
23
+ });
28
24
  }
29
25
  }
30
26
  );
27
+
28
+ parentPort.postMessage({ type: 'ready', module });
31
29
  }
32
30
 
33
31
  /**
@@ -36,20 +34,18 @@ export function forked(module, callback) {
36
34
  */
37
35
  const fn = function (opts) {
38
36
  return new Promise((fulfil, reject) => {
39
- const child = child_process.fork(fileURLToPath(module), {
40
- stdio: 'inherit',
37
+ const worker = new Worker(fileURLToPath(module), {
41
38
  env: {
42
39
  ...process.env,
43
40
  SVELTEKIT_FORK: 'true'
44
- },
45
- serialization: 'advanced'
41
+ }
46
42
  });
47
43
 
48
- child.on(
44
+ worker.on(
49
45
  'message',
50
46
  /** @param {any} data */ (data) => {
51
47
  if (data?.type === 'ready' && data.module === module) {
52
- child.send({
48
+ worker.postMessage({
53
49
  type: 'args',
54
50
  module,
55
51
  payload: opts
@@ -57,13 +53,13 @@ export function forked(module, callback) {
57
53
  }
58
54
 
59
55
  if (data?.type === 'result' && data.module === module) {
60
- child.kill();
56
+ worker.terminate();
61
57
  fulfil(data.payload);
62
58
  }
63
59
  }
64
60
  );
65
61
 
66
- child.on('exit', (code) => {
62
+ worker.on('exit', (code) => {
67
63
  if (code) {
68
64
  reject(new Error(`Failed with code ${code}`));
69
65
  }
@@ -96,7 +96,7 @@ export function parse_route_id(id) {
96
96
  return { pattern, params };
97
97
  }
98
98
 
99
- const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/;
99
+ const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
100
100
 
101
101
  /**
102
102
  * Parses a route ID, then resolves it to a path by replacing parameters with actual values from `entry`.
@@ -112,29 +112,23 @@ export function resolve_entry(id, entry) {
112
112
  return (
113
113
  '/' +
114
114
  segments
115
- .map((segment) => {
116
- const match = basic_param_pattern.exec(segment);
117
-
118
- // static content -- i.e. not a param
119
- if (!match) return segment;
120
-
121
- const optional = !!match[1];
122
- const name = match[2];
123
- const param_value = entry[name];
124
-
125
- // This is nested so TS correctly narrows the type
126
- if (!param_value) {
127
- if (optional) return '';
128
- throw new Error(`Missing parameter '${name}' in route ${id}`);
129
- }
130
-
131
- if (param_value.startsWith('/') || param_value.endsWith('/'))
132
- throw new Error(
133
- `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
134
- );
135
-
136
- return param_value;
137
- })
115
+ .map((segment) =>
116
+ segment.replace(basic_param_pattern, (_, optional, name) => {
117
+ const param_value = entry[name];
118
+
119
+ // This is nested so TS correctly narrows the type
120
+ if (!param_value) {
121
+ if (optional) return '';
122
+ throw new Error(`Missing parameter '${name}' in route ${id}`);
123
+ }
124
+
125
+ if (param_value.startsWith('/') || param_value.endsWith('/'))
126
+ throw new Error(
127
+ `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
128
+ );
129
+ return param_value;
130
+ })
131
+ )
138
132
  .filter(Boolean)
139
133
  .join('/')
140
134
  );
@@ -80,15 +80,36 @@ declare module '$app/forms' {
80
80
  Invalid extends Record<string, unknown> | undefined = Record<string, any>
81
81
  > = (input: {
82
82
  action: URL;
83
+ /**
84
+ * use `formData` instead of `data`
85
+ * @deprecated
86
+ */
83
87
  data: FormData;
88
+ formData: FormData;
89
+ /**
90
+ * use `formElement` instead of `form`
91
+ * @deprecated
92
+ */
84
93
  form: HTMLFormElement;
94
+ formElement: HTMLFormElement;
85
95
  controller: AbortController;
86
96
  cancel(): void;
87
97
  submitter: HTMLElement | null;
88
98
  }) => MaybePromise<
89
99
  | void
90
100
  | ((opts: {
101
+ /**
102
+ * use `formData` instead of `data`
103
+ * @deprecated
104
+ */
105
+ data: FormData;
106
+ formData: FormData;
107
+ /**
108
+ * use `formElement` instead of `form`
109
+ * @deprecated
110
+ */
91
111
  form: HTMLFormElement;
112
+ formElement: HTMLFormElement;
92
113
  action: URL;
93
114
  result: ActionResult<Success, Invalid>;
94
115
  /**
@@ -108,7 +129,7 @@ declare module '$app/forms' {
108
129
  Success extends Record<string, unknown> | undefined = Record<string, any>,
109
130
  Invalid extends Record<string, unknown> | undefined = Record<string, any>
110
131
  >(
111
- form: HTMLFormElement,
132
+ formElement: HTMLFormElement,
112
133
  /**
113
134
  * Called upon submission with the given FormData and the `action` that should be triggered.
114
135
  * If `cancel` is called, the form will not be submitted.
package/types/index.d.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  UniqueInterface
21
21
  } from './private.js';
22
22
  import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js';
23
+ import type { PluginOptions } from '@sveltejs/vite-plugin-svelte';
23
24
 
24
25
  export { PrerenderOption } from './private.js';
25
26
 
@@ -186,6 +187,8 @@ export interface Config {
186
187
  };
187
188
  /** Preprocessor options, if any. Preprocessing can alternatively also be done through Vite's preprocessor capabilities. */
188
189
  preprocess?: any;
190
+ /** `vite-plugin-svelte` plugin options. */
191
+ vitePlugin?: PluginOptions;
189
192
  /** Any additional options required by tooling that integrates with Svelte. */
190
193
  [key: string]: any;
191
194
  }
@@ -1,11 +0,0 @@
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
- }