@sveltejs/kit 1.0.0-next.168 → 1.0.0-next.171

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.
@@ -1,601 +1,382 @@
1
- import fs__default from 'fs';
2
- import path__default from 'path';
3
- import { svelte } from '@sveltejs/vite-plugin-svelte';
4
- import { c as create_manifest_data, g as glob, a as create_app, d as deep_merge } from './index2.js';
5
- import vite from 'vite';
6
- import { r as rimraf, a as resolve_entry, b as posixify, c as copy_assets, p as print_config_conflicts } from '../cli.js';
1
+ import { m as mkdirp, r as rimraf, d as copy, $, l as logger } from '../cli.js';
7
2
  import { S as SVELTE_KIT } from './constants.js';
8
- import './standard.js';
9
- import 'os';
3
+ import { readFileSync, writeFileSync } from 'fs';
4
+ import { resolve, join, dirname } from 'path';
5
+ import { pathToFileURL, resolve as resolve$1, URL } from 'url';
6
+ import { __fetch_polyfill } from '../install-fetch.js';
7
+ import { g as get_single_valued_header } from './http.js';
10
8
  import 'sade';
11
9
  import 'child_process';
12
10
  import 'net';
13
- import 'url';
11
+ import 'os';
14
12
  import './error.js';
15
-
16
- /** @param {any} value */
17
- const s = (value) => JSON.stringify(value);
18
-
19
- /** @typedef {Record<string, {
20
- * file: string;
21
- * css: string[];
22
- * imports: string[];
23
- * }>} ClientManifest */
13
+ import 'http';
14
+ import 'https';
15
+ import 'zlib';
16
+ import 'stream';
17
+ import 'util';
18
+ import 'crypto';
24
19
 
25
20
  /**
26
- * @param {import('types/config').ValidatedConfig} config
27
- * @param {{
28
- * cwd?: string;
29
- * runtime?: string;
30
- * }} [opts]
31
- * @returns {Promise<import('types/internal').BuildData>}
21
+ * @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
22
+ * @typedef {import('types/config').PrerenderOnErrorValue} OnError
23
+ * @typedef {import('types/internal').Logger} Logger
32
24
  */
33
- async function build(config, { cwd = process.cwd(), runtime = '@sveltejs/kit/ssr' } = {}) {
34
- const build_dir = path__default.resolve(cwd, `${SVELTE_KIT}/build`);
35
-
36
- rimraf(build_dir);
37
-
38
- const output_dir = path__default.resolve(cwd, `${SVELTE_KIT}/output`);
39
-
40
- const options = {
41
- cwd,
42
- config,
43
- build_dir,
44
- // TODO this is so that Vite's preloading works. Unfortunately, it fails
45
- // during `svelte-kit preview`, because we use a local asset path. If Vite
46
- // used relative paths, I _think_ this could get fixed. Issue here:
47
- // https://github.com/vitejs/vite/issues/2009
48
- assets_base: `${config.kit.paths.assets || config.kit.paths.base}/${config.kit.appDir}/`,
49
- manifest: create_manifest_data({
50
- config,
51
- output: build_dir,
52
- cwd
53
- }),
54
- output_dir,
55
- client_entry_file: `${SVELTE_KIT}/build/runtime/internal/start.js`,
56
- service_worker_entry_file: resolve_entry(config.kit.files.serviceWorker)
57
- };
58
25
 
59
- const client_manifest = await build_client(options);
60
- await build_server(options, client_manifest, runtime);
26
+ /** @param {string} html */
27
+ function clean_html(html) {
28
+ return html
29
+ .replace(/<!\[CDATA\[[\s\S]*?\]\]>/gm, '')
30
+ .replace(/(<script[\s\S]*?>)[\s\S]*?<\/script>/gm, '$1</' + 'script>')
31
+ .replace(/(<style[\s\S]*?>)[\s\S]*?<\/style>/gm, '$1</' + 'style>')
32
+ .replace(/<!--[\s\S]*?-->/gm, '');
33
+ }
61
34
 
62
- if (options.service_worker_entry_file) {
63
- if (config.kit.paths.assets) {
64
- throw new Error('Cannot use service worker alongside config.kit.paths.assets');
65
- }
35
+ /** @param {string} attrs */
36
+ function get_href(attrs) {
37
+ const match = /(?:[\s'"]|^)href\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
38
+ return match && (match[1] || match[2] || match[3]);
39
+ }
40
+
41
+ /** @param {string} attrs */
42
+ function get_src(attrs) {
43
+ const match = /(?:[\s'"]|^)src\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
44
+ return match && (match[1] || match[2] || match[3]);
45
+ }
46
+
47
+ /** @param {string} attrs */
48
+ function is_rel_external(attrs) {
49
+ const match = /rel\s*=\s*(?:["'][^>]*(external)[^>]*["']|(external))/.exec(attrs);
50
+ return !!match;
51
+ }
66
52
 
67
- await build_service_worker(options, client_manifest);
53
+ /** @param {string} attrs */
54
+ function get_srcset_urls(attrs) {
55
+ const results = [];
56
+ // Note that the srcset allows any ASCII whitespace, including newlines.
57
+ const match = /([\s'"]|^)srcset\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/s.exec(attrs);
58
+ if (match) {
59
+ const attr_content = match[1] || match[2] || match[3];
60
+ // Parse the content of the srcset attribute.
61
+ // The regexp is modelled after the srcset specs (https://html.spec.whatwg.org/multipage/images.html#srcset-attribute)
62
+ // and should cover most reasonable cases.
63
+ const regex = /\s*([^\s,]\S+[^\s,])\s*((?:\d+w)|(?:-?\d+(?:\.\d+)?(?:[eE]-?\d+)?x))?/gm;
64
+ let sub_matches;
65
+ while ((sub_matches = regex.exec(attr_content))) {
66
+ results.push(sub_matches[1]);
67
+ }
68
68
  }
69
+ return results;
70
+ }
69
71
 
70
- const client = glob('**', { cwd: `${output_dir}/client`, filesOnly: true }).map(posixify);
71
- const server = glob('**', { cwd: `${output_dir}/server`, filesOnly: true }).map(posixify);
72
+ /** @type {(errorDetails: Parameters<PrerenderErrorHandler>[0] ) => string} */
73
+ function errorDetailsToString({ status, path, referrer, referenceType }) {
74
+ return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
75
+ }
72
76
 
73
- return {
74
- client,
75
- server,
76
- static: options.manifest.assets.map((asset) => posixify(asset.file)),
77
- entries: options.manifest.routes
78
- .map((route) => (route.type === 'page' ? route.path : ''))
79
- .filter(Boolean)
80
- };
77
+ /** @type {(log: Logger, onError: OnError) => PrerenderErrorHandler} */
78
+ function chooseErrorHandler(log, onError) {
79
+ switch (onError) {
80
+ case 'continue':
81
+ return (errorDetails) => {
82
+ log.error(errorDetailsToString(errorDetails));
83
+ };
84
+ case 'fail':
85
+ return (errorDetails) => {
86
+ throw new Error(errorDetailsToString(errorDetails));
87
+ };
88
+ default:
89
+ return onError;
90
+ }
81
91
  }
82
92
 
93
+ const OK = 2;
94
+ const REDIRECT = 3;
95
+
83
96
  /**
84
97
  * @param {{
85
98
  * cwd: string;
86
- * assets_base: string;
87
- * config: import('types/config').ValidatedConfig
88
- * manifest: import('types/internal').ManifestData
89
- * build_dir: string;
90
- * output_dir: string;
91
- * client_entry_file: string;
92
- * service_worker_entry_file: string | null;
93
- * }} options
99
+ * out: string;
100
+ * log: Logger;
101
+ * config: import('types/config').ValidatedConfig;
102
+ * build_data: import('types/internal').BuildData;
103
+ * fallback?: string;
104
+ * all: boolean; // disregard `export const prerender = true`
105
+ * }} opts
94
106
  */
95
- async function build_client({
96
- cwd,
97
- assets_base,
98
- config,
99
- manifest,
100
- build_dir,
101
- output_dir,
102
- client_entry_file
103
- }) {
104
- create_app({
105
- manifest_data: manifest,
106
- output: build_dir,
107
- cwd
108
- });
107
+ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
108
+ if (!config.kit.prerender.enabled && !fallback) {
109
+ return;
110
+ }
109
111
 
110
- copy_assets(build_dir);
112
+ __fetch_polyfill();
111
113
 
112
- process.env.VITE_SVELTEKIT_AMP = config.kit.amp ? 'true' : '';
114
+ const dir = resolve(cwd, `${SVELTE_KIT}/output`);
113
115
 
114
- const client_out_dir = `${output_dir}/client/${config.kit.appDir}`;
115
- const client_manifest_file = `${client_out_dir}/manifest.json`;
116
+ const seen = new Set();
116
117
 
117
- /** @type {Record<string, string>} */
118
- const input = {
119
- start: path__default.resolve(cwd, client_entry_file)
120
- };
118
+ const server_root = resolve(dir);
119
+
120
+ /** @type {import('types/app').App} */
121
+ const app = await import(pathToFileURL(`${server_root}/server/app.js`).href);
121
122
 
122
- // This step is optional — Vite/Rollup will create the necessary chunks
123
- // for everything regardless — but it means that entry chunks reflect
124
- // their location in the source code, which is helpful for debugging
125
- manifest.components.forEach((file) => {
126
- const resolved = path__default.resolve(cwd, file);
127
- const relative = path__default.relative(config.kit.files.routes, resolved);
128
-
129
- const name = relative.startsWith('..')
130
- ? path__default.basename(file)
131
- : posixify(path__default.join('pages', relative));
132
- input[name] = resolved;
123
+ app.init({
124
+ paths: config.kit.paths,
125
+ prerendering: true,
126
+ read: (file) => readFileSync(join(config.kit.files.assets, file))
133
127
  });
134
128
 
135
- /** @type {any} */
136
- const vite_config = config.kit.vite();
129
+ const error = chooseErrorHandler(log, config.kit.prerender.onError);
137
130
 
138
- const default_config = {
139
- server: {
140
- fs: {
141
- strict: true
142
- }
143
- }
144
- };
131
+ const files = new Set([...build_data.static, ...build_data.client]);
145
132
 
146
- // don't warn on overriding defaults
147
- const [modified_vite_config] = deep_merge(default_config, vite_config);
148
-
149
- /** @type {[any, string[]]} */
150
- const [merged_config, conflicts] = deep_merge(modified_vite_config, {
151
- configFile: false,
152
- root: cwd,
153
- base: assets_base,
154
- build: {
155
- cssCodeSplit: true,
156
- manifest: true,
157
- outDir: client_out_dir,
158
- polyfillDynamicImport: false,
159
- rollupOptions: {
160
- input,
161
- output: {
162
- entryFileNames: '[name]-[hash].js',
163
- chunkFileNames: 'chunks/[name]-[hash].js',
164
- assetFileNames: 'assets/[name]-[hash][extname]'
165
- },
166
- preserveEntrySignatures: 'strict'
167
- }
168
- },
169
- resolve: {
170
- alias: {
171
- $app: path__default.resolve(`${build_dir}/runtime/app`),
172
- $lib: config.kit.files.lib
173
- }
174
- },
175
- plugins: [
176
- svelte({
177
- extensions: config.extensions,
178
- emitCss: !config.kit.amp,
179
- compilerOptions: {
180
- hydratable: !!config.kit.hydrate
181
- }
182
- })
183
- ]
133
+ build_data.static.forEach((file) => {
134
+ if (file.endsWith('/index.html')) {
135
+ files.add(file.slice(0, -11));
136
+ }
184
137
  });
185
138
 
186
- print_config_conflicts(conflicts, 'kit.vite.', 'build_client');
139
+ /**
140
+ * @param {string} path
141
+ */
142
+ function normalize(path) {
143
+ if (config.kit.trailingSlash === 'always') {
144
+ return path.endsWith('/') ? path : `${path}/`;
145
+ } else if (config.kit.trailingSlash === 'never') {
146
+ return !path.endsWith('/') || path === '/' ? path : path.slice(0, -1);
147
+ }
187
148
 
188
- await vite.build(merged_config);
149
+ return path;
150
+ }
189
151
 
190
- /** @type {ClientManifest} */
191
- const client_manifest = JSON.parse(fs__default.readFileSync(client_manifest_file, 'utf-8'));
192
- fs__default.renameSync(client_manifest_file, `${output_dir}/manifest.json`); // inspectable but not shipped
152
+ /**
153
+ * @param {string} decoded_path
154
+ * @param {string?} referrer
155
+ */
156
+ async function visit(decoded_path, referrer) {
157
+ const path = encodeURI(normalize(decoded_path));
158
+
159
+ if (seen.has(path)) return;
160
+ seen.add(path);
161
+
162
+ /** @type {Map<string, import('types/hooks').ServerResponse>} */
163
+ const dependencies = new Map();
164
+
165
+ const rendered = await app.render(
166
+ {
167
+ host: config.kit.host,
168
+ method: 'GET',
169
+ headers: {},
170
+ path,
171
+ rawBody: null,
172
+ query: new URLSearchParams()
173
+ },
174
+ {
175
+ prerender: {
176
+ all,
177
+ dependencies
178
+ }
179
+ }
180
+ );
193
181
 
194
- return client_manifest;
195
- }
182
+ if (rendered) {
183
+ const response_type = Math.floor(rendered.status / 100);
184
+ const headers = rendered.headers;
185
+ const type = headers && headers['content-type'];
186
+ const is_html = response_type === REDIRECT || type === 'text/html';
196
187
 
197
- /**
198
- * @param {{
199
- * cwd: string;
200
- * assets_base: string;
201
- * config: import('types/config').ValidatedConfig
202
- * manifest: import('types/internal').ManifestData
203
- * build_dir: string;
204
- * output_dir: string;
205
- * client_entry_file: string;
206
- * service_worker_entry_file: string | null;
207
- * }} options
208
- * @param {ClientManifest} client_manifest
209
- * @param {string} runtime
210
- */
211
- async function build_server(
212
- {
213
- cwd,
214
- assets_base,
215
- config,
216
- manifest,
217
- build_dir,
218
- output_dir,
219
- client_entry_file,
220
- service_worker_entry_file
221
- },
222
- client_manifest,
223
- runtime
224
- ) {
225
- let hooks_file = resolve_entry(config.kit.files.hooks);
226
- if (!hooks_file || !fs__default.existsSync(hooks_file)) {
227
- hooks_file = path__default.resolve(cwd, `${SVELTE_KIT}/build/hooks.js`);
228
- fs__default.writeFileSync(hooks_file, '');
229
- }
188
+ const parts = decoded_path.split('/');
189
+ if (is_html && parts[parts.length - 1] !== 'index.html') {
190
+ parts.push('index.html');
191
+ }
230
192
 
231
- const app_file = `${build_dir}/app.js`;
193
+ const file = `${out}${parts.join('/')}`;
194
+ mkdirp(dirname(file));
232
195
 
233
- /** @type {(file: string) => string} */
234
- const app_relative = (file) => {
235
- const relative_file = path__default.relative(build_dir, path__default.resolve(cwd, file));
236
- return relative_file[0] === '.' ? relative_file : `./${relative_file}`;
237
- };
196
+ if (response_type === REDIRECT) {
197
+ const location = get_single_valued_header(headers, 'location');
238
198
 
239
- const prefix = `/${config.kit.appDir}/`;
199
+ if (location) {
200
+ log.warn(`${rendered.status} ${decoded_path} -> ${location}`);
201
+ writeFileSync(file, `<meta http-equiv="refresh" content="0;url=${encodeURI(location)}">`);
202
+ } else {
203
+ log.warn(`location header missing on redirect received from ${decoded_path}`);
204
+ }
240
205
 
241
- /**
242
- * @param {string} file
243
- * @param {Set<string>} js_deps
244
- * @param {Set<string>} css_deps
245
- */
246
- function find_deps(file, js_deps, css_deps) {
247
- const chunk = client_manifest[file];
206
+ return;
207
+ }
248
208
 
249
- if (js_deps.has(chunk.file)) return;
250
- js_deps.add(chunk.file);
209
+ if (rendered.status === 200) {
210
+ log.info(`${rendered.status} ${decoded_path}`);
211
+ writeFileSync(file, rendered.body || '');
212
+ } else if (response_type !== OK) {
213
+ error({ status: rendered.status, path, referrer, referenceType: 'linked' });
214
+ }
251
215
 
252
- if (chunk.css) {
253
- chunk.css.forEach((file) => css_deps.add(file));
254
- }
216
+ dependencies.forEach((result, dependency_path) => {
217
+ const response_type = Math.floor(result.status / 100);
255
218
 
256
- if (chunk.imports) {
257
- chunk.imports.forEach((file) => find_deps(file, js_deps, css_deps));
258
- }
259
- }
219
+ const is_html = result.headers['content-type'] === 'text/html';
220
+
221
+ const parts = dependency_path.split('/');
222
+ if (is_html && parts[parts.length - 1] !== 'index.html') {
223
+ parts.push('index.html');
224
+ }
260
225
 
261
- /** @type {Record<string, { entry: string, css: string[], js: string[], styles: string[] }>} */
262
- const metadata_lookup = {};
226
+ const file = `${out}${parts.join('/')}`;
227
+ mkdirp(dirname(file));
263
228
 
264
- manifest.components.forEach((file) => {
265
- const js_deps = new Set();
266
- const css_deps = new Set();
229
+ if (result.body) writeFileSync(file, result.body);
267
230
 
268
- find_deps(file, js_deps, css_deps);
231
+ if (response_type === OK) {
232
+ log.info(`${result.status} ${dependency_path}`);
233
+ } else {
234
+ error({
235
+ status: result.status,
236
+ path: dependency_path,
237
+ referrer: path,
238
+ referenceType: 'fetched'
239
+ });
240
+ }
241
+ });
269
242
 
270
- const js = Array.from(js_deps);
271
- const css = Array.from(css_deps);
243
+ if (is_html && config.kit.prerender.crawl) {
244
+ const cleaned = clean_html(/** @type {string} */ (rendered.body));
272
245
 
273
- const styles = config.kit.amp
274
- ? Array.from(css_deps).map((url) => {
275
- const resolved = `${output_dir}/client/${config.kit.appDir}/${url}`;
276
- return fs__default.readFileSync(resolved, 'utf-8');
277
- })
278
- : [];
246
+ let match;
247
+ const pattern = /<(a|img|link|source)\s+([\s\S]+?)>/gm;
279
248
 
280
- metadata_lookup[file] = {
281
- entry: client_manifest[file].file,
282
- css,
283
- js,
284
- styles
285
- };
286
- });
249
+ const hrefs = [];
287
250
 
288
- /** @type {Set<string>} */
289
- const entry_js = new Set();
290
- /** @type {Set<string>} */
291
- const entry_css = new Set();
292
-
293
- find_deps(client_entry_file, entry_js, entry_css);
294
-
295
- // prettier-ignore
296
- fs__default.writeFileSync(
297
- app_file,
298
- `
299
- import { respond } from '${runtime}';
300
- import root from './generated/root.svelte';
301
- import { set_paths, assets } from './runtime/paths.js';
302
- import { set_prerendering } from './runtime/env.js';
303
- import * as user_hooks from ${s(app_relative(hooks_file))};
304
-
305
- const template = ({ head, body }) => ${s(fs__default.readFileSync(config.kit.files.template, 'utf-8'))
306
- .replace('%svelte.head%', '" + head + "')
307
- .replace('%svelte.body%', '" + body + "')};
308
-
309
- let options = null;
310
-
311
- const default_settings = { paths: ${s(config.kit.paths)} };
312
-
313
- // allow paths to be overridden in svelte-kit preview
314
- // and in prerendering
315
- export function init(settings = default_settings) {
316
- set_paths(settings.paths);
317
- set_prerendering(settings.prerendering || false);
318
-
319
- const hooks = get_hooks(user_hooks);
320
-
321
- options = {
322
- amp: ${config.kit.amp},
323
- dev: false,
324
- entry: {
325
- file: assets + ${s(prefix + client_manifest[client_entry_file].file)},
326
- css: [${Array.from(entry_css).map(dep => 'assets + ' + s(prefix + dep))}],
327
- js: [${Array.from(entry_js).map(dep => 'assets + ' + s(prefix + dep))}]
328
- },
329
- fetched: undefined,
330
- floc: ${config.kit.floc},
331
- get_component_path: id => assets + ${s(prefix)} + entry_lookup[id],
332
- get_stack: error => String(error), // for security
333
- handle_error: (error, request) => {
334
- hooks.handleError({ error, request });
335
- error.stack = options.get_stack(error);
336
- },
337
- hooks,
338
- hydrate: ${s(config.kit.hydrate)},
339
- initiator: undefined,
340
- load_component,
341
- manifest,
342
- paths: settings.paths,
343
- prerender: ${config.kit.prerender.enabled},
344
- read: settings.read,
345
- root,
346
- service_worker: ${service_worker_entry_file ? "'/service-worker.js'" : 'null'},
347
- router: ${s(config.kit.router)},
348
- ssr: ${s(config.kit.ssr)},
349
- target: ${s(config.kit.target)},
350
- template,
351
- trailing_slash: ${s(config.kit.trailingSlash)}
352
- };
353
- }
251
+ while ((match = pattern.exec(cleaned))) {
252
+ const element = match[1];
253
+ const attrs = match[2];
354
254
 
355
- const d = decodeURIComponent;
356
- const empty = () => ({});
357
-
358
- const manifest = {
359
- assets: ${s(manifest.assets)},
360
- layout: ${s(manifest.layout)},
361
- error: ${s(manifest.error)},
362
- routes: [
363
- ${manifest.routes
364
- .map((route) => {
365
- if (route.type === 'page') {
366
- const params = get_params(route.params);
367
-
368
- return `{
369
- type: 'page',
370
- pattern: ${route.pattern},
371
- params: ${params},
372
- a: [${route.a.map(file => file && s(file)).join(', ')}],
373
- b: [${route.b.map(file => file && s(file)).join(', ')}]
374
- }`;
255
+ if (element === 'a' || element === 'link') {
256
+ if (is_rel_external(attrs)) continue;
257
+
258
+ hrefs.push(get_href(attrs));
375
259
  } else {
376
- const params = get_params(route.params);
377
- const load = `() => import(${s(app_relative(route.file))})`;
378
-
379
- return `{
380
- type: 'endpoint',
381
- pattern: ${route.pattern},
382
- params: ${params},
383
- load: ${load}
384
- }`;
260
+ if (element === 'img') {
261
+ hrefs.push(get_src(attrs));
262
+ }
263
+ hrefs.push(...get_srcset_urls(attrs));
385
264
  }
386
- })
387
- .join(',\n\t\t\t\t\t')}
388
- ]
389
- };
265
+ }
390
266
 
391
- // this looks redundant, but the indirection allows us to access
392
- // named imports without triggering Rollup's missing import detection
393
- const get_hooks = hooks => ({
394
- getSession: hooks.getSession || (() => ({})),
395
- handle: hooks.handle || (({ request, resolve }) => resolve(request)),
396
- handleError: hooks.handleError || (({ error }) => console.error(error.stack)),
397
- externalFetch: hooks.externalFetch || fetch
398
- });
267
+ for (const href of hrefs) {
268
+ if (!href) continue;
399
269
 
400
- const module_lookup = {
401
- ${manifest.components.map(file => `${s(file)}: () => import(${s(app_relative(file))})`)}
402
- };
270
+ const resolved = resolve$1(path, href);
271
+ if (!resolved.startsWith('/') || resolved.startsWith('//')) continue;
403
272
 
404
- const metadata_lookup = ${s(metadata_lookup)};
405
-
406
- async function load_component(file) {
407
- const { entry, css, js, styles } = metadata_lookup[file];
408
- return {
409
- module: await module_lookup[file](),
410
- entry: assets + ${s(prefix)} + entry,
411
- css: css.map(dep => assets + ${s(prefix)} + dep),
412
- js: js.map(dep => assets + ${s(prefix)} + dep),
413
- styles
414
- };
415
- }
273
+ const parsed = new URL(resolved, 'http://localhost');
274
+ const pathname = decodeURI(parsed.pathname).replace(config.kit.paths.base, '');
416
275
 
417
- export function render(request, {
418
- prerender
419
- } = {}) {
420
- const host = ${config.kit.host ? s(config.kit.host) : `request.headers[${s(config.kit.hostHeader || 'host')}]`};
421
- return respond({ ...request, host }, options, { prerender });
422
- }
423
- `
424
- .replace(/^\t{3}/gm, '')
425
- .trim()
426
- );
427
-
428
- /** @type {import('vite').UserConfig} */
429
- const vite_config = config.kit.vite();
430
-
431
- const default_config = {
432
- server: {
433
- fs: {
434
- strict: true
276
+ const file = pathname.slice(1);
277
+ if (files.has(file)) continue;
278
+
279
+ if (parsed.search) ;
280
+
281
+ await visit(pathname, path);
282
+ }
435
283
  }
436
284
  }
437
- };
285
+ }
438
286
 
439
- // don't warn on overriding defaults
440
- const [modified_vite_config] = deep_merge(default_config, vite_config);
441
-
442
- /** @type {[any, string[]]} */
443
- const [merged_config, conflicts] = deep_merge(modified_vite_config, {
444
- configFile: false,
445
- root: cwd,
446
- base: assets_base,
447
- build: {
448
- target: 'es2018',
449
- ssr: true,
450
- outDir: `${output_dir}/server`,
451
- polyfillDynamicImport: false,
452
- rollupOptions: {
453
- input: {
454
- app: app_file
455
- },
456
- output: {
457
- format: 'esm',
458
- entryFileNames: '[name].js',
459
- chunkFileNames: 'chunks/[name]-[hash].js',
460
- assetFileNames: 'assets/[name]-[hash][extname]',
461
- inlineDynamicImports: true
462
- },
463
- preserveEntrySignatures: 'strict'
464
- }
465
- },
466
- plugins: [
467
- svelte({
468
- extensions: config.extensions,
469
- compilerOptions: {
470
- hydratable: !!config.kit.hydrate
287
+ if (config.kit.prerender.enabled) {
288
+ for (const entry of config.kit.prerender.entries) {
289
+ if (entry === '*') {
290
+ for (const entry of build_data.entries) {
291
+ await visit(entry, null);
471
292
  }
472
- })
473
- ],
474
- resolve: {
475
- alias: {
476
- $app: path__default.resolve(`${build_dir}/runtime/app`),
477
- $lib: config.kit.files.lib
293
+ } else {
294
+ await visit(entry, null);
478
295
  }
479
296
  }
480
- });
297
+ }
481
298
 
482
- print_config_conflicts(conflicts, 'kit.vite.', 'build_server');
299
+ if (fallback) {
300
+ const rendered = await app.render(
301
+ {
302
+ host: config.kit.host,
303
+ method: 'GET',
304
+ headers: {},
305
+ path: '[fallback]', // this doesn't matter, but it's easiest if it's a string
306
+ rawBody: null,
307
+ query: new URLSearchParams()
308
+ },
309
+ {
310
+ prerender: {
311
+ fallback,
312
+ all: false
313
+ }
314
+ }
315
+ );
483
316
 
484
- await vite.build(merged_config);
317
+ const file = join(out, fallback);
318
+ mkdirp(dirname(file));
319
+ writeFileSync(file, rendered.body || '');
320
+ }
485
321
  }
486
322
 
487
323
  /**
488
324
  * @param {{
489
325
  * cwd: string;
490
- * assets_base: string;
491
- * config: import('types/config').ValidatedConfig
492
- * manifest: import('types/internal').ManifestData
493
- * build_dir: string;
494
- * output_dir: string;
495
- * client_entry_file: string;
496
- * service_worker_entry_file: string | null;
497
- * }} options
498
- * @param {ClientManifest} client_manifest
326
+ * config: import('types/config').ValidatedConfig;
327
+ * build_data: import('types/internal').BuildData;
328
+ * log: import('types/internal').Logger;
329
+ * }} opts
330
+ * @returns {import('types/config').AdapterUtils}
499
331
  */
500
- async function build_service_worker(
501
- { cwd, assets_base, config, manifest, build_dir, output_dir, service_worker_entry_file },
502
- client_manifest
503
- ) {
504
- // TODO add any assets referenced in template .html file, e.g. favicon?
505
- const app_files = new Set();
506
- for (const key in client_manifest) {
507
- const { file, css } = client_manifest[key];
508
- app_files.add(file);
509
- if (css) {
510
- css.forEach((file) => {
511
- app_files.add(file);
512
- });
513
- }
514
- }
332
+ function get_utils({ cwd, config, build_data, log }) {
333
+ return {
334
+ log,
335
+ rimraf,
336
+ mkdirp,
337
+ copy,
515
338
 
516
- fs__default.writeFileSync(
517
- `${build_dir}/runtime/service-worker.js`,
518
- `
519
- export const timestamp = ${Date.now()};
520
-
521
- export const build = [
522
- ${Array.from(app_files)
523
- .map((file) => `${s(`${config.kit.paths.base}/${config.kit.appDir}/${file}`)}`)
524
- .join(',\n\t\t\t\t')}
525
- ];
526
-
527
- export const files = [
528
- ${manifest.assets
529
- .map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`)
530
- .join(',\n\t\t\t\t')}
531
- ];
532
- `
533
- .replace(/^\t{3}/gm, '')
534
- .trim()
535
- );
536
-
537
- /** @type {any} */
538
- const vite_config = config.kit.vite();
539
-
540
- const default_config = {
541
- server: {
542
- fs: {
543
- strict: true
544
- }
545
- }
546
- };
339
+ copy_client_files(dest) {
340
+ copy(`${cwd}/${SVELTE_KIT}/output/client`, dest, (file) => file[0] !== '.');
341
+ },
547
342
 
548
- // don't warn on overriding defaults
549
- const [modified_vite_config] = deep_merge(default_config, vite_config);
550
-
551
- /** @type {[any, string[]]} */
552
- const [merged_config, conflicts] = deep_merge(modified_vite_config, {
553
- configFile: false,
554
- root: cwd,
555
- base: assets_base,
556
- build: {
557
- lib: {
558
- entry: service_worker_entry_file,
559
- name: 'app',
560
- formats: ['es']
561
- },
562
- rollupOptions: {
563
- output: {
564
- entryFileNames: 'service-worker.js'
565
- }
566
- },
567
- outDir: `${output_dir}/client`,
568
- emptyOutDir: false
343
+ copy_server_files(dest) {
344
+ copy(`${cwd}/${SVELTE_KIT}/output/server`, dest, (file) => file[0] !== '.');
569
345
  },
570
- resolve: {
571
- alias: {
572
- '$service-worker': path__default.resolve(`${build_dir}/runtime/service-worker`),
573
- $lib: config.kit.files.lib
574
- }
575
- }
576
- });
577
346
 
578
- print_config_conflicts(conflicts, 'kit.vite.', 'build_service_worker');
347
+ copy_static_files(dest) {
348
+ copy(config.kit.files.assets, dest);
349
+ },
579
350
 
580
- await vite.build(merged_config);
351
+ async prerender({ all = false, dest, fallback }) {
352
+ await prerender({
353
+ out: dest,
354
+ all,
355
+ cwd,
356
+ config,
357
+ build_data,
358
+ fallback,
359
+ log
360
+ });
361
+ }
362
+ };
581
363
  }
582
364
 
583
- /** @param {string[]} array */
584
- function get_params(array) {
585
- // given an array of params like `['x', 'y', 'z']` for
586
- // src/routes/[x]/[y]/[z]/svelte, create a function
587
- // that turns a RexExpMatchArray into ({ x, y, z })
588
- return array.length
589
- ? '(m) => ({ ' +
590
- array
591
- .map((param, i) => {
592
- return param.startsWith('...')
593
- ? `${param.slice(3)}: d(m[${i + 1}] || '')`
594
- : `${param}: d(m[${i + 1}])`;
595
- })
596
- .join(', ') +
597
- '})'
598
- : 'empty';
365
+ /**
366
+ * @param {import('types/config').ValidatedConfig} config
367
+ * @param {import('types/internal').BuildData} build_data
368
+ * @param {{ cwd?: string, verbose: boolean }} opts
369
+ */
370
+ async function adapt(config, build_data, { cwd = process.cwd(), verbose }) {
371
+ const { name, adapt } = config.kit.adapter;
372
+
373
+ console.log($.bold().cyan(`\n> Using ${name}`));
374
+
375
+ const log = logger({ verbose });
376
+ const utils = get_utils({ cwd, config, build_data, log });
377
+ await adapt({ utils, config });
378
+
379
+ log.success('done');
599
380
  }
600
381
 
601
- export { build };
382
+ export { adapt };