@sveltejs/kit 2.21.1 → 2.21.3

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": "2.21.1",
3
+ "version": "2.21.3",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -29,7 +29,8 @@
29
29
  "mrmime": "^2.0.0",
30
30
  "sade": "^1.8.1",
31
31
  "set-cookie-parser": "^2.6.0",
32
- "sirv": "^3.0.0"
32
+ "sirv": "^3.0.0",
33
+ "vitefu": "^1.0.6"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@playwright/test": "^1.51.1",
@@ -63,6 +63,7 @@ async function analyse({
63
63
  internal.set_manifest(manifest);
64
64
  internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
65
65
 
66
+ /** @type {Map<string, { page_options: Record<string, any> | null, children: string[] }>} */
66
67
  const static_exports = new Map();
67
68
 
68
69
  // first, build server nodes without the client manifest so we can analyse it
@@ -73,6 +74,7 @@ async function analyse({
73
74
  server_manifest,
74
75
  null,
75
76
  null,
77
+ null,
76
78
  output_config,
77
79
  static_exports
78
80
  );
@@ -468,7 +468,7 @@ export interface KitConfig {
468
468
  errorTemplate?: string;
469
469
  };
470
470
  /**
471
- * Inline CSS inside a `<style>` block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the [String.length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length) property, to be inlined. All CSS files needed for the page and smaller than this value are merged and inlined in a `<style>` block.
471
+ * Inline CSS inside a `<style>` block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the [String.length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length) property, to be inlined. All CSS files needed for the page that are smaller than this value are merged and inlined in a `<style>` block.
472
472
  *
473
473
  * > [!NOTE] This results in fewer initial requests and can improve your [First Contentful Paint](https://web.dev/first-contentful-paint) score. However, it generates larger HTML output and reduces the effectiveness of browser caches. Use it advisedly.
474
474
  * @default 0
@@ -13,66 +13,49 @@ import { create_node_analyser } from '../static_analysis/index.js';
13
13
  * @param {import('types').ManifestData} manifest_data
14
14
  * @param {import('vite').Manifest} server_manifest
15
15
  * @param {import('vite').Manifest | null} client_manifest
16
- * @param {import('vite').Rollup.OutputAsset[] | null} css
16
+ * @param {import('vite').Rollup.OutputBundle | null} server_bundle
17
+ * @param {import('vite').Rollup.RollupOutput['output'] | null} client_bundle
17
18
  * @param {import('types').RecursiveRequired<import('types').ValidatedConfig['kit']['output']>} output_config
18
- * @param {Map<string, Record<string, any> | null>} static_exports
19
+ * @param {Map<string, { page_options: Record<string, any> | null, children: string[] }>} static_exports
19
20
  */
20
- export async function build_server_nodes(out, kit, manifest_data, server_manifest, client_manifest, css, output_config, static_exports) {
21
+ export async function build_server_nodes(out, kit, manifest_data, server_manifest, client_manifest, server_bundle, client_bundle, output_config, static_exports) {
21
22
  mkdirp(`${out}/server/nodes`);
22
23
  mkdirp(`${out}/server/stylesheets`);
23
24
 
24
25
  /** @type {Map<string, string>} */
25
- const stylesheet_lookup = new Map();
26
-
27
- if (css) {
28
- /** @type {Set<string>} */
29
- const client_stylesheets = new Set();
30
- for (const key in client_manifest) {
31
- client_manifest[key].css?.forEach((filename) => {
32
- client_stylesheets.add(filename);
33
- });
34
- }
26
+ const stylesheets_to_inline = new Map();
35
27
 
36
- /** @type {Map<number, string[]>} */
37
- const server_stylesheets = new Map();
38
- manifest_data.nodes.forEach((node, i) => {
39
- if (!node.component || !server_manifest[node.component]) return;
28
+ if (server_bundle && client_bundle && kit.inlineStyleThreshold > 0) {
29
+ const client = get_stylesheets(client_bundle);
40
30
 
41
- const { stylesheets } = find_deps(server_manifest, node.component, false);
31
+ const server_chunks = Object.values(server_bundle);
32
+ const server = get_stylesheets(server_chunks);
42
33
 
43
- if (stylesheets.length) {
44
- server_stylesheets.set(i, stylesheets);
45
- }
46
- });
47
-
48
- for (const asset of css) {
49
- // ignore dynamically imported stylesheets since we don't need to inline those
50
- if (!client_stylesheets.has(asset.fileName) || asset.source.length >= kit.inlineStyleThreshold) {
34
+ // map server stylesheet name to the client stylesheet name
35
+ for (const [id, client_stylesheet] of client.stylesheets_used) {
36
+ const server_stylesheet = server.stylesheets_used.get(id);
37
+ if (!server_stylesheet) {
51
38
  continue;
52
39
  }
40
+ client_stylesheet.forEach((file, i) => {
41
+ stylesheets_to_inline.set(file, server_stylesheet[i]);
42
+ })
43
+ }
53
44
 
54
- // We know that the names for entry points are numbers.
55
- const [index] = basename(asset.fileName).split('.');
56
- // There can also be other CSS files from shared components
57
- // for example, which we need to ignore here.
58
- if (isNaN(+index)) continue;
59
-
60
- const file = `${out}/server/stylesheets/${index}.js`;
61
-
62
- // we need to inline the server stylesheet instead of the client one
63
- // so that asset paths are correct on document load
64
- const filenames = server_stylesheets.get(+index);
65
-
66
- if (!filenames) {
67
- throw new Error('This should never happen, but if it does, it means we failed to find the server stylesheet for a node.');
45
+ // filter out stylesheets that should not be inlined
46
+ for (const [fileName, content] of client.stylesheet_content) {
47
+ if (content.length >= kit.inlineStyleThreshold) {
48
+ stylesheets_to_inline.delete(fileName);
68
49
  }
50
+ }
69
51
 
70
- const sources = filenames.map((filename) => {
71
- return fs.readFileSync(`${out}/server/${filename}`, 'utf-8');
72
- });
73
- fs.writeFileSync(file, `// ${filenames.join(', ')}\nexport default ${s(sources.join('\n'))};`);
74
-
75
- stylesheet_lookup.set(asset.fileName, index);
52
+ // map server stylesheet source to the client stylesheet name
53
+ for (const [client_file, server_file] of stylesheets_to_inline) {
54
+ const source = server.stylesheet_content.get(server_file);
55
+ if (!source) {
56
+ throw new Error(`Server stylesheet source not found for client stylesheet ${client_file}`);
57
+ }
58
+ stylesheets_to_inline.set(client_file, source);
76
59
  }
77
60
  }
78
61
 
@@ -139,8 +122,9 @@ export async function build_server_nodes(out, kit, manifest_data, server_manifes
139
122
  const entry_path = `${normalizePath(kit.outDir)}/generated/client-optimized/nodes/${i}.js`;
140
123
  const entry = find_deps(client_manifest, entry_path, true);
141
124
 
142
- // eagerly load stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
143
- // If it is not used during SSR, it can be lazily loaded in the browser.
125
+ // eagerly load client stylesheets and fonts imported by the SSR-ed page to avoid FOUC.
126
+ // However, if it is not used during SSR (not present in the server manifest),
127
+ // then it can be lazily loaded in the browser.
144
128
 
145
129
  /** @type {import('types').AssetDependencies | undefined} */
146
130
  let component;
@@ -155,26 +139,26 @@ export async function build_server_nodes(out, kit, manifest_data, server_manifes
155
139
  }
156
140
 
157
141
  /** @type {Set<string>} */
158
- const css_used_by_server = new Set();
142
+ const eager_css = new Set();
159
143
  /** @type {Set<string>} */
160
- const assets_used_by_server = new Set();
144
+ const eager_assets = new Set();
161
145
 
162
- entry.stylesheet_map.forEach((value, key) => {
163
- // pages and layouts are named as node indexes in the client manifest
164
- // so we need to use the original filename when checking against the server manifest
165
- if (key === entry_path) {
166
- key = node.component ?? key;
146
+ entry.stylesheet_map.forEach((value, filepath) => {
147
+ // pages and layouts are renamed to node indexes when optimised for the client
148
+ // so we use the original filename instead to check against the server manifest
149
+ if (filepath === entry_path) {
150
+ filepath = node.component ?? filepath;
167
151
  }
168
152
 
169
- if (component?.stylesheet_map.has(key) || universal?.stylesheet_map.has(key)) {
170
- value.css.forEach(file => css_used_by_server.add(file));
171
- value.assets.forEach(file => assets_used_by_server.add(file));
153
+ if (component?.stylesheet_map.has(filepath) || universal?.stylesheet_map.has(filepath)) {
154
+ value.css.forEach(file => eager_css.add(file));
155
+ value.assets.forEach(file => eager_assets.add(file));
172
156
  }
173
157
  });
174
158
 
175
159
  imported = entry.imports;
176
- stylesheets = Array.from(css_used_by_server);
177
- fonts = filter_fonts(Array.from(assets_used_by_server));
160
+ stylesheets = Array.from(eager_css);
161
+ fonts = filter_fonts(Array.from(eager_assets));
178
162
  }
179
163
 
180
164
  exports.push(
@@ -184,19 +168,26 @@ export async function build_server_nodes(out, kit, manifest_data, server_manifes
184
168
  );
185
169
 
186
170
  /** @type {string[]} */
187
- const styles = [];
188
-
189
- stylesheets.forEach((file) => {
190
- if (stylesheet_lookup.has(file)) {
191
- const index = stylesheet_lookup.get(file);
192
- const name = `stylesheet_${index}`;
193
- imports.push(`import ${name} from '../stylesheets/${index}.js';`);
194
- styles.push(`\t${s(file)}: ${name}`);
171
+ const inline_styles = [];
172
+
173
+ stylesheets.forEach((file, i) => {
174
+ if (stylesheets_to_inline.has(file)) {
175
+ const filename = basename(file);
176
+ const dest = `${out}/server/stylesheets/${filename}.js`;
177
+ const source = stylesheets_to_inline.get(file);
178
+ if (!source) {
179
+ throw new Error(`Server stylesheet source not found for client stylesheet ${file}`);
180
+ }
181
+ fs.writeFileSync(dest, `// ${filename}\nexport default ${s(source)};`);
182
+
183
+ const name = `stylesheet_${i}`;
184
+ imports.push(`import ${name} from '../stylesheets/${filename}.js';`);
185
+ inline_styles.push(`\t${s(file)}: ${name}`);
195
186
  }
196
187
  });
197
188
 
198
- if (styles.length > 0) {
199
- exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`);
189
+ if (inline_styles.length > 0) {
190
+ exports.push(`export const inline_styles = () => ({\n${inline_styles.join(',\n')}\n});`);
200
191
  }
201
192
 
202
193
  fs.writeFileSync(
@@ -205,3 +196,37 @@ export async function build_server_nodes(out, kit, manifest_data, server_manifes
205
196
  );
206
197
  }
207
198
  }
199
+
200
+ /**
201
+ * @param {(import('vite').Rollup.OutputAsset | import('vite').Rollup.OutputChunk)[]} chunks
202
+ */
203
+ function get_stylesheets(chunks) {
204
+ /**
205
+ * A map of module IDs and the stylesheets they use.
206
+ * @type {Map<string, string[]>}
207
+ */
208
+ const stylesheets_used = new Map();
209
+
210
+ /**
211
+ * A map of stylesheet names and their content.
212
+ * @type {Map<string, string>}
213
+ */
214
+ const stylesheet_content = new Map();
215
+
216
+ for (const chunk of chunks) {
217
+ if (chunk.type === 'asset') {
218
+ if (chunk.fileName.endsWith('.css')) {
219
+ stylesheet_content.set(chunk.fileName, chunk.source.toString());
220
+ }
221
+ continue;
222
+ }
223
+
224
+ if (chunk.viteMetadata?.importedCss.size) {
225
+ const css = Array.from(chunk.viteMetadata.importedCss);
226
+ for (const id of chunk.moduleIds) {
227
+ stylesheets_used.set(id, css );
228
+ }
229
+ }
230
+ }
231
+ return { stylesheets_used, stylesheet_content };
232
+ }
@@ -355,14 +355,18 @@ export async function dev(vite, vite_config, svelte_config) {
355
355
 
356
356
  // Debounce add/unlink events because in case of folder deletion or moves
357
357
  // they fire in rapid succession, causing needless invocations.
358
+ // These watchers only run for routes, param matchers, and client hooks.
358
359
  watch('add', () => debounce(update_manifest));
359
360
  watch('unlink', () => debounce(update_manifest));
360
361
  watch('change', (file) => {
361
362
  // Don't run for a single file if the whole manifest is about to get updated
362
363
  if (timeout || restarting) return;
363
364
 
365
+ if (/\+(page|layout).*$/.test(file)) {
366
+ invalidate_page_options(path.relative(cwd, file));
367
+ }
368
+
364
369
  sync.update(svelte_config, manifest_data, file);
365
- invalidate_page_options(path.relative(cwd, file));
366
370
  });
367
371
 
368
372
  const { appTemplate, errorTemplate, serviceWorker, hooks } = svelte_config.kit.files;
@@ -389,10 +393,10 @@ export async function dev(vite, vite_config, svelte_config) {
389
393
  }
390
394
  });
391
395
 
392
- // changing the svelte config requires restarting the dev server
393
- // the config is only read on start and passed on to vite-plugin-svelte
394
- // which needs up-to-date values to operate correctly
395
396
  vite.watcher.on('change', async (file) => {
397
+ // changing the svelte config requires restarting the dev server
398
+ // the config is only read on start and passed on to vite-plugin-svelte
399
+ // which needs up-to-date values to operate correctly
396
400
  if (path.basename(file) === 'svelte.config.js') {
397
401
  console.log(`svelte config changed, restarting vite dev-server. changed file: ${file}`);
398
402
  restarting = true;
@@ -36,6 +36,7 @@ import {
36
36
  } from './module_ids.js';
37
37
  import { import_peer } from '../../utils/import.js';
38
38
  import { compact } from '../../utils/array.js';
39
+ import { crawlFrameworkPkgs } from 'vitefu';
39
40
 
40
41
  const cwd = process.cwd();
41
42
 
@@ -227,7 +228,7 @@ async function kit({ svelte_config }) {
227
228
  * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file.
228
229
  * @see https://vitejs.dev/guide/api-plugin.html#config
229
230
  */
230
- config(config, config_env) {
231
+ async config(config, config_env) {
231
232
  initial_config = config;
232
233
  vite_config_env = config_env;
233
234
  is_build = config_env.command === 'build';
@@ -250,6 +251,20 @@ async function kit({ svelte_config }) {
250
251
 
251
252
  const generated = path.posix.join(kit.outDir, 'generated');
252
253
 
254
+ const packages_depending_on_svelte_kit = (
255
+ await crawlFrameworkPkgs({
256
+ root: cwd,
257
+ isBuild: is_build,
258
+ viteUserConfig: config,
259
+ isSemiFrameworkPkgByJson: (pkg_json) => {
260
+ return (
261
+ !!pkg_json.dependencies?.['@sveltejs/kit'] ||
262
+ !!pkg_json.peerDependencies?.['@sveltejs/kit']
263
+ );
264
+ }
265
+ })
266
+ ).ssr.noExternal;
267
+
253
268
  // dev and preview config can be shared
254
269
  /** @type {import('vite').UserConfig} */
255
270
  const new_config = {
@@ -306,7 +321,11 @@ async function kit({ svelte_config }) {
306
321
  // when it is detected to keep our virtual modules working.
307
322
  // See https://github.com/sveltejs/kit/pull/9172
308
323
  // and https://vitest.dev/config/#deps-registernodeloader
309
- '@sveltejs/kit'
324
+ '@sveltejs/kit',
325
+ // We need to bundle any packages depending on @sveltejs/kit so that
326
+ // everyone uses the same instances of classes such as `Redirect`
327
+ // which we use in `instanceof` checks
328
+ ...packages_depending_on_svelte_kit
310
329
  ]
311
330
  }
312
331
  };
@@ -787,7 +806,7 @@ Tips:
787
806
  */
788
807
  writeBundle: {
789
808
  sequential: true,
790
- async handler(_options) {
809
+ async handler(_options, bundle) {
791
810
  if (secondary_build_started) return; // only run this once
792
811
 
793
812
  const verbose = vite_config.logLevel === 'info';
@@ -843,7 +862,7 @@ Tips:
843
862
 
844
863
  secondary_build_started = true;
845
864
 
846
- const { output } = /** @type {import('vite').Rollup.RollupOutput} */ (
865
+ const { output: client_chunks } = /** @type {import('vite').Rollup.RollupOutput} */ (
847
866
  await vite.build({
848
867
  configFile: vite_config.configFile,
849
868
  // CLI args
@@ -886,7 +905,7 @@ Tips:
886
905
  imports: [...start.imports, ...app.imports],
887
906
  stylesheets: [...start.stylesheets, ...app.stylesheets],
888
907
  fonts: [...start.fonts, ...app.fonts],
889
- uses_env_dynamic_public: output.some(
908
+ uses_env_dynamic_public: client_chunks.some(
890
909
  (chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
891
910
  )
892
911
  };
@@ -935,14 +954,14 @@ Tips:
935
954
  imports: start.imports,
936
955
  stylesheets: start.stylesheets,
937
956
  fonts: start.fonts,
938
- uses_env_dynamic_public: output.some(
957
+ uses_env_dynamic_public: client_chunks.some(
939
958
  (chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
940
959
  )
941
960
  };
942
961
 
943
962
  if (svelte_config.kit.output.bundleStrategy === 'inline') {
944
963
  const style = /** @type {import('rollup').OutputAsset} */ (
945
- output.find(
964
+ client_chunks.find(
946
965
  (chunk) =>
947
966
  chunk.type === 'asset' &&
948
967
  chunk.names.length === 1 &&
@@ -957,11 +976,6 @@ Tips:
957
976
  }
958
977
  }
959
978
 
960
- const css = output.filter(
961
- /** @type {(value: any) => value is import('vite').Rollup.OutputAsset} */
962
- (value) => value.type === 'asset' && value.fileName.endsWith('.css')
963
- );
964
-
965
979
  // regenerate manifest now that we have client entry...
966
980
  fs.writeFileSync(
967
981
  manifest_path,
@@ -980,7 +994,8 @@ Tips:
980
994
  manifest_data,
981
995
  server_manifest,
982
996
  client_manifest,
983
- css,
997
+ bundle,
998
+ client_chunks,
984
999
  svelte_config.kit.output,
985
1000
  static_exports
986
1001
  );
@@ -4,23 +4,22 @@ import { read } from '../../../utils/filesystem.js';
4
4
 
5
5
  const inheritable_page_options = new Set(['ssr', 'prerender', 'csr', 'trailingSlash', 'config']);
6
6
 
7
- const page_options = new Set([...inheritable_page_options, 'entries']);
7
+ const valid_page_options = new Set([...inheritable_page_options, 'entries']);
8
8
 
9
9
  const skip_parsing_regex = new RegExp(
10
- `${Array.from(page_options).join('|')}|(?:export[\\s\\n]+\\*[\\s\\n]+from)`
10
+ `${Array.from(valid_page_options).join('|')}|(?:export[\\s\\n]+\\*[\\s\\n]+from)`
11
11
  );
12
12
 
13
13
  const parser = Parser.extend(tsPlugin());
14
14
 
15
15
  /**
16
- * Collects exported page options from a +page.js/+layout.js file.
17
- * We ignore reassignments and use the declared value.
18
- * Returns `null` if any export is too difficult to analyse.
19
- * @param {string} filename
16
+ * Collects page options from a +page.js/+layout.js file, ignoring reassignments
17
+ * and using the declared value. Returns `null` if any export is too difficult to analyse.
18
+ * @param {string} filename The name of the file to report when an error occurs
20
19
  * @param {string} input
21
20
  * @returns {Record<string, any> | null}
22
21
  */
23
- export function statically_analyse_exports(filename, input) {
22
+ export function statically_analyse_page_options(filename, input) {
24
23
  // if there's a chance there are no page exports or export all declaration,
25
24
  // then we can skip the AST parsing which is expensive
26
25
  if (!skip_parsing_regex.test(input)) {
@@ -34,14 +33,14 @@ export function statically_analyse_exports(filename, input) {
34
33
  });
35
34
 
36
35
  /** @type {Map<string, import('acorn').Literal['value']>} */
37
- const static_exports = new Map();
36
+ const page_options = new Map();
38
37
 
39
38
  for (const statement of source.body) {
40
39
  // ignore export all declarations with aliases that are not page options
41
40
  if (
42
41
  statement.type === 'ExportAllDeclaration' &&
43
42
  statement.exported &&
44
- !page_options.has(get_name(statement.exported))
43
+ !valid_page_options.has(get_name(statement.exported))
45
44
  ) {
46
45
  continue;
47
46
  }
@@ -60,7 +59,7 @@ export function statically_analyse_exports(filename, input) {
60
59
  const export_specifiers = new Map();
61
60
  for (const specifier of statement.specifiers) {
62
61
  const exported_name = get_name(specifier.exported);
63
- if (!page_options.has(exported_name)) {
62
+ if (!valid_page_options.has(exported_name)) {
64
63
  continue;
65
64
  }
66
65
 
@@ -109,7 +108,7 @@ export function statically_analyse_exports(filename, input) {
109
108
  }
110
109
 
111
110
  if (variable_declarator.init?.type === 'Literal') {
112
- static_exports.set(
111
+ page_options.set(
113
112
  /** @type {string} */ (export_specifiers.get(variable_declarator.id.name)),
114
113
  variable_declarator.init.value
115
114
  );
@@ -138,7 +137,7 @@ export function statically_analyse_exports(filename, input) {
138
137
 
139
138
  // class and function declarations
140
139
  if (statement.declaration.type !== 'VariableDeclaration') {
141
- if (page_options.has(statement.declaration.id.name)) {
140
+ if (valid_page_options.has(statement.declaration.id.name)) {
142
141
  return null;
143
142
  }
144
143
  continue;
@@ -149,12 +148,12 @@ export function statically_analyse_exports(filename, input) {
149
148
  return null;
150
149
  }
151
150
 
152
- if (!page_options.has(declaration.id.name)) {
151
+ if (!valid_page_options.has(declaration.id.name)) {
153
152
  continue;
154
153
  }
155
154
 
156
155
  if (declaration.init?.type === 'Literal') {
157
- static_exports.set(declaration.id.name, declaration.init.value);
156
+ page_options.set(declaration.id.name, declaration.init.value);
158
157
  continue;
159
158
  }
160
159
 
@@ -163,10 +162,10 @@ export function statically_analyse_exports(filename, input) {
163
162
  }
164
163
  }
165
164
 
166
- return Object.fromEntries(static_exports);
165
+ return Object.fromEntries(page_options);
167
166
  } catch (error) {
168
167
  if (error instanceof Error) {
169
- error.message = `Failed to statically analyse ${filename}. ${error.message}`;
168
+ error.message = `Failed to statically analyse page options for ${filename}. ${error.message}`;
170
169
  }
171
170
  throw error;
172
171
  }
@@ -183,65 +182,67 @@ export function get_name(node) {
183
182
  /**
184
183
  * @param {{
185
184
  * resolve: (file: string) => Promise<Record<string, any>>;
186
- * static_exports?: Map<string, Record<string, any> | null>;
185
+ * static_exports?: Map<string, { page_options: Record<string, any> | null, children: string[] }>;
187
186
  * }} opts
188
187
  */
189
188
  export function create_node_analyser({ resolve, static_exports = new Map() }) {
190
189
  /**
191
190
  * Computes the final page options for a node (if possible). Otherwise, returns `null`.
192
191
  * @param {import('types').PageNode} node
193
- * @returns {Promise<import('types').UniversalNode | null>}
192
+ * @returns {Promise<Record<string, any> | null>}
194
193
  */
195
194
  const get_page_options = async (node) => {
196
- if (node.universal && static_exports.has(node.universal)) {
197
- return /** @type {import('types').UniversalNode | null} */ (
198
- static_exports.get(node.universal)
199
- );
195
+ const key = node.universal || node.server;
196
+ if (key && static_exports.has(key)) {
197
+ return { ...static_exports.get(key)?.page_options };
200
198
  }
201
199
 
202
- /** @type {Record<string, any> | null} */
200
+ /** @type {Record<string, any>} */
203
201
  let page_options = {};
204
202
 
205
- if (node.server) {
206
- const module = await resolve(node.server);
207
- for (const key in inheritable_page_options) {
208
- if (key in module) {
209
- page_options[key] = module[key];
210
- }
211
- }
212
- }
203
+ if (node.parent) {
204
+ const parent_options = await get_page_options(node.parent);
213
205
 
214
- if (node.universal) {
215
- let universal_exports = static_exports.get(node.universal);
216
- if (universal_exports === undefined) {
217
- const input = read(node.universal);
218
- universal_exports = statically_analyse_exports(node.universal, input);
206
+ const parent_key = node.parent.universal || node.parent.server;
207
+ if (key && parent_key) {
208
+ static_exports.get(parent_key)?.children.push(key);
219
209
  }
220
210
 
221
- if (universal_exports === null) {
222
- static_exports.set(node.universal, null);
211
+ if (parent_options === null) {
212
+ // if the parent cannot be analysed, we can't know what page options
213
+ // the child node inherits, so we also mark it as unanalysable
214
+ if (key) {
215
+ static_exports.set(key, { page_options: null, children: [] });
216
+ }
223
217
  return null;
224
218
  }
225
219
 
226
- page_options = { ...page_options, ...universal_exports };
220
+ page_options = { ...parent_options };
227
221
  }
228
222
 
229
- if (node.parent) {
230
- const parent_options = await get_page_options(node.parent);
231
- if (parent_options === null) {
232
- // if the parent cannot be statically analysed, we can't know what
233
- // page options the current node inherits, so we invalidate it too
234
- if (node.universal) {
235
- static_exports.set(node.universal, null);
223
+ if (node.server) {
224
+ const module = await resolve(node.server);
225
+ for (const page_option in inheritable_page_options) {
226
+ if (page_option in module) {
227
+ page_options[page_option] = module[page_option];
236
228
  }
229
+ }
230
+ }
231
+
232
+ if (node.universal) {
233
+ const input = read(node.universal);
234
+ const universal_page_options = statically_analyse_page_options(node.universal, input);
235
+
236
+ if (universal_page_options === null) {
237
+ static_exports.set(node.universal, { page_options: null, children: [] });
237
238
  return null;
238
239
  }
239
240
 
240
- page_options = { ...parent_options, ...page_options };
241
+ page_options = { ...page_options, ...universal_page_options };
241
242
  }
242
243
 
243
- if (node.universal) {
244
- static_exports.set(node.universal, page_options);
244
+ if (key) {
245
+ static_exports.set(key, { page_options, children: [] });
245
246
  }
246
247
 
247
248
  return page_options;
@@ -249,11 +250,14 @@ export function create_node_analyser({ resolve, static_exports = new Map() }) {
249
250
 
250
251
  /**
251
252
  * @param {string} file
252
- * @returns {void}
253
253
  */
254
254
  const invalidate_page_options = (file) => {
255
+ static_exports.get(file)?.children.forEach((child) => static_exports.delete(child));
255
256
  static_exports.delete(file);
256
257
  };
257
258
 
258
- return { get_page_options, invalidate_page_options };
259
+ return {
260
+ get_page_options,
261
+ invalidate_page_options
262
+ };
259
263
  }
@@ -1,10 +1,13 @@
1
1
  import * as devalue from 'devalue';
2
- import { DEV } from 'esm-env';
2
+ import { BROWSER, DEV } from 'esm-env';
3
3
  import { invalidateAll } from './navigation.js';
4
- import { app, applyAction } from '../client/client.js';
4
+ import { app as client_app, applyAction } from '../client/client.js';
5
+ import { app as server_app } from '../server/app.js';
5
6
 
6
7
  export { applyAction };
7
8
 
9
+ const decoders = BROWSER ? client_app.decoders : server_app?.decoders;
10
+
8
11
  /**
9
12
  * Use this function to deserialize the response from a form submission.
10
13
  * Usage:
@@ -31,7 +34,7 @@ export function deserialize(result) {
31
34
  const parsed = JSON.parse(result);
32
35
 
33
36
  if (parsed.data) {
34
- parsed.data = devalue.parse(parsed.data, app.decoders);
37
+ parsed.data = devalue.parse(parsed.data, decoders);
35
38
  }
36
39
 
37
40
  return parsed;
@@ -312,12 +312,16 @@ export async function start(_app, _target, hydrate) {
312
312
  // if we reload the page, or Cmd-Shift-T back to it,
313
313
  // recover scroll position
314
314
  const scroll = scroll_positions[current_history_index];
315
- if (scroll) {
316
- history.scrollRestoration = 'manual';
317
- scrollTo(scroll.x, scroll.y);
315
+ function restore_scroll() {
316
+ if (scroll) {
317
+ history.scrollRestoration = 'manual';
318
+ scrollTo(scroll.x, scroll.y);
319
+ }
318
320
  }
319
321
 
320
322
  if (hydrate) {
323
+ restore_scroll();
324
+
321
325
  await _hydrate(target, hydrate);
322
326
  } else {
323
327
  await navigate({
@@ -325,6 +329,8 @@ export async function start(_app, _target, hydrate) {
325
329
  url: resolve_url(app.hash ? decode_hash(new URL(location.href)) : location.href),
326
330
  replace_state: true
327
331
  });
332
+
333
+ restore_scroll();
328
334
  }
329
335
 
330
336
  _start_router();
@@ -2794,22 +2800,43 @@ function reset_focus() {
2794
2800
  autofocus.focus();
2795
2801
  } else {
2796
2802
  // Reset page selection and focus
2797
- // We try to mimic browsers' behaviour as closely as possible by targeting the
2798
- // first scrollable region, but unfortunately it's not a perfect match — e.g.
2799
- // shift-tabbing won't immediately cycle up from the end of the page on Chromium
2800
- // See https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area
2801
- const root = document.body;
2802
- const tabindex = root.getAttribute('tabindex');
2803
-
2804
- root.tabIndex = -1;
2805
- // @ts-expect-error
2806
- root.focus({ preventScroll: true, focusVisible: false });
2803
+ // TODO: find a fix that works with hash routing too
2804
+ if (!app.hash && location.hash && document.querySelector(location.hash)) {
2805
+ const { x, y } = scroll_state();
2807
2806
 
2808
- // restore `tabindex` as to prevent `root` from stealing input from elements
2809
- if (tabindex !== null) {
2810
- root.setAttribute('tabindex', tabindex);
2807
+ setTimeout(() => {
2808
+ const history_state = history.state;
2809
+ // Mimic the browsers' behaviour and set the sequential focus navigation
2810
+ // starting point to the fragment identifier
2811
+ location.replace(location.hash);
2812
+ // but Firefox has a bug that sets the history state as null so we
2813
+ // need to restore the history state
2814
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1199924
2815
+ history.replaceState(history_state, '', location.hash);
2816
+
2817
+ // Scroll management has already happened earlier so we need to restore
2818
+ // the scroll position after setting the sequential focus navigation starting point
2819
+ scrollTo(x, y);
2820
+ });
2811
2821
  } else {
2812
- root.removeAttribute('tabindex');
2822
+ // We try to mimic browsers' behaviour as closely as possible by targeting the
2823
+ // first scrollable region, but unfortunately it's not a perfect match — e.g.
2824
+ // shift-tabbing won't immediately cycle up from the end of the page on Chromium
2825
+ // See https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area
2826
+ const root = document.body;
2827
+ const tabindex = root.getAttribute('tabindex');
2828
+
2829
+ root.tabIndex = -1;
2830
+ // @ts-expect-error options.focusVisible is only supported in Firefox
2831
+ // See https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#browser_compatibility
2832
+ root.focus({ preventScroll: true, focusVisible: false });
2833
+
2834
+ // restore `tabindex` as to prevent `root` from stealing input from elements
2835
+ if (tabindex !== null) {
2836
+ root.setAttribute('tabindex', tabindex);
2837
+ } else {
2838
+ root.removeAttribute('tabindex');
2839
+ }
2813
2840
  }
2814
2841
 
2815
2842
  // capture current selection, so we can compare the state after
@@ -0,0 +1,9 @@
1
+ /** @type {{ decoders: Record<string, (data: any) => any> }} */
2
+ export let app;
3
+
4
+ /**
5
+ * @param {{ decoders: Record<string, (data: any) => any> }} value
6
+ */
7
+ export function set_app(value) {
8
+ app = value;
9
+ }
@@ -5,6 +5,7 @@ import { DEV } from 'esm-env';
5
5
  import { filter_private_env, filter_public_env } from '../../utils/env.js';
6
6
  import { prerendering } from '__sveltekit/environment';
7
7
  import { set_read_implementation, set_manifest } from '__sveltekit/server';
8
+ import { set_app } from './app.js';
8
9
 
9
10
  /** @type {ProxyHandler<{ type: 'public' | 'private' }>} */
10
11
  const prerender_env_handler = {
@@ -74,12 +75,21 @@ export class Server {
74
75
 
75
76
  this.#options.hooks = {
76
77
  handle: module.handle || (({ event, resolve }) => resolve(event)),
77
- handleError: module.handleError || (({ error }) => console.error(error)),
78
+ handleError:
79
+ module.handleError ||
80
+ (({ status, error }) =>
81
+ console.error((status === 404 && /** @type {Error} */ (error)?.message) || error)),
78
82
  handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)),
79
83
  reroute: module.reroute || (() => {}),
80
84
  transport: module.transport || {}
81
85
  };
82
86
 
87
+ set_app({
88
+ decoders: module.transport
89
+ ? Object.fromEntries(Object.entries(module.transport).map(([k, v]) => [k, v.decode]))
90
+ : {}
91
+ });
92
+
83
93
  if (module.init) {
84
94
  await module.init();
85
95
  }
@@ -94,6 +104,10 @@ export class Server {
94
104
  reroute: () => {},
95
105
  transport: {}
96
106
  };
107
+
108
+ set_app({
109
+ decoders: {}
110
+ });
97
111
  } else {
98
112
  throw error;
99
113
  }
@@ -51,6 +51,8 @@ const page_methods = new Set(['GET', 'HEAD', 'POST']);
51
51
 
52
52
  const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']);
53
53
 
54
+ let warned_on_devtools_json_request = false;
55
+
54
56
  /**
55
57
  * @param {Request} request
56
58
  * @param {import('types').SSROptions} options
@@ -573,6 +575,21 @@ export async function respond(request, options, manifest, state) {
573
575
  // if this request came direct from the user, rather than
574
576
  // via our own `fetch`, render a 404 page
575
577
  if (state.depth === 0) {
578
+ // In local development, Chrome requests this file for its 'automatic workspace folders' feature,
579
+ // causing console spam. If users want to serve this file they can install
580
+ // https://github.com/ChromeDevTools/vite-plugin-devtools-json
581
+ if (DEV && event.url.pathname === '/.well-known/appspecific/com.chrome.devtools.json') {
582
+ if (!warned_on_devtools_json_request) {
583
+ console.warn(
584
+ `\nGoogle Chrome is requesting ${event.url.pathname} to automatically configure devtools project settings. To serve this file, add this plugin to your Vite config:\n\nhttps://github.com/ChromeDevTools/vite-plugin-devtools-json\n`
585
+ );
586
+
587
+ warned_on_devtools_json_request = true;
588
+ }
589
+
590
+ return new Response(undefined, { status: 404 });
591
+ }
592
+
576
593
  return await respond_with_error({
577
594
  event,
578
595
  options,
package/src/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '2.21.1';
4
+ export const VERSION = '2.21.3';
package/types/index.d.ts CHANGED
@@ -450,7 +450,7 @@ declare module '@sveltejs/kit' {
450
450
  errorTemplate?: string;
451
451
  };
452
452
  /**
453
- * Inline CSS inside a `<style>` block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the [String.length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length) property, to be inlined. All CSS files needed for the page and smaller than this value are merged and inlined in a `<style>` block.
453
+ * Inline CSS inside a `<style>` block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the [String.length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length) property, to be inlined. All CSS files needed for the page that are smaller than this value are merged and inlined in a `<style>` block.
454
454
  *
455
455
  * > [!NOTE] This results in fewer initial requests and can improve your [First Contentful Paint](https://web.dev/first-contentful-paint) score. However, it generates larger HTML output and reduces the effectiveness of browser caches. Use it advisedly.
456
456
  * @default 0
@@ -169,6 +169,6 @@
169
169
  null,
170
170
  null
171
171
  ],
172
- "mappings": "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",
172
+ "mappings": "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",
173
173
  "ignoreList": []
174
174
  }