@forgebook/sveltekit 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +145 -2
- package/dist/src/app/env.d.ts +8 -0
- package/dist/src/app/env.js +15 -0
- package/dist/src/app/forms.d.ts +68 -0
- package/dist/src/app/forms.js +125 -0
- package/dist/src/app/manifest.d.ts +21 -0
- package/dist/src/app/manifest.js +17 -0
- package/dist/src/app/navigation.d.ts +97 -0
- package/dist/src/app/navigation.js +147 -0
- package/dist/src/app/paths.d.ts +32 -0
- package/dist/src/app/paths.js +56 -0
- package/dist/src/app/state.d.ts +27 -0
- package/dist/src/app/state.js +60 -0
- package/dist/src/app/stores.d.ts +31 -0
- package/dist/src/app/stores.js +36 -0
- package/dist/src/appSettings.d.ts +44 -0
- package/dist/src/appSettings.js +55 -0
- package/dist/src/kitParameters.d.ts +272 -0
- package/dist/src/kitParameters.js +291 -0
- package/dist/src/kitRenderer.d.ts +3 -0
- package/dist/src/kitRenderer.js +254 -0
- package/dist/src/kitStore.d.ts +90 -0
- package/dist/src/kitStore.js +301 -0
- package/dist/src/mountFrame.d.ts +14 -0
- package/dist/src/mountFrame.js +15 -0
- package/dist/src/routeIds.d.ts +44 -0
- package/dist/src/routeIds.js +150 -0
- package/dist/src/routeOrder.d.ts +18 -0
- package/dist/src/routeOrder.js +187 -0
- package/dist/src/stories.d.ts +48 -0
- package/dist/src/stories.js +11 -0
- package/dist/src/storyCode.d.ts +3 -0
- package/dist/src/storyCode.js +9 -0
- package/dist/src/storyPage.d.ts +35 -0
- package/dist/src/storyPage.js +19 -0
- package/dist/src/vite/envModules.d.ts +67 -0
- package/dist/src/vite/envModules.js +181 -0
- package/dist/src/vite/extractDocs.d.ts +14 -0
- package/dist/src/vite/extractDocs.js +20 -0
- package/dist/src/vite/kitPlugin.d.ts +28 -0
- package/dist/src/vite/kitPlugin.js +241 -0
- package/dist/src/vite/kitProject.d.ts +27 -0
- package/dist/src/vite/kitProject.js +176 -0
- package/dist/src/vite/projectFiles.d.ts +14 -0
- package/dist/src/vite/projectFiles.js +70 -0
- package/dist/src/vite/sveltekit.d.ts +26 -0
- package/dist/src/vite/sveltekit.js +39 -0
- package/package.json +113 -3
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A story's SvelteKit settings, read from its `parameters.sveltekit`: the page its components see (`$app/state`'s
|
|
3
|
+
* `page`), the `load` that gives the page its data, and what a navigation, a link or a form calls.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Pure: parameters in, settings out, so it is tested without a page. Storybook's `sveltekit_experimental` is read
|
|
7
|
+
* too, as its SvelteKit framework writes it (`state.page`, `stores.page`, `navigation`, `forms`, `hrefs`), so a story
|
|
8
|
+
* moved from Storybook keeps its page; `sveltekit` wins where both are given. The page's path is matched against the
|
|
9
|
+
* project's routes, so a story that gives only `url: "/blog/hello"` gets the route id and the params SvelteKit would.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { type RouteParams } from "./routeIds.ts";
|
|
14
|
+
/** An error as `page.error` holds it (`App.Error`): its message, and its status in SvelteKit 3. */
|
|
15
|
+
export type KitError = {
|
|
16
|
+
/** The error's message. */
|
|
17
|
+
readonly message: string;
|
|
18
|
+
/** Its HTTP status. */
|
|
19
|
+
readonly status?: number;
|
|
20
|
+
};
|
|
21
|
+
/** The page a story's components see, as `page` from `$app/state` holds it. */
|
|
22
|
+
export type KitPage = {
|
|
23
|
+
/** The page's address, its base path included; its origin is `http://localhost` unless the story names one. */
|
|
24
|
+
readonly url: URL;
|
|
25
|
+
/** The route's params, by name. */
|
|
26
|
+
readonly params: RouteParams;
|
|
27
|
+
/** The route: its id (`/blog/[slug]`), or `null` when no route of the project matches. */
|
|
28
|
+
readonly route: {
|
|
29
|
+
readonly id: string | null;
|
|
30
|
+
};
|
|
31
|
+
/** The page's HTTP status. */
|
|
32
|
+
readonly status: number;
|
|
33
|
+
/** The error a load or an action gave, or `null`. */
|
|
34
|
+
readonly error: KitError | null;
|
|
35
|
+
/** The page's data: every load's, merged. */
|
|
36
|
+
readonly data: Readonly<Record<string, unknown>>;
|
|
37
|
+
/** The state `pushState` and `replaceState` set (`App.PageState`). */
|
|
38
|
+
readonly state: Readonly<Record<string, unknown>>;
|
|
39
|
+
/** What the last form action returned, or `null`. */
|
|
40
|
+
readonly form: unknown;
|
|
41
|
+
};
|
|
42
|
+
/** The page a story starts on (`parameters.sveltekit.page`): every field optional. */
|
|
43
|
+
export type PageParameters = {
|
|
44
|
+
/** The path (`/blog/hello?tab=1`), from the base path, or an absolute address; `/` by default. */
|
|
45
|
+
readonly url?: string | URL;
|
|
46
|
+
/** The route's params; read from the path by the project's routes when not given. */
|
|
47
|
+
readonly params?: RouteParams;
|
|
48
|
+
/** The route's id, or `{ id }`; read from the path when not given. With params and no `url`, it gives the path. */
|
|
49
|
+
readonly route?: string | {
|
|
50
|
+
readonly id: string | null;
|
|
51
|
+
};
|
|
52
|
+
/** The HTTP status; 200 by default. */
|
|
53
|
+
readonly status?: number;
|
|
54
|
+
/** The page's error; `null` by default. */
|
|
55
|
+
readonly error?: KitError | null;
|
|
56
|
+
/** The page's data, as its loads would give it (the layouts' among them); `{}` by default. */
|
|
57
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
58
|
+
/** The page's state (`App.PageState`); `{}` by default. */
|
|
59
|
+
readonly state?: Readonly<Record<string, unknown>>;
|
|
60
|
+
/** What a form action returned; `null` by default. */
|
|
61
|
+
readonly form?: unknown;
|
|
62
|
+
};
|
|
63
|
+
/** Where a navigation goes or comes from, as `$app/navigation`'s callbacks receive it. */
|
|
64
|
+
export type NavigationTarget = {
|
|
65
|
+
/** The target's params, or `null` for a page outside the app. */
|
|
66
|
+
readonly params: RouteParams | null;
|
|
67
|
+
/** The target's route. */
|
|
68
|
+
readonly route: {
|
|
69
|
+
readonly id: string | null;
|
|
70
|
+
};
|
|
71
|
+
/** The target's address. */
|
|
72
|
+
readonly url: URL;
|
|
73
|
+
};
|
|
74
|
+
/** How a navigation started, as SvelteKit names it. */
|
|
75
|
+
export type NavigationType = "enter" | "form" | "leave" | "link" | "goto" | "popstate";
|
|
76
|
+
/** A navigation, as `beforeNavigate`, `onNavigate`, `afterNavigate` and `navigating` give it. */
|
|
77
|
+
export type KitNavigation = {
|
|
78
|
+
/** Where it comes from; `null` for the page's first. */
|
|
79
|
+
readonly from: NavigationTarget | null;
|
|
80
|
+
/** Where it goes. */
|
|
81
|
+
readonly to: NavigationTarget | null;
|
|
82
|
+
/** How it started. */
|
|
83
|
+
readonly type: NavigationType;
|
|
84
|
+
/** Always `false`: a story's navigations stay in its page. */
|
|
85
|
+
readonly willUnload: false;
|
|
86
|
+
/** The steps through the history; only for `popstate`. */
|
|
87
|
+
readonly delta?: number;
|
|
88
|
+
/** Resolves once the navigation is done. */
|
|
89
|
+
readonly complete: Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
/** What a universal `load` receives (`+page.ts`'s `load`), as the catalogue gives it. */
|
|
92
|
+
export type KitLoadEvent = {
|
|
93
|
+
/** The route's params. */
|
|
94
|
+
readonly params: RouteParams;
|
|
95
|
+
/** The page's address. */
|
|
96
|
+
readonly url: URL;
|
|
97
|
+
/** The route. */
|
|
98
|
+
readonly route: {
|
|
99
|
+
readonly id: string | null;
|
|
100
|
+
};
|
|
101
|
+
/** The page's `fetch`: the frame's own, so the catalogue's mocks answer it. */
|
|
102
|
+
readonly fetch: typeof fetch;
|
|
103
|
+
/** What the page's server load returned (`parameters.sveltekit.serverData`); `null` without one. */
|
|
104
|
+
readonly data: Readonly<Record<string, unknown>> | null;
|
|
105
|
+
/** The data of the layouts above: the page's `data` parameter and the loads before this one, merged. */
|
|
106
|
+
readonly parent: () => Promise<Readonly<Record<string, unknown>>>;
|
|
107
|
+
/** Names what the data depends on, for `invalidate`; recorded, nothing more. */
|
|
108
|
+
readonly depends: (...dependencies: string[]) => void;
|
|
109
|
+
/** Sets the response's headers: there is no response, so nothing. */
|
|
110
|
+
readonly setHeaders: (headers: Readonly<Record<string, string>>) => void;
|
|
111
|
+
/** Runs a function without tracking what it reads: it is run. */
|
|
112
|
+
readonly untrack: <TValue>(read: () => TValue) => TValue;
|
|
113
|
+
};
|
|
114
|
+
/** A universal `load`, as the catalogue calls it. */
|
|
115
|
+
export type KitLoad = (event: KitLoadEvent) => unknown;
|
|
116
|
+
/**
|
|
117
|
+
* A universal `load`, as `+page.ts` or `+layout.ts` exports it (`PageLoad`). Its event is typed by the project's own
|
|
118
|
+
* `./$types`, with more than a catalogue gives (`tracing`) and params by name, so no event type both takes it and
|
|
119
|
+
* types a load written in the story: any function is taken, and called with a `KitLoadEvent`. A load written in a
|
|
120
|
+
* story types its own event (`satisfies PageLoad`).
|
|
121
|
+
*/
|
|
122
|
+
export type StoryLoad = (event: never) => unknown;
|
|
123
|
+
/** What a link to an href calls instead of moving the story's page: Storybook's `hrefs` entry. */
|
|
124
|
+
export type HrefCallback = (to: string, event: Event) => void;
|
|
125
|
+
/** An `hrefs` entry: a callback, or one with whether its key is a regular expression. */
|
|
126
|
+
export type HrefParameter = HrefCallback | {
|
|
127
|
+
readonly callback: HrefCallback;
|
|
128
|
+
readonly asRegex?: boolean;
|
|
129
|
+
};
|
|
130
|
+
/** A function a story gives to hear a call of `$app/navigation`, with the call's own arguments. */
|
|
131
|
+
export type NavigationCallback = (...values: never[]) => unknown;
|
|
132
|
+
/** What a story hears of `$app/navigation` (`parameters.sveltekit.navigation`), as Storybook's SvelteKit names it. */
|
|
133
|
+
export type NavigationParameters = {
|
|
134
|
+
/** Called with `goto`'s arguments. */
|
|
135
|
+
readonly goto?: NavigationCallback;
|
|
136
|
+
/** Called with `invalidate`'s arguments. */
|
|
137
|
+
readonly invalidate?: NavigationCallback;
|
|
138
|
+
/** Called when `invalidateAll` is. */
|
|
139
|
+
readonly invalidateAll?: NavigationCallback;
|
|
140
|
+
/** Called when `refreshAll` is. */
|
|
141
|
+
readonly refreshAll?: NavigationCallback;
|
|
142
|
+
/** Called with `pushState`'s arguments. */
|
|
143
|
+
readonly pushState?: NavigationCallback;
|
|
144
|
+
/** Called with `replaceState`'s arguments. */
|
|
145
|
+
readonly replaceState?: NavigationCallback;
|
|
146
|
+
/** Called with `preloadData`'s arguments. */
|
|
147
|
+
readonly preloadData?: NavigationCallback;
|
|
148
|
+
/** Called with `preloadCode`'s arguments. */
|
|
149
|
+
readonly preloadCode?: NavigationCallback;
|
|
150
|
+
/** What `afterNavigate`'s callbacks get as the page is drawn, over the `enter` navigation the catalogue writes. */
|
|
151
|
+
readonly afterNavigate?: Readonly<Record<string, unknown>>;
|
|
152
|
+
};
|
|
153
|
+
/** An action's result, as `$app/forms` gives it to `applyAction` and `use:enhance`'s callback. */
|
|
154
|
+
export type KitActionResult = {
|
|
155
|
+
readonly type: "success";
|
|
156
|
+
readonly status: number;
|
|
157
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
158
|
+
} | {
|
|
159
|
+
readonly type: "failure";
|
|
160
|
+
readonly status: number;
|
|
161
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
162
|
+
} | {
|
|
163
|
+
readonly type: "redirect";
|
|
164
|
+
readonly status: number;
|
|
165
|
+
readonly location: string;
|
|
166
|
+
} | {
|
|
167
|
+
readonly type: "error";
|
|
168
|
+
readonly status?: number;
|
|
169
|
+
readonly error: unknown;
|
|
170
|
+
};
|
|
171
|
+
/** What a story's forms do (`parameters.sveltekit.forms`). */
|
|
172
|
+
export type FormsParameters = {
|
|
173
|
+
/** Called when a form with `use:enhance` is submitted, as Storybook's SvelteKit calls it. */
|
|
174
|
+
readonly enhance?: () => void;
|
|
175
|
+
/** What the action answers a form with `use:enhance`; `{ type: "success", status: 200 }` by default. */
|
|
176
|
+
readonly result?: KitActionResult;
|
|
177
|
+
};
|
|
178
|
+
/** A story's SvelteKit settings (`parameters.sveltekit`). */
|
|
179
|
+
export type SveltekitParameters = {
|
|
180
|
+
/** The page the story starts on. */
|
|
181
|
+
readonly page?: PageParameters;
|
|
182
|
+
/** What `navigating` holds as the story starts; `null` by default. */
|
|
183
|
+
readonly navigating?: Partial<KitNavigation> | null;
|
|
184
|
+
/** What `updated.current` holds; `false` by default. */
|
|
185
|
+
readonly updated?: boolean;
|
|
186
|
+
/** The page's universal loads, the layouts' first: run in the frame before the story is drawn, and again on `invalidate`. */
|
|
187
|
+
readonly load?: StoryLoad | readonly StoryLoad[];
|
|
188
|
+
/** What the page's server load returned: the `data` the universal load receives, or the page's data without one. */
|
|
189
|
+
readonly serverData?: Readonly<Record<string, unknown>>;
|
|
190
|
+
/** What the story hears of `$app/navigation`. */
|
|
191
|
+
readonly navigation?: NavigationParameters;
|
|
192
|
+
/** What a form with `use:enhance` does. */
|
|
193
|
+
readonly forms?: FormsParameters;
|
|
194
|
+
/** Links whose href is handled by a callback rather than moving the page, by href (or regular expression). */
|
|
195
|
+
readonly hrefs?: Readonly<Record<string, HrefParameter>>;
|
|
196
|
+
};
|
|
197
|
+
/** A story's SvelteKit settings, read: every field there. */
|
|
198
|
+
export type StoryKit = {
|
|
199
|
+
/** The page the story starts on. */
|
|
200
|
+
readonly page: KitPage;
|
|
201
|
+
/** What `navigating` holds as the story starts. */
|
|
202
|
+
readonly navigating: Partial<KitNavigation> | null;
|
|
203
|
+
/** What `updated.current` holds as the story starts. */
|
|
204
|
+
readonly updated: boolean;
|
|
205
|
+
/** The page's universal loads, in order. */
|
|
206
|
+
readonly loads: readonly KitLoad[];
|
|
207
|
+
/** What the page's server load returned, or `null`. */
|
|
208
|
+
readonly serverData: Readonly<Record<string, unknown>> | null;
|
|
209
|
+
/**
|
|
210
|
+
* Whether the story is a page's: it gives its page data (`page.data`, `serverData`, `load`) or an action's answer
|
|
211
|
+
* (`forms.result`), so its component gets `data`, `form` and `params`, as SvelteKit draws a `+page.svelte`.
|
|
212
|
+
*/
|
|
213
|
+
readonly isPage: boolean;
|
|
214
|
+
/** What the story hears of `$app/navigation`. */
|
|
215
|
+
readonly navigation: NavigationParameters;
|
|
216
|
+
/** What a form with `use:enhance` does. */
|
|
217
|
+
readonly forms: FormsParameters;
|
|
218
|
+
/** The links handled by a callback. */
|
|
219
|
+
readonly hrefs: Readonly<Record<string, HrefParameter>>;
|
|
220
|
+
};
|
|
221
|
+
/** The origin a path is given when the story names none: the same in every catalogue, so a page's address is too. */
|
|
222
|
+
export declare const PAGE_ORIGIN = "http://localhost";
|
|
223
|
+
/**
|
|
224
|
+
* A path made an address: an absolute one as it is, a path after the base path.
|
|
225
|
+
*
|
|
226
|
+
* @param url - the path or the address.
|
|
227
|
+
* @param base - the base path (`/docs`), or empty.
|
|
228
|
+
*/
|
|
229
|
+
export declare const pageUrl: (url: string | URL, base: string) => URL;
|
|
230
|
+
/**
|
|
231
|
+
* A path without the base path, as routes are matched.
|
|
232
|
+
*
|
|
233
|
+
* @param pathname - the address's path.
|
|
234
|
+
* @param base - the base path, or empty.
|
|
235
|
+
*/
|
|
236
|
+
export declare const routePath: (pathname: string, base: string) => string;
|
|
237
|
+
/**
|
|
238
|
+
* The page a path gives: its route and params from the project's routes.
|
|
239
|
+
*
|
|
240
|
+
* @param url - the page's address.
|
|
241
|
+
* @param routes - the project's page routes.
|
|
242
|
+
* @param base - the base path.
|
|
243
|
+
*/
|
|
244
|
+
export declare const routedPage: (url: URL, routes: readonly string[], base: string) => Pick<KitPage, "url" | "params" | "route">;
|
|
245
|
+
/**
|
|
246
|
+
* A page's error read from a story's parameters, or `null`.
|
|
247
|
+
*
|
|
248
|
+
* @param error - `page.error`.
|
|
249
|
+
*/
|
|
250
|
+
export declare const errorOf: (error: unknown) => KitError | null;
|
|
251
|
+
/**
|
|
252
|
+
* An action's result read from a story's parameters.
|
|
253
|
+
*
|
|
254
|
+
* @param result - the value, an object with a `type`.
|
|
255
|
+
*/
|
|
256
|
+
export declare const actionResultOf: (result: Readonly<Record<string, unknown>>) => KitActionResult;
|
|
257
|
+
/**
|
|
258
|
+
* A story's SvelteKit settings.
|
|
259
|
+
*
|
|
260
|
+
* @param parameters - the story's parameters (the preview's, the meta's and its own, merged).
|
|
261
|
+
* @param routes - the project's page routes, to read a path's route and params.
|
|
262
|
+
* @param base - the base path.
|
|
263
|
+
*/
|
|
264
|
+
export declare const storyKitOf: (parameters: Readonly<Record<string, unknown>>, routes: readonly string[], base: string) => StoryKit;
|
|
265
|
+
/**
|
|
266
|
+
* The callbacks of the `hrefs` entries a link's href matches, in order, each of them called as Storybook's SvelteKit
|
|
267
|
+
* calls them.
|
|
268
|
+
*
|
|
269
|
+
* @param hrefs - the story's links handled by a callback.
|
|
270
|
+
* @param href - the link's href, as written.
|
|
271
|
+
*/
|
|
272
|
+
export declare const hrefCallbacksOf: (hrefs: Readonly<Record<string, HrefParameter>>, href: string) => HrefCallback[];
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A story's SvelteKit settings, read from its `parameters.sveltekit`: the page its components see (`$app/state`'s
|
|
3
|
+
* `page`), the `load` that gives the page its data, and what a navigation, a link or a form calls.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Pure: parameters in, settings out, so it is tested without a page. Storybook's `sveltekit_experimental` is read
|
|
7
|
+
* too, as its SvelteKit framework writes it (`state.page`, `stores.page`, `navigation`, `forms`, `hrefs`), so a story
|
|
8
|
+
* moved from Storybook keeps its page; `sveltekit` wins where both are given. The page's path is matched against the
|
|
9
|
+
* project's routes, so a story that gives only `url: "/blog/hello"` gets the route id and the params SvelteKit would.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { filledRoute, matchRoute } from "./routeIds.js";
|
|
14
|
+
/** The origin a path is given when the story names none: the same in every catalogue, so a page's address is too. */
|
|
15
|
+
export const PAGE_ORIGIN = "http://localhost";
|
|
16
|
+
/** Whether a value is a plain object. */
|
|
17
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
+
/**
|
|
19
|
+
* A plain object read from a story's parameters, or none.
|
|
20
|
+
*
|
|
21
|
+
* @param value - the value.
|
|
22
|
+
*/
|
|
23
|
+
const recordOf = (value) => isRecord(value) ? value : undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The params of an object read from a story's parameters: what is not a text left out.
|
|
26
|
+
*
|
|
27
|
+
* @param value - the object.
|
|
28
|
+
*/
|
|
29
|
+
const paramsOf = (value) => isRecord(value)
|
|
30
|
+
? Object.fromEntries(Object.entries(value).flatMap(([name, item]) => typeof item === "string" || typeof item === "number" ? [[name, String(item)]] : []))
|
|
31
|
+
: undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Whether a value is a function a story gave.
|
|
34
|
+
*
|
|
35
|
+
* @param value - the value.
|
|
36
|
+
*/
|
|
37
|
+
const isCallback = (value) => typeof value === "function";
|
|
38
|
+
/**
|
|
39
|
+
* A path made an address: an absolute one as it is, a path after the base path.
|
|
40
|
+
*
|
|
41
|
+
* @param url - the path or the address.
|
|
42
|
+
* @param base - the base path (`/docs`), or empty.
|
|
43
|
+
*/
|
|
44
|
+
export const pageUrl = (url, base) => {
|
|
45
|
+
if (url instanceof URL)
|
|
46
|
+
return new URL(url.href);
|
|
47
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(url))
|
|
48
|
+
return new URL(url);
|
|
49
|
+
const path = url.startsWith("/") ? url : `/${url}`;
|
|
50
|
+
return new URL(`${base}${path}`, PAGE_ORIGIN);
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A path without the base path, as routes are matched.
|
|
54
|
+
*
|
|
55
|
+
* @param pathname - the address's path.
|
|
56
|
+
* @param base - the base path, or empty.
|
|
57
|
+
*/
|
|
58
|
+
export const routePath = (pathname, base) => base !== "" && (pathname === base || pathname.startsWith(`${base}/`))
|
|
59
|
+
? pathname.slice(base.length) || "/"
|
|
60
|
+
: pathname;
|
|
61
|
+
/**
|
|
62
|
+
* The page a path gives: its route and params from the project's routes.
|
|
63
|
+
*
|
|
64
|
+
* @param url - the page's address.
|
|
65
|
+
* @param routes - the project's page routes.
|
|
66
|
+
* @param base - the base path.
|
|
67
|
+
*/
|
|
68
|
+
export const routedPage = (url, routes, base) => {
|
|
69
|
+
const match = matchRoute(routes, routePath(url.pathname, base));
|
|
70
|
+
return { url, params: match?.params ?? {}, route: { id: match?.id ?? null } };
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The route id a story's `route` gives.
|
|
74
|
+
*
|
|
75
|
+
* @param route - `page.route`.
|
|
76
|
+
*/
|
|
77
|
+
const routeIdOf = (route) => {
|
|
78
|
+
if (typeof route === "string")
|
|
79
|
+
return route;
|
|
80
|
+
if (isRecord(route) && (typeof route["id"] === "string" || route["id"] === null))
|
|
81
|
+
return route["id"];
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* A page's error read from a story's parameters, or `null`.
|
|
86
|
+
*
|
|
87
|
+
* @param error - `page.error`.
|
|
88
|
+
*/
|
|
89
|
+
export const errorOf = (error) => {
|
|
90
|
+
if (!isRecord(error) || typeof error["message"] !== "string")
|
|
91
|
+
return null;
|
|
92
|
+
const { status } = error;
|
|
93
|
+
return { message: error["message"], ...(typeof status === "number" ? { status } : {}) };
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The page a story's page parameters give.
|
|
97
|
+
*
|
|
98
|
+
* @param page - the merged page parameters.
|
|
99
|
+
* @param routes - the project's page routes.
|
|
100
|
+
* @param base - the base path.
|
|
101
|
+
*/
|
|
102
|
+
const pageOf = (page, routes, base) => {
|
|
103
|
+
const params = paramsOf(page["params"]);
|
|
104
|
+
const routeId = routeIdOf(page["route"]);
|
|
105
|
+
const givenUrl = page["url"];
|
|
106
|
+
const hasUrl = typeof givenUrl === "string" || givenUrl instanceof URL;
|
|
107
|
+
const url = hasUrl
|
|
108
|
+
? pageUrl(givenUrl, base)
|
|
109
|
+
: pageUrl(typeof routeId === "string" ? filledRoute(routeId, params ?? {}) : "/", base);
|
|
110
|
+
const routed = routedPage(url, routes, base);
|
|
111
|
+
const error = page["error"];
|
|
112
|
+
return {
|
|
113
|
+
url,
|
|
114
|
+
params: params ?? routed.params,
|
|
115
|
+
route: { id: routeId === undefined ? routed.route.id : routeId },
|
|
116
|
+
status: typeof page["status"] === "number" ? page["status"] : 200,
|
|
117
|
+
error: errorOf(error),
|
|
118
|
+
data: recordOf(page["data"]) ?? {},
|
|
119
|
+
state: recordOf(page["state"]) ?? {},
|
|
120
|
+
form: page["form"] ?? null
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Storybook's settings, as Forgebook's: `state.page` (or `stores.page`) is the page, its `navigating` and `updated`
|
|
125
|
+
* likewise.
|
|
126
|
+
*
|
|
127
|
+
* @param experimental - `parameters.sveltekit_experimental`.
|
|
128
|
+
*/
|
|
129
|
+
const fromStorybook = (experimental) => {
|
|
130
|
+
const state = recordOf(experimental["state"]) ?? recordOf(experimental["stores"]) ?? {};
|
|
131
|
+
const updated = state["updated"];
|
|
132
|
+
return {
|
|
133
|
+
page: state["page"],
|
|
134
|
+
navigating: state["navigating"],
|
|
135
|
+
updated: isRecord(updated) ? updated["current"] : updated,
|
|
136
|
+
navigation: experimental["navigation"],
|
|
137
|
+
forms: experimental["forms"],
|
|
138
|
+
hrefs: experimental["hrefs"]
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* The loads a story gives: one, or a list.
|
|
143
|
+
*
|
|
144
|
+
* @param load - `sveltekit.load`.
|
|
145
|
+
*/
|
|
146
|
+
const loadsOf = (load) => {
|
|
147
|
+
if (typeof load === "function")
|
|
148
|
+
return [(event) => Reflect.apply(load, undefined, [event])];
|
|
149
|
+
if (!Array.isArray(load))
|
|
150
|
+
return [];
|
|
151
|
+
return load.flatMap((item) => loadsOf(item));
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* The navigation callbacks a story gives.
|
|
155
|
+
*
|
|
156
|
+
* @param navigation - `sveltekit.navigation`.
|
|
157
|
+
*/
|
|
158
|
+
const navigationOf = (navigation) => {
|
|
159
|
+
const read = recordOf(navigation) ?? {};
|
|
160
|
+
const callbacks = Object.fromEntries([
|
|
161
|
+
"goto",
|
|
162
|
+
"invalidate",
|
|
163
|
+
"invalidateAll",
|
|
164
|
+
"refreshAll",
|
|
165
|
+
"pushState",
|
|
166
|
+
"replaceState",
|
|
167
|
+
"preloadData",
|
|
168
|
+
"preloadCode"
|
|
169
|
+
]
|
|
170
|
+
.map((name) => [name, read[name]])
|
|
171
|
+
.filter((entry) => isCallback(entry[1])));
|
|
172
|
+
const after = recordOf(read["afterNavigate"]);
|
|
173
|
+
return after === undefined ? callbacks : { ...callbacks, afterNavigate: after };
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* The forms' settings a story gives.
|
|
177
|
+
*
|
|
178
|
+
* @param forms - `sveltekit.forms`.
|
|
179
|
+
*/
|
|
180
|
+
const formsOf = (forms) => {
|
|
181
|
+
const read = recordOf(forms) ?? {};
|
|
182
|
+
const { enhance, result } = read;
|
|
183
|
+
return {
|
|
184
|
+
...(typeof enhance === "function"
|
|
185
|
+
? { enhance: () => Reflect.apply(enhance, undefined, []) }
|
|
186
|
+
: {}),
|
|
187
|
+
...(isRecord(result) && typeof result["type"] === "string" ? { result: actionResultOf(result) } : {})
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* An action's result read from a story's parameters.
|
|
192
|
+
*
|
|
193
|
+
* @param result - the value, an object with a `type`.
|
|
194
|
+
*/
|
|
195
|
+
export const actionResultOf = (result) => {
|
|
196
|
+
const status = typeof result["status"] === "number" ? result["status"] : undefined;
|
|
197
|
+
const data = recordOf(result["data"]);
|
|
198
|
+
switch (result["type"]) {
|
|
199
|
+
case "failure":
|
|
200
|
+
return { type: "failure", status: status ?? 400, ...(data === undefined ? {} : { data }) };
|
|
201
|
+
case "redirect":
|
|
202
|
+
return { type: "redirect", status: status ?? 303, location: String(result["location"] ?? "/") };
|
|
203
|
+
case "error":
|
|
204
|
+
return { type: "error", ...(status === undefined ? {} : { status }), error: result["error"] };
|
|
205
|
+
default:
|
|
206
|
+
return { type: "success", status: status ?? 200, ...(data === undefined ? {} : { data }) };
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* The links a story handles with a callback.
|
|
211
|
+
*
|
|
212
|
+
* @param hrefs - `sveltekit.hrefs`.
|
|
213
|
+
*/
|
|
214
|
+
const hrefsOf = (hrefs) => Object.fromEntries(Object.entries(recordOf(hrefs) ?? {}).flatMap(([href, entry]) => {
|
|
215
|
+
if (typeof entry === "function")
|
|
216
|
+
return [[href, (to, event) => entry(to, event)]];
|
|
217
|
+
const callback = isRecord(entry) ? entry["callback"] : undefined;
|
|
218
|
+
if (typeof callback !== "function")
|
|
219
|
+
return [];
|
|
220
|
+
return [
|
|
221
|
+
[
|
|
222
|
+
href,
|
|
223
|
+
{
|
|
224
|
+
callback: (to, event) => Reflect.apply(callback, undefined, [to, event]),
|
|
225
|
+
asRegex: isRecord(entry) && entry["asRegex"] === true
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
];
|
|
229
|
+
}));
|
|
230
|
+
/**
|
|
231
|
+
* A story's SvelteKit settings.
|
|
232
|
+
*
|
|
233
|
+
* @param parameters - the story's parameters (the preview's, the meta's and its own, merged).
|
|
234
|
+
* @param routes - the project's page routes, to read a path's route and params.
|
|
235
|
+
* @param base - the base path.
|
|
236
|
+
*/
|
|
237
|
+
export const storyKitOf = (parameters, routes, base) => {
|
|
238
|
+
const storybook = fromStorybook(recordOf(parameters["sveltekit_experimental"]) ?? {});
|
|
239
|
+
const own = recordOf(parameters["sveltekit"]) ?? {};
|
|
240
|
+
const read = (name) => (own[name] === undefined ? storybook[name] : own[name]);
|
|
241
|
+
const page = { ...(recordOf(storybook["page"]) ?? {}), ...(recordOf(own["page"]) ?? {}) };
|
|
242
|
+
const loads = loadsOf(read("load"));
|
|
243
|
+
const serverData = recordOf(read("serverData")) ?? null;
|
|
244
|
+
const navigating = read("navigating");
|
|
245
|
+
const forms = formsOf(read("forms"));
|
|
246
|
+
const readPage = pageOf(page, routes, base);
|
|
247
|
+
return {
|
|
248
|
+
// without a universal load, SvelteKit gives the page its server load's data as it is
|
|
249
|
+
page: loads.length === 0 && serverData !== null
|
|
250
|
+
? { ...readPage, data: { ...readPage.data, ...serverData } }
|
|
251
|
+
: readPage,
|
|
252
|
+
navigating: isRecord(navigating) ? navigating : null,
|
|
253
|
+
updated: read("updated") === true,
|
|
254
|
+
loads,
|
|
255
|
+
serverData,
|
|
256
|
+
isPage: page["data"] !== undefined || serverData !== null || loads.length > 0 || forms.result !== undefined,
|
|
257
|
+
navigation: navigationOf(read("navigation")),
|
|
258
|
+
forms,
|
|
259
|
+
hrefs: hrefsOf(read("hrefs"))
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Whether a key of `hrefs` matches an href: as it is, or as a regular expression; a key that is no valid one matches
|
|
264
|
+
* nothing.
|
|
265
|
+
*
|
|
266
|
+
* @param key - the key.
|
|
267
|
+
* @param isRegex - whether it is a regular expression.
|
|
268
|
+
* @param href - the link's href.
|
|
269
|
+
*/
|
|
270
|
+
const keyMatches = (key, isRegex, href) => {
|
|
271
|
+
if (!isRegex)
|
|
272
|
+
return key === href;
|
|
273
|
+
try {
|
|
274
|
+
return new RegExp(key).test(href);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* The callbacks of the `hrefs` entries a link's href matches, in order, each of them called as Storybook's SvelteKit
|
|
282
|
+
* calls them.
|
|
283
|
+
*
|
|
284
|
+
* @param hrefs - the story's links handled by a callback.
|
|
285
|
+
* @param href - the link's href, as written.
|
|
286
|
+
*/
|
|
287
|
+
export const hrefCallbacksOf = (hrefs, href) => Object.entries(hrefs).flatMap(([key, entry]) => {
|
|
288
|
+
const callback = typeof entry === "function" ? entry : entry.callback;
|
|
289
|
+
const isRegex = typeof entry !== "function" && entry.asRegex === true;
|
|
290
|
+
return keyMatches(key, isRegex, href) ? [callback] : [];
|
|
291
|
+
});
|