@sveltejs/kit 1.0.0-next.430 → 1.0.0-next.433
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/package.json +1 -2
- package/src/core/adapt/builder.js +28 -13
- package/src/core/generate_manifest/index.js +12 -33
- package/src/core/prerender/prerender.js +3 -2
- package/src/core/sync/create_manifest_data/index.js +273 -333
- package/src/core/sync/create_manifest_data/types.d.ts +1 -4
- package/src/core/sync/sync.js +3 -3
- package/src/core/sync/utils.js +0 -11
- package/src/core/sync/write_client_manifest.js +27 -13
- package/src/core/sync/write_root.js +1 -1
- package/src/core/sync/write_types.js +156 -384
- package/src/runtime/client/ambient.d.ts +4 -3
- package/src/runtime/client/client.js +208 -132
- package/src/runtime/client/parse.js +10 -5
- package/src/runtime/client/types.d.ts +10 -29
- package/src/runtime/server/endpoint.js +2 -10
- package/src/runtime/server/index.js +87 -44
- package/src/runtime/server/page/fetch.js +1 -1
- package/src/runtime/server/page/index.js +13 -25
- package/src/runtime/server/page/load_data.js +47 -10
- package/src/utils/array.js +9 -0
- package/src/utils/functions.js +16 -0
- package/src/utils/routing.js +30 -1
- package/src/vite/build/build_server.js +23 -12
- package/src/vite/dev/index.js +22 -30
- package/src/vite/index.js +7 -1
- package/src/vite/preview/index.js +1 -1
- package/types/index.d.ts +1 -1
- package/types/internal.d.ts +103 -41
|
@@ -3,9 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import mime from 'mime';
|
|
4
4
|
import { runtime_directory } from '../../utils.js';
|
|
5
5
|
import { posixify } from '../../../utils/filesystem.js';
|
|
6
|
-
import { parse_route_id } from '../../../utils/routing.js';
|
|
7
|
-
|
|
8
|
-
const DEFAULT = 'default';
|
|
6
|
+
import { parse_route_id, affects_path } from '../../../utils/routing.js';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* @param {{
|
|
@@ -20,261 +18,296 @@ export default function create_manifest_data({
|
|
|
20
18
|
fallback = `${runtime_directory}/components`,
|
|
21
19
|
cwd = process.cwd()
|
|
22
20
|
}) {
|
|
23
|
-
|
|
24
|
-
const
|
|
21
|
+
const assets = create_assets(config);
|
|
22
|
+
const matchers = create_matchers(config, cwd);
|
|
23
|
+
const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return {
|
|
26
|
+
assets,
|
|
27
|
+
matchers,
|
|
28
|
+
nodes,
|
|
29
|
+
routes
|
|
30
|
+
};
|
|
31
|
+
}
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @param {import('types').ValidatedConfig} config
|
|
35
|
+
*/
|
|
36
|
+
function create_assets(config) {
|
|
37
|
+
return list_files(config.kit.files.assets).map((file) => ({
|
|
38
|
+
file,
|
|
39
|
+
size: fs.statSync(path.resolve(config.kit.files.assets, file)).size,
|
|
40
|
+
type: mime.getType(file)
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @param {import('types').ValidatedConfig} config
|
|
46
|
+
* @param {string} cwd
|
|
47
|
+
*/
|
|
48
|
+
function create_matchers(config, cwd) {
|
|
49
|
+
const params_base = path.relative(cwd, config.kit.files.params);
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/** @param {string} id */
|
|
45
|
-
function tree_node(id) {
|
|
46
|
-
if (!tree.has(id)) {
|
|
47
|
-
tree.set(id, {
|
|
48
|
-
error: undefined,
|
|
49
|
-
layouts: {}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
51
|
+
/** @type {Record<string, string>} */
|
|
52
|
+
const matchers = {};
|
|
53
|
+
if (fs.existsSync(config.kit.files.params)) {
|
|
54
|
+
for (const file of fs.readdirSync(config.kit.files.params)) {
|
|
55
|
+
const ext = path.extname(file);
|
|
56
|
+
if (!config.kit.moduleExtensions.includes(ext)) continue;
|
|
57
|
+
const type = file.slice(0, -ext.length);
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
if (/^\w+$/.test(type)) {
|
|
60
|
+
const matcher_file = path.join(params_base, file);
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
// Disallow same matcher with different extensions
|
|
63
|
+
if (matchers[type]) {
|
|
64
|
+
throw new Error(`Duplicate matchers: ${matcher_file} and ${matchers[type]}`);
|
|
65
|
+
} else {
|
|
66
|
+
matchers[type] = matcher_file;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Matcher names can only have underscores and alphanumeric characters — "${file}" is invalid`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const extension = valid_extensions.find((ext) => filepath.endsWith(ext));
|
|
62
|
-
if (!extension) return;
|
|
76
|
+
return matchers;
|
|
77
|
+
}
|
|
63
78
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
/**
|
|
80
|
+
* @param {import('types').ValidatedConfig} config
|
|
81
|
+
* @param {string} cwd
|
|
82
|
+
* @param {string} fallback
|
|
83
|
+
*/
|
|
84
|
+
function create_routes_and_nodes(cwd, config, fallback) {
|
|
85
|
+
const route_map = new Map();
|
|
67
86
|
|
|
68
|
-
|
|
87
|
+
/** @type {Map<string, import('./types').Part[][]>} */
|
|
88
|
+
const segment_map = new Map();
|
|
69
89
|
|
|
70
|
-
|
|
71
|
-
const id = segments.join('/');
|
|
90
|
+
const routes_base = posixify(path.relative(cwd, config.kit.files.routes));
|
|
72
91
|
|
|
73
|
-
|
|
74
|
-
throw new Error(`Invalid route ${project_relative} — parameters must be separated`);
|
|
75
|
-
}
|
|
92
|
+
const valid_extensions = [...config.extensions, ...config.kit.moduleExtensions];
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
94
|
+
/** @type {import('types').PageNode[]} */
|
|
95
|
+
const nodes = [];
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
if (fs.existsSync(config.kit.files.routes)) {
|
|
98
|
+
/**
|
|
99
|
+
* @param {number} depth
|
|
100
|
+
* @param {string} id
|
|
101
|
+
* @param {string} segment
|
|
102
|
+
* @param {import('types').RouteData | null} parent
|
|
103
|
+
*/
|
|
104
|
+
const walk = (depth, id, segment, parent) => {
|
|
105
|
+
const { pattern, names, types } = parse_route_id(id);
|
|
106
|
+
|
|
107
|
+
const segments = id.split('/');
|
|
108
|
+
|
|
109
|
+
segment_map.set(
|
|
110
|
+
id,
|
|
111
|
+
segments.filter(Boolean).map((segment) => {
|
|
112
|
+
/** @type {import('./types').Part[]} */
|
|
113
|
+
const parts = [];
|
|
114
|
+
segment.split(/\[(.+?)\]/).map((content, i) => {
|
|
115
|
+
const dynamic = !!(i % 2);
|
|
116
|
+
|
|
117
|
+
if (!content) return;
|
|
118
|
+
|
|
119
|
+
parts.push({
|
|
120
|
+
content,
|
|
121
|
+
dynamic,
|
|
122
|
+
rest: dynamic && content.startsWith('...'),
|
|
123
|
+
type: (dynamic && content.split('=')[1]) || null
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return parts;
|
|
127
|
+
})
|
|
128
|
+
);
|
|
93
129
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
130
|
+
/** @type {import('types').RouteData} */
|
|
131
|
+
const route = {
|
|
132
|
+
id,
|
|
133
|
+
parent,
|
|
98
134
|
|
|
99
|
-
|
|
135
|
+
segment,
|
|
136
|
+
pattern,
|
|
137
|
+
names,
|
|
138
|
+
types,
|
|
100
139
|
|
|
101
|
-
|
|
140
|
+
layout: null,
|
|
141
|
+
error: null,
|
|
142
|
+
leaf: null,
|
|
143
|
+
page: null,
|
|
144
|
+
endpoint: null
|
|
145
|
+
};
|
|
102
146
|
|
|
103
|
-
|
|
147
|
+
// important to do this before walking children, so that child
|
|
148
|
+
// routes appear later
|
|
149
|
+
route_map.set(id, route);
|
|
104
150
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Duplicate layout ${project_relative} already defined at ${defined[item.kind]}`
|
|
109
|
-
);
|
|
110
|
-
}
|
|
151
|
+
// if we don't do this, the route map becomes unwieldy to console.log
|
|
152
|
+
Object.defineProperty(route, 'parent', { enumerable: false });
|
|
111
153
|
|
|
112
|
-
|
|
154
|
+
const dir = path.join(cwd, routes_base, id);
|
|
113
155
|
|
|
114
|
-
|
|
115
|
-
|
|
156
|
+
const files = fs.readdirSync(dir, {
|
|
157
|
+
withFileTypes: true
|
|
158
|
+
});
|
|
116
159
|
|
|
117
|
-
|
|
160
|
+
// process files first
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
if (file.isDirectory()) continue;
|
|
163
|
+
if (!file.name.startsWith('+')) continue;
|
|
164
|
+
if (!valid_extensions.find((ext) => file.name.endsWith(ext))) continue;
|
|
118
165
|
|
|
119
|
-
|
|
120
|
-
// note that we are relying on +server being lexically ordered after
|
|
121
|
-
// all other route files — if we added +view or something this is
|
|
122
|
-
// potentially brittle, since the server might be added before
|
|
123
|
-
// another route file. a problem for another day
|
|
124
|
-
throw new Error(
|
|
125
|
-
`${file} cannot share a directory with other route files (${project_relative})`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
166
|
+
const project_relative = posixify(path.relative(cwd, path.join(dir, file.name)));
|
|
128
167
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
segments.filter(Boolean).map((segment) => {
|
|
135
|
-
/** @type {import('./types').Part[]} */
|
|
136
|
-
const parts = [];
|
|
137
|
-
segment.split(/\[(.+?)\]/).map((content, i) => {
|
|
138
|
-
const dynamic = !!(i % 2);
|
|
139
|
-
|
|
140
|
-
if (!content) return;
|
|
141
|
-
|
|
142
|
-
parts.push({
|
|
143
|
-
content,
|
|
144
|
-
dynamic,
|
|
145
|
-
rest: dynamic && content.startsWith('...'),
|
|
146
|
-
type: (dynamic && content.split('=')[1]) || null
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
return parts;
|
|
150
|
-
})
|
|
168
|
+
const item = analyze(
|
|
169
|
+
project_relative,
|
|
170
|
+
file.name,
|
|
171
|
+
config.extensions,
|
|
172
|
+
config.kit.moduleExtensions
|
|
151
173
|
);
|
|
152
174
|
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
})
|
|
175
|
+
if (item.kind === 'component') {
|
|
176
|
+
if (item.is_error) {
|
|
177
|
+
route.error = {
|
|
178
|
+
depth,
|
|
179
|
+
component: project_relative
|
|
180
|
+
};
|
|
181
|
+
} else if (item.is_layout) {
|
|
182
|
+
if (!route.layout) route.layout = { depth };
|
|
183
|
+
route.layout.component = project_relative;
|
|
184
|
+
if (item.uses_layout !== undefined) route.layout.parent_id = item.uses_layout;
|
|
185
|
+
} else {
|
|
186
|
+
if (!route.leaf) route.leaf = { depth };
|
|
187
|
+
route.leaf.component = project_relative;
|
|
188
|
+
if (item.uses_layout !== undefined) route.leaf.parent_id = item.uses_layout;
|
|
189
|
+
}
|
|
190
|
+
} else if (item.is_layout) {
|
|
191
|
+
if (!route.layout) route.layout = { depth };
|
|
192
|
+
route.layout[item.kind] = project_relative;
|
|
193
|
+
} else if (item.is_page) {
|
|
194
|
+
if (!route.leaf) route.leaf = { depth };
|
|
195
|
+
route.leaf[item.kind] = project_relative;
|
|
160
196
|
} else {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
pattern,
|
|
165
|
-
errors: [],
|
|
166
|
-
layouts: [],
|
|
167
|
-
leaf: {}
|
|
168
|
-
});
|
|
197
|
+
route.endpoint = {
|
|
198
|
+
file: project_relative
|
|
199
|
+
};
|
|
169
200
|
}
|
|
170
201
|
}
|
|
171
202
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// and only redirect or throw an error, but are set to the Svelte file definition if it exists.
|
|
177
|
-
// This ensures the proper error page is used and rendered in the proper layout.
|
|
178
|
-
if (item.kind === 'component' || route.layouts.length === 0) {
|
|
179
|
-
const { layouts, errors } = trace(
|
|
180
|
-
tree,
|
|
181
|
-
id,
|
|
182
|
-
item.kind === 'component' ? item.uses_layout : undefined,
|
|
183
|
-
project_relative
|
|
184
|
-
);
|
|
185
|
-
route.layouts = layouts;
|
|
186
|
-
route.errors = errors;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (item.kind === 'component') {
|
|
190
|
-
route.leaf.component = project_relative;
|
|
191
|
-
} else if (item.kind === 'server') {
|
|
192
|
-
route.leaf.server = project_relative;
|
|
193
|
-
} else {
|
|
194
|
-
route.leaf.shared = project_relative;
|
|
203
|
+
// then handle children
|
|
204
|
+
for (const file of files) {
|
|
205
|
+
if (file.isDirectory()) {
|
|
206
|
+
walk(depth + 1, path.posix.join(id, file.name), file.name, route);
|
|
195
207
|
}
|
|
196
208
|
}
|
|
197
|
-
}
|
|
209
|
+
};
|
|
198
210
|
|
|
199
|
-
|
|
200
|
-
if (route_map.size === 0) {
|
|
201
|
-
throw new Error(
|
|
202
|
-
'The filesystem router API has changed, see https://github.com/sveltejs/kit/discussions/5774 for details'
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
211
|
+
walk(0, '', '', null);
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
const nodes = [];
|
|
213
|
+
const root = /** @type {import('types').RouteData} */ (route_map.get(''));
|
|
209
214
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
// TODO remove for 1.0
|
|
216
|
+
if (route_map.size === 1) {
|
|
217
|
+
if (!root.leaf && !root.error && !root.layout && !root.endpoint) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
'The filesystem router API has changed, see https://github.com/sveltejs/kit/discussions/5774 for details'
|
|
220
|
+
);
|
|
221
|
+
}
|
|
215
222
|
}
|
|
216
223
|
|
|
217
|
-
if (
|
|
218
|
-
|
|
224
|
+
if (!root.layout?.component) {
|
|
225
|
+
if (!root.layout) root.layout = { depth: 0 };
|
|
226
|
+
root.layout.component = posixify(path.relative(cwd, `${fallback}/layout.svelte`));
|
|
219
227
|
}
|
|
220
228
|
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
}
|
|
229
|
+
if (!root.error?.component) {
|
|
230
|
+
if (!root.error) root.error = { depth: 0 };
|
|
231
|
+
root.error.component = posixify(path.relative(cwd, `${fallback}/error.svelte`));
|
|
225
232
|
}
|
|
226
|
-
});
|
|
227
233
|
|
|
228
|
-
|
|
229
|
-
|
|
234
|
+
// we do layouts/errors first as they are more likely to be reused,
|
|
235
|
+
// and smaller indexes take fewer bytes. also, this guarantees that
|
|
236
|
+
// the default error/layout are 0/1
|
|
237
|
+
route_map.forEach((route) => {
|
|
238
|
+
if (route.layout) nodes.push(route.layout);
|
|
239
|
+
if (route.error) nodes.push(route.error);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
/** @type {Map<string, string>} */
|
|
243
|
+
const conflicts = new Map();
|
|
244
|
+
|
|
245
|
+
route_map.forEach((route) => {
|
|
246
|
+
if (!route.leaf) return;
|
|
247
|
+
|
|
230
248
|
nodes.push(route.leaf);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
249
|
|
|
234
|
-
|
|
250
|
+
const normalized = route.id.split('/').filter(affects_path).join('/');
|
|
235
251
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
file,
|
|
240
|
-
size: fs.statSync(`${config.kit.files.assets}/${file}`).size,
|
|
241
|
-
type: mime.getType(file)
|
|
242
|
-
}))
|
|
243
|
-
: [];
|
|
252
|
+
if (conflicts.has(normalized)) {
|
|
253
|
+
throw new Error(`${conflicts.get(normalized)} and ${route.id} occupy the same route`);
|
|
254
|
+
}
|
|
244
255
|
|
|
245
|
-
|
|
256
|
+
conflicts.set(normalized, route.id);
|
|
257
|
+
});
|
|
246
258
|
|
|
247
|
-
|
|
248
|
-
const matchers = {};
|
|
249
|
-
if (fs.existsSync(config.kit.files.params)) {
|
|
250
|
-
for (const file of fs.readdirSync(config.kit.files.params)) {
|
|
251
|
-
const ext = path.extname(file);
|
|
252
|
-
if (!config.kit.moduleExtensions.includes(ext)) continue;
|
|
253
|
-
const type = file.slice(0, -ext.length);
|
|
259
|
+
const indexes = new Map(nodes.map((node, i) => [node, i]));
|
|
254
260
|
|
|
255
|
-
|
|
256
|
-
|
|
261
|
+
route_map.forEach((route) => {
|
|
262
|
+
if (!route.leaf) return;
|
|
257
263
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
if (route.leaf && route.endpoint) {
|
|
265
|
+
// TODO possibly relax this https://github.com/sveltejs/kit/issues/5896
|
|
266
|
+
throw new Error(`${route.endpoint.file} cannot share a directory with other route files`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
route.page = {
|
|
270
|
+
layouts: [],
|
|
271
|
+
errors: [],
|
|
272
|
+
leaf: /** @type {number} */ (indexes.get(route.leaf))
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/** @type {import('types').RouteData | null} */
|
|
276
|
+
let current_route = route;
|
|
277
|
+
let current_node = route.leaf;
|
|
278
|
+
let parent_id = route.leaf.parent_id;
|
|
279
|
+
|
|
280
|
+
while (current_route) {
|
|
281
|
+
if (parent_id === undefined || current_route.segment === parent_id) {
|
|
282
|
+
if (current_route.layout || current_route.error) {
|
|
283
|
+
route.page.layouts.unshift(
|
|
284
|
+
current_route.layout ? indexes.get(current_route.layout) : undefined
|
|
285
|
+
);
|
|
286
|
+
route.page.errors.unshift(
|
|
287
|
+
current_route.error ? indexes.get(current_route.error) : undefined
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (current_route.layout) {
|
|
292
|
+
current_node.parent = current_node = current_route.layout;
|
|
293
|
+
parent_id = current_node.parent_id;
|
|
294
|
+
} else {
|
|
295
|
+
parent_id = undefined;
|
|
296
|
+
}
|
|
263
297
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
`Matcher names can only have underscores and alphanumeric characters — "${file}" is invalid`
|
|
267
|
-
);
|
|
298
|
+
|
|
299
|
+
current_route = current_route.parent;
|
|
268
300
|
}
|
|
269
|
-
|
|
301
|
+
|
|
302
|
+
if (parent_id !== undefined) {
|
|
303
|
+
throw new Error(`${current_node.component} references missing segment "${parent_id}"`);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
270
306
|
}
|
|
271
307
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
routes,
|
|
276
|
-
matchers
|
|
277
|
-
};
|
|
308
|
+
const routes = Array.from(route_map.values()).sort((a, b) => compare(a, b, segment_map));
|
|
309
|
+
|
|
310
|
+
return { nodes, routes };
|
|
278
311
|
}
|
|
279
312
|
|
|
280
313
|
/**
|
|
@@ -288,10 +321,16 @@ function analyze(project_relative, file, component_extensions, module_extensions
|
|
|
288
321
|
const component_extension = component_extensions.find((ext) => file.endsWith(ext));
|
|
289
322
|
if (component_extension) {
|
|
290
323
|
const name = file.slice(0, -component_extension.length);
|
|
291
|
-
const pattern =
|
|
292
|
-
/^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?)|(error))$/;
|
|
324
|
+
const pattern = /^\+(?:(page(?:@([a-zA-Z0-9_-]*))?)|(layout(?:@([a-zA-Z0-9_-]*))?)|(error))$/;
|
|
293
325
|
const match = pattern.exec(name);
|
|
294
326
|
if (!match) {
|
|
327
|
+
// TODO remove for 1.0
|
|
328
|
+
if (/^\+layout-/.test(name)) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`${project_relative} should be reimplemented with layout groups: https://kit.svelte.dev/docs/advanced-routing#advanced-layouts`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
295
334
|
throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`);
|
|
296
335
|
}
|
|
297
336
|
|
|
@@ -299,9 +338,8 @@ function analyze(project_relative, file, component_extensions, module_extensions
|
|
|
299
338
|
kind: 'component',
|
|
300
339
|
is_page: !!match[1],
|
|
301
340
|
is_layout: !!match[3],
|
|
302
|
-
is_error: !!match[
|
|
303
|
-
uses_layout: match[2]
|
|
304
|
-
declares_layout: match[4]
|
|
341
|
+
is_error: !!match[5],
|
|
342
|
+
uses_layout: match[2] ?? match[4]
|
|
305
343
|
};
|
|
306
344
|
}
|
|
307
345
|
|
|
@@ -309,96 +347,29 @@ function analyze(project_relative, file, component_extensions, module_extensions
|
|
|
309
347
|
if (module_extension) {
|
|
310
348
|
const name = file.slice(0, -module_extension.length);
|
|
311
349
|
const pattern =
|
|
312
|
-
/^\+(?:(server)|(page(
|
|
350
|
+
/^\+(?:(server)|(page(?:(@[a-zA-Z0-9_-]*))?(\.server)?)|(layout(?:(@[a-zA-Z0-9_-]*))?(\.server)?))$/;
|
|
313
351
|
const match = pattern.exec(name);
|
|
314
352
|
if (!match) {
|
|
315
353
|
throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`);
|
|
316
|
-
} else if (match[3] || match[
|
|
354
|
+
} else if (match[3] || match[6]) {
|
|
317
355
|
throw new Error(
|
|
318
356
|
// prettier-ignore
|
|
319
|
-
`Only Svelte files can reference named layouts. Remove '
|
|
357
|
+
`Only Svelte files can reference named layouts. Remove '${match[3] || match[6]}' from ${file} (at ${project_relative})`
|
|
320
358
|
);
|
|
321
359
|
}
|
|
322
360
|
|
|
323
|
-
const kind = !!(match[1] || match[4] || match[
|
|
361
|
+
const kind = !!(match[1] || match[4] || match[7]) ? 'server' : 'shared';
|
|
324
362
|
|
|
325
363
|
return {
|
|
326
364
|
kind,
|
|
327
365
|
is_page: !!match[2],
|
|
328
|
-
is_layout: !!match[5]
|
|
329
|
-
declares_layout: match[6]
|
|
366
|
+
is_layout: !!match[5]
|
|
330
367
|
};
|
|
331
368
|
}
|
|
332
369
|
|
|
333
370
|
throw new Error(`Files and directories prefixed with + are reserved (saw ${project_relative})`);
|
|
334
371
|
}
|
|
335
372
|
|
|
336
|
-
/**
|
|
337
|
-
* @param {import('./types').RouteTree} tree
|
|
338
|
-
* @param {string} id
|
|
339
|
-
* @param {string} layout_id
|
|
340
|
-
* @param {string} project_relative
|
|
341
|
-
*/
|
|
342
|
-
function trace(tree, id, layout_id = DEFAULT, project_relative) {
|
|
343
|
-
/** @type {Array<import('types').PageNode | undefined>} */
|
|
344
|
-
const layouts = [];
|
|
345
|
-
|
|
346
|
-
/** @type {Array<import('types').PageNode | undefined>} */
|
|
347
|
-
const errors = [];
|
|
348
|
-
|
|
349
|
-
const parts = id.split('/').filter(Boolean);
|
|
350
|
-
|
|
351
|
-
// walk up the tree, find which +layout and +error components
|
|
352
|
-
// apply to this page
|
|
353
|
-
while (true) {
|
|
354
|
-
const node = tree.get(parts.join('/'));
|
|
355
|
-
const layout = node?.layouts[layout_id];
|
|
356
|
-
|
|
357
|
-
if (layout && layouts.indexOf(layout) > -1) {
|
|
358
|
-
// TODO this needs to be fixed for #5748
|
|
359
|
-
throw new Error(
|
|
360
|
-
`Recursive layout detected: ${layout.component} -> ${layouts
|
|
361
|
-
.map((l) => l?.component)
|
|
362
|
-
.join(' -> ')}`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// any segment that has neither a +layout nor an +error can be discarded.
|
|
367
|
-
// in other words these...
|
|
368
|
-
// layouts: [a, , b, c]
|
|
369
|
-
// errors: [d, , e, ]
|
|
370
|
-
//
|
|
371
|
-
// ...can be compacted to these:
|
|
372
|
-
// layouts: [a, b, c]
|
|
373
|
-
// errors: [d, e, ]
|
|
374
|
-
if (node?.error || layout) {
|
|
375
|
-
errors.unshift(node?.error);
|
|
376
|
-
layouts.unshift(layout);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const parent_layout_id = layout?.component?.split('/').at(-1)?.split('@')[1]?.split('.')[0];
|
|
380
|
-
|
|
381
|
-
if (parent_layout_id) {
|
|
382
|
-
layout_id = parent_layout_id;
|
|
383
|
-
} else {
|
|
384
|
-
if (layout) layout_id = DEFAULT;
|
|
385
|
-
if (parts.length === 0) break;
|
|
386
|
-
parts.pop();
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (layout_id !== DEFAULT) {
|
|
391
|
-
throw new Error(`${project_relative} references missing layout "${layout_id}"`);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// trim empty space off the end of the errors array
|
|
395
|
-
let i = errors.length;
|
|
396
|
-
while (i--) if (errors[i]) break;
|
|
397
|
-
errors.length = i + 1;
|
|
398
|
-
|
|
399
|
-
return { layouts, errors };
|
|
400
|
-
}
|
|
401
|
-
|
|
402
373
|
/**
|
|
403
374
|
* @param {import('types').RouteData} a
|
|
404
375
|
* @param {import('types').RouteData} b
|
|
@@ -445,62 +416,31 @@ function compare(a, b, segment_map) {
|
|
|
445
416
|
}
|
|
446
417
|
}
|
|
447
418
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (a_is_endpoint !== b_is_endpoint) {
|
|
452
|
-
return a_is_endpoint ? -1 : +1;
|
|
419
|
+
if (!!a.endpoint !== !!b.endpoint) {
|
|
420
|
+
return a.endpoint ? -1 : +1;
|
|
453
421
|
}
|
|
454
422
|
|
|
455
423
|
return a < b ? -1 : 1;
|
|
456
424
|
}
|
|
457
425
|
|
|
458
|
-
/**
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* @param {string} dir
|
|
472
|
-
* @param {string} [path]
|
|
473
|
-
* @param {string[]} [files]
|
|
474
|
-
*/
|
|
475
|
-
function list_files(dir, path = '', files = []) {
|
|
476
|
-
fs.readdirSync(dir)
|
|
477
|
-
.sort((a, b) => {
|
|
478
|
-
// sort each directory in (+layout, +error, everything else) order
|
|
479
|
-
// so that we can trace layouts/errors immediately
|
|
480
|
-
|
|
481
|
-
if (a.startsWith('+layout') || a.startsWith('+error')) {
|
|
482
|
-
if (!b.startsWith('+layout') && !b.startsWith('+error')) return -1;
|
|
483
|
-
} else if (b.startsWith('+layout') || b.startsWith('+error')) {
|
|
484
|
-
return 1;
|
|
485
|
-
} else if (a.startsWith('__')) {
|
|
486
|
-
if (!b.startsWith('__')) return -1;
|
|
487
|
-
} else if (b.startsWith('__')) {
|
|
488
|
-
return 1;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return a < b ? -1 : 1;
|
|
492
|
-
})
|
|
493
|
-
.forEach((file) => {
|
|
494
|
-
const full = `${dir}/${file}`;
|
|
495
|
-
const stats = fs.statSync(full);
|
|
496
|
-
const joined = path ? `${path}/${file}` : file;
|
|
497
|
-
|
|
498
|
-
if (stats.isDirectory()) {
|
|
499
|
-
list_files(full, joined, files);
|
|
426
|
+
/** @param {string} dir */
|
|
427
|
+
function list_files(dir) {
|
|
428
|
+
/** @type {string[]} */
|
|
429
|
+
const files = [];
|
|
430
|
+
|
|
431
|
+
/** @param {string} current */
|
|
432
|
+
function walk(current) {
|
|
433
|
+
for (const file of fs.readdirSync(path.resolve(dir, current))) {
|
|
434
|
+
const child = path.posix.join(current, file);
|
|
435
|
+
if (fs.statSync(path.resolve(dir, child)).isDirectory()) {
|
|
436
|
+
walk(child);
|
|
500
437
|
} else {
|
|
501
|
-
files.push(
|
|
438
|
+
files.push(child);
|
|
502
439
|
}
|
|
503
|
-
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (fs.existsSync(dir)) walk('');
|
|
504
444
|
|
|
505
445
|
return files;
|
|
506
446
|
}
|