@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,34 +1,615 @@
1
- import http from 'http';
2
- import https from 'https';
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';
7
+ import { S as SVELTE_KIT } from './constants.js';
8
+ import './standard.js';
9
+ import 'os';
10
+ import 'sade';
11
+ import 'child_process';
12
+ import 'net';
13
+ import 'url';
14
+ 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 */
24
+
25
+ /**
26
+ * @param {import('types/config').ValidatedConfig} config
27
+ * @param {{
28
+ * cwd?: string;
29
+ * runtime?: string;
30
+ * }} [opts]
31
+ * @returns {Promise<import('types/internal').BuildData>}
32
+ */
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
+
59
+ const client_manifest = await build_client(options);
60
+ await build_server(options, client_manifest, runtime);
61
+
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
+ }
66
+
67
+ await build_service_worker(options, client_manifest);
68
+ }
69
+
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
+
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
+ };
81
+ }
3
82
 
4
83
  /**
5
- * @param {boolean} use_https
6
- * @param {import('vite').UserConfig} user_config
7
- * @param {(req: http.IncomingMessage, res: http.ServerResponse) => void} handler
8
- * @returns {Promise<import('net').Server>}
84
+ * @param {{
85
+ * 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
9
94
  */
10
- async function get_server(use_https, user_config, handler) {
11
- /** @type {https.ServerOptions} */
12
- const https_options = {};
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
+ });
109
+
110
+ copy_assets(build_dir);
111
+
112
+ process.env.VITE_SVELTEKIT_AMP = config.kit.amp ? 'true' : '';
113
+
114
+ const client_out_dir = `${output_dir}/client/${config.kit.appDir}`;
115
+ const client_manifest_file = `${client_out_dir}/manifest.json`;
116
+
117
+ /** @type {Record<string, string>} */
118
+ const input = {
119
+ start: path__default.resolve(cwd, client_entry_file)
120
+ };
121
+
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;
133
+ });
134
+
135
+ /** @type {any} */
136
+ const vite_config = config.kit.vite();
137
+
138
+ const default_config = {
139
+ server: {
140
+ fs: {
141
+ strict: true
142
+ }
143
+ }
144
+ };
145
+
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
+ ]
184
+ });
185
+
186
+ print_config_conflicts(conflicts, 'kit.vite.', 'build_client');
187
+
188
+ await vite.build(merged_config);
189
+
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
193
+
194
+ return client_manifest;
195
+ }
196
+
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
+ }
230
+
231
+ const app_file = `${build_dir}/app.js`;
232
+
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
+ };
238
+
239
+ const prefix = `/${config.kit.appDir}/`;
240
+
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];
248
+
249
+ if (js_deps.has(chunk.file)) return;
250
+ js_deps.add(chunk.file);
251
+
252
+ if (chunk.css) {
253
+ chunk.css.forEach((file) => css_deps.add(file));
254
+ }
255
+
256
+ if (chunk.imports) {
257
+ chunk.imports.forEach((file) => find_deps(file, js_deps, css_deps));
258
+ }
259
+ }
260
+
261
+ /** @type {Record<string, { entry: string, css: string[], js: string[], styles: string[] }>} */
262
+ const metadata_lookup = {};
13
263
 
14
- if (use_https) {
15
- const secure_opts = user_config.server
16
- ? /** @type {import('tls').SecureContextOptions} */ (user_config.server.https)
17
- : {};
264
+ manifest.components.forEach((file) => {
265
+ const js_deps = new Set();
266
+ const css_deps = new Set();
18
267
 
19
- if (secure_opts.key && secure_opts.cert) {
20
- https_options.key = secure_opts.key.toString();
21
- https_options.cert = secure_opts.cert.toString();
22
- } else {
23
- https_options.key = https_options.cert = (await import('./cert.js')).createCertificate();
268
+ find_deps(file, js_deps, css_deps);
269
+
270
+ const js = Array.from(js_deps);
271
+ const css = Array.from(css_deps);
272
+
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
+ : [];
279
+
280
+ metadata_lookup[file] = {
281
+ entry: client_manifest[file].file,
282
+ css,
283
+ js,
284
+ styles
285
+ };
286
+ });
287
+
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
+ }
354
+
355
+ // input has already been decoded by decodeURI
356
+ // now handle the rest that decodeURIComponent would do
357
+ const d = s => s
358
+ .replace(/%23/g, '#')
359
+ .replace(/%3[Bb]/g, ';')
360
+ .replace(/%2[Cc]/g, ',')
361
+ .replace(/%2[Ff]/g, '/')
362
+ .replace(/%3[Ff]/g, '?')
363
+ .replace(/%3[Aa]/g, ':')
364
+ .replace(/%40/g, '@')
365
+ .replace(/%26/g, '&')
366
+ .replace(/%3[Dd]/g, '=')
367
+ .replace(/%2[Bb]/g, '+')
368
+ .replace(/%24/g, '$');
369
+
370
+ const empty = () => ({});
371
+
372
+ const manifest = {
373
+ assets: ${s(manifest.assets)},
374
+ layout: ${s(manifest.layout)},
375
+ error: ${s(manifest.error)},
376
+ routes: [
377
+ ${manifest.routes
378
+ .map((route) => {
379
+ if (route.type === 'page') {
380
+ const params = get_params(route.params);
381
+
382
+ return `{
383
+ type: 'page',
384
+ pattern: ${route.pattern},
385
+ params: ${params},
386
+ a: [${route.a.map(file => file && s(file)).join(', ')}],
387
+ b: [${route.b.map(file => file && s(file)).join(', ')}]
388
+ }`;
389
+ } else {
390
+ const params = get_params(route.params);
391
+ const load = `() => import(${s(app_relative(route.file))})`;
392
+
393
+ return `{
394
+ type: 'endpoint',
395
+ pattern: ${route.pattern},
396
+ params: ${params},
397
+ load: ${load}
398
+ }`;
399
+ }
400
+ })
401
+ .join(',\n\t\t\t\t\t')}
402
+ ]
403
+ };
404
+
405
+ // this looks redundant, but the indirection allows us to access
406
+ // named imports without triggering Rollup's missing import detection
407
+ const get_hooks = hooks => ({
408
+ getSession: hooks.getSession || (() => ({})),
409
+ handle: hooks.handle || (({ request, resolve }) => resolve(request)),
410
+ handleError: hooks.handleError || (({ error }) => console.error(error.stack)),
411
+ externalFetch: hooks.externalFetch || fetch
412
+ });
413
+
414
+ const module_lookup = {
415
+ ${manifest.components.map(file => `${s(file)}: () => import(${s(app_relative(file))})`)}
416
+ };
417
+
418
+ const metadata_lookup = ${s(metadata_lookup)};
419
+
420
+ async function load_component(file) {
421
+ const { entry, css, js, styles } = metadata_lookup[file];
422
+ return {
423
+ module: await module_lookup[file](),
424
+ entry: assets + ${s(prefix)} + entry,
425
+ css: css.map(dep => assets + ${s(prefix)} + dep),
426
+ js: js.map(dep => assets + ${s(prefix)} + dep),
427
+ styles
428
+ };
429
+ }
430
+
431
+ export function render(request, {
432
+ prerender
433
+ } = {}) {
434
+ const host = ${config.kit.host ? s(config.kit.host) : `request.headers[${s(config.kit.hostHeader || 'host')}]`};
435
+ return respond({ ...request, host }, options, { prerender });
436
+ }
437
+ `
438
+ .replace(/^\t{3}/gm, '')
439
+ .trim()
440
+ );
441
+
442
+ /** @type {import('vite').UserConfig} */
443
+ const vite_config = config.kit.vite();
444
+
445
+ const default_config = {
446
+ server: {
447
+ fs: {
448
+ strict: true
449
+ }
450
+ }
451
+ };
452
+
453
+ // don't warn on overriding defaults
454
+ const [modified_vite_config] = deep_merge(default_config, vite_config);
455
+
456
+ /** @type {[any, string[]]} */
457
+ const [merged_config, conflicts] = deep_merge(modified_vite_config, {
458
+ configFile: false,
459
+ root: cwd,
460
+ base: assets_base,
461
+ build: {
462
+ target: 'es2018',
463
+ ssr: true,
464
+ outDir: `${output_dir}/server`,
465
+ polyfillDynamicImport: false,
466
+ rollupOptions: {
467
+ input: {
468
+ app: app_file
469
+ },
470
+ output: {
471
+ format: 'esm',
472
+ entryFileNames: '[name].js',
473
+ chunkFileNames: 'chunks/[name]-[hash].js',
474
+ assetFileNames: 'assets/[name]-[hash][extname]',
475
+ inlineDynamicImports: true
476
+ },
477
+ preserveEntrySignatures: 'strict'
478
+ }
479
+ },
480
+ plugins: [
481
+ svelte({
482
+ extensions: config.extensions,
483
+ compilerOptions: {
484
+ hydratable: !!config.kit.hydrate
485
+ }
486
+ })
487
+ ],
488
+ resolve: {
489
+ alias: {
490
+ $app: path__default.resolve(`${build_dir}/runtime/app`),
491
+ $lib: config.kit.files.lib
492
+ }
493
+ }
494
+ });
495
+
496
+ print_config_conflicts(conflicts, 'kit.vite.', 'build_server');
497
+
498
+ await vite.build(merged_config);
499
+ }
500
+
501
+ /**
502
+ * @param {{
503
+ * cwd: string;
504
+ * assets_base: string;
505
+ * config: import('types/config').ValidatedConfig
506
+ * manifest: import('types/internal').ManifestData
507
+ * build_dir: string;
508
+ * output_dir: string;
509
+ * client_entry_file: string;
510
+ * service_worker_entry_file: string | null;
511
+ * }} options
512
+ * @param {ClientManifest} client_manifest
513
+ */
514
+ async function build_service_worker(
515
+ { cwd, assets_base, config, manifest, build_dir, output_dir, service_worker_entry_file },
516
+ client_manifest
517
+ ) {
518
+ // TODO add any assets referenced in template .html file, e.g. favicon?
519
+ const app_files = new Set();
520
+ for (const key in client_manifest) {
521
+ const { file, css } = client_manifest[key];
522
+ app_files.add(file);
523
+ if (css) {
524
+ css.forEach((file) => {
525
+ app_files.add(file);
526
+ });
24
527
  }
25
528
  }
26
529
 
27
- return Promise.resolve(
28
- use_https
29
- ? https.createServer(/** @type {https.ServerOptions} */ (https_options), handler)
30
- : http.createServer(handler)
530
+ fs__default.writeFileSync(
531
+ `${build_dir}/runtime/service-worker.js`,
532
+ `
533
+ export const timestamp = ${Date.now()};
534
+
535
+ export const build = [
536
+ ${Array.from(app_files)
537
+ .map((file) => `${s(`${config.kit.paths.base}/${config.kit.appDir}/${file}`)}`)
538
+ .join(',\n\t\t\t\t')}
539
+ ];
540
+
541
+ export const files = [
542
+ ${manifest.assets
543
+ .map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`)
544
+ .join(',\n\t\t\t\t')}
545
+ ];
546
+ `
547
+ .replace(/^\t{3}/gm, '')
548
+ .trim()
31
549
  );
550
+
551
+ /** @type {any} */
552
+ const vite_config = config.kit.vite();
553
+
554
+ const default_config = {
555
+ server: {
556
+ fs: {
557
+ strict: true
558
+ }
559
+ }
560
+ };
561
+
562
+ // don't warn on overriding defaults
563
+ const [modified_vite_config] = deep_merge(default_config, vite_config);
564
+
565
+ /** @type {[any, string[]]} */
566
+ const [merged_config, conflicts] = deep_merge(modified_vite_config, {
567
+ configFile: false,
568
+ root: cwd,
569
+ base: assets_base,
570
+ build: {
571
+ lib: {
572
+ entry: service_worker_entry_file,
573
+ name: 'app',
574
+ formats: ['es']
575
+ },
576
+ rollupOptions: {
577
+ output: {
578
+ entryFileNames: 'service-worker.js'
579
+ }
580
+ },
581
+ outDir: `${output_dir}/client`,
582
+ emptyOutDir: false
583
+ },
584
+ resolve: {
585
+ alias: {
586
+ '$service-worker': path__default.resolve(`${build_dir}/runtime/service-worker`),
587
+ $lib: config.kit.files.lib
588
+ }
589
+ }
590
+ });
591
+
592
+ print_config_conflicts(conflicts, 'kit.vite.', 'build_service_worker');
593
+
594
+ await vite.build(merged_config);
595
+ }
596
+
597
+ /** @param {string[]} array */
598
+ function get_params(array) {
599
+ // given an array of params like `['x', 'y', 'z']` for
600
+ // src/routes/[x]/[y]/[z]/svelte, create a function
601
+ // that turns a RexExpMatchArray into ({ x, y, z })
602
+ return array.length
603
+ ? '(m) => ({ ' +
604
+ array
605
+ .map((param, i) => {
606
+ return param.startsWith('...')
607
+ ? `${param.slice(3)}: d(m[${i + 1}] || '')`
608
+ : `${param}: d(m[${i + 1}])`;
609
+ })
610
+ .join(', ') +
611
+ '})'
612
+ : 'empty';
32
613
  }
33
614
 
34
- export { get_server as g };
615
+ export { build };