@sveltejs/kit 1.0.0-next.345 → 1.0.0-next.348

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,183 +1,267 @@
1
- import { $ } from '../cli.js';
2
- import { r as rimraf, m as mkdirp, c as copy } from './filesystem.js';
3
- import { g as generate_manifest } from './index3.js';
4
- import 'sade';
5
- import 'fs';
6
- import 'path';
7
- import 'child_process';
8
- import 'net';
1
+ import fs__default from 'fs';
2
+ import http from 'http';
3
+ import https from 'https';
4
+ import { join } from 'path';
5
+ import { S as SVELTE_KIT_ASSETS, s as sirv } from './constants.js';
6
+ import { pathToFileURL } from 'url';
7
+ import { getRequest, setResponse } from '../node.js';
8
+ import { installPolyfills } from '../node/polyfills.js';
9
+ import { d as load_config } from '../cli.js';
10
+ import 'querystring';
11
+ import 'stream';
12
+ import 'node:http';
13
+ import 'node:https';
14
+ import 'node:zlib';
15
+ import 'node:stream';
16
+ import 'node:buffer';
17
+ import 'node:util';
18
+ import 'node:url';
19
+ import 'node:net';
20
+ import 'node:fs';
21
+ import 'node:path';
22
+ import 'crypto';
9
23
  import 'chokidar';
10
- import 'url';
24
+ import 'net';
25
+ import 'child_process';
26
+ import 'sade';
27
+ import 'vite';
11
28
  import 'os';
12
- import './misc.js';
29
+
30
+ /** @typedef {import('http').IncomingMessage} Req */
31
+ /** @typedef {import('http').ServerResponse} Res */
32
+ /** @typedef {(req: Req, res: Res, next: () => void) => void} Handler */
33
+
34
+ /**
35
+ * @param {string} dir
36
+ * @returns {Handler}
37
+ */
38
+ const mutable = (dir) =>
39
+ fs__default.existsSync(dir)
40
+ ? sirv(dir, {
41
+ etag: true,
42
+ maxAge: 0
43
+ })
44
+ : (req, res, next) => next();
13
45
 
14
46
  /**
15
47
  * @param {{
16
- * config: import('types').ValidatedConfig;
17
- * build_data: import('types').BuildData;
18
- * prerendered: import('types').Prerendered;
19
- * log: import('types').Logger;
48
+ * port: number;
49
+ * host?: string;
50
+ * https?: boolean;
51
+ * cwd?: string;
20
52
  * }} opts
21
- * @returns {import('types').Builder}
22
53
  */
23
- function create_builder({ config, build_data, prerendered, log }) {
24
- /** @type {Set<string>} */
25
- const prerendered_paths = new Set(prerendered.paths);
26
-
27
- /** @param {import('types').RouteData} route */
28
- // TODO routes should come pre-filtered
29
- function not_prerendered(route) {
30
- if (route.type === 'page' && route.path) {
31
- return !prerendered_paths.has(route.path);
32
- }
33
-
34
- return true;
35
- }
36
-
37
- return {
38
- log,
39
- rimraf,
40
- mkdirp,
41
- copy,
42
-
43
- config,
44
- prerendered,
45
-
46
- async createEntries(fn) {
47
- const { routes } = build_data.manifest_data;
48
-
49
- /** @type {import('types').RouteDefinition[]} */
50
- const facades = routes.map((route) => ({
51
- id: route.id,
52
- type: route.type,
53
- segments: route.id.split('/').map((segment) => ({
54
- dynamic: segment.includes('['),
55
- rest: segment.includes('[...'),
56
- content: segment
57
- })),
58
- pattern: route.pattern,
59
- methods: route.type === 'page' ? ['get'] : build_data.server.methods[route.file]
60
- }));
61
-
62
- const seen = new Set();
63
-
64
- for (let i = 0; i < routes.length; i += 1) {
65
- const route = routes[i];
66
- const { id, filter, complete } = fn(facades[i]);
67
-
68
- if (seen.has(id)) continue;
69
- seen.add(id);
70
-
71
- const group = [route];
72
-
73
- // figure out which lower priority routes should be considered fallbacks
74
- for (let j = i + 1; j < routes.length; j += 1) {
75
- if (filter(facades[j])) {
76
- group.push(routes[j]);
54
+ async function preview({ port, host, https: use_https = false }) {
55
+ installPolyfills();
56
+
57
+ const config = await load_config();
58
+ const { paths } = config.kit;
59
+ const base = paths.base;
60
+ const assets = paths.assets ? SVELTE_KIT_ASSETS : paths.base;
61
+
62
+ const etag = `"${Date.now()}"`;
63
+
64
+ const index_file = join(config.kit.outDir, 'output/server/index.js');
65
+ const manifest_file = join(config.kit.outDir, 'output/server/manifest.js');
66
+
67
+ /** @type {import('types').ServerModule} */
68
+ const { Server, override } = await import(pathToFileURL(index_file).href);
69
+ const { manifest } = await import(pathToFileURL(manifest_file).href);
70
+
71
+ override({
72
+ paths: { base, assets },
73
+ prerendering: false,
74
+ protocol: use_https ? 'https' : 'http',
75
+ read: (file) => fs__default.readFileSync(join(config.kit.files.assets, file))
76
+ });
77
+
78
+ const server = new Server(manifest);
79
+
80
+ const handle = compose([
81
+ // files in `static`
82
+ scoped(assets, mutable(config.kit.files.assets)),
83
+
84
+ // immutable generated client assets
85
+ scoped(
86
+ assets,
87
+ sirv(join(config.kit.outDir, 'output/client'), {
88
+ setHeaders: (res, pathname) => {
89
+ // only apply to build directory, not e.g. version.json
90
+ if (pathname.startsWith(`/${config.kit.appDir}/immutable`)) {
91
+ res.setHeader('cache-control', 'public,max-age=31536000,immutable');
77
92
  }
78
93
  }
79
-
80
- const filtered = new Set(group.filter(not_prerendered));
81
-
82
- // heuristic: if /foo/[bar] is included, /foo/[bar].json should
83
- // also be included, since the page likely needs the endpoint
84
- filtered.forEach((route) => {
85
- if (route.type === 'page') {
86
- const endpoint = routes.find((candidate) => candidate.id === route.id + '.json');
87
-
88
- if (endpoint) {
89
- filtered.add(endpoint);
90
- }
91
- }
92
- });
93
-
94
- if (filtered.size > 0) {
95
- await complete({
96
- generateManifest: ({ relativePath, format }) =>
97
- generate_manifest({
98
- build_data,
99
- relative_path: relativePath,
100
- routes: Array.from(filtered),
101
- format
102
- })
103
- });
104
- }
94
+ })
95
+ ),
96
+
97
+ (req, res, next) => {
98
+ const original_url = /** @type {string} */ (req.url);
99
+ const { pathname } = new URL(original_url, 'http://dummy');
100
+
101
+ if (pathname.startsWith(base)) {
102
+ next();
103
+ } else {
104
+ res.statusCode = 404;
105
+ res.end(`Not found (did you mean ${base + pathname}?)`);
105
106
  }
106
107
  },
107
108
 
108
- generateManifest: ({ relativePath, format }) => {
109
- return generate_manifest({
110
- build_data,
111
- relative_path: relativePath,
112
- routes: build_data.manifest_data.routes.filter(not_prerendered),
113
- format
114
- });
115
- },
109
+ // prerendered dependencies
110
+ scoped(base, mutable(join(config.kit.outDir, 'output/prerendered/dependencies'))),
116
111
 
117
- getBuildDirectory(name) {
118
- return `${config.kit.outDir}/${name}`;
119
- },
112
+ // prerendered pages (we can't just use sirv because we need to
113
+ // preserve the correct trailingSlash behaviour)
114
+ scoped(base, (req, res, next) => {
115
+ let if_none_match_value = req.headers['if-none-match'];
120
116
 
121
- getClientDirectory() {
122
- return `${config.kit.outDir}/output/client`;
123
- },
117
+ if (if_none_match_value?.startsWith('W/"')) {
118
+ if_none_match_value = if_none_match_value.substring(2);
119
+ }
124
120
 
125
- getServerDirectory() {
126
- return `${config.kit.outDir}/output/server`;
127
- },
121
+ if (if_none_match_value === etag) {
122
+ res.statusCode = 304;
123
+ res.end();
124
+ return;
125
+ }
128
126
 
129
- getStaticDirectory() {
130
- return config.kit.files.assets;
131
- },
127
+ const { pathname } = new URL(/** @type {string} */ (req.url), 'http://dummy');
132
128
 
133
- writeClient(dest) {
134
- return copy(`${config.kit.outDir}/output/client`, dest);
135
- },
129
+ // only treat this as a page if it doesn't include an extension
130
+ if (pathname === '/' || /\/[^./]+\/?$/.test(pathname)) {
131
+ const file = join(
132
+ config.kit.outDir,
133
+ 'output/prerendered/pages' + pathname + (pathname.endsWith('/') ? 'index.html' : '.html')
134
+ );
136
135
 
137
- writePrerendered(dest, { fallback } = {}) {
138
- const source = `${config.kit.outDir}/output/prerendered`;
139
- const files = [...copy(`${source}/pages`, dest), ...copy(`${source}/dependencies`, dest)];
136
+ if (fs__default.existsSync(file)) {
137
+ res.writeHead(200, {
138
+ 'content-type': 'text/html',
139
+ etag
140
+ });
140
141
 
141
- if (fallback) {
142
- files.push(fallback);
143
- copy(`${source}/fallback.html`, `${dest}/${fallback}`);
142
+ fs__default.createReadStream(file).pipe(res);
143
+ return;
144
+ }
144
145
  }
145
146
 
146
- return files;
147
- },
147
+ next();
148
+ }),
148
149
 
149
- writeServer(dest) {
150
- return copy(`${config.kit.outDir}/output/server`, dest);
151
- },
150
+ // SSR
151
+ async (req, res) => {
152
+ const protocol = use_https ? 'https' : 'http';
153
+ const host = req.headers['host'];
152
154
 
153
- writeStatic(dest) {
154
- return copy(config.kit.files.assets, dest);
155
- },
155
+ let request;
156
+
157
+ try {
158
+ request = await getRequest(`${protocol}://${host}`, req);
159
+ } catch (/** @type {any} */ err) {
160
+ res.statusCode = err.status || 400;
161
+ return res.end(err.reason || 'Invalid request body');
162
+ }
156
163
 
157
- // @ts-expect-error
158
- async prerender() {
159
- throw new Error(
160
- 'builder.prerender() has been removed. Prerendering now takes place in the build phase — see builder.prerender and builder.writePrerendered'
164
+ setResponse(
165
+ res,
166
+ await server.respond(request, {
167
+ getClientAddress: () => {
168
+ const { remoteAddress } = req.socket;
169
+ if (remoteAddress) return remoteAddress;
170
+ throw new Error('Could not determine clientAddress');
171
+ }
172
+ })
161
173
  );
162
174
  }
163
- };
175
+ ]);
176
+
177
+ const vite_config = (config.kit.vite && (await config.kit.vite())) || {};
178
+
179
+ const http_server = await get_server(use_https, vite_config, (req, res) => {
180
+ if (req.url == null) {
181
+ throw new Error('Invalid request url');
182
+ }
183
+
184
+ handle(req, res);
185
+ });
186
+
187
+ return new Promise((fulfil) => {
188
+ http_server.listen(port, host, () => {
189
+ fulfil({ server: http_server, config });
190
+ });
191
+ });
164
192
  }
165
193
 
166
194
  /**
167
- * @param {import('types').ValidatedConfig} config
168
- * @param {import('types').BuildData} build_data
169
- * @param {import('types').Prerendered} prerendered
170
- * @param {{ log: import('types').Logger }} opts
195
+ * @param {boolean} use_https
196
+ * @param {import('vite').UserConfig} user_config
197
+ * @param {(req: http.IncomingMessage, res: http.ServerResponse) => void} handler
198
+ * @returns {Promise<import('net').Server>}
171
199
  */
172
- async function adapt(config, build_data, prerendered, { log }) {
173
- const { name, adapt } = config.kit.adapter;
200
+ async function get_server(use_https, user_config, handler) {
201
+ /** @type {https.ServerOptions} */
202
+ const https_options = {};
203
+
204
+ if (use_https) {
205
+ const secure_opts = user_config.server
206
+ ? /** @type {import('tls').SecureContextOptions} */ (user_config.server.https)
207
+ : {};
208
+
209
+ if (secure_opts.key && secure_opts.cert) {
210
+ https_options.key = secure_opts.key.toString();
211
+ https_options.cert = secure_opts.cert.toString();
212
+ } else {
213
+ https_options.key = https_options.cert = (await import('./cert.js')).createCertificate();
214
+ }
215
+ }
174
216
 
175
- console.log($.bold().cyan(`\n> Using ${name}`));
217
+ return use_https
218
+ ? https.createServer(/** @type {https.ServerOptions} */ (https_options), handler)
219
+ : http.createServer(handler);
220
+ }
221
+
222
+ /** @param {Handler[]} handlers */
223
+ function compose(handlers) {
224
+ /**
225
+ * @param {Req} req
226
+ * @param {Res} res
227
+ */
228
+ return (req, res) => {
229
+ /** @param {number} i */
230
+ function next(i) {
231
+ const handler = handlers[i];
232
+
233
+ if (handler) {
234
+ handler(req, res, () => next(i + 1));
235
+ } else {
236
+ res.statusCode = 404;
237
+ res.end('Not found');
238
+ }
239
+ }
176
240
 
177
- const builder = create_builder({ config, build_data, prerendered, log });
178
- await adapt(builder);
241
+ next(0);
242
+ };
243
+ }
179
244
 
180
- log.success('done');
245
+ /**
246
+ * @param {string} scope
247
+ * @param {Handler} handler
248
+ * @returns {Handler}
249
+ */
250
+ function scoped(scope, handler) {
251
+ if (scope === '') return handler;
252
+
253
+ return (req, res, next) => {
254
+ if (req.url?.startsWith(scope)) {
255
+ const original_url = req.url;
256
+ req.url = req.url.slice(scope.length);
257
+ handler(req, res, () => {
258
+ req.url = original_url;
259
+ next();
260
+ });
261
+ } else {
262
+ next();
263
+ }
264
+ };
181
265
  }
182
266
 
183
- export { adapt };
267
+ export { preview };