@sveltejs/kit 1.0.0-next.305 → 1.0.0-next.306
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/assets/client/start.js +35 -34
- package/assets/server/index.js +4 -2
- package/dist/chunks/index.js +12 -5
- package/dist/chunks/index2.js +1 -0
- package/dist/chunks/index3.js +1 -1
- package/dist/chunks/misc.js +33 -32
- package/dist/chunks/sync.js +320 -254
- package/dist/cli.js +10 -7
- package/package.json +2 -2
- package/types/internal.d.ts +4 -6
package/assets/client/start.js
CHANGED
|
@@ -269,7 +269,7 @@ function parse_route_id(id) {
|
|
|
269
269
|
? /^\/$/
|
|
270
270
|
: new RegExp(
|
|
271
271
|
`^${decodeURIComponent(id)
|
|
272
|
-
.split(
|
|
272
|
+
.split(/(?:@(?:~|[a-zA-Z0-9_-]*))?(?:\/|$)/)
|
|
273
273
|
.map((segment, i, segments) => {
|
|
274
274
|
// special case — /[...rest]/ could contain zero segments
|
|
275
275
|
const match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
|
|
@@ -282,40 +282,41 @@ function parse_route_id(id) {
|
|
|
282
282
|
const is_last = i === segments.length - 1;
|
|
283
283
|
|
|
284
284
|
return (
|
|
285
|
+
segment &&
|
|
285
286
|
'/' +
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
287
|
+
segment
|
|
288
|
+
.split(/\[(.+?)\]/)
|
|
289
|
+
.map((content, i) => {
|
|
290
|
+
if (i % 2) {
|
|
291
|
+
const [, rest, name, type] = /** @type {RegExpMatchArray} */ (
|
|
292
|
+
param_pattern.exec(content)
|
|
293
|
+
);
|
|
294
|
+
names.push(name);
|
|
295
|
+
types.push(type);
|
|
296
|
+
return rest ? '(.*?)' : '([^/]+?)';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (is_last && content.includes('.')) add_trailing_slash = false;
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
content // allow users to specify characters on the file system in an encoded manner
|
|
303
|
+
.normalize()
|
|
304
|
+
// We use [ and ] to denote parameters, so users must encode these on the file
|
|
305
|
+
// system to match against them. We don't decode all characters since others
|
|
306
|
+
// can already be epressed and so that '%' can be easily used directly in filenames
|
|
307
|
+
.replace(/%5[Bb]/g, '[')
|
|
308
|
+
.replace(/%5[Dd]/g, ']')
|
|
309
|
+
// '#', '/', and '?' can only appear in URL path segments in an encoded manner.
|
|
310
|
+
// They will not be touched by decodeURI so need to be encoded here, so
|
|
311
|
+
// that we can match against them.
|
|
312
|
+
// We skip '/' since you can't create a file with it on any OS
|
|
313
|
+
.replace(/#/g, '%23')
|
|
314
|
+
.replace(/\?/g, '%3F')
|
|
315
|
+
// escape characters that have special meaning in regex
|
|
316
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
317
|
+
); // TODO handle encoding
|
|
318
|
+
})
|
|
319
|
+
.join('')
|
|
319
320
|
);
|
|
320
321
|
})
|
|
321
322
|
.join('')}${add_trailing_slash ? '/?' : ''}$`
|
package/assets/server/index.js
CHANGED
|
@@ -2181,7 +2181,8 @@ async function respond$1(opts) {
|
|
|
2181
2181
|
|
|
2182
2182
|
try {
|
|
2183
2183
|
nodes = await Promise.all(
|
|
2184
|
-
|
|
2184
|
+
// we use == here rather than === because [undefined] serializes as "[null]"
|
|
2185
|
+
route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]()))
|
|
2185
2186
|
);
|
|
2186
2187
|
} catch (err) {
|
|
2187
2188
|
const error = coalesce_to_error(err);
|
|
@@ -2280,7 +2281,8 @@ async function respond$1(opts) {
|
|
|
2280
2281
|
if (error) {
|
|
2281
2282
|
while (i--) {
|
|
2282
2283
|
if (route.b[i]) {
|
|
2283
|
-
const
|
|
2284
|
+
const index = /** @type {number} */ (route.b[i]);
|
|
2285
|
+
const error_node = await options.manifest._.nodes[index]();
|
|
2284
2286
|
|
|
2285
2287
|
/** @type {Loaded} */
|
|
2286
2288
|
let node_loaded;
|
package/dist/chunks/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { getRequest, setResponse } from '../node.js';
|
|
|
12
12
|
import { sequence } from '../hooks.js';
|
|
13
13
|
import { p as posixify } from './filesystem.js';
|
|
14
14
|
import { p as parse_route_id } from './misc.js';
|
|
15
|
+
import { n as normalize_path } from './url.js';
|
|
15
16
|
import 'sade';
|
|
16
17
|
import 'child_process';
|
|
17
18
|
import 'net';
|
|
@@ -131,8 +132,8 @@ async function create_plugin(config, cwd) {
|
|
|
131
132
|
return await vite.ssrLoadModule(url, { fixStacktrace: false });
|
|
132
133
|
}
|
|
133
134
|
: null,
|
|
134
|
-
a: route.a.map((id) => manifest_data.components.indexOf(id)),
|
|
135
|
-
b: route.b.map((id) => manifest_data.components.indexOf(id))
|
|
135
|
+
a: route.a.map((id) => (id ? manifest_data.components.indexOf(id) : undefined)),
|
|
136
|
+
b: route.b.map((id) => (id ? manifest_data.components.indexOf(id) : undefined))
|
|
136
137
|
};
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -214,7 +215,13 @@ async function create_plugin(config, cwd) {
|
|
|
214
215
|
|
|
215
216
|
if (req.url === '/favicon.ico') return not_found(res);
|
|
216
217
|
|
|
217
|
-
if (!decoded.startsWith(config.kit.paths.base))
|
|
218
|
+
if (!decoded.startsWith(config.kit.paths.base)) {
|
|
219
|
+
const suggestion = normalize_path(
|
|
220
|
+
config.kit.paths.base + req.url,
|
|
221
|
+
config.kit.trailingSlash
|
|
222
|
+
);
|
|
223
|
+
return not_found(res, `Not found (did you mean ${suggestion}?)`);
|
|
224
|
+
}
|
|
218
225
|
|
|
219
226
|
/** @type {Partial<import('types').Hooks>} */
|
|
220
227
|
const user_hooks = resolve_entry(config.kit.files.hooks)
|
|
@@ -370,9 +377,9 @@ async function create_plugin(config, cwd) {
|
|
|
370
377
|
}
|
|
371
378
|
|
|
372
379
|
/** @param {import('http').ServerResponse} res */
|
|
373
|
-
function not_found(res) {
|
|
380
|
+
function not_found(res, message = 'Not found') {
|
|
374
381
|
res.statusCode = 404;
|
|
375
|
-
res.end(
|
|
382
|
+
res.end(message);
|
|
376
383
|
}
|
|
377
384
|
|
|
378
385
|
/**
|
package/dist/chunks/index2.js
CHANGED
package/dist/chunks/index3.js
CHANGED
|
@@ -54,7 +54,7 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
|
|
|
54
54
|
assets.push(build_data.service_worker);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/** @param {string} id */
|
|
57
|
+
/** @param {string | undefined} id */
|
|
58
58
|
const get_index = (id) => id && /** @type {LookupEntry} */ (bundled_nodes.get(id)).index;
|
|
59
59
|
|
|
60
60
|
const matchers = new Set();
|
package/dist/chunks/misc.js
CHANGED
|
@@ -17,7 +17,7 @@ function parse_route_id(id) {
|
|
|
17
17
|
? /^\/$/
|
|
18
18
|
: new RegExp(
|
|
19
19
|
`^${decodeURIComponent(id)
|
|
20
|
-
.split(
|
|
20
|
+
.split(/(?:@(?:~|[a-zA-Z0-9_-]*))?(?:\/|$)/)
|
|
21
21
|
.map((segment, i, segments) => {
|
|
22
22
|
// special case — /[...rest]/ could contain zero segments
|
|
23
23
|
const match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
|
|
@@ -30,40 +30,41 @@ function parse_route_id(id) {
|
|
|
30
30
|
const is_last = i === segments.length - 1;
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
|
+
segment &&
|
|
33
34
|
'/' +
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
segment
|
|
36
|
+
.split(/\[(.+?)\]/)
|
|
37
|
+
.map((content, i) => {
|
|
38
|
+
if (i % 2) {
|
|
39
|
+
const [, rest, name, type] = /** @type {RegExpMatchArray} */ (
|
|
40
|
+
param_pattern.exec(content)
|
|
41
|
+
);
|
|
42
|
+
names.push(name);
|
|
43
|
+
types.push(type);
|
|
44
|
+
return rest ? '(.*?)' : '([^/]+?)';
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
if (is_last && content.includes('.')) add_trailing_slash = false;
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
49
|
+
return (
|
|
50
|
+
content // allow users to specify characters on the file system in an encoded manner
|
|
51
|
+
.normalize()
|
|
52
|
+
// We use [ and ] to denote parameters, so users must encode these on the file
|
|
53
|
+
// system to match against them. We don't decode all characters since others
|
|
54
|
+
// can already be epressed and so that '%' can be easily used directly in filenames
|
|
55
|
+
.replace(/%5[Bb]/g, '[')
|
|
56
|
+
.replace(/%5[Dd]/g, ']')
|
|
57
|
+
// '#', '/', and '?' can only appear in URL path segments in an encoded manner.
|
|
58
|
+
// They will not be touched by decodeURI so need to be encoded here, so
|
|
59
|
+
// that we can match against them.
|
|
60
|
+
// We skip '/' since you can't create a file with it on any OS
|
|
61
|
+
.replace(/#/g, '%23')
|
|
62
|
+
.replace(/\?/g, '%3F')
|
|
63
|
+
// escape characters that have special meaning in regex
|
|
64
|
+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
65
|
+
); // TODO handle encoding
|
|
66
|
+
})
|
|
67
|
+
.join('')
|
|
67
68
|
);
|
|
68
69
|
})
|
|
69
70
|
.join('')}${add_trailing_slash ? '/?' : ''}$`
|
package/dist/chunks/sync.js
CHANGED
|
@@ -121,18 +121,39 @@ var mime = new Mime(standard, other);
|
|
|
121
121
|
* rest: boolean;
|
|
122
122
|
* type: string | null;
|
|
123
123
|
* }} Part
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A route, consisting of an endpoint module and/or an array of components
|
|
128
|
+
* (n layouts and one leaf) for successful navigations and an array of
|
|
129
|
+
* n error components to render if navigation fails
|
|
130
|
+
* @typedef {{
|
|
131
|
+
* id: string;
|
|
132
|
+
* pattern: RegExp;
|
|
133
|
+
* segments: Part[][];
|
|
134
|
+
* page?: {
|
|
135
|
+
* a: Array<string | undefined>;
|
|
136
|
+
* b: Array<string | undefined>;
|
|
137
|
+
* };
|
|
138
|
+
* endpoint?: string;
|
|
139
|
+
* }} Unit
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
124
143
|
* @typedef {{
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*
|
|
132
|
-
* }} Item
|
|
144
|
+
* error: string | undefined;
|
|
145
|
+
* layouts: Record<string, { file: string, name: string }>
|
|
146
|
+
* }} Node
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @typedef {Map<string, Node>} Tree
|
|
133
151
|
*/
|
|
134
152
|
|
|
135
|
-
const
|
|
153
|
+
const layout_pattern = /^__layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?$/;
|
|
154
|
+
const dunder_pattern = /(^|\/)__(?!tests?__)/; // forbid __-prefixed files/directories except __error, __layout[-...], __test__, __tests__
|
|
155
|
+
|
|
156
|
+
const DEFAULT = 'default';
|
|
136
157
|
|
|
137
158
|
/**
|
|
138
159
|
* @param {{
|
|
@@ -147,172 +168,199 @@ function create_manifest_data({
|
|
|
147
168
|
fallback = `${get_runtime_path(config)}/components`,
|
|
148
169
|
cwd = process.cwd()
|
|
149
170
|
}) {
|
|
150
|
-
/**
|
|
151
|
-
* @param {string} file_name
|
|
152
|
-
* @param {string} dir
|
|
153
|
-
*/
|
|
154
|
-
function find_layout(file_name, dir) {
|
|
155
|
-
const files = config.extensions.map((ext) => posixify(path__default.join(dir, `${file_name}${ext}`)));
|
|
156
|
-
return files.find((file) => fs__default.existsSync(path__default.resolve(cwd, file)));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** @type {string[]} */
|
|
160
|
-
const components = [];
|
|
161
|
-
|
|
162
171
|
/** @type {import('types').RouteData[]} */
|
|
163
172
|
const routes = [];
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
const
|
|
174
|
+
/** @type {Map<string, Unit>} */
|
|
175
|
+
const units = new Map();
|
|
167
176
|
|
|
168
|
-
/**
|
|
169
|
-
|
|
170
|
-
* @param {string[]} parent_id
|
|
171
|
-
* @param {Array<string|undefined>} layout_stack // accumulated __layout.svelte components
|
|
172
|
-
* @param {Array<string|undefined>} error_stack // accumulated __error.svelte components
|
|
173
|
-
*/
|
|
174
|
-
function walk(dir, parent_id, layout_stack, error_stack) {
|
|
175
|
-
/** @type {Item[]} */
|
|
176
|
-
const items = [];
|
|
177
|
+
/** @type {Tree} */
|
|
178
|
+
const tree = new Map();
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const ext = is_dir
|
|
184
|
-
? ''
|
|
185
|
-
: config.extensions.find((ext) => basename.endsWith(ext)) ||
|
|
186
|
-
config.kit.endpointExtensions.find((ext) => basename.endsWith(ext));
|
|
180
|
+
const default_layout = {
|
|
181
|
+
file: posixify(path__default.relative(cwd, `${fallback}/layout.svelte`)),
|
|
182
|
+
name: DEFAULT
|
|
183
|
+
};
|
|
187
184
|
|
|
188
|
-
|
|
185
|
+
// set default root layout/error
|
|
186
|
+
tree.set('', {
|
|
187
|
+
error: posixify(path__default.relative(cwd, `${fallback}/error.svelte`)),
|
|
188
|
+
layouts: { [DEFAULT]: default_layout }
|
|
189
|
+
});
|
|
189
190
|
|
|
190
|
-
|
|
191
|
+
const routes_base = posixify(path__default.relative(cwd, config.kit.files.routes));
|
|
192
|
+
const valid_extensions = [...config.extensions, ...config.kit.endpointExtensions];
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
list_files(config.kit.files.routes).forEach((file) => {
|
|
195
|
+
const extension = valid_extensions.find((ext) => file.endsWith(ext));
|
|
196
|
+
if (!extension) return;
|
|
195
197
|
|
|
196
|
-
|
|
198
|
+
const id = file.slice(0, -extension.length).replace(/\/?index(\.[a-z]+)?$/, '$1');
|
|
199
|
+
const project_relative = `${routes_base}/${file}`;
|
|
197
200
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
name,
|
|
201
|
-
parts: get_parts(name, file),
|
|
202
|
-
route_suffix: basename.slice(basename.indexOf('.'), -ext.length),
|
|
203
|
-
is_dir,
|
|
204
|
-
is_index: !is_dir && basename.startsWith('index.'),
|
|
205
|
-
is_page: config.extensions.includes(ext)
|
|
206
|
-
});
|
|
207
|
-
});
|
|
201
|
+
const segments = id.split('/');
|
|
202
|
+
const name = /** @type {string} */ (segments.pop());
|
|
208
203
|
|
|
209
|
-
|
|
204
|
+
if (name === '__layout.reset') {
|
|
205
|
+
throw new Error(
|
|
206
|
+
'__layout.reset has been removed in favour of named layouts: https://kit.svelte.dev/docs/layouts#named-layouts'
|
|
207
|
+
);
|
|
208
|
+
}
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
const
|
|
210
|
+
if (name === '__error' || layout_pattern.test(name)) {
|
|
211
|
+
const dir = segments.join('/');
|
|
213
212
|
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
id_parts.push(item.name);
|
|
213
|
+
if (!tree.has(dir)) {
|
|
214
|
+
tree.set(dir, {
|
|
215
|
+
error: undefined,
|
|
216
|
+
layouts: {}
|
|
217
|
+
});
|
|
220
218
|
}
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
const group = /** @type {Node} */ (tree.get(dir));
|
|
221
|
+
|
|
222
|
+
if (name === '__error') {
|
|
223
|
+
group.error = project_relative;
|
|
224
|
+
} else {
|
|
225
|
+
const match = /** @type {RegExpMatchArray} */ (layout_pattern.exec(name));
|
|
226
226
|
|
|
227
|
-
if (
|
|
228
|
-
throw new Error(
|
|
227
|
+
if (match[1] === DEFAULT) {
|
|
228
|
+
throw new Error(`${project_relative} cannot use reserved "${DEFAULT}" name`);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
if (layout) components.push(layout);
|
|
233
|
-
if (error) components.push(error);
|
|
231
|
+
const layout_id = match[1] || DEFAULT;
|
|
234
232
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
} else {
|
|
242
|
-
const id = id_parts.join('/');
|
|
243
|
-
const { pattern } = parse_route_id(id);
|
|
233
|
+
const defined = group.layouts[layout_id];
|
|
234
|
+
if (defined && defined !== default_layout) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Duplicate layout ${project_relative} already defined at ${defined.file}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
244
239
|
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
group.layouts[layout_id] = {
|
|
241
|
+
file: project_relative,
|
|
242
|
+
name
|
|
243
|
+
};
|
|
244
|
+
}
|
|
247
245
|
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
return;
|
|
247
|
+
} else if (dunder_pattern.test(file)) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`Files and directories prefixed with __ are reserved (saw ${project_relative})`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
while (i--) {
|
|
253
|
-
if (!errors[i] && !concatenated[i]) {
|
|
254
|
-
errors.splice(i, 1);
|
|
255
|
-
concatenated.splice(i, 1);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
253
|
+
if (!config.kit.routes(file)) return;
|
|
258
254
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
255
|
+
if (/\]\[/.test(id)) {
|
|
256
|
+
throw new Error(`Invalid route ${project_relative} — parameters must be separated`);
|
|
257
|
+
}
|
|
263
258
|
|
|
264
|
-
|
|
259
|
+
if (count_occurrences('[', id) !== count_occurrences(']', id)) {
|
|
260
|
+
throw new Error(`Invalid route ${project_relative} — brackets are unbalanced`);
|
|
261
|
+
}
|
|
265
262
|
|
|
266
|
-
|
|
263
|
+
if (!units.has(id)) {
|
|
264
|
+
units.set(id, {
|
|
265
|
+
id,
|
|
266
|
+
pattern: parse_route_id(id).pattern,
|
|
267
|
+
segments: id
|
|
268
|
+
.split('/')
|
|
269
|
+
.filter(Boolean)
|
|
270
|
+
.map((segment) => {
|
|
271
|
+
/** @type {Part[]} */
|
|
272
|
+
const parts = [];
|
|
273
|
+
segment.split(/\[(.+?)\]/).map((content, i) => {
|
|
274
|
+
const dynamic = !!(i % 2);
|
|
275
|
+
|
|
276
|
+
if (!content) return;
|
|
277
|
+
|
|
278
|
+
parts.push({
|
|
279
|
+
content,
|
|
280
|
+
dynamic,
|
|
281
|
+
rest: dynamic && content.startsWith('...'),
|
|
282
|
+
type: (dynamic && content.split('=')[1]) || null
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
return parts;
|
|
286
|
+
}),
|
|
287
|
+
page: undefined,
|
|
288
|
+
endpoint: undefined
|
|
289
|
+
});
|
|
290
|
+
}
|
|
267
291
|
|
|
268
|
-
|
|
269
|
-
type: 'page',
|
|
270
|
-
id,
|
|
271
|
-
pattern,
|
|
272
|
-
path,
|
|
273
|
-
shadow: null,
|
|
274
|
-
a: /** @type {string[]} */ (concatenated),
|
|
275
|
-
b: /** @type {string[]} */ (errors)
|
|
276
|
-
});
|
|
277
|
-
} else {
|
|
278
|
-
routes.push({
|
|
279
|
-
type: 'endpoint',
|
|
280
|
-
id,
|
|
281
|
-
pattern,
|
|
282
|
-
file: item.file
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
}
|
|
292
|
+
const unit = /** @type {Unit} */ (units.get(id));
|
|
288
293
|
|
|
289
|
-
|
|
294
|
+
if (config.extensions.find((ext) => file.endsWith(ext))) {
|
|
295
|
+
const { layouts, errors } = trace(project_relative, file, tree, config.extensions);
|
|
296
|
+
unit.page = {
|
|
297
|
+
a: layouts.concat(project_relative),
|
|
298
|
+
b: errors
|
|
299
|
+
};
|
|
300
|
+
} else {
|
|
301
|
+
unit.endpoint = project_relative;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
290
304
|
|
|
291
|
-
|
|
292
|
-
const
|
|
305
|
+
/** @type {string[]} */
|
|
306
|
+
const components = [];
|
|
293
307
|
|
|
294
|
-
|
|
308
|
+
tree.forEach(({ layouts, error }) => {
|
|
309
|
+
// we do [default, error, ...other_layouts] so that components[0] and [1]
|
|
310
|
+
// are the root layout/error. kinda janky, there's probably a nicer way
|
|
311
|
+
if (layouts[DEFAULT]) {
|
|
312
|
+
components.push(layouts[DEFAULT].file);
|
|
313
|
+
}
|
|
295
314
|
|
|
296
|
-
|
|
315
|
+
if (error) {
|
|
316
|
+
components.push(error);
|
|
317
|
+
}
|
|
297
318
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (route.type === 'page') {
|
|
301
|
-
lookup.set(route.id, route);
|
|
319
|
+
for (const id in layouts) {
|
|
320
|
+
if (id !== DEFAULT) components.push(layouts[id].file);
|
|
302
321
|
}
|
|
303
|
-
}
|
|
322
|
+
});
|
|
304
323
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
lookup.get(route.id).shadow = route.file;
|
|
310
|
-
routes.splice(i, 1);
|
|
324
|
+
units.forEach((unit) => {
|
|
325
|
+
if (unit.page) {
|
|
326
|
+
const leaf = /** @type {string} */ (unit.page.a[unit.page.a.length - 1]);
|
|
327
|
+
components.push(leaf);
|
|
311
328
|
}
|
|
312
|
-
}
|
|
329
|
+
});
|
|
313
330
|
|
|
331
|
+
Array.from(units.values())
|
|
332
|
+
.sort(compare)
|
|
333
|
+
.forEach((unit) => {
|
|
334
|
+
// TODO when we introduce layout endpoints and scoped middlewares, we
|
|
335
|
+
// will probably want to have a single unified route type here
|
|
336
|
+
// (created in the list_files(...).forEach(...) callback)
|
|
337
|
+
if (unit.page) {
|
|
338
|
+
routes.push({
|
|
339
|
+
type: 'page',
|
|
340
|
+
id: unit.id,
|
|
341
|
+
pattern: unit.pattern,
|
|
342
|
+
path: unit.id.includes('[') ? '' : `/${unit.id.replace(/@(?:[a-zA-Z0-9_-]+)/g, '')}`,
|
|
343
|
+
shadow: unit.endpoint || null,
|
|
344
|
+
a: unit.page.a,
|
|
345
|
+
b: unit.page.b
|
|
346
|
+
});
|
|
347
|
+
} else if (unit.endpoint) {
|
|
348
|
+
routes.push({
|
|
349
|
+
type: 'endpoint',
|
|
350
|
+
id: unit.id,
|
|
351
|
+
pattern: unit.pattern,
|
|
352
|
+
file: unit.endpoint
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
/** @type {import('types').Asset[]} */
|
|
314
358
|
const assets = fs__default.existsSync(config.kit.files.assets)
|
|
315
|
-
? list_files(
|
|
359
|
+
? list_files(config.kit.files.assets).map((file) => ({
|
|
360
|
+
file,
|
|
361
|
+
size: fs__default.statSync(`${config.kit.files.assets}/${file}`).size,
|
|
362
|
+
type: mime.getType(file)
|
|
363
|
+
}))
|
|
316
364
|
: [];
|
|
317
365
|
|
|
318
366
|
const params_base = path__default.relative(cwd, config.kit.files.params);
|
|
@@ -336,8 +384,6 @@ function create_manifest_data({
|
|
|
336
384
|
|
|
337
385
|
return {
|
|
338
386
|
assets,
|
|
339
|
-
layout,
|
|
340
|
-
error,
|
|
341
387
|
components,
|
|
342
388
|
routes,
|
|
343
389
|
matchers
|
|
@@ -345,142 +391,162 @@ function create_manifest_data({
|
|
|
345
391
|
}
|
|
346
392
|
|
|
347
393
|
/**
|
|
348
|
-
* @param {string}
|
|
349
|
-
* @param {string}
|
|
394
|
+
* @param {string} file
|
|
395
|
+
* @param {string} path
|
|
396
|
+
* @param {Tree} tree
|
|
397
|
+
* @param {string[]} extensions
|
|
350
398
|
*/
|
|
351
|
-
function
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
399
|
+
function trace(file, path, tree, extensions) {
|
|
400
|
+
/** @type {Array<string | undefined>} */
|
|
401
|
+
const layouts = [];
|
|
402
|
+
|
|
403
|
+
/** @type {Array<string | undefined>} */
|
|
404
|
+
const errors = [];
|
|
405
|
+
|
|
406
|
+
const parts = path.split('/');
|
|
407
|
+
const filename = /** @type {string} */ (parts.pop());
|
|
408
|
+
const extension = /** @type {string} */ (extensions.find((ext) => path.endsWith(ext)));
|
|
409
|
+
const base = filename.slice(0, -extension.length);
|
|
410
|
+
|
|
411
|
+
let layout_id = base.includes('@') ? base.split('@')[1] : DEFAULT;
|
|
412
|
+
|
|
413
|
+
// walk up the tree, find which __layout and __error components
|
|
414
|
+
// apply to this page
|
|
415
|
+
// eslint-disable-next-line
|
|
416
|
+
while (true) {
|
|
417
|
+
const node = tree.get(parts.join('/'));
|
|
418
|
+
const layout = node?.layouts[layout_id];
|
|
419
|
+
|
|
420
|
+
// any segment that has neither a __layout nor an __error can be discarded.
|
|
421
|
+
// in other words these...
|
|
422
|
+
// layouts: [a, , b, c]
|
|
423
|
+
// errors: [d, , e, ]
|
|
424
|
+
//
|
|
425
|
+
// ...can be compacted to these:
|
|
426
|
+
// layouts: [a, b, c]
|
|
427
|
+
// errors: [d, e, ]
|
|
428
|
+
if (node?.error || layout?.file) {
|
|
429
|
+
errors.unshift(node?.error);
|
|
430
|
+
layouts.unshift(layout?.file);
|
|
431
|
+
}
|
|
360
432
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return spread_pattern.test(b.file) ? -1 : 1;
|
|
433
|
+
if (layout?.name.includes('@')) {
|
|
434
|
+
layout_id = layout.name.split('@')[1];
|
|
435
|
+
} else {
|
|
436
|
+
if (layout) layout_id = DEFAULT;
|
|
437
|
+
if (parts.length === 0) break;
|
|
438
|
+
parts.pop();
|
|
439
|
+
}
|
|
369
440
|
}
|
|
370
441
|
|
|
371
|
-
|
|
442
|
+
if (layout_id !== DEFAULT) {
|
|
443
|
+
throw new Error(`${file} references missing layout "${layout_id}"`);
|
|
444
|
+
}
|
|
372
445
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
446
|
+
// trim empty space off the end of the errors array
|
|
447
|
+
let i = errors.length;
|
|
448
|
+
while (i--) if (errors[i]) break;
|
|
449
|
+
errors.length = i + 1;
|
|
376
450
|
|
|
377
|
-
|
|
378
|
-
|
|
451
|
+
return { layouts, errors };
|
|
452
|
+
}
|
|
379
453
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
454
|
+
/**
|
|
455
|
+
* @param {Unit} a
|
|
456
|
+
* @param {Unit} b
|
|
457
|
+
*/
|
|
458
|
+
function compare(a, b) {
|
|
459
|
+
const max_segments = Math.max(a.segments.length, b.segments.length);
|
|
460
|
+
for (let i = 0; i < max_segments; i += 1) {
|
|
461
|
+
const sa = a.segments[i];
|
|
462
|
+
const sb = b.segments[i];
|
|
463
|
+
|
|
464
|
+
// /x < /x/y, but /[...x]/y < /[...x]
|
|
465
|
+
if (!sa) return a.id.includes('[...') ? +1 : -1;
|
|
466
|
+
if (!sb) return b.id.includes('[...') ? -1 : +1;
|
|
467
|
+
|
|
468
|
+
const max_parts = Math.max(sa.length, sb.length);
|
|
469
|
+
for (let i = 0; i < max_parts; i += 1) {
|
|
470
|
+
const pa = sa[i];
|
|
471
|
+
const pb = sb[i];
|
|
472
|
+
|
|
473
|
+
// xy < x[y], but [x].json < [x]
|
|
474
|
+
if (pa === undefined) return pb.dynamic ? -1 : +1;
|
|
475
|
+
if (pb === undefined) return pa.dynamic ? +1 : -1;
|
|
476
|
+
|
|
477
|
+
// x < [x]
|
|
478
|
+
if (pa.dynamic !== pb.dynamic) {
|
|
479
|
+
return pa.dynamic ? +1 : -1;
|
|
383
480
|
}
|
|
384
|
-
// sort alphabetically
|
|
385
|
-
return a_sub_part.content < b_sub_part.content ? -1 : 1;
|
|
386
|
-
}
|
|
387
481
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (a_sub_part.dynamic && !!a_sub_part.type !== !!b_sub_part.type) {
|
|
396
|
-
return a_sub_part.type ? -1 : 1;
|
|
397
|
-
}
|
|
482
|
+
if (pa.dynamic) {
|
|
483
|
+
// [x] < [...x]
|
|
484
|
+
if (pa.rest !== pb.rest) {
|
|
485
|
+
return pa.rest ? +1 : -1;
|
|
486
|
+
}
|
|
398
487
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
488
|
+
// [x=type] < [x]
|
|
489
|
+
if (!!pa.type !== !!pb.type) {
|
|
490
|
+
return pa.type ? -1 : +1;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
404
493
|
}
|
|
405
494
|
}
|
|
406
495
|
|
|
407
|
-
|
|
408
|
-
|
|
496
|
+
const a_is_endpoint = !a.page && a.endpoint;
|
|
497
|
+
const b_is_endpoint = !b.page && b.endpoint;
|
|
498
|
+
|
|
499
|
+
if (a_is_endpoint !== b_is_endpoint) {
|
|
500
|
+
return a_is_endpoint ? -1 : +1;
|
|
409
501
|
}
|
|
410
502
|
|
|
411
|
-
|
|
412
|
-
return a.file < b.file ? -1 : 1;
|
|
503
|
+
return a < b ? -1 : 1;
|
|
413
504
|
}
|
|
414
505
|
|
|
415
506
|
/**
|
|
416
|
-
* @param {string}
|
|
417
|
-
* @param {string}
|
|
507
|
+
* @param {string} needle
|
|
508
|
+
* @param {string} haystack
|
|
418
509
|
*/
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (count_occurrences('[', part) !== count_occurrences(']', part)) {
|
|
425
|
-
throw new Error(`Invalid route ${file} — brackets are unbalanced`);
|
|
510
|
+
function count_occurrences(needle, haystack) {
|
|
511
|
+
let count = 0;
|
|
512
|
+
for (let i = 0; i < haystack.length; i += 1) {
|
|
513
|
+
if (haystack[i] === needle) count += 1;
|
|
426
514
|
}
|
|
427
|
-
|
|
428
|
-
/** @type {Part[]} */
|
|
429
|
-
const result = [];
|
|
430
|
-
part.split(/\[(.+?\(.+?\)|.+?)\]/).map((str, i) => {
|
|
431
|
-
if (!str) return;
|
|
432
|
-
const dynamic = i % 2 === 1;
|
|
433
|
-
|
|
434
|
-
const [, content, type] = dynamic
|
|
435
|
-
? /^((?:\.\.\.)?[a-zA-Z_][a-zA-Z0-9_]*)(?:=([a-zA-Z_][a-zA-Z0-9_]*))?$/.exec(str) || [
|
|
436
|
-
null,
|
|
437
|
-
null,
|
|
438
|
-
null
|
|
439
|
-
]
|
|
440
|
-
: [null, str, null];
|
|
441
|
-
|
|
442
|
-
if (!content) {
|
|
443
|
-
throw new Error(
|
|
444
|
-
`Invalid route ${file} — parameter name and type must match /^[a-zA-Z_][a-zA-Z0-9_]*$/`
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
result.push({
|
|
449
|
-
content,
|
|
450
|
-
dynamic,
|
|
451
|
-
rest: dynamic && /^\.{3}.+$/.test(content),
|
|
452
|
-
type
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
return result;
|
|
515
|
+
return count;
|
|
457
516
|
}
|
|
458
517
|
|
|
459
518
|
/**
|
|
460
|
-
* @param {
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
* path: string;
|
|
464
|
-
* files?: import('types').Asset[]
|
|
465
|
-
* }} args
|
|
519
|
+
* @param {string} dir
|
|
520
|
+
* @param {string} [path]
|
|
521
|
+
* @param {string[]} [files]
|
|
466
522
|
*/
|
|
467
|
-
function list_files(
|
|
468
|
-
fs__default.readdirSync(dir
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
|
|
523
|
+
function list_files(dir, path = '', files = []) {
|
|
524
|
+
fs__default.readdirSync(dir, { withFileTypes: true })
|
|
525
|
+
.sort(({ name: a }, { name: b }) => {
|
|
526
|
+
// sort each directory in (__layout, __error, everything else) order
|
|
527
|
+
// so that we can trace layouts/errors immediately
|
|
528
|
+
|
|
529
|
+
if (a.startsWith('__layout')) {
|
|
530
|
+
if (!b.startsWith('__layout')) return -1;
|
|
531
|
+
} else if (b.startsWith('__layout')) {
|
|
532
|
+
return 1;
|
|
533
|
+
} else if (a.startsWith('__')) {
|
|
534
|
+
if (!b.startsWith('__')) return -1;
|
|
535
|
+
} else if (b.startsWith('__')) {
|
|
536
|
+
return 1;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return a < b ? -1 : 1;
|
|
540
|
+
})
|
|
541
|
+
.forEach((file) => {
|
|
542
|
+
const joined = path ? `${path}/${file.name}` : file.name;
|
|
543
|
+
|
|
544
|
+
if (file.isDirectory()) {
|
|
545
|
+
list_files(`${dir}/${file.name}`, joined, files);
|
|
546
|
+
} else {
|
|
547
|
+
files.push(joined);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
484
550
|
|
|
485
551
|
return files;
|
|
486
552
|
}
|
|
@@ -549,7 +615,7 @@ function write_manifest(manifest_data, base, output) {
|
|
|
549
615
|
.join(',\n\t\t\t\t\t')}
|
|
550
616
|
]`.replace(/^\t/gm, '');
|
|
551
617
|
|
|
552
|
-
/** @param {string
|
|
618
|
+
/** @param {Array<string | undefined>} parts */
|
|
553
619
|
const get_indices = (parts) =>
|
|
554
620
|
`[${parts.map((part) => (part ? component_indexes[part] : '')).join(', ')}]`;
|
|
555
621
|
|
package/dist/cli.js
CHANGED
|
@@ -853,8 +853,9 @@ function handle_error(e) {
|
|
|
853
853
|
/**
|
|
854
854
|
* @param {number} port
|
|
855
855
|
* @param {boolean} https
|
|
856
|
+
* @param {string} base
|
|
856
857
|
*/
|
|
857
|
-
async function launch(port, https) {
|
|
858
|
+
async function launch(port, https, base) {
|
|
858
859
|
const { exec } = await import('child_process');
|
|
859
860
|
let cmd = 'open';
|
|
860
861
|
if (process.platform == 'win32') {
|
|
@@ -866,10 +867,10 @@ async function launch(port, https) {
|
|
|
866
867
|
cmd = 'xdg-open';
|
|
867
868
|
}
|
|
868
869
|
}
|
|
869
|
-
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
|
|
870
|
+
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}${base}`);
|
|
870
871
|
}
|
|
871
872
|
|
|
872
|
-
const prog = sade('svelte-kit').version('1.0.0-next.
|
|
873
|
+
const prog = sade('svelte-kit').version('1.0.0-next.306');
|
|
873
874
|
|
|
874
875
|
prog
|
|
875
876
|
.command('dev')
|
|
@@ -903,6 +904,7 @@ prog
|
|
|
903
904
|
host: address_info.address,
|
|
904
905
|
https: !!(https || server_config.https),
|
|
905
906
|
open: open || !!server_config.open,
|
|
907
|
+
base: config.kit.paths.base,
|
|
906
908
|
loose: server_config.fs.strict === false,
|
|
907
909
|
allow: server_config.fs.allow,
|
|
908
910
|
cwd
|
|
@@ -970,7 +972,7 @@ prog
|
|
|
970
972
|
|
|
971
973
|
await preview({ port, host, config, https });
|
|
972
974
|
|
|
973
|
-
welcome({ port, host, https, open });
|
|
975
|
+
welcome({ port, host, https, open, base: config.kit.paths.base });
|
|
974
976
|
} catch (error) {
|
|
975
977
|
handle_error(error);
|
|
976
978
|
}
|
|
@@ -1039,15 +1041,16 @@ async function check_port(port) {
|
|
|
1039
1041
|
* host: string;
|
|
1040
1042
|
* https: boolean;
|
|
1041
1043
|
* port: number;
|
|
1044
|
+
* base: string;
|
|
1042
1045
|
* loose?: boolean;
|
|
1043
1046
|
* allow?: string[];
|
|
1044
1047
|
* cwd?: string;
|
|
1045
1048
|
* }} param0
|
|
1046
1049
|
*/
|
|
1047
|
-
function welcome({ port, host, https, open, loose, allow, cwd }) {
|
|
1048
|
-
if (open) launch(port, https);
|
|
1050
|
+
function welcome({ port, host, https, open, base, loose, allow, cwd }) {
|
|
1051
|
+
if (open) launch(port, https, base);
|
|
1049
1052
|
|
|
1050
|
-
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.
|
|
1053
|
+
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.306'}\n`));
|
|
1051
1054
|
|
|
1052
1055
|
const protocol = https ? 'https:' : 'http:';
|
|
1053
1056
|
const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.306",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"vite": "^2.9.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@playwright/test": "^1.
|
|
18
|
+
"@playwright/test": "^1.20.2",
|
|
19
19
|
"@rollup/plugin-replace": "^4.0.0",
|
|
20
20
|
"@types/amphtml-validator": "^1.0.1",
|
|
21
21
|
"@types/cookie": "^0.4.1",
|
package/types/internal.d.ts
CHANGED
|
@@ -101,8 +101,6 @@ export class InternalServer extends Server {
|
|
|
101
101
|
|
|
102
102
|
export interface ManifestData {
|
|
103
103
|
assets: Asset[];
|
|
104
|
-
layout: string;
|
|
105
|
-
error: string;
|
|
106
104
|
components: string[];
|
|
107
105
|
routes: RouteData[];
|
|
108
106
|
matchers: Record<string, string>;
|
|
@@ -128,8 +126,8 @@ export interface PageData {
|
|
|
128
126
|
shadow: string | null;
|
|
129
127
|
pattern: RegExp;
|
|
130
128
|
path: string;
|
|
131
|
-
a: string
|
|
132
|
-
b: string
|
|
129
|
+
a: Array<string | undefined>;
|
|
130
|
+
b: Array<string | undefined>;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
export type PayloadScriptAttributes =
|
|
@@ -285,12 +283,12 @@ export interface SSRPage {
|
|
|
285
283
|
/**
|
|
286
284
|
* plan a is to render 1 or more layout components followed by a leaf component.
|
|
287
285
|
*/
|
|
288
|
-
a: number
|
|
286
|
+
a: Array<number | undefined>;
|
|
289
287
|
/**
|
|
290
288
|
* plan b — if one of them components fails in `load` we backtrack until we find
|
|
291
289
|
* the nearest error component.
|
|
292
290
|
*/
|
|
293
|
-
b: number
|
|
291
|
+
b: Array<number | undefined>;
|
|
294
292
|
}
|
|
295
293
|
|
|
296
294
|
export interface SSRPagePart {
|