Skip to content

Commit 74016df

Browse files
feat: It basically works lol
1 parent 77d0a96 commit 74016df

File tree

6 files changed

+88
-6
lines changed

6 files changed

+88
-6
lines changed

packages/kit/src/core/postbuild/analyse.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { load_config } from '../config/index.js';
1010
import { forked } from '../../utils/fork.js';
1111
import { should_polyfill } from '../../utils/platform.js';
1212
import { installPolyfills } from '../../exports/node/polyfills.js';
13+
import { route_from_entry } from '../../utils/routing.js';
1314

1415
export default forked(import.meta.url, analyse);
1516

@@ -72,6 +73,8 @@ async function analyse({ manifest_path, env }) {
7273
let prerender = undefined;
7374
/** @type {any} */
7475
let config = undefined;
76+
/** @type {import('types').PrerenderEntriesGenerator | undefined} */
77+
let entries = undefined;
7578

7679
if (route.endpoint) {
7780
const mod = await route.endpoint();
@@ -95,6 +98,7 @@ async function analyse({ manifest_path, env }) {
9598
if (mod.OPTIONS) api_methods.push('OPTIONS');
9699

97100
config = mod.config;
101+
entries = mod.entries;
98102
}
99103

100104
if (route.page) {
@@ -125,6 +129,7 @@ async function analyse({ manifest_path, env }) {
125129
prerender = get_option(nodes, 'prerender') ?? false;
126130

127131
config = get_config(nodes);
132+
entries = get_option(nodes, 'entries');
128133
}
129134

130135
metadata.routes.set(route.id, {
@@ -136,7 +141,9 @@ async function analyse({ manifest_path, env }) {
136141
api: {
137142
methods: api_methods
138143
},
139-
prerender
144+
prerender,
145+
entries:
146+
entries && (await entries()).map((entryObject) => route_from_entry(route.id, entryObject))
140147
});
141148
}
142149

packages/kit/src/core/postbuild/prerender.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,18 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
363363
saved.set(file, dest);
364364
}
365365

366+
/** @type {Array<string>} */
367+
const route_level_entries = [];
368+
for (const route of metadata.routes.values()) {
369+
if (route.entries) {
370+
route_level_entries.push(...route.entries);
371+
}
372+
}
373+
366374
if (
367375
config.prerender.entries.length > 1 ||
368376
config.prerender.entries[0] !== '*' ||
377+
route_level_entries.length > 0 ||
369378
prerender_map.size > 0
370379
) {
371380
// Only log if we're actually going to do something to not confuse users
@@ -386,6 +395,12 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
386395
}
387396
}
388397

398+
console.log({ route_level_entries });
399+
400+
for (const entry of route_level_entries) {
401+
enqueue(null, config.paths.base + entry);
402+
}
403+
389404
await q.done();
390405

391406
// handle invalid fragment links

packages/kit/src/utils/exports.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,25 @@ function hint_for_supported_files(key, ext = '.js') {
5050
}
5151
}
5252

53-
const valid_common_exports = ['load', 'prerender', 'csr', 'ssr', 'trailingSlash', 'config'];
53+
// TODO: Need to distinguish between pages and layouts; should not be able to export entries from layouts
54+
const valid_common_exports = [
55+
'load',
56+
'prerender',
57+
'csr',
58+
'ssr',
59+
'trailingSlash',
60+
'config',
61+
'entries'
62+
];
5463
const valid_page_server_exports = [
5564
'load',
5665
'prerender',
5766
'csr',
5867
'ssr',
5968
'actions',
6069
'trailingSlash',
61-
'config'
70+
'config',
71+
'entries'
6272
];
6373
const valid_server_exports = [
6474
'GET',
@@ -69,7 +79,8 @@ const valid_server_exports = [
6979
'OPTIONS',
7080
'prerender',
7181
'trailingSlash',
72-
'config'
82+
'config',
83+
'entries'
7384
];
7485

7586
export const validate_common_exports = validator(valid_common_exports);

packages/kit/src/utils/options.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash'} Option
3-
* @template {Option extends 'prerender' ? import('types').PrerenderOption : Option extends 'trailingSlash' ? import('types').TrailingSlash : boolean} Value
2+
* @template {'prerender' | 'ssr' | 'csr' | 'trailingSlash' | 'entries'} Option
3+
* @template {Option extends 'prerender' ? import('types').PrerenderOption : Option extends 'trailingSlash' ? import('types').TrailingSlash : Option extends 'entries' ? import('types').PrerenderEntriesGenerator : boolean} Value
44
*
55
* @param {Array<import('types').SSRNode | undefined>} nodes
66
* @param {Option} option

packages/kit/src/utils/routing.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,49 @@ export function parse_route_id(id) {
9696
return { pattern, params };
9797
}
9898

99+
const basic_param_pattern = /\[(\[)?(?:\.\.\.)?(.+?)(?:=(.+))?\]\]?/;
100+
101+
/**
102+
* Parses a route ID, then resolves it to a route by replacing parameters with actual values from `entry`.
103+
* @param {string} id The route id
104+
* @param {Record<string, string>} entry The entry meant to populate the route. For example, if the route is `/blog/[slug]`, the entry would be `{ slug: 'hello-world' }`
105+
* @example
106+
* ```js
107+
* route_from_entry(`/blog/[slug]/[...somethingElse]`, { slug: 'hello-world', somethingElse: 'something/else' }); // `/blog/hello-world/something/else`
108+
* ```
109+
*/
110+
export function route_from_entry(id, entry) {
111+
const segments = get_route_segments(id);
112+
return (
113+
'/' +
114+
segments
115+
.map((segment) => {
116+
const match = basic_param_pattern.exec(segment);
117+
118+
// static content -- i.e. not a param
119+
if (!match) return segment;
120+
121+
const optional = !!match[1];
122+
const name = match[2];
123+
const paramValue = entry[name];
124+
125+
// If the param is optional and there's no value, don't do anything to the output string
126+
if (!paramValue && optional) return '';
127+
128+
if (!paramValue && !optional)
129+
throw new Error(`Missing parameter '${name}' in route '${id}'`);
130+
131+
if (paramValue.startsWith('/') || paramValue.endsWith('/'))
132+
throw new Error(
133+
`Parameter '${name}' in route '${id}' cannot start or end with a slash -- this would cause an invalid route like 'foo//bar'`
134+
);
135+
136+
return paramValue;
137+
})
138+
.join('/')
139+
);
140+
}
141+
99142
/**
100143
* Returns `false` for `(group)` segments
101144
* @param {string} segment

packages/kit/types/internal.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ export interface ServerMetadataRoute {
264264
};
265265
methods: HttpMethod[];
266266
prerender: PrerenderOption | undefined;
267+
entries: Array<string> | undefined;
267268
}
268269

269270
export interface ServerMetadata {
@@ -308,6 +309,7 @@ export interface SSRNode {
308309
csr?: boolean;
309310
trailingSlash?: TrailingSlash;
310311
config?: any;
312+
entries?: PrerenderEntriesGenerator;
311313
};
312314

313315
server: {
@@ -318,6 +320,7 @@ export interface SSRNode {
318320
trailingSlash?: TrailingSlash;
319321
actions?: Actions;
320322
config?: any;
323+
entries?: PrerenderEntriesGenerator;
321324
};
322325

323326
universal_id: string;
@@ -355,10 +358,13 @@ export interface PageNodeIndexes {
355358
leaf: number;
356359
}
357360

361+
export type PrerenderEntriesGenerator = () => MaybePromise<Array<Record<string, string>>>;
362+
358363
export type SSREndpoint = Partial<Record<HttpMethod, RequestHandler>> & {
359364
prerender?: PrerenderOption;
360365
trailingSlash?: TrailingSlash;
361366
config?: any;
367+
entries?: PrerenderEntriesGenerator;
362368
};
363369

364370
export interface SSRRoute {

0 commit comments

Comments
 (0)