@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.
- package/package.json +58 -36
- package/src/cli.js +14 -11
- package/src/constants.js +6 -1
- package/src/core/adapt/builder.js +54 -60
- package/src/core/adapt/index.js +2 -5
- package/src/core/config/index.js +141 -76
- package/src/core/config/options.js +260 -260
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +143 -13
- package/src/core/generate_manifest/index.js +18 -15
- package/src/core/postbuild/analyse.js +7 -19
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +4 -2
- package/src/core/postbuild/prerender.js +193 -69
- package/src/core/postbuild/queue.js +2 -1
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +72 -52
- package/src/core/sync/sync.js +35 -26
- package/src/core/sync/ts.js +1 -1
- package/src/core/sync/utils.js +22 -2
- package/src/core/sync/{write_non_ambient.js → write_app_types.js} +105 -56
- package/src/core/sync/write_client_manifest.js +13 -28
- package/src/core/sync/write_env.js +7 -5
- package/src/core/sync/write_server.js +25 -33
- package/src/core/sync/write_tsconfig/index.js +274 -0
- package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
- package/src/core/sync/write_tsconfig/utils.js +77 -0
- package/src/core/sync/write_tsconfig/validate.js +128 -0
- package/src/core/sync/write_types/index.js +119 -116
- package/src/core/utils.js +30 -5
- package/src/exports/env/index.js +77 -0
- package/src/exports/env/public.d.ts +55 -0
- package/src/exports/hooks/index.js +3 -9
- package/src/exports/hooks/public.d.ts +194 -0
- package/src/exports/hooks/sequence.js +9 -6
- package/src/exports/index.js +53 -20
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +8 -5
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +1 -2
- package/src/exports/internal/server/index.js +37 -0
- package/src/exports/internal/server/telemetry.js +95 -0
- package/src/exports/internal/shared.js +90 -0
- package/src/exports/node/index.js +64 -22
- package/src/exports/params/index.js +70 -0
- package/src/exports/params/public.d.ts +63 -0
- package/src/exports/public.d.ts +219 -1118
- package/src/exports/url.js +86 -0
- package/src/exports/vite/build/build_server.js +53 -64
- package/src/exports/vite/build/remote.js +24 -19
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/dev/index.js +199 -125
- package/src/exports/vite/index.js +1514 -833
- package/src/exports/vite/module_ids.js +2 -2
- package/src/exports/vite/preview/index.js +32 -24
- package/src/exports/vite/utils.js +83 -45
- package/src/pathname.js +55 -0
- package/src/runner.js +15 -0
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/env/types.d.ts +1 -1
- package/src/runtime/app/environment/index.js +3 -3
- package/src/runtime/app/{forms.js → forms/index.js} +73 -42
- package/src/runtime/app/forms/public.d.ts +2 -0
- package/src/runtime/app/forms/types.d.ts +56 -0
- package/src/runtime/app/internal/transport.js +53 -0
- package/src/runtime/app/manifest/index.js +1 -0
- package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
- package/src/runtime/app/navigation/public.d.ts +237 -0
- package/src/runtime/app/paths/client.js +37 -37
- package/src/runtime/app/paths/index.js +1 -1
- package/src/runtime/app/paths/internal/client.js +34 -2
- package/src/runtime/app/paths/internal/server.js +6 -23
- package/src/runtime/app/paths/internal.d.ts +3 -0
- package/src/runtime/app/paths/public.d.ts +1 -29
- package/src/runtime/app/paths/server.js +36 -17
- package/src/runtime/app/paths/types.d.ts +11 -19
- package/src/runtime/app/server/index.js +2 -2
- package/src/runtime/app/server/public.d.ts +519 -0
- package/src/runtime/app/server/remote/command.js +13 -11
- package/src/runtime/app/server/remote/form.js +61 -39
- package/src/runtime/app/server/remote/prerender.js +45 -45
- package/src/runtime/app/server/remote/query.js +106 -108
- package/src/runtime/app/server/remote/requested.js +27 -19
- package/src/runtime/app/server/remote/shared.js +75 -75
- package/src/runtime/app/service-worker/index.js +24 -0
- package/src/runtime/app/state/client.js +4 -2
- package/src/runtime/app/state/index.js +7 -5
- package/src/runtime/app/state/public.d.ts +72 -0
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/app/stores.js +15 -78
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +1336 -648
- package/src/runtime/client/constants.js +3 -6
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/fetcher.js +25 -25
- package/src/runtime/client/ndjson.js +1 -1
- package/src/runtime/client/parse.js +1 -1
- package/src/runtime/client/payload.js +17 -0
- package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
- package/src/runtime/client/remote-functions/command.svelte.js +37 -61
- package/src/runtime/client/remote-functions/form.svelte.js +267 -178
- package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
- package/src/runtime/client/remote-functions/query/index.js +10 -17
- package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
- package/src/runtime/client/remote-functions/query/proxy.js +5 -5
- package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
- package/src/runtime/client/remote-functions/query-live/index.js +1 -1
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
- package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
- package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
- package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
- package/src/runtime/client/snapshots.js +147 -0
- package/src/runtime/client/state.svelte.js +94 -55
- package/src/runtime/client/stream.js +3 -2
- package/src/runtime/client/types.d.ts +13 -9
- package/src/runtime/client/utils.js +22 -110
- package/src/runtime/components/root.svelte +56 -0
- package/src/runtime/env/dynamic/private.js +7 -0
- package/src/runtime/env/dynamic/public.js +7 -0
- package/src/runtime/env/static/private.js +6 -0
- package/src/runtime/env/static/public.js +6 -0
- package/src/runtime/form-utils.js +172 -61
- package/src/runtime/pathname.js +20 -32
- package/src/runtime/props.svelte.js +72 -0
- package/src/runtime/server/constants.js +0 -3
- package/src/runtime/server/cookie.js +70 -53
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +48 -52
- package/src/runtime/server/dev.js +22 -0
- package/src/runtime/server/endpoint.js +3 -4
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/errors.js +160 -0
- package/src/runtime/server/fetch.js +31 -38
- package/src/runtime/server/index.js +90 -64
- package/src/runtime/server/internal.js +71 -0
- package/src/runtime/server/page/actions.js +84 -64
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +88 -104
- package/src/runtime/server/page/data_serializer.js +22 -23
- package/src/runtime/server/page/index.js +35 -53
- package/src/runtime/server/page/load_data.js +47 -54
- package/src/runtime/server/page/render.js +172 -238
- package/src/runtime/server/page/respond_with_error.js +16 -30
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +85 -26
- package/src/runtime/server/remote-functions.js +639 -0
- package/src/runtime/server/respond.js +188 -129
- package/src/runtime/server/sourcemaps.js +183 -0
- package/src/runtime/server/state.js +53 -0
- package/src/runtime/server/utils.js +12 -154
- package/src/runtime/shared.js +20 -40
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +11 -1
- package/src/types/ambient.d.ts +87 -36
- package/src/types/global-private.d.ts +25 -25
- package/src/types/internal.d.ts +160 -150
- package/src/types/private.d.ts +41 -1
- package/src/utils/error.js +28 -4
- package/src/utils/escape.js +9 -25
- package/src/utils/features.js +1 -1
- package/src/utils/filesystem.js +1 -23
- package/src/utils/fork.js +7 -2
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +9 -2
- package/src/utils/imports.js +83 -0
- package/src/utils/mime.js +9 -0
- package/src/utils/page_nodes.js +6 -7
- package/src/utils/params.js +67 -0
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +145 -73
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +33 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +2291 -2135
- package/types/index.d.ts.map +116 -108
- package/src/core/sync/write_ambient.js +0 -18
- package/src/core/sync/write_root.js +0 -148
- package/src/core/sync/write_tsconfig.js +0 -250
- package/src/exports/internal/server.js +0 -22
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/server/app.js +0 -9
- package/src/runtime/server/remote.js +0 -457
- package/src/runtime/shared-server.js +0 -7
- package/src/runtime/telemetry/otel.js +0 -21
- package/src/runtime/telemetry/record_span.js +0 -65
- package/src/types/synthetic/$lib.md +0 -5
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
- /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
+
import { clearLine, moveCursor } from 'node:readline';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import {
|
|
6
|
+
import { walk } from '../../utils/filesystem.js';
|
|
6
7
|
import { posixify } from '../../utils/os.js';
|
|
7
8
|
import { noop } from '../../utils/functions.js';
|
|
8
9
|
import { decode_uri, is_root_relative, resolve } from '../../utils/url.js';
|
|
@@ -17,6 +18,7 @@ import * as devalue from 'devalue';
|
|
|
17
18
|
import { createReadableStream } from '@sveltejs/kit/node';
|
|
18
19
|
import generate_fallback from './fallback.js';
|
|
19
20
|
import { stringify_remote_arg } from '../../runtime/shared.js';
|
|
21
|
+
import { matches_content_type } from '../../utils/http.js';
|
|
20
22
|
|
|
21
23
|
export default forked(import.meta.url, prerender);
|
|
22
24
|
|
|
@@ -35,46 +37,94 @@ const SPECIAL_HASHLINKS = new Set(['', 'top']);
|
|
|
35
37
|
* verbose: boolean;
|
|
36
38
|
* env: Record<string, string>;
|
|
37
39
|
* vite_config_file: string | undefined;
|
|
40
|
+
* is_tty: boolean | undefined;
|
|
38
41
|
* }} opts
|
|
39
42
|
*/
|
|
40
|
-
async function prerender({
|
|
43
|
+
async function prerender({
|
|
44
|
+
hash,
|
|
45
|
+
out,
|
|
46
|
+
manifest_path,
|
|
47
|
+
metadata,
|
|
48
|
+
verbose,
|
|
49
|
+
env,
|
|
50
|
+
vite_config_file,
|
|
51
|
+
is_tty
|
|
52
|
+
}) {
|
|
41
53
|
/** @type {import('@sveltejs/kit').SSRManifest} */
|
|
42
54
|
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
|
43
55
|
|
|
44
56
|
/** @type {import('types').ServerInternalModule} */
|
|
45
|
-
const
|
|
57
|
+
const { set_building, set_prerendering, set_manifest, set_read_implementation, log_response } =
|
|
58
|
+
await import(pathToFileURL(`${out}/server/internal.js`).href);
|
|
59
|
+
|
|
60
|
+
// configure `import { building } from `$app/env` —
|
|
61
|
+
// essential we do this before analysing the code
|
|
62
|
+
set_building();
|
|
63
|
+
set_prerendering();
|
|
64
|
+
|
|
65
|
+
// `set_env` and `Server` live in modules that import the user's `src/env` config. We import them
|
|
66
|
+
// *after* `set_building()` so that `building`-dependent expressions resolve correctly
|
|
67
|
+
/** @type {typeof import('__sveltekit/env')} */
|
|
68
|
+
const { set_env } = await import(pathToFileURL(`${out}/server/env.js`).href);
|
|
69
|
+
set_env(env);
|
|
46
70
|
|
|
47
71
|
/** @type {import('types').ServerModule} */
|
|
48
72
|
const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
|
|
49
73
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
internal.set_prerendering();
|
|
74
|
+
const throw_handled = () => {
|
|
75
|
+
throw new Error('__handled__');
|
|
76
|
+
};
|
|
54
77
|
|
|
55
78
|
/**
|
|
56
79
|
* @template {{message: string}} T
|
|
57
80
|
* @template {Omit<T, 'message'>} K
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {'fail' | 'warn' | 'ignore' | ((details: T) => void)} input
|
|
81
|
+
* @param {string} name
|
|
82
|
+
* @param {'fail' | 'warn' | 'ignore' | undefined | ((details: T) => void)} input
|
|
60
83
|
* @param {(details: K) => string} format
|
|
61
|
-
* @returns {(details: K) => void}
|
|
84
|
+
* @returns {(details: K & { error?: unknown }) => void}
|
|
62
85
|
*/
|
|
63
|
-
function normalise_error_handler(
|
|
86
|
+
function normalise_error_handler(name, input, format) {
|
|
87
|
+
/**
|
|
88
|
+
* @param {any} details
|
|
89
|
+
*/
|
|
90
|
+
function log_failure(details) {
|
|
91
|
+
const message = format(details);
|
|
92
|
+
log.error(`\n${message}\n`);
|
|
93
|
+
}
|
|
94
|
+
|
|
64
95
|
switch (input) {
|
|
65
96
|
case 'fail':
|
|
66
97
|
return (details) => {
|
|
67
|
-
|
|
98
|
+
log_failure(details);
|
|
99
|
+
throw_handled();
|
|
68
100
|
};
|
|
69
101
|
case 'warn':
|
|
70
|
-
return
|
|
71
|
-
log.error(format(details));
|
|
72
|
-
};
|
|
102
|
+
return log_failure;
|
|
73
103
|
case 'ignore':
|
|
74
104
|
return noop;
|
|
105
|
+
case undefined: {
|
|
106
|
+
return (details) => {
|
|
107
|
+
log_failure(details);
|
|
108
|
+
|
|
109
|
+
log.err(
|
|
110
|
+
`To suppress or handle this error, implement \`${name}\` in https://svelte.dev/docs/kit/configuration#prerender\n`
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
throw_handled();
|
|
114
|
+
};
|
|
115
|
+
}
|
|
75
116
|
default:
|
|
76
|
-
|
|
77
|
-
|
|
117
|
+
return (details) => {
|
|
118
|
+
const message = format(details);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// @ts-expect-error TS thinks T might be of a different kind, but it's not
|
|
122
|
+
input({ ...details, message });
|
|
123
|
+
} catch (error) {
|
|
124
|
+
log.prettyError(error, import.meta.dirname);
|
|
125
|
+
throw_handled();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
78
128
|
}
|
|
79
129
|
}
|
|
80
130
|
|
|
@@ -105,19 +155,21 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
105
155
|
|
|
106
156
|
const config = extract_svelte_config(vite_config).kit;
|
|
107
157
|
|
|
158
|
+
const prerender_origin = config.paths.origin || 'http://sveltekit-prerender';
|
|
159
|
+
|
|
108
160
|
if (hash) {
|
|
109
161
|
const fallback = await generate_fallback({
|
|
110
162
|
manifest_path,
|
|
111
163
|
env,
|
|
112
164
|
out_dir: config.outDir,
|
|
113
|
-
origin:
|
|
165
|
+
origin: prerender_origin,
|
|
114
166
|
assets: config.files.assets
|
|
115
167
|
});
|
|
116
168
|
|
|
117
169
|
const file = output_filename('/', true);
|
|
118
170
|
const dest = `${config.outDir}/output/prerendered/pages/${file}`;
|
|
119
171
|
|
|
120
|
-
|
|
172
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
121
173
|
writeFileSync(dest, fallback);
|
|
122
174
|
|
|
123
175
|
prerendered.pages.set('/', { file });
|
|
@@ -125,13 +177,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
125
177
|
return { prerendered, prerender_map };
|
|
126
178
|
}
|
|
127
179
|
|
|
128
|
-
|
|
129
|
-
/** @type {Adapter | undefined} */
|
|
130
|
-
const adapter = vite_config.plugins.find(
|
|
131
|
-
(plugin) => plugin.name === 'vite-plugin-sveltekit-adapter'
|
|
132
|
-
)?.api?.adapter;
|
|
133
|
-
|
|
134
|
-
const emulator = await adapter?.emulate?.();
|
|
180
|
+
const emulator = await config.adapter?.emulate?.();
|
|
135
181
|
|
|
136
182
|
/** @type {import('types').Logger} */
|
|
137
183
|
const log = logger({ verbose });
|
|
@@ -140,43 +186,58 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
140
186
|
const saved = new Map();
|
|
141
187
|
|
|
142
188
|
const handle_http_error = normalise_error_handler(
|
|
143
|
-
|
|
189
|
+
'handleHttpError',
|
|
144
190
|
config.prerender.handleHttpError,
|
|
145
191
|
({ status, path, referrer, referenceType }) => {
|
|
146
|
-
|
|
147
|
-
status === 404 && !path.startsWith(config.paths.base)
|
|
148
|
-
? `${path} does not begin with \`base\`, which is configured in \`paths.base\` and can be imported from \`$app/paths\` - see https://svelte.dev/docs/kit/configuration#paths for more info`
|
|
149
|
-
: path;
|
|
192
|
+
let message = `Failed to prerender ${path}`;
|
|
150
193
|
|
|
151
|
-
|
|
194
|
+
if (status === 404) {
|
|
195
|
+
if (!path.startsWith(config.paths.base)) {
|
|
196
|
+
message = referrer ? `${path} (${referenceType} from ${referrer})` : path;
|
|
197
|
+
|
|
198
|
+
message += ` does not begin with \`base\`. You can fix this by using \`resolve('${path}')\` from \`$app/paths\`. The base path is configurable from \`paths.base\``;
|
|
199
|
+
} else if (referrer) {
|
|
200
|
+
message = `${path} was ${referenceType} from ${referrer}`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return message;
|
|
152
205
|
}
|
|
153
206
|
);
|
|
154
207
|
|
|
155
208
|
const handle_missing_id = normalise_error_handler(
|
|
156
|
-
|
|
209
|
+
'handleMissingId',
|
|
157
210
|
config.prerender.handleMissingId,
|
|
158
211
|
({ path, id, referrers }) => {
|
|
159
212
|
return (
|
|
160
|
-
`The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path}
|
|
213
|
+
`The following pages contain links to ${path}#${id}, but no element with id="${id}" exists on ${path}:` +
|
|
161
214
|
referrers.map((l) => `\n - ${l}`).join('')
|
|
162
215
|
);
|
|
163
216
|
}
|
|
164
217
|
);
|
|
165
218
|
|
|
166
219
|
const handle_entry_generator_mismatch = normalise_error_handler(
|
|
167
|
-
|
|
220
|
+
'handleEntryGeneratorMismatch',
|
|
168
221
|
config.prerender.handleEntryGeneratorMismatch,
|
|
169
222
|
({ generatedFromId, entry, matchedId }) => {
|
|
170
|
-
return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId
|
|
223
|
+
return `The entries export from ${generatedFromId} generated entry ${entry}, which was matched by ${matchedId === entry ? 'a static route' : matchedId}`;
|
|
171
224
|
}
|
|
172
225
|
);
|
|
173
226
|
|
|
174
227
|
const handle_not_prerendered_route = normalise_error_handler(
|
|
175
|
-
|
|
228
|
+
'handleUnseenRoutes',
|
|
176
229
|
config.prerender.handleUnseenRoutes,
|
|
177
230
|
({ routes }) => {
|
|
178
231
|
const list = routes.map((id) => ` - ${id}`).join('\n');
|
|
179
|
-
return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}
|
|
232
|
+
return `The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:\n${list}`;
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const handle_invalid_url = normalise_error_handler(
|
|
237
|
+
'handleInvalidUrl',
|
|
238
|
+
config.prerender.handleInvalidUrl,
|
|
239
|
+
({ href, referrer }) => {
|
|
240
|
+
return `Invalid URL ${href}${referrer ? ` (linked from ${referrer})` : ''}`;
|
|
180
241
|
}
|
|
181
242
|
);
|
|
182
243
|
|
|
@@ -210,12 +271,56 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
210
271
|
|
|
211
272
|
const seen = new Set();
|
|
212
273
|
const written = new Set();
|
|
274
|
+
|
|
275
|
+
/** @type {Map<string, Promise<any>>} */
|
|
213
276
|
const remote_responses = new Map();
|
|
214
277
|
|
|
278
|
+
/** @type {(path: string) => void} */
|
|
279
|
+
let progress_line = noop;
|
|
280
|
+
|
|
281
|
+
if (is_tty) {
|
|
282
|
+
// Where possible, provide progress feedback by showing the path we're
|
|
283
|
+
// currently requesting, then clearing the line once the response comes in.
|
|
284
|
+
// This avoids the wall of text that happens when you prerender
|
|
285
|
+
// many pages and log each response
|
|
286
|
+
let current = false;
|
|
287
|
+
const stdout_write = process.stdout.write;
|
|
288
|
+
const stderr_write = process.stderr.write;
|
|
289
|
+
|
|
290
|
+
process.stdout.write = new Proxy(stdout_write, {
|
|
291
|
+
apply(target, this_arg, args) {
|
|
292
|
+
current = false;
|
|
293
|
+
return Reflect.apply(target, this_arg, args);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
process.stderr.write = new Proxy(stderr_write, {
|
|
298
|
+
apply(target, this_arg, args) {
|
|
299
|
+
current = false;
|
|
300
|
+
return Reflect.apply(target, this_arg, args);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
progress_line = (path) => {
|
|
305
|
+
// If app code writes to stdout or stderr, don't move the cursor to clear
|
|
306
|
+
// the previous progress log, because that will corrupt things
|
|
307
|
+
if (current) {
|
|
308
|
+
moveCursor(process.stdout, 0, -1);
|
|
309
|
+
clearLine(process.stdout, 0);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
stdout_write.call(process.stdout, `rendering ${path}...\n`);
|
|
313
|
+
current = true;
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** @type {Set<string>} */
|
|
318
|
+
const resolved_route_ids = new Set();
|
|
319
|
+
|
|
215
320
|
/** @type {Map<string, Set<string>>} */
|
|
216
321
|
const expected_hashlinks = new Map();
|
|
217
322
|
|
|
218
|
-
/** @type {Map<string, string
|
|
323
|
+
/** @type {Map<string, Set<string>>} */
|
|
219
324
|
const actual_hashlinks = new Map();
|
|
220
325
|
|
|
221
326
|
/**
|
|
@@ -249,13 +354,18 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
249
354
|
/** @type {Map<string, import('types').PrerenderDependency>} */
|
|
250
355
|
const dependencies = new Map();
|
|
251
356
|
|
|
252
|
-
|
|
357
|
+
progress_line(decoded);
|
|
358
|
+
|
|
359
|
+
const request = new Request(prerender_origin + encoded);
|
|
360
|
+
|
|
361
|
+
const response = await server.respond(request, {
|
|
253
362
|
getClientAddress() {
|
|
254
363
|
throw new Error('Cannot read clientAddress during prerendering');
|
|
255
364
|
},
|
|
256
365
|
prerendering: {
|
|
257
366
|
dependencies,
|
|
258
|
-
remote_responses
|
|
367
|
+
remote_responses,
|
|
368
|
+
resolved_route_ids
|
|
259
369
|
},
|
|
260
370
|
read: (file) => {
|
|
261
371
|
// stuff we just wrote
|
|
@@ -287,6 +397,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
287
397
|
});
|
|
288
398
|
}
|
|
289
399
|
|
|
400
|
+
if (response.status >= 400) {
|
|
401
|
+
log_response(response.status, request);
|
|
402
|
+
}
|
|
403
|
+
|
|
290
404
|
const body = Buffer.from(await response.arrayBuffer());
|
|
291
405
|
|
|
292
406
|
const category = decoded.startsWith(remote_prefix) ? 'data' : 'pages';
|
|
@@ -331,17 +445,25 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
331
445
|
const headers = Object.fromEntries(response.headers);
|
|
332
446
|
|
|
333
447
|
// if it's a 200 HTML response, crawl it. Skip error responses, as we don't save those
|
|
334
|
-
if (
|
|
335
|
-
|
|
448
|
+
if (
|
|
449
|
+
response.ok &&
|
|
450
|
+
config.prerender.crawl &&
|
|
451
|
+
matches_content_type(headers['content-type'], 'text/html')
|
|
452
|
+
) {
|
|
453
|
+
const { ids, hrefs, invalid } = crawl(body.toString(), decoded);
|
|
454
|
+
|
|
455
|
+
for (const href of invalid) {
|
|
456
|
+
handle_invalid_url({ href, referrer: decoded });
|
|
457
|
+
}
|
|
336
458
|
|
|
337
|
-
actual_hashlinks.set(decoded, ids);
|
|
459
|
+
actual_hashlinks.set(decoded, new Set(ids));
|
|
338
460
|
|
|
339
461
|
/** @param {string} href */
|
|
340
462
|
const removePrerenderOrigin = (href) => {
|
|
341
|
-
if (href.startsWith(
|
|
342
|
-
if (href ===
|
|
343
|
-
if (href.at(
|
|
344
|
-
return href.slice(
|
|
463
|
+
if (href.startsWith(prerender_origin)) {
|
|
464
|
+
if (href === prerender_origin) return '/';
|
|
465
|
+
if (href.at(prerender_origin.length) !== '/') return href;
|
|
466
|
+
return href.slice(prerender_origin.length);
|
|
345
467
|
}
|
|
346
468
|
return href;
|
|
347
469
|
};
|
|
@@ -384,7 +506,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
384
506
|
const headers = Object.fromEntries(response.headers);
|
|
385
507
|
|
|
386
508
|
const type = headers['content-type'];
|
|
387
|
-
const is_html = response_type === REDIRECT || type
|
|
509
|
+
const is_html = response_type === REDIRECT || matches_content_type(type, 'text/html');
|
|
510
|
+
|
|
511
|
+
if (!is_html && response.status === 200 && decoded.slice(config.paths.base.length + 1) === '') {
|
|
512
|
+
throw new Error(
|
|
513
|
+
`Cannot prerender a root +server.js that returns a non-HTML response - static hosts always serve an HTML file for \`${config.paths.base || '/'}\``
|
|
514
|
+
);
|
|
515
|
+
}
|
|
388
516
|
|
|
389
517
|
const file = output_filename(decoded, is_html);
|
|
390
518
|
const dest = `${config.outDir}/output/prerendered/${category}/${file}`;
|
|
@@ -405,9 +533,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
405
533
|
}
|
|
406
534
|
|
|
407
535
|
if (!headers['x-sveltekit-normalize']) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
log.warn(`${response.status} ${decoded} -> ${location}`);
|
|
536
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
411
537
|
|
|
412
538
|
writeFileSync(
|
|
413
539
|
dest,
|
|
@@ -453,9 +579,8 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
453
579
|
);
|
|
454
580
|
}
|
|
455
581
|
|
|
456
|
-
|
|
582
|
+
mkdirSync(dir, { recursive: true });
|
|
457
583
|
|
|
458
|
-
log.info(`${response.status} ${decoded}`);
|
|
459
584
|
writeFileSync(dest, body);
|
|
460
585
|
written.add(file);
|
|
461
586
|
|
|
@@ -471,7 +596,12 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
471
596
|
|
|
472
597
|
prerendered.paths.push(decoded);
|
|
473
598
|
} else if (response_type !== OK) {
|
|
474
|
-
handle_http_error({
|
|
599
|
+
handle_http_error({
|
|
600
|
+
status: response.status,
|
|
601
|
+
path: decoded,
|
|
602
|
+
referrer,
|
|
603
|
+
referenceType
|
|
604
|
+
});
|
|
475
605
|
}
|
|
476
606
|
|
|
477
607
|
manifest.assets.add(file);
|
|
@@ -495,12 +625,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
495
625
|
}
|
|
496
626
|
}
|
|
497
627
|
|
|
498
|
-
// the user's remote function modules may reference
|
|
499
|
-
//
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
internal.set_manifest(manifest);
|
|
503
|
-
internal.set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
|
|
628
|
+
// the user's remote function modules may reference `read` or the `manifest` at the top-level
|
|
629
|
+
// so we need to set them before evaluating those modules to avoid potential runtime errors
|
|
630
|
+
set_manifest(manifest);
|
|
631
|
+
set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
|
|
504
632
|
|
|
505
633
|
/** @type {Array<import('types').RemotePrerenderInternals>} */
|
|
506
634
|
const prerender_functions = [];
|
|
@@ -554,14 +682,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
554
682
|
}
|
|
555
683
|
}
|
|
556
684
|
|
|
557
|
-
const transport = (await internal.get_hooks()).transport ?? {};
|
|
558
685
|
for (const internals of prerender_functions) {
|
|
559
686
|
if (internals.has_arg) {
|
|
560
687
|
for (const arg of (await internals.inputs?.()) ?? []) {
|
|
561
|
-
void enqueue(
|
|
562
|
-
null,
|
|
563
|
-
remote_prefix + internals.id + '/' + stringify_remote_arg(arg, transport)
|
|
564
|
-
);
|
|
688
|
+
void enqueue(null, remote_prefix + internals.id + '/' + stringify_remote_arg(arg));
|
|
565
689
|
}
|
|
566
690
|
} else {
|
|
567
691
|
void enqueue(null, remote_prefix + internals.id);
|
|
@@ -580,7 +704,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env, vit
|
|
|
580
704
|
// ignore fragment links to pages that were not prerendered
|
|
581
705
|
if (!hashlinks) continue;
|
|
582
706
|
|
|
583
|
-
if (!hashlinks.
|
|
707
|
+
if (!hashlinks.has(id) && !SPECIAL_HASHLINKS.has(id)) {
|
|
584
708
|
handle_missing_id({ id, path, referrers: Array.from(referrers) });
|
|
585
709
|
}
|
|
586
710
|
}
|
|
@@ -28,7 +28,7 @@ export function queue(concurrency) {
|
|
|
28
28
|
|
|
29
29
|
if (task) {
|
|
30
30
|
current += 1;
|
|
31
|
-
const promise =
|
|
31
|
+
const promise = (async () => task.fn())(); // could throw synchronously
|
|
32
32
|
|
|
33
33
|
void promise
|
|
34
34
|
.then(task.fulfil, (err) => {
|
|
@@ -54,6 +54,7 @@ export function queue(concurrency) {
|
|
|
54
54
|
const promise = new Promise((fulfil, reject) => {
|
|
55
55
|
tasks.push({ fn, fulfil, reject });
|
|
56
56
|
});
|
|
57
|
+
promise.catch(() => {});
|
|
57
58
|
|
|
58
59
|
dequeue();
|
|
59
60
|
return promise;
|
|
@@ -60,7 +60,7 @@ function normalize_route_id(id) {
|
|
|
60
60
|
.replace(/(?<=^|\/)\(.+?\)(?=$|\/)/g, '')
|
|
61
61
|
|
|
62
62
|
.replace(/\[[ux]\+([0-9a-f]+)\]/g, (_, x) =>
|
|
63
|
-
String.
|
|
63
|
+
String.fromCodePoint(parseInt(x, 16)).replace(/\//g, '%2f')
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
// replace `[param]` with `<*>`, `[param=x]` with `<x>`, and `[[param]]` with `<?*>`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { lookup } from '
|
|
1
|
+
import { lookup } from '../../../utils/mime.js';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { styleText } from 'node:util';
|
|
@@ -29,27 +29,46 @@ export default function create_manifest_data({
|
|
|
29
29
|
}) {
|
|
30
30
|
const assets = create_assets(config);
|
|
31
31
|
const hooks = create_hooks(config, cwd);
|
|
32
|
-
const
|
|
32
|
+
const params = resolve_params(config, cwd);
|
|
33
33
|
const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
|
|
34
34
|
|
|
35
|
-
// validate matcher names used in parameterised routes
|
|
36
|
-
for (const route of routes) {
|
|
37
|
-
for (const param of route.params) {
|
|
38
|
-
if (param.matcher && !matchers[param.matcher]) {
|
|
39
|
-
throw new Error(`No matcher found for parameter '${param.matcher}' in route ${route.id}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
35
|
return {
|
|
45
36
|
assets,
|
|
46
37
|
hooks,
|
|
47
|
-
|
|
38
|
+
params,
|
|
48
39
|
nodes,
|
|
49
40
|
routes
|
|
50
41
|
};
|
|
51
42
|
}
|
|
52
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Whether this route has a `+page`. Independent of `is_endpoint_route` — a route can be both.
|
|
46
|
+
* @param {import('types').RouteData} route
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
export function is_page_route(route) {
|
|
50
|
+
return !!route.page;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether this route has a `+server`. Independent of `is_page_route` — a route can be both.
|
|
55
|
+
* @param {import('types').RouteData} route
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
export function is_endpoint_route(route) {
|
|
59
|
+
return !!route.endpoint;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the router can match this route. `manifest_data.routes` also contains entries for
|
|
64
|
+
* directories with layouts or errors, which never reach `event.route.id`.
|
|
65
|
+
* @param {import('types').RouteData} route
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
export function is_app_route(route) {
|
|
69
|
+
return is_page_route(route) || is_endpoint_route(route);
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
/**
|
|
54
73
|
* Returns a list of files in the `static` directory.
|
|
55
74
|
* @param {import('types').ValidatedConfig} config
|
|
@@ -82,38 +101,9 @@ function create_hooks(config, cwd) {
|
|
|
82
101
|
* @param {import('types').ValidatedConfig} config
|
|
83
102
|
* @param {string} cwd
|
|
84
103
|
*/
|
|
85
|
-
function
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
/** @type {Record<string, string>} */
|
|
89
|
-
const matchers = {};
|
|
90
|
-
if (fs.existsSync(config.kit.files.params)) {
|
|
91
|
-
for (const file of fs.readdirSync(config.kit.files.params)) {
|
|
92
|
-
const ext = path.extname(file);
|
|
93
|
-
if (!config.kit.moduleExtensions.includes(ext)) continue;
|
|
94
|
-
const type = file.slice(0, -ext.length);
|
|
95
|
-
|
|
96
|
-
if (/^\w+$/.test(type)) {
|
|
97
|
-
const matcher_file = path.join(params_base, file);
|
|
98
|
-
|
|
99
|
-
// Disallow same matcher with different extensions
|
|
100
|
-
if (matchers[type]) {
|
|
101
|
-
throw new Error(`Duplicate matchers: ${matcher_file} and ${matchers[type]}`);
|
|
102
|
-
} else {
|
|
103
|
-
matchers[type] = matcher_file;
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
// Allow for matcher test collocation
|
|
107
|
-
if (type.endsWith('.test') || type.endsWith('.spec')) continue;
|
|
108
|
-
|
|
109
|
-
throw new Error(
|
|
110
|
-
`Matcher names can only have underscores and alphanumeric characters — "${file}" is invalid`
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return matchers;
|
|
104
|
+
function resolve_params(config, cwd) {
|
|
105
|
+
const params_file = resolve_entry(config.kit.files.params);
|
|
106
|
+
return params_file ? posixify(path.relative(cwd, params_file)) : null;
|
|
117
107
|
}
|
|
118
108
|
|
|
119
109
|
/**
|
|
@@ -123,7 +113,7 @@ function create_matchers(config, cwd) {
|
|
|
123
113
|
*/
|
|
124
114
|
function create_routes_and_nodes(cwd, config, fallback) {
|
|
125
115
|
/** @type {import('types').RouteData[]} */
|
|
126
|
-
|
|
116
|
+
let routes = [];
|
|
127
117
|
|
|
128
118
|
const routes_base = posixify(path.relative(cwd, config.kit.files.routes));
|
|
129
119
|
|
|
@@ -163,7 +153,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
163
153
|
);
|
|
164
154
|
}
|
|
165
155
|
|
|
166
|
-
return String.
|
|
156
|
+
return String.fromCodePoint(parseInt(code, 16));
|
|
167
157
|
}
|
|
168
158
|
});
|
|
169
159
|
|
|
@@ -180,7 +170,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
180
170
|
throw new Error(`Route ${id} should be renamed to ${id.replace(/#/g, '[x+23]')}`);
|
|
181
171
|
}
|
|
182
172
|
|
|
183
|
-
if (/\[
|
|
173
|
+
if (/\[\.\.\.[\w-]+\]\/\[\[/.test(id)) {
|
|
184
174
|
throw new Error(
|
|
185
175
|
`Invalid route ${id} — an [[optional]] route segment cannot follow a [...rest] route segment`
|
|
186
176
|
);
|
|
@@ -221,10 +211,17 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
221
211
|
|
|
222
212
|
// We can't use withFileTypes because of a NodeJs bug which returns wrong results
|
|
223
213
|
// with isDirectory() in case of symlinks: https://github.com/nodejs/node/issues/30646
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
214
|
+
// We sort the entries because `readdirSync` order is not guaranteed and differs
|
|
215
|
+
// between runtimes (e.g. Node returns entries alphabetically, Bun in directory
|
|
216
|
+
// order). Node indices are assigned from this traversal order, so without sorting
|
|
217
|
+
// the SSR and client manifests can disagree, causing hydration mismatches.
|
|
218
|
+
const files = fs
|
|
219
|
+
.readdirSync(dir)
|
|
220
|
+
.sort()
|
|
221
|
+
.map((name) => ({
|
|
222
|
+
is_dir: fs.statSync(path.join(dir, name)).isDirectory(),
|
|
223
|
+
name
|
|
224
|
+
}));
|
|
228
225
|
|
|
229
226
|
// process files first
|
|
230
227
|
for (const file of files) {
|
|
@@ -254,6 +251,15 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
254
251
|
continue;
|
|
255
252
|
}
|
|
256
253
|
|
|
254
|
+
// allow e.g. `+page.stories.svelte` or `+server.test.ts`
|
|
255
|
+
if (
|
|
256
|
+
file.name.includes('.test.') ||
|
|
257
|
+
file.name.includes('.spec.') ||
|
|
258
|
+
file.name.includes('.stories.')
|
|
259
|
+
) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
257
263
|
if (file.name.endsWith('.d.ts')) {
|
|
258
264
|
let name = file.name.slice(0, -5);
|
|
259
265
|
const ext = valid_extensions.find((ext) => name.endsWith(ext));
|
|
@@ -368,7 +374,7 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
368
374
|
const root = routes[0];
|
|
369
375
|
if (!root.leaf && !root.error && !root.layout && !root.endpoint) {
|
|
370
376
|
throw new Error(
|
|
371
|
-
'No routes found. If you are using a custom src/routes directory, make sure it is specified in your
|
|
377
|
+
'No routes found. If you are using a custom src/routes directory, make sure it is specified in your SvelteKit Vite plugin options'
|
|
372
378
|
);
|
|
373
379
|
}
|
|
374
380
|
}
|
|
@@ -471,6 +477,9 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
471
477
|
}
|
|
472
478
|
}
|
|
473
479
|
|
|
480
|
+
// remove route objects with no route file
|
|
481
|
+
routes = routes.filter((route) => route.endpoint || route.leaf || route.layout || route.error);
|
|
482
|
+
|
|
474
483
|
// add parents to error nodes so that we can compute which page options apply to them
|
|
475
484
|
for (const route of routes) {
|
|
476
485
|
if (!route.error) continue;
|
|
@@ -495,6 +504,17 @@ function create_routes_and_nodes(cwd, config, fallback) {
|
|
|
495
504
|
if (route.endpoint) {
|
|
496
505
|
route.endpoint.page_options = get_page_options(route.endpoint.file, cwd);
|
|
497
506
|
}
|
|
507
|
+
|
|
508
|
+
if (route.page && route.endpoint) {
|
|
509
|
+
const page = nodes[route.page.leaf];
|
|
510
|
+
if (page.page_options?.prerender || route.endpoint.page_options?.prerender) {
|
|
511
|
+
const endpoint_file = route.endpoint.file.split('/').pop();
|
|
512
|
+
|
|
513
|
+
throw new Error(
|
|
514
|
+
`Cannot prerender a route (${route.id}) with both a \`+page.svelte\` and a \`${endpoint_file}\``
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
498
518
|
}
|
|
499
519
|
|
|
500
520
|
return {
|