@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.20

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.
Files changed (192) hide show
  1. package/package.json +58 -36
  2. package/src/cli.js +14 -11
  3. package/src/constants.js +6 -1
  4. package/src/core/adapt/builder.js +54 -60
  5. package/src/core/adapt/index.js +2 -5
  6. package/src/core/config/index.js +141 -76
  7. package/src/core/config/options.js +260 -260
  8. package/src/core/config/types.d.ts +1 -1
  9. package/src/core/env.js +143 -13
  10. package/src/core/generate_manifest/index.js +18 -15
  11. package/src/core/postbuild/analyse.js +7 -19
  12. package/src/core/postbuild/crawl.js +22 -6
  13. package/src/core/postbuild/entities.js +8 -2
  14. package/src/core/postbuild/fallback.js +4 -2
  15. package/src/core/postbuild/prerender.js +193 -69
  16. package/src/core/postbuild/queue.js +2 -1
  17. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  18. package/src/core/sync/create_manifest_data/index.js +72 -52
  19. package/src/core/sync/sync.js +35 -26
  20. package/src/core/sync/ts.js +1 -1
  21. package/src/core/sync/utils.js +22 -2
  22. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +105 -56
  23. package/src/core/sync/write_client_manifest.js +13 -28
  24. package/src/core/sync/write_env.js +7 -5
  25. package/src/core/sync/write_server.js +25 -33
  26. package/src/core/sync/write_tsconfig/index.js +274 -0
  27. package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
  28. package/src/core/sync/write_tsconfig/utils.js +77 -0
  29. package/src/core/sync/write_tsconfig/validate.js +128 -0
  30. package/src/core/sync/write_types/index.js +119 -116
  31. package/src/core/utils.js +30 -5
  32. package/src/exports/env/index.js +77 -0
  33. package/src/exports/env/public.d.ts +55 -0
  34. package/src/exports/hooks/index.js +3 -9
  35. package/src/exports/hooks/public.d.ts +194 -0
  36. package/src/exports/hooks/sequence.js +9 -6
  37. package/src/exports/index.js +53 -20
  38. package/src/exports/internal/client.js +5 -0
  39. package/src/exports/internal/env.js +8 -5
  40. package/src/exports/internal/index.js +1 -90
  41. package/src/exports/internal/{event.js → server/event.js} +1 -2
  42. package/src/exports/internal/server/index.js +37 -0
  43. package/src/exports/internal/server/telemetry.js +95 -0
  44. package/src/exports/internal/shared.js +90 -0
  45. package/src/exports/node/index.js +64 -22
  46. package/src/exports/params/index.js +70 -0
  47. package/src/exports/params/public.d.ts +63 -0
  48. package/src/exports/public.d.ts +219 -1118
  49. package/src/exports/url.js +86 -0
  50. package/src/exports/vite/build/build_server.js +53 -64
  51. package/src/exports/vite/build/remote.js +24 -19
  52. package/src/exports/vite/build/utils.js +0 -8
  53. package/src/exports/vite/dev/index.js +199 -125
  54. package/src/exports/vite/index.js +1514 -833
  55. package/src/exports/vite/module_ids.js +2 -2
  56. package/src/exports/vite/preview/index.js +32 -24
  57. package/src/exports/vite/utils.js +83 -45
  58. package/src/pathname.js +55 -0
  59. package/src/runner.js +15 -0
  60. package/src/runtime/app/env/internal.js +4 -4
  61. package/src/runtime/app/env/types.d.ts +1 -1
  62. package/src/runtime/app/environment/index.js +3 -3
  63. package/src/runtime/app/{forms.js → forms/index.js} +73 -42
  64. package/src/runtime/app/forms/public.d.ts +2 -0
  65. package/src/runtime/app/forms/types.d.ts +56 -0
  66. package/src/runtime/app/internal/transport.js +53 -0
  67. package/src/runtime/app/manifest/index.js +1 -0
  68. package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
  69. package/src/runtime/app/navigation/public.d.ts +237 -0
  70. package/src/runtime/app/paths/client.js +37 -37
  71. package/src/runtime/app/paths/index.js +1 -1
  72. package/src/runtime/app/paths/internal/client.js +34 -2
  73. package/src/runtime/app/paths/internal/server.js +6 -23
  74. package/src/runtime/app/paths/internal.d.ts +3 -0
  75. package/src/runtime/app/paths/public.d.ts +1 -29
  76. package/src/runtime/app/paths/server.js +36 -17
  77. package/src/runtime/app/paths/types.d.ts +11 -19
  78. package/src/runtime/app/server/index.js +2 -2
  79. package/src/runtime/app/server/public.d.ts +519 -0
  80. package/src/runtime/app/server/remote/command.js +13 -11
  81. package/src/runtime/app/server/remote/form.js +61 -39
  82. package/src/runtime/app/server/remote/prerender.js +45 -45
  83. package/src/runtime/app/server/remote/query.js +106 -108
  84. package/src/runtime/app/server/remote/requested.js +27 -19
  85. package/src/runtime/app/server/remote/shared.js +75 -75
  86. package/src/runtime/app/service-worker/index.js +24 -0
  87. package/src/runtime/app/state/client.js +4 -2
  88. package/src/runtime/app/state/index.js +7 -5
  89. package/src/runtime/app/state/public.d.ts +72 -0
  90. package/src/runtime/app/state/server.js +3 -0
  91. package/src/runtime/app/stores.js +15 -78
  92. package/src/runtime/client/bundle.js +1 -1
  93. package/src/runtime/client/client-entry.js +3 -0
  94. package/src/runtime/client/client.js +1336 -648
  95. package/src/runtime/client/constants.js +3 -6
  96. package/src/runtime/client/entry.js +24 -3
  97. package/src/runtime/client/fetcher.js +25 -25
  98. package/src/runtime/client/ndjson.js +1 -1
  99. package/src/runtime/client/parse.js +1 -1
  100. package/src/runtime/client/payload.js +17 -0
  101. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  102. package/src/runtime/client/remote-functions/command.svelte.js +37 -61
  103. package/src/runtime/client/remote-functions/form.svelte.js +267 -178
  104. package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
  105. package/src/runtime/client/remote-functions/query/index.js +10 -17
  106. package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
  107. package/src/runtime/client/remote-functions/query/proxy.js +5 -5
  108. package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
  109. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  110. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
  111. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
  112. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  113. package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
  114. package/src/runtime/client/snapshots.js +147 -0
  115. package/src/runtime/client/state.svelte.js +94 -55
  116. package/src/runtime/client/stream.js +3 -2
  117. package/src/runtime/client/types.d.ts +13 -9
  118. package/src/runtime/client/utils.js +22 -110
  119. package/src/runtime/components/root.svelte +56 -0
  120. package/src/runtime/env/dynamic/private.js +7 -0
  121. package/src/runtime/env/dynamic/public.js +7 -0
  122. package/src/runtime/env/static/private.js +6 -0
  123. package/src/runtime/env/static/public.js +6 -0
  124. package/src/runtime/form-utils.js +172 -61
  125. package/src/runtime/pathname.js +20 -32
  126. package/src/runtime/props.svelte.js +72 -0
  127. package/src/runtime/server/constants.js +0 -3
  128. package/src/runtime/server/cookie.js +70 -53
  129. package/src/runtime/server/csrf.js +65 -0
  130. package/src/runtime/server/data/index.js +48 -52
  131. package/src/runtime/server/dev.js +22 -0
  132. package/src/runtime/server/endpoint.js +3 -4
  133. package/src/runtime/server/env_module.js +0 -5
  134. package/src/runtime/server/errors.js +160 -0
  135. package/src/runtime/server/fetch.js +31 -38
  136. package/src/runtime/server/index.js +90 -64
  137. package/src/runtime/server/internal.js +71 -0
  138. package/src/runtime/server/page/actions.js +84 -64
  139. package/src/runtime/server/page/crypto.js +2 -2
  140. package/src/runtime/server/page/csp.js +88 -104
  141. package/src/runtime/server/page/data_serializer.js +22 -23
  142. package/src/runtime/server/page/index.js +35 -53
  143. package/src/runtime/server/page/load_data.js +47 -54
  144. package/src/runtime/server/page/render.js +172 -238
  145. package/src/runtime/server/page/respond_with_error.js +16 -30
  146. package/src/runtime/server/page/serialize_data.js +2 -13
  147. package/src/runtime/server/page/server_routing.js +85 -26
  148. package/src/runtime/server/remote-functions.js +639 -0
  149. package/src/runtime/server/respond.js +188 -129
  150. package/src/runtime/server/sourcemaps.js +183 -0
  151. package/src/runtime/server/state.js +53 -0
  152. package/src/runtime/server/utils.js +12 -154
  153. package/src/runtime/shared.js +20 -40
  154. package/src/runtime/utils.js +3 -0
  155. package/src/types/ambient-private.d.ts +11 -1
  156. package/src/types/ambient.d.ts +87 -36
  157. package/src/types/global-private.d.ts +25 -25
  158. package/src/types/internal.d.ts +160 -150
  159. package/src/types/private.d.ts +41 -1
  160. package/src/utils/error.js +28 -4
  161. package/src/utils/escape.js +9 -25
  162. package/src/utils/features.js +1 -1
  163. package/src/utils/filesystem.js +1 -23
  164. package/src/utils/fork.js +7 -2
  165. package/src/utils/hash.js +21 -0
  166. package/src/utils/http.js +8 -7
  167. package/src/utils/import.js +9 -2
  168. package/src/utils/imports.js +83 -0
  169. package/src/utils/mime.js +9 -0
  170. package/src/utils/page_nodes.js +6 -7
  171. package/src/utils/params.js +67 -0
  172. package/src/utils/regex.js +9 -0
  173. package/src/utils/routing.js +145 -73
  174. package/src/utils/shared-iterator.js +5 -0
  175. package/src/utils/streaming.js +14 -4
  176. package/src/utils/url.js +33 -2
  177. package/src/version.js +1 -1
  178. package/types/index.d.ts +2291 -2135
  179. package/types/index.d.ts.map +116 -108
  180. package/src/core/sync/write_ambient.js +0 -18
  181. package/src/core/sync/write_root.js +0 -148
  182. package/src/core/sync/write_tsconfig.js +0 -250
  183. package/src/exports/internal/server.js +0 -22
  184. package/src/exports/node/polyfills.js +0 -30
  185. package/src/runtime/server/app.js +0 -9
  186. package/src/runtime/server/remote.js +0 -457
  187. package/src/runtime/shared-server.js +0 -7
  188. package/src/runtime/telemetry/otel.js +0 -21
  189. package/src/runtime/telemetry/record_span.js +0 -65
  190. package/src/types/synthetic/$lib.md +0 -5
  191. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
  192. /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
@@ -1,11 +1,10 @@
1
1
  import path from 'node:path';
2
- import { styleText } from 'node:util';
3
2
  import { hash } from '../../utils/hash.js';
4
3
  import { resolve_entry } from '../../utils/filesystem.js';
5
4
  import { posixify } from '../../utils/os.js';
6
5
  import { s } from '../../utils/misc.js';
7
6
  import { load_error_page, load_template } from '../config/index.js';
8
- import { write_if_changed } from './utils.js';
7
+ import { check_spelling, write_if_changed } from './utils.js';
9
8
  import { escape_html } from '../../utils/escape.js';
10
9
 
11
10
  /**
@@ -15,7 +14,6 @@ import { escape_html } from '../../utils/escape.js';
15
14
  * config: import('types').ValidatedConfig;
16
15
  * has_service_worker: boolean;
17
16
  * template: string;
18
- * error_page: string;
19
17
  * }} opts
20
18
  */
21
19
  const server_template = ({
@@ -23,18 +21,15 @@ const server_template = ({
23
21
  server_hooks,
24
22
  universal_hooks,
25
23
  has_service_worker,
26
- template,
27
- error_page
24
+ template
28
25
  }) => `
29
- import root from '../root.js';
30
26
  import { set_building, set_prerendering } from '$app/env/internal';
31
27
  import { set_assets } from '$app/paths/internal/server';
32
- import { set_manifest, set_read_implementation } from '__sveltekit/server';
33
- import { set_env } from '__sveltekit/env';
28
+ import { set_fix_stack_trace, set_manifest, set_read_implementation, log_response } from '__sveltekit/server';
29
+ import error from '../shared/error-template.js';
34
30
 
35
31
  export const options = {
36
32
  app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
37
- async: ${s(!!config.compilerOptions?.experimental?.async)},
38
33
  csp: ${s(config.kit.csp)},
39
34
  csrf_check_origin: ${s(!config.kit.csrf.trustedOrigins.includes('*'))},
40
35
  csrf_trusted_origins: ${s(config.kit.csrf.trustedOrigins)},
@@ -42,10 +37,9 @@ export const options = {
42
37
  hash_routing: ${s(config.kit.router.type === 'hash')},
43
38
  hooks: null, // added lazily, via \`get_hooks\`
44
39
  link_header_preload: ${s(config.kit.output.linkHeaderPreload)},
45
- root,
40
+ paths_origin: ${s(config.kit.paths.origin)},
46
41
  service_worker: ${has_service_worker},
47
42
  service_worker_options: ${config.kit.serviceWorker.register ? s(config.kit.serviceWorker.options) : 'null'},
48
- server_error_boundaries: ${s(!!config.kit.experimental.handleRenderingErrors)},
49
43
  templates: {
50
44
  app: ({ head, body, assets, nonce, env }) => ${s(template)
51
45
  .replace('%sveltekit.head%', '" + head + "')
@@ -57,10 +51,9 @@ export const options = {
57
51
  /%sveltekit\.env\.([^%]+)%/g,
58
52
  (_match, capture) => `" + (env[${s(capture)}] ?? "") + "`
59
53
  )},
60
- error: ({ status, message }) => ${s(error_page)
61
- .replace(/%sveltekit\.status%/g, '" + status + "')
62
- .replace(/%sveltekit\.error\.message%/g, '" + message + "')}
54
+ error
63
55
  },
56
+ version: ${s(config.kit.version.name)},
64
57
  version_hash: ${s(hash(config.kit.version.name))}
65
58
  };
66
59
 
@@ -68,9 +61,8 @@ export async function get_hooks() {
68
61
  let handle;
69
62
  let handleFetch;
70
63
  let handleError;
71
- let handleValidationError;
72
64
  let init;
73
- ${server_hooks ? `({ handle, handleFetch, handleError, handleValidationError, init } = await import(${s(server_hooks)}));` : ''}
65
+ ${server_hooks ? `({ handle, handleFetch, handleError, init } = await import(${s(server_hooks)}));` : ''}
74
66
 
75
67
  let reroute;
76
68
  let transport;
@@ -80,20 +72,15 @@ export async function get_hooks() {
80
72
  handle,
81
73
  handleFetch,
82
74
  handleError,
83
- handleValidationError,
84
75
  init,
85
76
  reroute,
86
77
  transport
87
78
  };
88
79
  }
89
80
 
90
- export { set_assets, set_building, set_env, set_manifest, set_prerendering, set_read_implementation };
81
+ export { set_assets, set_building, set_fix_stack_trace, set_manifest, set_prerendering, set_read_implementation, log_response };
91
82
  `;
92
83
 
93
- // TODO need to re-run this whenever src/app.html or src/error.html are
94
- // created or changed, or src/service-worker.js is created or deleted.
95
- // Also, need to check that updating hooks.server.js works
96
-
97
84
  /**
98
85
  * Write server configuration to disk
99
86
  * @param {import('types').ValidatedConfig} config
@@ -104,15 +91,14 @@ export function write_server(config, output, root) {
104
91
  const server_hooks_file = resolve_entry(config.kit.files.hooks.server);
105
92
  const universal_hooks_file = resolve_entry(config.kit.files.hooks.universal);
106
93
 
107
- const typo = resolve_entry('src/+hooks.server');
108
- if (typo) {
109
- console.log(
110
- styleText(
111
- ['bold', 'yellow'],
112
- `Unexpected + prefix. Did you mean ${typo.split('/').at(-1)?.slice(1)}?` +
113
- ` at ${path.resolve(typo)}`
114
- )
115
- );
94
+ if (!server_hooks_file) {
95
+ check_spelling('src/hooks.server', 'src/+hooks.server', 'Unexpected + prefix');
96
+ check_spelling('src/hooks.server', 'src/hook.server', 'Missing s suffix');
97
+ }
98
+
99
+ if (!universal_hooks_file) {
100
+ check_spelling('src/hooks', 'src/+hooks', 'Unexpected + prefix');
101
+ check_spelling('src/hooks', 'src/hook', 'Missing s suffix');
116
102
  }
117
103
 
118
104
  /** @param {string} file */
@@ -120,6 +106,13 @@ export function write_server(config, output, root) {
120
106
  return posixify(path.relative(`${output}/server`, file));
121
107
  }
122
108
 
109
+ write_if_changed(
110
+ `${output}/shared/error-template.js`,
111
+ `export default ({ status, message }) => ${s(load_error_page(config))
112
+ .replace(/%sveltekit\.status%/g, '" + status + "')
113
+ .replace(/%sveltekit\.error\.message%/g, '" + message + "')};`
114
+ );
115
+
123
116
  // Contains the stringified version of
124
117
  /** @type {import('types').SSROptions} */
125
118
  write_if_changed(
@@ -130,8 +123,7 @@ export function write_server(config, output, root) {
130
123
  universal_hooks: universal_hooks_file ? relative(universal_hooks_file) : null,
131
124
  has_service_worker:
132
125
  config.kit.serviceWorker.register && !!resolve_entry(config.kit.files.serviceWorker),
133
- template: load_template(root, config),
134
- error_page: load_error_page(config)
126
+ template: load_template(root, config)
135
127
  })
136
128
  );
137
129
  }
@@ -0,0 +1,274 @@
1
+ /** @import { ValidatedKitConfig } from 'types' */
2
+ import process from 'node:process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { styleText } from 'node:util';
6
+ import { write_if_changed } from '../utils.js';
7
+ import {
8
+ ESSENTIAL_OPTIONS,
9
+ normalize_config,
10
+ RECOMMENDED_OPTIONS,
11
+ remove_trailing_slashstar
12
+ } from './utils.js';
13
+ import { extends_id, validate_resolved_config } from './validate.js';
14
+
15
+ /** @type {typeof import('typescript')} */
16
+ let ts;
17
+ try {
18
+ ts = await import('typescript');
19
+ } catch {
20
+ // The user has not installed TypeScript.
21
+ }
22
+
23
+ /**
24
+ * Generates the tsconfig that the user's tsconfig inherits from.
25
+ * @param {import('types').ValidatedKitConfig} kit
26
+ * @param {string} root
27
+ */
28
+ export function write_tsconfig(kit, root) {
29
+ const paths = get_paths(kit, root);
30
+ const types = ['$app/types'];
31
+
32
+ write_parent_tsconfig(
33
+ root,
34
+ '$app/tsconfig',
35
+ {
36
+ compilerOptions: {
37
+ paths,
38
+ rootDirs: ['.', `${kit.outDir}/types`],
39
+ types,
40
+
41
+ // This is required for svelte-package to work as expected
42
+ // Can be overwritten
43
+ lib: ['ESNext', 'DOM', 'DOM.Iterable'],
44
+
45
+ ...ESSENTIAL_OPTIONS,
46
+ ...RECOMMENDED_OPTIONS
47
+ }
48
+ },
49
+ kit.typescript.config
50
+ );
51
+
52
+ validate_config(root, {
53
+ paths,
54
+ types,
55
+ exclusions: [kit.files.serviceWorker],
56
+ example: {
57
+ extends: '$app/tsconfig'
58
+ }
59
+ });
60
+
61
+ write_parent_tsconfig(root, '$app/tsconfig/service-worker', {
62
+ compilerOptions: {
63
+ paths,
64
+ types,
65
+ lib: ['ESNext', 'WebWorker'],
66
+ ...ESSENTIAL_OPTIONS,
67
+ ...RECOMMENDED_OPTIONS
68
+ }
69
+ });
70
+
71
+ validate_config(kit.files.serviceWorker, {
72
+ paths,
73
+ types,
74
+ example: {
75
+ extends: '$app/tsconfig/service-worker'
76
+ }
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Write a generated `tsconfig.json` inside `node_modules`, for the
82
+ * user config to extend
83
+ * @param {string} root The project root
84
+ * @param {string} id The id of the generated config
85
+ * @param {any} config The contents of the generated tsconfig, with paths relative to `root`
86
+ * @param {ValidatedKitConfig['typescript']['config']} [transform] TODO get rid of this
87
+ */
88
+ function write_parent_tsconfig(root, id, config, transform) {
89
+ // simplified tsconfig resolvers (e.g. Playwright's) only find `${id}.json`, not `${id}/tsconfig.json`
90
+ const out_file = path.join(root, `node_modules/${id}.json`);
91
+
92
+ let normalized = normalize_config(out_file, config);
93
+ normalized = transform?.(normalized) ?? normalized;
94
+
95
+ write_if_changed(out_file, JSON.stringify(normalized, null, '\t'));
96
+ }
97
+
98
+ /**
99
+ * @param {string} dir The directory to resolve a user config from
100
+ * @param {Object} options
101
+ * @param {Record<string, string[]>} options.paths The expected paths
102
+ * @param {string[]} options.types The expected types
103
+ * @param {string[]} [options.exclusions] Files that should be excluded
104
+ * @param {any} options.example What to print if the user config does _not_ extend the generated config
105
+ */
106
+ function validate_config(dir, options) {
107
+ if (!ts) {
108
+ // The user has not installed TypeScript. Skip validation of config.
109
+ return;
110
+ }
111
+
112
+ const user_config = load_user_tsconfig(dir);
113
+ if (!user_config || !modified_since_last_check(user_config.file)) return;
114
+
115
+ // now that we've written the parent config, we can resolve the
116
+ // user config and validate that nothing important was overwritten
117
+ if (!extends_id(user_config.options, options.example.extends)) {
118
+ console.warn(
119
+ styleText(
120
+ ['bold', 'yellow'],
121
+ `${path.relative(process.cwd(), user_config.file)} should extend SvelteKit's built-in configuration:`
122
+ )
123
+ );
124
+
125
+ console.warn(JSON.stringify(options.example, null, ' '));
126
+
127
+ return;
128
+ }
129
+
130
+ const resolved = ts.parseJsonConfigFileContent(user_config.options, ts.sys, dir);
131
+
132
+ const warnings = validate_resolved_config(
133
+ dir,
134
+ resolved,
135
+ options.paths,
136
+ options.types,
137
+ options.exclusions
138
+ );
139
+
140
+ if (warnings.length > 0) {
141
+ console.warn(
142
+ styleText(
143
+ ['bold', 'yellow'],
144
+ `Found issues while validating ${path.relative(process.cwd(), user_config.file)}`
145
+ )
146
+ );
147
+
148
+ for (const warning of warnings) {
149
+ console.warn(` - ${warning}`);
150
+ }
151
+ }
152
+ }
153
+
154
+ /** @type {Map<string, number>} */
155
+ const mtimes = new Map();
156
+
157
+ /**
158
+ * Returns true if the file was modified since we last got here,
159
+ * otherwise we can skip warnings
160
+ * @param {string} file
161
+ */
162
+ function modified_since_last_check(file) {
163
+ const a = mtimes.get(file) ?? -1;
164
+ const b = fs.statSync(file).mtimeMs;
165
+ mtimes.set(file, b);
166
+
167
+ return b > a;
168
+ }
169
+
170
+ /**
171
+ * @param {string} cwd
172
+ * @param {string} file
173
+ */
174
+ function maybe_file(cwd, file) {
175
+ const resolved = path.resolve(cwd, file);
176
+ if (fs.existsSync(resolved)) {
177
+ return resolved;
178
+ }
179
+ }
180
+
181
+ /** @param {string} cwd */
182
+ function load_user_tsconfig(cwd) {
183
+ const file = maybe_file(cwd, 'tsconfig.json') || maybe_file(cwd, 'jsconfig.json');
184
+ if (!file) return;
185
+
186
+ const options = load_tsconfig(file);
187
+
188
+ return {
189
+ file,
190
+ kind: path.basename(file),
191
+ options
192
+ };
193
+ }
194
+
195
+ /**
196
+ * @param {string} file
197
+ */
198
+ function load_tsconfig(file) {
199
+ const options = ts.readConfigFile(file, ts.sys.readFile);
200
+
201
+ if (options.error) {
202
+ let message = `Failed to parse TypeScript config`;
203
+
204
+ if (typeof options.error.messageText === 'string') {
205
+ message += `: ${options.error.messageText}`;
206
+ }
207
+
208
+ const error = new Error(message);
209
+ error.stack = '';
210
+
211
+ if (options.error.file && options.error.start !== undefined) {
212
+ const line_start = options.error.file.text.lastIndexOf('\n', options.error.start);
213
+
214
+ const line =
215
+ line_start === -1
216
+ ? 0
217
+ : options.error.file.text.slice(0, options.error.start).split('\n').length;
218
+ const column = options.error.start - line_start;
219
+
220
+ error.stack = `${error.message}\n at ${path.relative(process.cwd(), file)}:${line}:${column}`;
221
+ }
222
+
223
+ throw error;
224
+ }
225
+
226
+ return options.config;
227
+ }
228
+
229
+ /** Matches anything (except the empty string/string with newline), and separates any trailing `/*` */
230
+ const alias_key = /^(.+?)(\/\*)?$/;
231
+
232
+ /** Matches anything (except the empty string/string with newline), and separates any trailing `/*` or file extension */
233
+ const alias_value = /^(.+?)((\/\*)|(\.\w+))?$/;
234
+
235
+ /**
236
+ * Generates tsconfig path aliases from kit's aliases and the package.json `imports` field.
237
+ * Related to vite alias creation.
238
+ *
239
+ * @param {import('types').ValidatedKitConfig} config
240
+ * @param {string} root
241
+ * @returns {Record<string, string[]>}
242
+ */
243
+ function get_paths(config, root) {
244
+ const alias = {
245
+ ...config.alias
246
+ };
247
+
248
+ /** @type {Record<string, string[]>} */
249
+ const paths = {};
250
+
251
+ for (const [key, value] of Object.entries(alias)) {
252
+ const key_match = alias_key.exec(key);
253
+ if (!key_match) throw new Error(`Invalid alias key: ${key}`);
254
+
255
+ const value_match = alias_value.exec(value);
256
+ if (!value_match) throw new Error(`Invalid alias value: ${value}`);
257
+
258
+ const resolved = path.resolve(root, remove_trailing_slashstar(value));
259
+ const slashstar = key_match[2];
260
+
261
+ if (slashstar) {
262
+ paths[key] = [resolved + '/*'];
263
+ } else {
264
+ paths[key] = [resolved];
265
+ const fileending = value_match[4];
266
+
267
+ if (!fileending && !(key + '/*' in alias)) {
268
+ paths[key + '/*'] = [resolved + '/*'];
269
+ }
270
+ }
271
+ }
272
+
273
+ return paths;
274
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "private": true,
3
+ "imports": {
4
+ "#lib": "./src/lib",
5
+ "#lib/*": "./src/lib/*"
6
+ }
7
+ }
@@ -0,0 +1,77 @@
1
+ import path from 'node:path';
2
+ import { posixify } from '../../../utils/os.js';
3
+
4
+ /**
5
+ * Without these, compilation will fail
6
+ * @type {Record<string, any>}
7
+ */
8
+ export const ESSENTIAL_OPTIONS = {
9
+ // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
10
+ // to enforce using \`import type\` instead of \`import\` for Types.
11
+ // Also, TypeScript doesn't know about import usages in the template because it only sees the
12
+ // script of a Svelte file. Therefore preserve all value imports.
13
+ verbatimModuleSyntax: true,
14
+ // Vite compiles modules one at a time
15
+ isolatedModules: true
16
+ };
17
+
18
+ /**
19
+ * Options that are strongly recommended, either because not having them is silly or
20
+ * because the align TypeScript's behaviour with Vite's, but which can be overwritten
21
+ * @type {Record<string, any>}
22
+ */
23
+ export const RECOMMENDED_OPTIONS = {
24
+ allowJs: true,
25
+ checkJs: true,
26
+ forceConsistentCasingInFileNames: true,
27
+ resolveJsonModule: true,
28
+ moduleDetection: 'force',
29
+ moduleResolution: 'bundler',
30
+ allowImportingTsExtensions: true,
31
+ module: 'esnext',
32
+ target: 'esnext',
33
+ skipLibCheck: true,
34
+ esModuleInterop: true,
35
+ noEmit: true // prevent tsconfig error "overwriting input files" - Vite handles the build and ignores this
36
+ };
37
+
38
+ /**
39
+ * Makes paths relative to the output file
40
+ * @param {string} out
41
+ * @param {any} config
42
+ * @param {(input: any) => any} [transform] TODO get rid of this option
43
+ */
44
+ export function normalize_config(out, config, transform) {
45
+ const dir = path.dirname(out);
46
+ const relative = (/** @type {string} */ file) => posixify(path.relative(dir, file));
47
+
48
+ const paths =
49
+ config.compilerOptions?.paths &&
50
+ Object.fromEntries(
51
+ Object.entries(config.compilerOptions?.paths).map(([k, v]) => [k, v.map(relative)])
52
+ );
53
+
54
+ const normalized = {
55
+ ...config,
56
+ compilerOptions: {
57
+ ...config.compilerOptions,
58
+ paths,
59
+ rootDirs: config.compilerOptions?.rootDirs?.map(relative)
60
+ },
61
+ include: config.include?.map(relative),
62
+ exclude: config.exclude?.map(relative)
63
+ };
64
+
65
+ return transform?.(normalized) ?? normalized;
66
+ }
67
+
68
+ /**
69
+ * @param {string} file
70
+ */
71
+ export function remove_trailing_slashstar(file) {
72
+ if (file.endsWith('/*')) {
73
+ return file.slice(0, -2);
74
+ } else {
75
+ return file;
76
+ }
77
+ }
@@ -0,0 +1,128 @@
1
+ /** @import ts from 'typescript' */
2
+ import path from 'node:path';
3
+ import { ESSENTIAL_OPTIONS, remove_trailing_slashstar } from './utils.js';
4
+ import { posixify } from '../../../utils/os.js';
5
+
6
+ const formatter = new Intl.ListFormat('en-gb', { type: 'conjunction' });
7
+
8
+ /**
9
+ * @param {string[]} things
10
+ */
11
+ function join(things) {
12
+ return formatter.format(things.map((thing) => JSON.stringify(thing)));
13
+ }
14
+
15
+ /**
16
+ * @param {string} dir
17
+ * @param {ts.ParsedCommandLine} resolved
18
+ * @param {Record<string, string[]>} paths
19
+ * @param {string[]} types
20
+ * @param {string[]} [exclusions]
21
+ */
22
+ export function validate_resolved_config(dir, resolved, paths, types, exclusions) {
23
+ /** @type {string[]} */
24
+ const warnings = [];
25
+
26
+ if (resolved.options.types) {
27
+ validate_types(warnings, types, resolved.options.types);
28
+ }
29
+
30
+ if (resolved.options.paths) {
31
+ validate_paths(
32
+ warnings,
33
+ /** @type {string} */ (resolved.options.pathsBasePath),
34
+ paths,
35
+ resolved.options.paths
36
+ );
37
+ }
38
+
39
+ if (exclusions) {
40
+ validate_exclusions(warnings, dir, exclusions, resolved.fileNames);
41
+ }
42
+
43
+ for (const key in ESSENTIAL_OPTIONS) {
44
+ if (resolved.options[key] !== ESSENTIAL_OPTIONS[key]) {
45
+ warnings.push(
46
+ `"${key}" was overwritten. It should be ${JSON.stringify(ESSENTIAL_OPTIONS[key])}`
47
+ );
48
+ }
49
+ }
50
+
51
+ return warnings;
52
+ }
53
+
54
+ /**
55
+ * @param {string[]} warnings
56
+ * @param {string[]} expected
57
+ * @param {string[]} actual
58
+ */
59
+ export function validate_types(warnings, expected, actual) {
60
+ const missing_types = expected.filter((/** @type {string} */ type) => !actual.includes(type));
61
+
62
+ if (missing_types.length > 0) {
63
+ warnings.push(`"types" was overwritten. It must include ${join(missing_types)}`);
64
+ }
65
+
66
+ return warnings;
67
+ }
68
+
69
+ /**
70
+ * @param {string[]} warnings
71
+ * @param {string} base
72
+ * @param {Record<string, string[]>} expected
73
+ * @param {ts.MapLike<string[]>} actual
74
+ */
75
+ export function validate_paths(warnings, base, expected, actual) {
76
+ /** @type {Set<string>} */
77
+ const mismatch = new Set();
78
+
79
+ for (const [k, e] of Object.entries(expected)) {
80
+ const a = actual[k]?.map((/** @type {string} */ x) =>
81
+ path.resolve(/** @type {string} */ (base), x)
82
+ );
83
+
84
+ if (JSON.stringify(e) !== JSON.stringify(a)) {
85
+ mismatch.add(remove_trailing_slashstar(k));
86
+ }
87
+ }
88
+
89
+ if (mismatch.size > 0) {
90
+ const joined = join([...mismatch]);
91
+
92
+ warnings.push(`"paths" was overwritten. Imports from ${joined} may not typecheck`);
93
+ }
94
+
95
+ return warnings;
96
+ }
97
+
98
+ /**
99
+ * @param {string[]} warnings
100
+ * @param {string} dir
101
+ * @param {string[]} exclusions
102
+ * @param {string[]} files
103
+ */
104
+ export function validate_exclusions(warnings, dir, exclusions, files) {
105
+ const missing_exclusions = exclusions.map(posixify).filter((exclusion) => {
106
+ return files.some(
107
+ (f) => f === exclusion || f.startsWith(exclusion + '/') || f.startsWith(exclusion + '.')
108
+ );
109
+ });
110
+
111
+ if (missing_exclusions.length > 0) {
112
+ warnings.push(
113
+ `${join(missing_exclusions.map((file) => posixify(path.relative(dir, file))))} should be added to the "exclude" array`
114
+ );
115
+ }
116
+
117
+ return warnings;
118
+ }
119
+
120
+ /**
121
+ * Validates that a tsconfig contains `"extends": "<id>"`
122
+ * @param {any} options
123
+ * @param {string} id
124
+ */
125
+ export function extends_id(options, id) {
126
+ const o = options.extends;
127
+ return Array.isArray(o) ? o.includes(id) : o === id;
128
+ }