@sveltejs/kit 1.0.0-next.343 → 1.0.0-next.346

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