@forgebook/nuxt 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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -2
  3. package/dist/src/app/components.d.ts +119 -0
  4. package/dist/src/app/components.js +196 -0
  5. package/dist/src/app/data.d.ts +109 -0
  6. package/dist/src/app/data.js +155 -0
  7. package/dist/src/app/define.d.ts +30 -0
  8. package/dist/src/app/define.js +30 -0
  9. package/dist/src/app/errors.d.ts +43 -0
  10. package/dist/src/app/errors.js +41 -0
  11. package/dist/src/app/fetch.d.ts +63 -0
  12. package/dist/src/app/fetch.js +73 -0
  13. package/dist/src/app/head.d.ts +28 -0
  14. package/dist/src/app/head.js +23 -0
  15. package/dist/src/app/router.d.ts +69 -0
  16. package/dist/src/app/router.js +93 -0
  17. package/dist/src/app/state.d.ts +107 -0
  18. package/dist/src/app/state.js +137 -0
  19. package/dist/src/asyncDataRun.d.ts +39 -0
  20. package/dist/src/asyncDataRun.js +128 -0
  21. package/dist/src/nuxtError.d.ts +50 -0
  22. package/dist/src/nuxtError.js +65 -0
  23. package/dist/src/nuxtRenderer.d.ts +14 -0
  24. package/dist/src/nuxtRenderer.js +67 -0
  25. package/dist/src/nuxtStore.d.ts +144 -0
  26. package/dist/src/nuxtStore.js +196 -0
  27. package/dist/src/pageProject.d.ts +11 -0
  28. package/dist/src/pageProject.js +11 -0
  29. package/dist/src/parameters.d.ts +67 -0
  30. package/dist/src/parameters.js +89 -0
  31. package/dist/src/project.d.ts +49 -0
  32. package/dist/src/project.js +7 -0
  33. package/dist/src/renderer.d.ts +9 -0
  34. package/dist/src/renderer.js +10 -0
  35. package/dist/src/stories.d.ts +54 -0
  36. package/dist/src/stories.js +11 -0
  37. package/dist/src/storyCode.d.ts +3 -0
  38. package/dist/src/storyCode.js +9 -0
  39. package/dist/src/storyFetch.d.ts +74 -0
  40. package/dist/src/storyFetch.js +146 -0
  41. package/dist/src/storyNuxt.d.ts +48 -0
  42. package/dist/src/storyNuxt.js +26 -0
  43. package/dist/src/vite/componentNames.d.ts +36 -0
  44. package/dist/src/vite/componentNames.js +72 -0
  45. package/dist/src/vite/extractDocs.d.ts +16 -0
  46. package/dist/src/vite/extractDocs.js +22 -0
  47. package/dist/src/vite/nuxt.d.ts +13 -0
  48. package/dist/src/vite/nuxt.js +36 -0
  49. package/dist/src/vite/nuxtConfig.d.ts +85 -0
  50. package/dist/src/vite/nuxtConfig.js +261 -0
  51. package/dist/src/vite/nuxtPlugin.d.ts +51 -0
  52. package/dist/src/vite/nuxtPlugin.js +466 -0
  53. package/dist/src/vite/pageRoutes.d.ts +23 -0
  54. package/dist/src/vite/pageRoutes.js +88 -0
  55. package/dist/src/vite/placeKeys.d.ts +10 -0
  56. package/dist/src/vite/placeKeys.js +117 -0
  57. package/dist/src/vite/standIns.d.ts +26 -0
  58. package/dist/src/vite/standIns.js +96 -0
  59. package/package.json +118 -3
@@ -0,0 +1,144 @@
1
+ /**
2
+ * A story's Nuxt, while it is drawn: its router, the calls it made, the data, state and cookies its composables share,
3
+ * its runtime and app config, and the app `useNuxtApp` returns.
4
+ *
5
+ * @remarks
6
+ * The router is vue-router's own, on a memory history, with a route for each of the project's pages and one that
7
+ * takes any other path: a link clicked in a story moves it, `useRoute` follows, `back()` returns, and the frame's own
8
+ * address never changes (it names the story). Each move and each `navigateTo` is told to the catalogue, which lists it
9
+ * among the story's events.
10
+ *
11
+ * A story's composables find its store through Vue's injection, so each cell of a matrix has its own. Outside a
12
+ * component's setup (an event handler, a play function) they read the frame's current store, the story drawn last, as
13
+ * Nuxt's composables read its one app; it sits on a global symbol, so two copies of this module share it.
14
+ *
15
+ * @module
16
+ */
17
+ import { type App, type InjectionKey, type Ref } from "vue";
18
+ import { type RouteLocationRaw, type Router } from "vue-router";
19
+ import type { NuxtError } from "./nuxtError.ts";
20
+ import { type NuxtProject, type PublicRuntimeConfig } from "./project.ts";
21
+ import { type StoryFetch } from "./storyFetch.ts";
22
+ /** A call a story made to Nuxt: its function (`router.push`, `navigateTo`) and what it was called with. */
23
+ export type NuxtCall = {
24
+ /** The function, as the catalogue lists it. */
25
+ readonly name: string;
26
+ /** What it was called with: a location as its full path. */
27
+ readonly values: readonly unknown[];
28
+ };
29
+ /** Where an `useAsyncData` stands. */
30
+ export type AsyncDataStatus = "idle" | "pending" | "success" | "error";
31
+ /** What the `useAsyncData` and `useFetch` of one key share. */
32
+ export type AsyncDataEntry = {
33
+ /** What the handler returned, or the story's data for the key. */
34
+ readonly data: Ref<unknown>;
35
+ /** Where it stands. */
36
+ readonly status: Ref<AsyncDataStatus>;
37
+ /** What the handler threw. */
38
+ readonly error: Ref<NuxtError | undefined>;
39
+ /** The data the key has before a handler returns, and after `clearNuxtData`: the first call's `default`. */
40
+ readonly defaultData: () => unknown;
41
+ /** The run under way, which a second call with the key waits for instead of calling its handler again. */
42
+ running: Promise<void> | undefined;
43
+ /** The number of the latest run: a run that is no longer the latest drops its result. */
44
+ runId: number;
45
+ /** What aborts the run under way, when a later one starts. */
46
+ controller: AbortController | undefined;
47
+ /** Runs the handler of the first call again; nothing until a call gives one. */
48
+ refresh: (() => Promise<void>) | undefined;
49
+ };
50
+ /** Tells the catalogue about a call: the drawing's `onEvent`, when the catalogue gives one. */
51
+ export type CallReport = (name: string, values: readonly unknown[]) => void;
52
+ /** The app `useNuxtApp` returns: what of Nuxt's a component reads from it. */
53
+ export type NuxtAppStandIn = {
54
+ /** The story's Vue app, once the story's app is prepared. */
55
+ vueApp: App | undefined;
56
+ /** The payload: the data and state Nuxt would have sent from the server; empty, since there is no server. */
57
+ readonly payload: {
58
+ readonly data: Record<string, unknown>;
59
+ readonly state: Record<string, unknown>;
60
+ };
61
+ /** The public runtime config. */
62
+ readonly $config: PublicRuntimeConfig;
63
+ /** The story's router. */
64
+ readonly $router: Router;
65
+ /** Always `false`: a story is drawn in the page, never hydrated. */
66
+ readonly isHydrating: boolean;
67
+ /** Gives the app a value as `$name`, as a plugin's `provide` does. */
68
+ readonly provide: (name: string, value: unknown) => void;
69
+ /** Adds a listener to one of Nuxt's hooks; returns what removes it. */
70
+ readonly hook: (name: string, listener: (...values: unknown[]) => unknown) => () => void;
71
+ /** Calls the listeners of a hook, one after the other. */
72
+ readonly callHook: (name: string, ...values: unknown[]) => Promise<void>;
73
+ /** Runs a function, as it would run with the app as its context. */
74
+ readonly runWithContext: <TResult>(run: () => TResult) => TResult;
75
+ /** What `provide` gave, as `$name`. */
76
+ readonly provided: Record<string, unknown>;
77
+ };
78
+ /** A story's Nuxt. */
79
+ export type NuxtStore = {
80
+ /** The story's router. */
81
+ readonly router: Router;
82
+ /** The app `useNuxtApp` returns. */
83
+ readonly nuxtApp: NuxtAppStandIn;
84
+ /** The runtime config: the project's, the story's public values over it. */
85
+ readonly runtimeConfig: PublicRuntimeConfig;
86
+ /** The app config: the project's, the story's over it; reactive, as `updateAppConfig` changes it. */
87
+ readonly appConfig: Record<string, unknown>;
88
+ /** The data the story gives `useAsyncData` and `useFetch`, by key. */
89
+ readonly storyData: Readonly<Record<string, unknown>>;
90
+ /** What `useAsyncData` and `useFetch` share, by key. */
91
+ readonly asyncData: Map<string, AsyncDataEntry>;
92
+ /** The values the story gives `useState`, by key. */
93
+ readonly storyState: Readonly<Record<string, unknown>>;
94
+ /** What `useState` shares, by key. */
95
+ readonly state: Map<string, Ref<unknown>>;
96
+ /** The values the story gives `useCookie`, by name. */
97
+ readonly storyCookies: Readonly<Record<string, unknown>>;
98
+ /** What `useCookie` shares, by name. */
99
+ readonly cookies: Map<string, Ref<unknown>>;
100
+ /** The error `showError` set and `useError` returns. */
101
+ readonly error: Ref<NuxtError | undefined>;
102
+ /** The keys `callOnce` ran. */
103
+ readonly once: Set<string>;
104
+ /** `$fetch`, with the app's base path. */
105
+ readonly fetch: StoryFetch;
106
+ /** Records a call and tells the catalogue. */
107
+ readonly record: (name: string, values: readonly unknown[]) => void;
108
+ /** The calls the story made, in order. */
109
+ readonly calls: () => readonly NuxtCall[];
110
+ /** A key of its own for a call that gives none, as Nuxt's compiler writes one per call site. */
111
+ readonly nextKey: (kind: string) => string;
112
+ /** Moves the router without recording the move: for a function that records a call of its own (`navigateTo`). */
113
+ readonly move: (to: RouteLocationRaw, isReplace: boolean) => Promise<unknown>;
114
+ /**
115
+ * Whether the story is drawn on the server (a generated server test): there is no server to answer a request there,
116
+ * so `useAsyncData` runs no handler, and draws what the story's data gives.
117
+ */
118
+ readonly isServer: boolean;
119
+ /** Gives a story's app the router and the store. */
120
+ readonly install: (app: App) => void;
121
+ };
122
+ /** The key a story's app provides its store under. */
123
+ export declare const NUXT_STORE: InjectionKey<NuxtStore>;
124
+ /** The name of the route that takes any path the project's pages do not. */
125
+ export declare const ANY_PATH_NAME = "forgebook-any-path";
126
+ /**
127
+ * A story's Nuxt.
128
+ *
129
+ * @param project - what the page knows of the project.
130
+ * @param parameters - the story's parameters, which give its route, data, state, cookies and config.
131
+ * @param onEvent - tells the catalogue about each call (the drawing's `onEvent`); none outside a catalogue.
132
+ * @param place - where the story is drawn: in the page, or on the server; the page by default.
133
+ */
134
+ export declare const createNuxtStore: (project: NuxtProject, parameters: Readonly<Record<string, unknown>>, onEvent: CallReport | undefined, place?: "page" | "server") => NuxtStore;
135
+ /**
136
+ * Makes a store the frame's current one: the story drawn last.
137
+ *
138
+ * @param store - the store.
139
+ */
140
+ export declare const setCurrentStore: (store: NuxtStore) => void;
141
+ /** The frame's current store: the story drawn last, or one made at `/` for code run outside a story. */
142
+ export declare const currentStore: () => NuxtStore;
143
+ /** The story's store: its app's, in a component's setup, else the frame's current one. */
144
+ export declare const useNuxtStore: () => NuxtStore;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * A story's Nuxt, while it is drawn: its router, the calls it made, the data, state and cookies its composables share,
3
+ * its runtime and app config, and the app `useNuxtApp` returns.
4
+ *
5
+ * @remarks
6
+ * The router is vue-router's own, on a memory history, with a route for each of the project's pages and one that
7
+ * takes any other path: a link clicked in a story moves it, `useRoute` follows, `back()` returns, and the frame's own
8
+ * address never changes (it names the story). Each move and each `navigateTo` is told to the catalogue, which lists it
9
+ * among the story's events.
10
+ *
11
+ * A story's composables find its store through Vue's injection, so each cell of a matrix has its own. Outside a
12
+ * component's setup (an event handler, a play function) they read the frame's current store, the story drawn last, as
13
+ * Nuxt's composables read its one app; it sits on a global symbol, so two copies of this module share it.
14
+ *
15
+ * @module
16
+ */
17
+ import { defineComponent, hasInjectionContext, inject, reactive, ref } from "vue";
18
+ import { createMemoryHistory, createRouter } from "vue-router";
19
+ import { mergedRecords, storyNuxtOf } from "./parameters.js";
20
+ import { EMPTY_PROJECT } from "./project.js";
21
+ import { createStoryFetch } from "./storyFetch.js";
22
+ /** The key a story's app provides its store under. */
23
+ export const NUXT_STORE = Symbol("forgebook.nuxt.store");
24
+ /** What a page's route draws: nothing, since a story draws its own component, not the page. */
25
+ const EMPTY_PAGE = defineComponent({ name: "ForgebookNuxtPage", render: () => null });
26
+ /** The name of the route that takes any path the project's pages do not. */
27
+ export const ANY_PATH_NAME = "forgebook-any-path";
28
+ /** The route that takes any path the project's pages do not. */
29
+ const ANY_PATH = { path: "/:forgebookPath(.*)*", name: ANY_PATH_NAME, component: EMPTY_PAGE };
30
+ /**
31
+ * The app `useNuxtApp` returns.
32
+ *
33
+ * @param router - the story's router.
34
+ * @param config - the runtime config.
35
+ */
36
+ const createNuxtApp = (router, config) => {
37
+ const hooks = new Map();
38
+ const provided = {};
39
+ return {
40
+ vueApp: undefined,
41
+ payload: { data: {}, state: {} },
42
+ // biome-ignore lint/style/useNamingConvention: Nuxt's app names it so
43
+ $config: config,
44
+ // biome-ignore lint/style/useNamingConvention: Nuxt's app names it so
45
+ $router: router,
46
+ isHydrating: false,
47
+ provided,
48
+ provide: (name, value) => {
49
+ provided[`$${name}`] = value;
50
+ },
51
+ hook: (name, listener) => {
52
+ hooks.set(name, [...(hooks.get(name) ?? []), listener]);
53
+ return () => {
54
+ hooks.set(name, (hooks.get(name) ?? []).filter((each) => each !== listener));
55
+ };
56
+ },
57
+ callHook: async (name, ...values) => {
58
+ for (const listener of hooks.get(name) ?? [])
59
+ await listener(...values);
60
+ },
61
+ runWithContext: (run) => run()
62
+ };
63
+ };
64
+ /**
65
+ * The router's methods wrapped so each move is recorded, as a story's events list it.
66
+ *
67
+ * @param router - the router.
68
+ * @param record - records a call.
69
+ */
70
+ const recordMoves = (router, record) => {
71
+ const { push, replace, go, back, forward } = router;
72
+ const pathOf = (to) => {
73
+ try {
74
+ return router.resolve(to).fullPath;
75
+ }
76
+ catch {
77
+ return typeof to === "string" ? to : JSON.stringify(to);
78
+ }
79
+ };
80
+ router.push = (to) => {
81
+ record("router.push", [pathOf(to)]);
82
+ return push(to);
83
+ };
84
+ router.replace = (to) => {
85
+ record("router.replace", [pathOf(to)]);
86
+ return replace(to);
87
+ };
88
+ router.go = (delta) => {
89
+ record("router.go", [delta]);
90
+ go(delta);
91
+ };
92
+ router.back = () => {
93
+ record("router.back", []);
94
+ back();
95
+ };
96
+ router.forward = () => {
97
+ record("router.forward", []);
98
+ forward();
99
+ };
100
+ };
101
+ /**
102
+ * A story's Nuxt.
103
+ *
104
+ * @param project - what the page knows of the project.
105
+ * @param parameters - the story's parameters, which give its route, data, state, cookies and config.
106
+ * @param onEvent - tells the catalogue about each call (the drawing's `onEvent`); none outside a catalogue.
107
+ * @param place - where the story is drawn: in the page, or on the server; the page by default.
108
+ */
109
+ export const createNuxtStore = (project, parameters, onEvent, place = "page") => {
110
+ const read = storyNuxtOf(parameters);
111
+ const runtimeConfig = {
112
+ public: mergedRecords(project.runtimeConfig.public, read.publicRuntimeConfig),
113
+ app: project.runtimeConfig.app
114
+ };
115
+ const history = createMemoryHistory(runtimeConfig.app.baseURL);
116
+ const router = createRouter({
117
+ history,
118
+ routes: [...project.routes.map((route) => ({ ...route, component: EMPTY_PAGE })), ANY_PATH]
119
+ });
120
+ // the story's route before anything is drawn: a component reads its params once, in its setup
121
+ let start = router.resolve("/");
122
+ try {
123
+ start = router.resolve(read.route);
124
+ }
125
+ catch {
126
+ // a location naming no route stays at `/`
127
+ }
128
+ history.replace(start.fullPath);
129
+ // every route has a name (the project's pages and the one for any other path), which the current route requires
130
+ if (start.name !== null)
131
+ router.currentRoute.value = { ...start, name: start.name };
132
+ const { push, replace } = router;
133
+ const made = [];
134
+ const record = (name, values) => {
135
+ made.push({ name, values });
136
+ onEvent?.(name, values);
137
+ };
138
+ recordMoves(router, record);
139
+ let keys = 0;
140
+ const nuxtApp = createNuxtApp(router, runtimeConfig);
141
+ const store = {
142
+ router,
143
+ nuxtApp,
144
+ runtimeConfig,
145
+ appConfig: reactive([...project.appConfigs, read.appConfig].reduce(mergedRecords, {})),
146
+ storyData: read.data,
147
+ asyncData: new Map(),
148
+ storyState: read.state,
149
+ state: new Map(),
150
+ storyCookies: read.cookies,
151
+ cookies: new Map(),
152
+ error: ref(undefined),
153
+ once: new Set(),
154
+ fetch: createStoryFetch(runtimeConfig.app.baseURL),
155
+ record,
156
+ calls: () => [...made],
157
+ nextKey: (kind) => {
158
+ keys += 1;
159
+ return `$forgebook-${kind}-${keys}`;
160
+ },
161
+ isServer: place === "server",
162
+ move: (to, isReplace) => isReplace ? replace(to) : push(to),
163
+ install: (app) => {
164
+ nuxtApp.vueApp = app;
165
+ app.use(router);
166
+ app.provide(NUXT_STORE, store);
167
+ app.config.globalProperties["$nuxt"] = nuxtApp;
168
+ // the route is already set, so vue-router does not start; a navigation to it makes it ready and listening
169
+ void replace({ path: start.path, query: start.query, hash: start.hash, force: true }).catch(() => undefined);
170
+ }
171
+ };
172
+ return store;
173
+ };
174
+ /** Where the frame's current store is kept: one key for every copy of this module. */
175
+ const CURRENT_KEY = Symbol.for("forgebook.nuxt.store");
176
+ /**
177
+ * Makes a store the frame's current one: the story drawn last.
178
+ *
179
+ * @param store - the store.
180
+ */
181
+ export const setCurrentStore = (store) => {
182
+ Reflect.set(globalThis, CURRENT_KEY, store);
183
+ };
184
+ /** Whether a value is a store this module made. */
185
+ const isNuxtStore = (value) => typeof value === "object" && value !== null && "router" in value && "nuxtApp" in value && "record" in value;
186
+ /** The frame's current store: the story drawn last, or one made at `/` for code run outside a story. */
187
+ export const currentStore = () => {
188
+ const store = Reflect.get(globalThis, CURRENT_KEY);
189
+ if (isNuxtStore(store))
190
+ return store;
191
+ const idle = createNuxtStore(EMPTY_PROJECT, {}, undefined);
192
+ setCurrentStore(idle);
193
+ return idle;
194
+ };
195
+ /** The story's store: its app's, in a component's setup, else the frame's current one. */
196
+ export const useNuxtStore = () => (hasInjectionContext() ? inject(NUXT_STORE, undefined) : undefined) ?? currentStore();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The project the page knows, as the plugin writes it: in a catalogue, the plugin answers this module with the
3
+ * project's own (its routes, config and components, `nuxtPlugin.ts`); anywhere else it is an empty project.
4
+ *
5
+ * Internal: the code the plugin writes imports it (the renderer reads it); a story never imports this module, whose exports may change in any release.
6
+ *
7
+ * @module
8
+ */
9
+ import { type NuxtProject } from "./project.ts";
10
+ /** The project the page knows: replaced by the project's own in a catalogue. */
11
+ export declare const project: NuxtProject;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The project the page knows, as the plugin writes it: in a catalogue, the plugin answers this module with the
3
+ * project's own (its routes, config and components, `nuxtPlugin.ts`); anywhere else it is an empty project.
4
+ *
5
+ * Internal: the code the plugin writes imports it (the renderer reads it); a story never imports this module, whose exports may change in any release.
6
+ *
7
+ * @module
8
+ */
9
+ import { EMPTY_PROJECT } from "./project.js";
10
+ /** The project the page knows: replaced by the project's own in a catalogue. */
11
+ export const project = EMPTY_PROJECT;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * A story's Nuxt settings (`parameters.nuxt`), read from the story's parameters: where it starts, the data, state and
3
+ * cookies its composables find, and the config over the project's.
4
+ *
5
+ * @remarks
6
+ * Pure: the parameters are values a story file wrote, typed `unknown` by the frame, so each is read with what it must
7
+ * be and anything else left out. A route is kept as a path, or as a location by path or by name, which is what
8
+ * vue-router resolves.
9
+ *
10
+ * @module
11
+ */
12
+ /** A route's query or params: a text, or several, by name. */
13
+ export type RouteValues = Record<string, string | string[]>;
14
+ /** Where a story starts: a path (`/blog/hello?tab=1`), or a location by path or by a route's name. */
15
+ export type StoryLocation = string | {
16
+ /** The path. */
17
+ readonly path: string;
18
+ /** The search params, by name. */
19
+ readonly query?: RouteValues;
20
+ /** The hash, with its `#`. */
21
+ readonly hash?: string;
22
+ } | {
23
+ /** The route's name, as Nuxt names it from its file (`blog-slug`). */
24
+ readonly name: string;
25
+ /** The route's params (`{ slug: "hello" }`). */
26
+ readonly params?: RouteValues;
27
+ /** The search params, by name. */
28
+ readonly query?: RouteValues;
29
+ /** The hash, with its `#`. */
30
+ readonly hash?: string;
31
+ };
32
+ /** A story's Nuxt settings, read. */
33
+ export type StoryNuxt = {
34
+ /** Where the story starts; `/` when it says nothing. */
35
+ readonly route: StoryLocation;
36
+ /** What `useAsyncData` and `useFetch` return, by key, instead of calling their handler. */
37
+ readonly data: Readonly<Record<string, unknown>>;
38
+ /** What `useState` starts with, by key. */
39
+ readonly state: Readonly<Record<string, unknown>>;
40
+ /** What `useCookie` reads, by name. */
41
+ readonly cookies: Readonly<Record<string, unknown>>;
42
+ /** The public runtime config over the project's. */
43
+ readonly publicRuntimeConfig: Readonly<Record<string, unknown>>;
44
+ /** The app config over the project's. */
45
+ readonly appConfig: Readonly<Record<string, unknown>>;
46
+ };
47
+ /** Whether a value is a plain object. */
48
+ export declare const isRecord: (value: unknown) => value is Readonly<Record<string, unknown>>;
49
+ /**
50
+ * Where a story starts, read from its `nuxt.route`: `/` for anything that is not a path or a location.
51
+ *
52
+ * @param value - `nuxt.route`.
53
+ */
54
+ export declare const storyLocationOf: (value: unknown) => StoryLocation;
55
+ /**
56
+ * A story's Nuxt settings, from its parameters (the preview's, the meta's and its own, merged).
57
+ *
58
+ * @param parameters - the story's parameters.
59
+ */
60
+ export declare const storyNuxtOf: (parameters: Readonly<Record<string, unknown>>) => StoryNuxt;
61
+ /**
62
+ * Two objects merged, the second's values over the first's and objects merged in depth, as Nuxt merges its configs.
63
+ *
64
+ * @param base - the first.
65
+ * @param over - the second.
66
+ */
67
+ export declare const mergedRecords: (base: Readonly<Record<string, unknown>>, over: Readonly<Record<string, unknown>>) => Readonly<Record<string, unknown>>;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * A story's Nuxt settings (`parameters.nuxt`), read from the story's parameters: where it starts, the data, state and
3
+ * cookies its composables find, and the config over the project's.
4
+ *
5
+ * @remarks
6
+ * Pure: the parameters are values a story file wrote, typed `unknown` by the frame, so each is read with what it must
7
+ * be and anything else left out. A route is kept as a path, or as a location by path or by name, which is what
8
+ * vue-router resolves.
9
+ *
10
+ * @module
11
+ */
12
+ /** Whether a value is a plain object. */
13
+ export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14
+ /**
15
+ * An object read from a story's parameters, or an empty one.
16
+ *
17
+ * @param value - the value.
18
+ */
19
+ const recordOf = (value) => (isRecord(value) ? value : {});
20
+ /**
21
+ * A query or params read from a story's parameters: what is not a text, a number or a list of them left out.
22
+ *
23
+ * @param value - the value.
24
+ */
25
+ const routeValues = (value) => Object.fromEntries(Object.entries(recordOf(value)).flatMap(([name, item]) => {
26
+ if (typeof item === "string" || typeof item === "number")
27
+ return [[name, String(item)]];
28
+ if (Array.isArray(item))
29
+ return [
30
+ [
31
+ name,
32
+ item.flatMap((each) => typeof each === "string" || typeof each === "number" ? [String(each)] : [])
33
+ ]
34
+ ];
35
+ return [];
36
+ }));
37
+ /**
38
+ * The hash of a location read from a story's parameters, when it gives one.
39
+ *
40
+ * @param location - the location.
41
+ */
42
+ const hashOf = (location) => typeof location["hash"] === "string" ? { hash: location["hash"] } : {};
43
+ /**
44
+ * Where a story starts, read from its `nuxt.route`: `/` for anything that is not a path or a location.
45
+ *
46
+ * @param value - `nuxt.route`.
47
+ */
48
+ export const storyLocationOf = (value) => {
49
+ if (typeof value === "string")
50
+ return value;
51
+ if (!isRecord(value))
52
+ return "/";
53
+ const query = routeValues(value["query"]);
54
+ if (typeof value["name"] === "string")
55
+ return { name: value["name"], params: routeValues(value["params"]), query, ...hashOf(value) };
56
+ if (typeof value["path"] === "string")
57
+ return { path: value["path"], query, ...hashOf(value) };
58
+ return "/";
59
+ };
60
+ /**
61
+ * A story's Nuxt settings, from its parameters (the preview's, the meta's and its own, merged).
62
+ *
63
+ * @param parameters - the story's parameters.
64
+ */
65
+ export const storyNuxtOf = (parameters) => {
66
+ const nuxt = recordOf(parameters["nuxt"]);
67
+ return {
68
+ route: storyLocationOf(nuxt["route"]),
69
+ data: recordOf(nuxt["data"]),
70
+ state: recordOf(nuxt["state"]),
71
+ cookies: recordOf(nuxt["cookies"]),
72
+ publicRuntimeConfig: recordOf(recordOf(nuxt["runtimeConfig"])["public"]),
73
+ appConfig: recordOf(nuxt["appConfig"])
74
+ };
75
+ };
76
+ /**
77
+ * Two objects merged, the second's values over the first's and objects merged in depth, as Nuxt merges its configs.
78
+ *
79
+ * @param base - the first.
80
+ * @param over - the second.
81
+ */
82
+ export const mergedRecords = (base, over) => {
83
+ const merged = { ...base };
84
+ for (const [name, value] of Object.entries(over)) {
85
+ const known = merged[name];
86
+ merged[name] = isRecord(known) && isRecord(value) ? mergedRecords(known, value) : value;
87
+ }
88
+ return merged;
89
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * What the page knows of a Nuxt project, as the plugin reads it on Node and hands it to the frame: its pages' routes,
3
+ * its public runtime config, its app config and its components.
4
+ *
5
+ * @remarks
6
+ * The plugin writes it as a module (`@forgebook/nuxt/project`, `pageProject.ts`), so it holds only what may reach a page: the
7
+ * runtime config's `public` part and `app`, never its private keys, which Nuxt keeps on the server too.
8
+ *
9
+ * @module
10
+ */
11
+ import type { Component } from "vue";
12
+ /** A route of the project's pages, as Nuxt names it from its file (`pages/blog/[slug].vue`). */
13
+ export type ProjectRoute = {
14
+ /** The route's path, as vue-router writes it (`/blog/:slug()`). */
15
+ readonly path: string;
16
+ /** The route's name, as Nuxt writes it (`blog-slug`). */
17
+ readonly name: string;
18
+ };
19
+ /** The runtime config the page reads: `useRuntimeConfig()`. */
20
+ export type PublicRuntimeConfig = {
21
+ /** The project's `runtimeConfig.public`, its `NUXT_PUBLIC_` variables over it. */
22
+ readonly public: Readonly<Record<string, unknown>>;
23
+ /** Where the app is served from. */
24
+ readonly app: {
25
+ /** The app's base path (`app.baseURL`); `/` by default. */
26
+ readonly baseURL: string;
27
+ /** Where Nuxt serves its built files from; `/_nuxt/` by default. */
28
+ readonly buildAssetsDir: string;
29
+ /** The CDN the files are served from; empty by default. */
30
+ readonly cdnURL: string;
31
+ };
32
+ };
33
+ /** Loads one of the project's components, for a component a story's template names without importing it. */
34
+ export type ComponentLoader = () => Promise<Component | {
35
+ readonly default: Component;
36
+ }>;
37
+ /** What the page knows of a Nuxt project. */
38
+ export type NuxtProject = {
39
+ /** The routes of its pages; none when it has no `pages` folder. */
40
+ readonly routes: readonly ProjectRoute[];
41
+ /** Its runtime config, as the page reads it. */
42
+ readonly runtimeConfig: PublicRuntimeConfig;
43
+ /** Its app configs, the later over the earlier: `nuxt.config`'s `appConfig`, then `app.config.ts`'s. */
44
+ readonly appConfigs: readonly Readonly<Record<string, unknown>>[];
45
+ /** Its components, by the name Nuxt gives each, loaded when a template names one. */
46
+ readonly components: Readonly<Record<string, ComponentLoader>>;
47
+ };
48
+ /** The project of a page with no Nuxt project read: no pages, an empty config, no components. */
49
+ export declare const EMPTY_PROJECT: NuxtProject;
@@ -0,0 +1,7 @@
1
+ /** The project of a page with no Nuxt project read: no pages, an empty config, no components. */
2
+ export const EMPTY_PROJECT = {
3
+ routes: [],
4
+ runtimeConfig: { public: {}, app: { baseURL: "/", buildAssetsDir: "/_nuxt/", cdnURL: "" } },
5
+ appConfigs: [],
6
+ components: {}
7
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The renderer of a Nuxt catalogue, over the project the plugin read: what the frame page draws with, and what the
3
+ * generated server tests take (`storyServerTests(source, { renderer: nuxtRenderer })`).
4
+ *
5
+ * @module
6
+ */
7
+ import type { StoryRenderer } from "forgebook/renderer";
8
+ /** Vue's renderer, each story given its Nuxt. */
9
+ export declare const nuxtRenderer: StoryRenderer;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The renderer of a Nuxt catalogue, over the project the plugin read: what the frame page draws with, and what the
3
+ * generated server tests take (`storyServerTests(source, { renderer: nuxtRenderer })`).
4
+ *
5
+ * @module
6
+ */
7
+ import { project } from "@forgebook/nuxt/project";
8
+ import { createNuxtRenderer } from "./nuxtRenderer.js";
9
+ /** Vue's renderer, each story given its Nuxt. */
10
+ export const nuxtRenderer = createNuxtRenderer(project);
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The story types for a Nuxt project: Vue's, with the `parameters.nuxt` a story sets its route, data, state, cookies and
3
+ * config with (`import type { Meta, Story } from "@forgebook/nuxt"`).
4
+ *
5
+ * @remarks
6
+ * Types only. `route` is a path or a location by path or by name, resolved against the project's pages
7
+ * (`{ name: "blog-slug", params: { slug: "hello" } }` or `/blog/hello`); `data` is what `useAsyncData` and `useFetch`
8
+ * return by key instead of calling their handler (`useFetch`'s key is the request as it is made, its URL with its query, unless it is given one).
9
+ *
10
+ * @module
11
+ */
12
+ import type { StoryParameters } from "@forgebook/story-format/csf";
13
+ import type { AnyComponent, NoComponent, Meta as VueMeta, Preview as VuePreview, Story as VueStory } from "@forgebook/vue";
14
+ import type { StoryLocation } from "./parameters.ts";
15
+ /** A story's Nuxt settings: where it starts, what its composables find, the config over the project's. */
16
+ export type NuxtParameters = {
17
+ /**
18
+ * Where the story starts: a path (`/blog/hello?tab=1`), a location by path (`{ path, query, hash }`) or by a page's
19
+ * name (`{ name: "blog-slug", params: { slug: "hello" } }`); `/` by default.
20
+ */
21
+ readonly route?: StoryLocation;
22
+ /** What `useAsyncData` and `useFetch` return, by key, without calling their handler. */
23
+ readonly data?: Readonly<Record<string, unknown>>;
24
+ /** What `useState` starts with, by key. */
25
+ readonly state?: Readonly<Record<string, unknown>>;
26
+ /** What `useCookie` reads, by name. */
27
+ readonly cookies?: Readonly<Record<string, unknown>>;
28
+ /** The runtime config's public values over the project's. */
29
+ readonly runtimeConfig?: {
30
+ readonly public?: Readonly<Record<string, unknown>>;
31
+ };
32
+ /** The app config over the project's. */
33
+ readonly appConfig?: Readonly<Record<string, unknown>>;
34
+ };
35
+ /** A story's parameters: the catalogue's settings, and its Nuxt ones. */
36
+ export type NuxtStoryParameters = StoryParameters & {
37
+ /** The story's route, data, state, cookies and config. */
38
+ readonly nuxt?: NuxtParameters;
39
+ };
40
+ /** A story file's default export: `{ title, component, parameters: { nuxt } } satisfies Meta<typeof Nav>`. */
41
+ export type Meta<TComponent extends AnyComponent = NoComponent> = Omit<VueMeta<TComponent>, "parameters"> & {
42
+ /** Settings for every story of the file, its Nuxt among them. */
43
+ readonly parameters?: NuxtStoryParameters;
44
+ };
45
+ /** A story: a named export of a story file. */
46
+ export type Story<TComponent extends AnyComponent = NoComponent> = Omit<VueStory<TComponent>, "parameters"> & {
47
+ /** Settings over the meta's, its Nuxt among them. */
48
+ readonly parameters?: NuxtStoryParameters;
49
+ };
50
+ /** A Nuxt catalogue's preview (`forgebook.preview.ts`): Vue's, with Nuxt settings every story starts from. */
51
+ export type Preview = Omit<VuePreview, "parameters"> & {
52
+ /** Settings every story starts from, its Nuxt among them. */
53
+ readonly parameters?: NuxtStoryParameters;
54
+ };