@forgebook/nuxt 0.0.0-stage → 0.2.1
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 +121 -2
- package/dist/src/app/components.d.ts +119 -0
- package/dist/src/app/components.js +196 -0
- package/dist/src/app/data.d.ts +109 -0
- package/dist/src/app/data.js +155 -0
- package/dist/src/app/define.d.ts +30 -0
- package/dist/src/app/define.js +30 -0
- package/dist/src/app/errors.d.ts +43 -0
- package/dist/src/app/errors.js +41 -0
- package/dist/src/app/fetch.d.ts +63 -0
- package/dist/src/app/fetch.js +73 -0
- package/dist/src/app/head.d.ts +28 -0
- package/dist/src/app/head.js +23 -0
- package/dist/src/app/router.d.ts +69 -0
- package/dist/src/app/router.js +93 -0
- package/dist/src/app/state.d.ts +107 -0
- package/dist/src/app/state.js +137 -0
- package/dist/src/asyncDataRun.d.ts +39 -0
- package/dist/src/asyncDataRun.js +128 -0
- package/dist/src/nuxtError.d.ts +50 -0
- package/dist/src/nuxtError.js +65 -0
- package/dist/src/nuxtRenderer.d.ts +14 -0
- package/dist/src/nuxtRenderer.js +67 -0
- package/dist/src/nuxtStore.d.ts +144 -0
- package/dist/src/nuxtStore.js +196 -0
- package/dist/src/pageProject.d.ts +11 -0
- package/dist/src/pageProject.js +11 -0
- package/dist/src/parameters.d.ts +67 -0
- package/dist/src/parameters.js +89 -0
- package/dist/src/project.d.ts +49 -0
- package/dist/src/project.js +7 -0
- package/dist/src/renderer.d.ts +9 -0
- package/dist/src/renderer.js +10 -0
- package/dist/src/stories.d.ts +54 -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/storyFetch.d.ts +74 -0
- package/dist/src/storyFetch.js +146 -0
- package/dist/src/storyNuxt.d.ts +48 -0
- package/dist/src/storyNuxt.js +26 -0
- package/dist/src/vite/componentNames.d.ts +36 -0
- package/dist/src/vite/componentNames.js +72 -0
- package/dist/src/vite/extractDocs.d.ts +16 -0
- package/dist/src/vite/extractDocs.js +22 -0
- package/dist/src/vite/nuxt.d.ts +13 -0
- package/dist/src/vite/nuxt.js +36 -0
- package/dist/src/vite/nuxtConfig.d.ts +85 -0
- package/dist/src/vite/nuxtConfig.js +261 -0
- package/dist/src/vite/nuxtPlugin.d.ts +51 -0
- package/dist/src/vite/nuxtPlugin.js +466 -0
- package/dist/src/vite/pageRoutes.d.ts +23 -0
- package/dist/src/vite/pageRoutes.js +88 -0
- package/dist/src/vite/placeKeys.d.ts +10 -0
- package/dist/src/vite/placeKeys.js +117 -0
- package/dist/src/vite/standIns.d.ts +26 -0
- package/dist/src/vite/standIns.js +96 -0
- package/package.json +118 -3
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
*/
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The code writer of a Nuxt catalogue: Vue's template writer, since a Nuxt story is a Vue story. The shell's page
|
|
3
|
+
* imports it from this package, which a Nuxt catalogue installs, rather than from `@forgebook/vue`, which it need not.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { codeOf as vueCodeOf } from "@forgebook/vue/code";
|
|
8
|
+
/** Writes a story drawn from its args as a template, as Vue's adapter writes it. */
|
|
9
|
+
export const codeOf = vueCodeOf;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt's `$fetch` in a catalogue: the browser's `fetch` with what ofetch, Nuxt's `$fetch`, adds that a component relies
|
|
3
|
+
* on: the app's base path, a query, a body sent as JSON, the answer read as JSON or text, and an error that carries the
|
|
4
|
+
* status and the answer's data when the status is not a success.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Written on `fetch` rather than taking ofetch, so a catalogue's page loads nothing it would have to find in the
|
|
8
|
+
* project's installed packages, and the catalogue's MSW handlers answer it as they answer `fetch`. ofetch's retries
|
|
9
|
+
* and interceptors are left out: a story's answers come at once.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** A query's values, by name. */
|
|
14
|
+
export type FetchQuery = Readonly<Record<string, unknown>>;
|
|
15
|
+
/** What `$fetch` takes besides the address. */
|
|
16
|
+
export type StoryFetchOptions = {
|
|
17
|
+
/** The method; GET by default. */
|
|
18
|
+
readonly method?: string;
|
|
19
|
+
/** The search params, by name: a list repeats its name, `undefined` is left out. */
|
|
20
|
+
readonly query?: FetchQuery;
|
|
21
|
+
/** `query`'s other name. */
|
|
22
|
+
readonly params?: FetchQuery;
|
|
23
|
+
/** The body: an object or an array is sent as JSON. */
|
|
24
|
+
readonly body?: unknown;
|
|
25
|
+
/** The headers. */
|
|
26
|
+
readonly headers?: HeadersInit;
|
|
27
|
+
/** What a relative address is resolved against; the app's base path by default. */
|
|
28
|
+
readonly baseURL?: string;
|
|
29
|
+
/** How the answer is read; from its content type by default. */
|
|
30
|
+
readonly responseType?: "json" | "text" | "blob" | "arrayBuffer";
|
|
31
|
+
/** What aborts the request. */
|
|
32
|
+
readonly signal?: AbortSignal;
|
|
33
|
+
};
|
|
34
|
+
/** The error of an answer whose status is not a success. */
|
|
35
|
+
export declare class FetchError extends Error {
|
|
36
|
+
/** The answer's status. */
|
|
37
|
+
readonly statusCode: number;
|
|
38
|
+
/** The answer's status text. */
|
|
39
|
+
readonly statusMessage: string;
|
|
40
|
+
/** The answer's body, read. */
|
|
41
|
+
readonly data: unknown;
|
|
42
|
+
/**
|
|
43
|
+
* The error of an answer.
|
|
44
|
+
*
|
|
45
|
+
* @param address - the address asked.
|
|
46
|
+
* @param response - the answer.
|
|
47
|
+
* @param data - its body, read.
|
|
48
|
+
*/
|
|
49
|
+
constructor(address: string, response: Response, data: unknown);
|
|
50
|
+
}
|
|
51
|
+
/** Fetches an address, as Nuxt's `$fetch`. */
|
|
52
|
+
export type StoryFetch = <TResult = unknown>(address: string, options?: StoryFetchOptions) => Promise<TResult>;
|
|
53
|
+
/**
|
|
54
|
+
* An address with its base and query: a relative one resolved against the base, as ofetch does.
|
|
55
|
+
*
|
|
56
|
+
* @param address - the address.
|
|
57
|
+
* @param baseURL - the base.
|
|
58
|
+
* @param query - the search params.
|
|
59
|
+
*/
|
|
60
|
+
export declare const fetchAddress: (address: string, baseURL: string, query: FetchQuery) => string;
|
|
61
|
+
/**
|
|
62
|
+
* An address with a query after it: a list repeats its name, `undefined` is left out.
|
|
63
|
+
*
|
|
64
|
+
* @param address - the address, which may have a search of its own.
|
|
65
|
+
* @param query - the search params.
|
|
66
|
+
*/
|
|
67
|
+
export declare const withSearch: (address: string, query: FetchQuery) => string;
|
|
68
|
+
/**
|
|
69
|
+
* `$fetch` with a base path.
|
|
70
|
+
*
|
|
71
|
+
* @param baseURL - the app's base path.
|
|
72
|
+
* @returns the function.
|
|
73
|
+
*/
|
|
74
|
+
export declare const createStoryFetch: (baseURL: string) => StoryFetch;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nuxt's `$fetch` in a catalogue: the browser's `fetch` with what ofetch, Nuxt's `$fetch`, adds that a component relies
|
|
3
|
+
* on: the app's base path, a query, a body sent as JSON, the answer read as JSON or text, and an error that carries the
|
|
4
|
+
* status and the answer's data when the status is not a success.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Written on `fetch` rather than taking ofetch, so a catalogue's page loads nothing it would have to find in the
|
|
8
|
+
* project's installed packages, and the catalogue's MSW handlers answer it as they answer `fetch`. ofetch's retries
|
|
9
|
+
* and interceptors are left out: a story's answers come at once.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** The error of an answer whose status is not a success. */
|
|
14
|
+
export class FetchError extends Error {
|
|
15
|
+
/** The answer's status. */
|
|
16
|
+
statusCode;
|
|
17
|
+
/** The answer's status text. */
|
|
18
|
+
statusMessage;
|
|
19
|
+
/** The answer's body, read. */
|
|
20
|
+
data;
|
|
21
|
+
/**
|
|
22
|
+
* The error of an answer.
|
|
23
|
+
*
|
|
24
|
+
* @param address - the address asked.
|
|
25
|
+
* @param response - the answer.
|
|
26
|
+
* @param data - its body, read.
|
|
27
|
+
*/
|
|
28
|
+
constructor(address, response, data) {
|
|
29
|
+
super(`[${response.status}] ${response.statusText} ${address}`.replace(/\s+/g, " ").trim());
|
|
30
|
+
this.name = "FetchError";
|
|
31
|
+
this.statusCode = response.status;
|
|
32
|
+
this.statusMessage = response.statusText;
|
|
33
|
+
this.data = data;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* An address with its base and query: a relative one resolved against the base, as ofetch does.
|
|
38
|
+
*
|
|
39
|
+
* @param address - the address.
|
|
40
|
+
* @param baseURL - the base.
|
|
41
|
+
* @param query - the search params.
|
|
42
|
+
*/
|
|
43
|
+
export const fetchAddress = (address, baseURL, query) => {
|
|
44
|
+
const isAbsolute = /^[a-z][a-z\d+.-]*:/i.test(address) || address.startsWith("//");
|
|
45
|
+
const joined = isAbsolute ? address : `${baseURL.replace(/\/+$/, "")}/${address.replace(/^\/+/, "")}`;
|
|
46
|
+
return withSearch(joined, query);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* An address with a query after it: a list repeats its name, `undefined` is left out.
|
|
50
|
+
*
|
|
51
|
+
* @param address - the address, which may have a search of its own.
|
|
52
|
+
* @param query - the search params.
|
|
53
|
+
*/
|
|
54
|
+
export const withSearch = (address, query) => {
|
|
55
|
+
const search = new URLSearchParams();
|
|
56
|
+
for (const [name, value] of Object.entries(query)) {
|
|
57
|
+
if (value === undefined)
|
|
58
|
+
continue;
|
|
59
|
+
for (const item of Array.isArray(value) ? value : [value])
|
|
60
|
+
search.append(name, String(item));
|
|
61
|
+
}
|
|
62
|
+
const text = search.toString();
|
|
63
|
+
if (text === "")
|
|
64
|
+
return address;
|
|
65
|
+
return `${address}${address.includes("?") ? "&" : "?"}${text}`;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* A body as `fetch` sends it: an object or an array as JSON, anything else as it is.
|
|
69
|
+
*
|
|
70
|
+
* @param body - the body.
|
|
71
|
+
*/
|
|
72
|
+
const sentBody = (body) => {
|
|
73
|
+
if (body === undefined || body === null)
|
|
74
|
+
return { body: undefined, isJson: false };
|
|
75
|
+
if (typeof body === "string" || body instanceof Blob || body instanceof FormData)
|
|
76
|
+
return { body, isJson: false };
|
|
77
|
+
if (body instanceof URLSearchParams || body instanceof ArrayBuffer)
|
|
78
|
+
return { body, isJson: false };
|
|
79
|
+
return { body: JSON.stringify(body), isJson: true };
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* An answer's body, read as its type or its content type says.
|
|
83
|
+
*
|
|
84
|
+
* @param response - the answer.
|
|
85
|
+
* @param type - how to read it; from the content type when none.
|
|
86
|
+
*/
|
|
87
|
+
const readBody = async (response, type) => {
|
|
88
|
+
if (type === "blob")
|
|
89
|
+
return response.blob();
|
|
90
|
+
if (type === "arrayBuffer")
|
|
91
|
+
return response.arrayBuffer();
|
|
92
|
+
const text = await response.text();
|
|
93
|
+
const isJson = type === "json" || (type === undefined && /json/i.test(response.headers.get("content-type") ?? ""));
|
|
94
|
+
if (!isJson || text === "")
|
|
95
|
+
return text;
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(text);
|
|
98
|
+
return parsed;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return text;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* A request as `fetch` takes it: its address, with its base and query, and its method, headers and body.
|
|
106
|
+
*
|
|
107
|
+
* @param address - the address.
|
|
108
|
+
* @param baseURL - the app's base path.
|
|
109
|
+
* @param options - the method, query, body, headers.
|
|
110
|
+
*/
|
|
111
|
+
const requestOf = (address, baseURL, options) => {
|
|
112
|
+
const url = fetchAddress(address, options.baseURL ?? baseURL, options.query ?? options.params ?? {});
|
|
113
|
+
const { body, isJson } = sentBody(options.body);
|
|
114
|
+
const headers = new Headers(options.headers);
|
|
115
|
+
if (isJson && !headers.has("content-type"))
|
|
116
|
+
headers.set("content-type", "application/json");
|
|
117
|
+
if (!headers.has("accept"))
|
|
118
|
+
headers.set("accept", "application/json");
|
|
119
|
+
const method = options.method ?? (body === undefined ? "GET" : "POST");
|
|
120
|
+
return {
|
|
121
|
+
url,
|
|
122
|
+
init: {
|
|
123
|
+
method: method.toUpperCase(),
|
|
124
|
+
headers,
|
|
125
|
+
...(body === undefined ? {} : { body }),
|
|
126
|
+
...(options.signal === undefined ? {} : { signal: options.signal })
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* `$fetch` with a base path.
|
|
132
|
+
*
|
|
133
|
+
* @param baseURL - the app's base path.
|
|
134
|
+
* @returns the function.
|
|
135
|
+
*/
|
|
136
|
+
export const createStoryFetch = (baseURL) => {
|
|
137
|
+
async function storyFetch(address, options = {}) {
|
|
138
|
+
const { url, init } = requestOf(address, baseURL, options);
|
|
139
|
+
const response = await fetch(url, init);
|
|
140
|
+
const data = await readBody(response, options.responseType);
|
|
141
|
+
if (!response.ok)
|
|
142
|
+
throw new FetchError(url, response, data);
|
|
143
|
+
return data;
|
|
144
|
+
}
|
|
145
|
+
return storyFetch;
|
|
146
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a play function reads of a Nuxt story: where its links and router calls took it, the calls it made
|
|
3
|
+
* (`router.push`, `navigateTo`), and its state, cookies and data, to assert on.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { storyNuxt } from "@forgebook/nuxt/story";
|
|
8
|
+
*
|
|
9
|
+
* export const GoesToAbout: Story<typeof Nav> = {
|
|
10
|
+
* play: async ({ canvasElement }) => {
|
|
11
|
+
* await userEvent.click(within(canvasElement).getByRole("link", { name: "About" }));
|
|
12
|
+
* await waitFor(() => expect(storyNuxt().route.path).toBe("/about"));
|
|
13
|
+
* }
|
|
14
|
+
* };
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { LocationQuery, RouteParamsGeneric } from "vue-router";
|
|
20
|
+
import { type NuxtCall } from "./nuxtStore.ts";
|
|
21
|
+
/** A Nuxt story's route and what it made, as a play function reads them. */
|
|
22
|
+
export type StoryNuxtState = {
|
|
23
|
+
/** Where the story is. */
|
|
24
|
+
readonly route: {
|
|
25
|
+
/** The path (`/blog/hello`). */
|
|
26
|
+
readonly path: string;
|
|
27
|
+
/** The path with its search and hash. */
|
|
28
|
+
readonly fullPath: string;
|
|
29
|
+
/** The route's name (`blog-slug`), or none for a path no page has. */
|
|
30
|
+
readonly name: string | undefined;
|
|
31
|
+
/** The route's params. */
|
|
32
|
+
readonly params: RouteParamsGeneric;
|
|
33
|
+
/** The search params. */
|
|
34
|
+
readonly query: LocationQuery;
|
|
35
|
+
/** The hash, with its `#`, or empty. */
|
|
36
|
+
readonly hash: string;
|
|
37
|
+
};
|
|
38
|
+
/** The calls the story made to Nuxt, in order. */
|
|
39
|
+
readonly calls: readonly NuxtCall[];
|
|
40
|
+
/** The values of `useState`, by key. */
|
|
41
|
+
readonly state: Readonly<Record<string, unknown>>;
|
|
42
|
+
/** The values of `useCookie`, by name. */
|
|
43
|
+
readonly cookies: Readonly<Record<string, unknown>>;
|
|
44
|
+
/** The data of `useAsyncData` and `useFetch`, by key. */
|
|
45
|
+
readonly data: Readonly<Record<string, unknown>>;
|
|
46
|
+
};
|
|
47
|
+
/** The route of the story drawn now, and what it made. */
|
|
48
|
+
export declare const storyNuxt: () => StoryNuxtState;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ANY_PATH_NAME, currentStore } from "./nuxtStore.js";
|
|
2
|
+
/**
|
|
3
|
+
* The values of refs, by key.
|
|
4
|
+
*
|
|
5
|
+
* @param refs - the refs.
|
|
6
|
+
*/
|
|
7
|
+
const valuesOf = (refs) => Object.fromEntries([...refs].map(([key, ref]) => [key, ref.value]));
|
|
8
|
+
/** The route of the story drawn now, and what it made. */
|
|
9
|
+
export const storyNuxt = () => {
|
|
10
|
+
const store = currentStore();
|
|
11
|
+
const route = store.router.currentRoute.value;
|
|
12
|
+
return {
|
|
13
|
+
route: {
|
|
14
|
+
path: route.path,
|
|
15
|
+
fullPath: route.fullPath,
|
|
16
|
+
name: typeof route.name === "string" && route.name !== ANY_PATH_NAME ? route.name : undefined,
|
|
17
|
+
params: route.params,
|
|
18
|
+
query: route.query,
|
|
19
|
+
hash: route.hash
|
|
20
|
+
},
|
|
21
|
+
calls: store.calls(),
|
|
22
|
+
state: valuesOf(store.state),
|
|
23
|
+
cookies: valuesOf(store.cookies),
|
|
24
|
+
data: Object.fromEntries([...store.asyncData].map(([key, entry]) => [key, entry.data.value]))
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names Nuxt gives a project's components from their files: `components/base/Button.vue` is `BaseButton`, and
|
|
3
|
+
* `components/base/BaseButton.vue` is `BaseButton` too, the folder's part not repeated.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Pure: paths in, names out, as Nuxt's components module names them: the folders' names before the file's (unless
|
|
7
|
+
* `pathPrefix: false`), the folder's `prefix` first, the words a file's name repeats from its folders dropped, an
|
|
8
|
+
* `index` file named by its folder, `.client`, `.server` and `.global` left out of the name. Stories, tests,
|
|
9
|
+
* declaration files and mixins in a components' folder are not components, as Nuxt ignores them.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** The extensions of a component. */
|
|
14
|
+
export declare const COMPONENT_EXTENSIONS: RegExp;
|
|
15
|
+
/** A component's name and its file. */
|
|
16
|
+
export type ProjectComponent = {
|
|
17
|
+
/** Its name (`BaseButton`). */
|
|
18
|
+
readonly name: string;
|
|
19
|
+
/** Its file, absolute. */
|
|
20
|
+
readonly file: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A name split into its words, at a dash, an underscore, a dot or a change of case (`baseButton` is `base`, `Button`).
|
|
24
|
+
*
|
|
25
|
+
* @param name - the name.
|
|
26
|
+
*/
|
|
27
|
+
export declare const wordsOf: (name: string) => string[];
|
|
28
|
+
/**
|
|
29
|
+
* A component's name, from its path in its folder.
|
|
30
|
+
*
|
|
31
|
+
* @param file - its path from the folder, with `/` (`base/Button.vue`).
|
|
32
|
+
* @param prefix - what every name of the folder starts with.
|
|
33
|
+
* @param pathPrefix - whether its folders are part of its name.
|
|
34
|
+
* @returns its name, or none for a file that is not a component.
|
|
35
|
+
*/
|
|
36
|
+
export declare const componentName: (file: string, prefix: string, pathPrefix: boolean) => string | undefined;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names Nuxt gives a project's components from their files: `components/base/Button.vue` is `BaseButton`, and
|
|
3
|
+
* `components/base/BaseButton.vue` is `BaseButton` too, the folder's part not repeated.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Pure: paths in, names out, as Nuxt's components module names them: the folders' names before the file's (unless
|
|
7
|
+
* `pathPrefix: false`), the folder's `prefix` first, the words a file's name repeats from its folders dropped, an
|
|
8
|
+
* `index` file named by its folder, `.client`, `.server` and `.global` left out of the name. Stories, tests,
|
|
9
|
+
* declaration files and mixins in a components' folder are not components, as Nuxt ignores them.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** The extensions of a component. */
|
|
14
|
+
export const COMPONENT_EXTENSIONS = /\.(vue|tsx|jsx|ts|js|mjs)$/;
|
|
15
|
+
/** Files in a components' folder that are not components. */
|
|
16
|
+
const NOT_COMPONENTS = /\.(stories|story|test|spec)\.[^.]+$|\.d\.[cm]?ts$|(?:M|\.m|-m)ixin\.[jt]sx?$/;
|
|
17
|
+
/**
|
|
18
|
+
* A name split into its words, at a dash, an underscore, a dot or a change of case (`baseButton` is `base`, `Button`).
|
|
19
|
+
*
|
|
20
|
+
* @param name - the name.
|
|
21
|
+
*/
|
|
22
|
+
export const wordsOf = (name) => name
|
|
23
|
+
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
|
|
24
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
25
|
+
.split(/[\s\-_.]+/)
|
|
26
|
+
.filter((word) => word !== "");
|
|
27
|
+
/**
|
|
28
|
+
* Words joined as a component's name: each starting with a capital.
|
|
29
|
+
*
|
|
30
|
+
* @param words - the words.
|
|
31
|
+
*/
|
|
32
|
+
const pascalOf = (words) => words.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join("");
|
|
33
|
+
/**
|
|
34
|
+
* The words of a component's name: its folders' words, the ones its file's name repeats at the end dropped, then the
|
|
35
|
+
* file's.
|
|
36
|
+
*
|
|
37
|
+
* @param fileWords - the file name's words.
|
|
38
|
+
* @param prefixWords - the folders' words, the prefix's first.
|
|
39
|
+
*/
|
|
40
|
+
const nameWords = (fileWords, prefixWords) => {
|
|
41
|
+
const file = fileWords.map((word) => word.toLowerCase());
|
|
42
|
+
// the longest end of the folders' words the file's name starts with is said once
|
|
43
|
+
for (let start = 0; start < prefixWords.length; start += 1) {
|
|
44
|
+
const end = prefixWords.slice(start).map((word) => word.toLowerCase());
|
|
45
|
+
if (end.every((word, index) => file[index] === word))
|
|
46
|
+
return [...prefixWords.slice(0, start), ...fileWords];
|
|
47
|
+
}
|
|
48
|
+
return [...prefixWords, ...fileWords];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* A component's name, from its path in its folder.
|
|
52
|
+
*
|
|
53
|
+
* @param file - its path from the folder, with `/` (`base/Button.vue`).
|
|
54
|
+
* @param prefix - what every name of the folder starts with.
|
|
55
|
+
* @param pathPrefix - whether its folders are part of its name.
|
|
56
|
+
* @returns its name, or none for a file that is not a component.
|
|
57
|
+
*/
|
|
58
|
+
export const componentName = (file, prefix, pathPrefix) => {
|
|
59
|
+
if (!COMPONENT_EXTENSIONS.test(file) || NOT_COMPONENTS.test(file))
|
|
60
|
+
return undefined;
|
|
61
|
+
const parts = file
|
|
62
|
+
.replace(COMPONENT_EXTENSIONS, "")
|
|
63
|
+
.replace(/\.(client|server|global|island)$/, "")
|
|
64
|
+
.split("/");
|
|
65
|
+
const base = parts.at(-1) ?? "";
|
|
66
|
+
const folders = parts.slice(0, -1);
|
|
67
|
+
// an `index` file is named by its folder, which the folders' words then say once
|
|
68
|
+
const fileWords = wordsOf(base === "index" ? (folders.at(-1) ?? "") : base);
|
|
69
|
+
const prefixWords = [...wordsOf(prefix), ...(pathPrefix ? folders.flatMap(wordsOf) : [])];
|
|
70
|
+
const words = nameWords(fileWords, prefixWords);
|
|
71
|
+
return words.length === 0 ? undefined : pascalOf(words);
|
|
72
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtractFileComponent, ExtractStoryComponent, StoryFileShape } from "forgebook/vite/adapter";
|
|
2
|
+
/** The module a new story file imports its types from. */
|
|
3
|
+
export declare const storyTypesModule = "@forgebook/nuxt";
|
|
4
|
+
/** Reads a story file's component, as Vue's adapter reads it. */
|
|
5
|
+
export declare const extractStoryComponent: ExtractStoryComponent;
|
|
6
|
+
/** Reads a component a file exports, as Vue's adapter reads it. */
|
|
7
|
+
export declare const extractFileComponent: ExtractFileComponent;
|
|
8
|
+
/**
|
|
9
|
+
* How `forgebook new` writes a component's stories: as Vue's adapter writes them.
|
|
10
|
+
*
|
|
11
|
+
* @param name - the component's name.
|
|
12
|
+
* @param file - the component's file, absolute.
|
|
13
|
+
*/
|
|
14
|
+
export declare const storyFileShape: (name: string, file: string) => StoryFileShape;
|
|
15
|
+
/** The worker heard that a source changed: Vue's checkers read again on their next read. */
|
|
16
|
+
export declare const sourcesChanged: () => void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The docs module of a Nuxt catalogue: Vue's, since a Nuxt project's components are Vue components, with the story types
|
|
3
|
+
* a new story file imports being this package's (`forgebook new` writes `@forgebook/nuxt`).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { extractFileComponent as extractVueFileComponent, extractStoryComponent as extractVueStoryComponent, sourcesChanged as vueSourcesChanged, storyFileShape as vueStoryFileShape } from "@forgebook/vue/docs";
|
|
8
|
+
/** The module a new story file imports its types from. */
|
|
9
|
+
export const storyTypesModule = "@forgebook/nuxt";
|
|
10
|
+
/** Reads a story file's component, as Vue's adapter reads it. */
|
|
11
|
+
export const extractStoryComponent = (storyFile) => extractVueStoryComponent(storyFile);
|
|
12
|
+
/** Reads a component a file exports, as Vue's adapter reads it. */
|
|
13
|
+
export const extractFileComponent = (file, exportName) => extractVueFileComponent(file, exportName);
|
|
14
|
+
/**
|
|
15
|
+
* How `forgebook new` writes a component's stories: as Vue's adapter writes them.
|
|
16
|
+
*
|
|
17
|
+
* @param name - the component's name.
|
|
18
|
+
* @param file - the component's file, absolute.
|
|
19
|
+
*/
|
|
20
|
+
export const storyFileShape = (name, file) => vueStoryFileShape(name, file);
|
|
21
|
+
/** The worker heard that a source changed: Vue's checkers read again on their next read. */
|
|
22
|
+
export const sourcesChanged = () => vueSourcesChanged();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FrameworkAdapter } from "forgebook/vite/adapter";
|
|
2
|
+
/** The options of `nuxt()`. */
|
|
3
|
+
export type NuxtOptions = {
|
|
4
|
+
/** The Nuxt project's folder, from the catalogue's; the catalogue's own folder by default. */
|
|
5
|
+
readonly dir?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Nuxt, as the plugin reads and draws its components.
|
|
9
|
+
*
|
|
10
|
+
* @param options - the Nuxt project's folder, when it is not the catalogue's.
|
|
11
|
+
* @returns the adapter.
|
|
12
|
+
*/
|
|
13
|
+
export declare const nuxt: (options?: NuxtOptions) => FrameworkAdapter;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgebook's adapter for Nuxt, for the Vite plugin: `defineConfig({ framework: nuxt() })`.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Nuxt is Vue: the props are read as Vue's, and the stories drawn with Vue's renderer, each story's app given its Nuxt
|
|
6
|
+
* (its router, data, state and config). What Nuxt adds is Vite's work: Vue's plugin, and this package's plugin for
|
|
7
|
+
* Nuxt's aliases, auto-imports, components and `#imports` (`nuxtPlugin.ts`). Forgebook stays on Vite and does not run
|
|
8
|
+
* Nuxt's build: a Nuxt module's or plugin's work is not done.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { extname } from "node:path";
|
|
13
|
+
import { vue } from "@forgebook/vue/vite";
|
|
14
|
+
import { FRAME_MODULE, nuxtPlugins } from "./nuxtPlugin.js";
|
|
15
|
+
/**
|
|
16
|
+
* Nuxt, as the plugin reads and draws its components.
|
|
17
|
+
*
|
|
18
|
+
* @param options - the Nuxt project's folder, when it is not the catalogue's.
|
|
19
|
+
* @returns the adapter.
|
|
20
|
+
*/
|
|
21
|
+
export const nuxt = (options = {}) => {
|
|
22
|
+
const base = vue();
|
|
23
|
+
return {
|
|
24
|
+
name: "nuxt",
|
|
25
|
+
// beside this module, with its extension: `.ts` in the sources, `.js` once built for npm
|
|
26
|
+
docsModule: new URL(`./extractDocs${extname(import.meta.url)}`, import.meta.url).href,
|
|
27
|
+
// both are this package's: a catalogue resolves only the packages it installs, and it need not install Vue's adapter
|
|
28
|
+
pages: { frame: FRAME_MODULE, code: "@forgebook/nuxt/code" },
|
|
29
|
+
browserDependencies: [...(base.browserDependencies ?? []), "vue-router", "forgebook/frame"],
|
|
30
|
+
...(base.componentFiles === undefined ? {} : { componentFiles: base.componentFiles }),
|
|
31
|
+
vitePlugins: async () => [
|
|
32
|
+
...((await base.vitePlugins?.()) ?? []),
|
|
33
|
+
...nuxtPlugins(options)
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** A folder of components, as the catalogue reads it. */
|
|
2
|
+
export type ComponentsDir = {
|
|
3
|
+
/** The folder, absolute. */
|
|
4
|
+
readonly path: string;
|
|
5
|
+
/** What every name in it starts with; none by default. */
|
|
6
|
+
readonly prefix: string;
|
|
7
|
+
/** Whether a component's folders are part of its name (`base/Button.vue` is `BaseButton`); `true` by default. */
|
|
8
|
+
readonly pathPrefix: boolean;
|
|
9
|
+
};
|
|
10
|
+
/** What a catalogue reads of a Nuxt project. */
|
|
11
|
+
export type NuxtProjectConfig = {
|
|
12
|
+
/** The file read, or none. */
|
|
13
|
+
readonly file: string | undefined;
|
|
14
|
+
/** The project's folder. */
|
|
15
|
+
readonly rootDir: string;
|
|
16
|
+
/** Its sources' folder: `app/` in Nuxt 4's layout, the project's folder in Nuxt 3's. */
|
|
17
|
+
readonly srcDir: string;
|
|
18
|
+
/** Its pages' folder. */
|
|
19
|
+
readonly pagesDir: string;
|
|
20
|
+
/** The aliases, by name, each to an absolute path or a package. */
|
|
21
|
+
readonly alias: Readonly<Record<string, string>>;
|
|
22
|
+
/** Its components' folders. */
|
|
23
|
+
readonly componentsDirs: readonly ComponentsDir[];
|
|
24
|
+
/** The folders whose exports are auto-imported. */
|
|
25
|
+
readonly importsDirs: readonly string[];
|
|
26
|
+
/** Whether auto-imports are on; `imports.autoImport: false` turns them off. */
|
|
27
|
+
readonly isAutoImport: boolean;
|
|
28
|
+
/** The style sheets every page loads (`css`), as paths or packages. */
|
|
29
|
+
readonly css: readonly string[];
|
|
30
|
+
/** The runtime config's `public` part, before the environment's variables. */
|
|
31
|
+
readonly publicRuntimeConfig: Readonly<Record<string, unknown>>;
|
|
32
|
+
/** The app's base path; `/` by default. */
|
|
33
|
+
readonly baseURL: string;
|
|
34
|
+
/** Where Nuxt serves its built files from; `/_nuxt/` by default. */
|
|
35
|
+
readonly buildAssetsDir: string;
|
|
36
|
+
/** `appConfig`: the app config's values `nuxt.config` gives. */
|
|
37
|
+
readonly appConfig: Readonly<Record<string, unknown>>;
|
|
38
|
+
/** The project's `app.config` file, or none. */
|
|
39
|
+
readonly appConfigFile: string | undefined;
|
|
40
|
+
};
|
|
41
|
+
/** The names `nuxt.config` may have, in the order Nuxt looks for them. */
|
|
42
|
+
export declare const NUXT_CONFIG_FILES: readonly ["nuxt.config.ts", "nuxt.config.mts", "nuxt.config.js", "nuxt.config.mjs", "nuxt.config.cjs"];
|
|
43
|
+
/**
|
|
44
|
+
* A path as Nuxt resolves one written in its config: `~`, `@`, `~~` and `@@` replaced, a relative path from the
|
|
45
|
+
* project's folder, a package left as it is.
|
|
46
|
+
*
|
|
47
|
+
* @param path - the path.
|
|
48
|
+
* @param rootDir - the project's folder.
|
|
49
|
+
* @param srcDir - its sources' folder.
|
|
50
|
+
*/
|
|
51
|
+
export declare const resolvedPath: (path: string, rootDir: string, srcDir: string) => string;
|
|
52
|
+
/**
|
|
53
|
+
* What a catalogue reads of a configuration Nuxt would read.
|
|
54
|
+
*
|
|
55
|
+
* @param raw - what the file exported.
|
|
56
|
+
* @param file - the file.
|
|
57
|
+
* @param projectDir - the project's folder.
|
|
58
|
+
*/
|
|
59
|
+
export declare const readNuxtConfig: (raw: unknown, file: string | undefined, projectDir: string) => NuxtProjectConfig;
|
|
60
|
+
/**
|
|
61
|
+
* The project's `nuxt.config` file: the first Nuxt would find in its folder.
|
|
62
|
+
*
|
|
63
|
+
* @param rootDir - the project's folder.
|
|
64
|
+
*/
|
|
65
|
+
export declare const nuxtConfigFile: (rootDir: string) => string | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Loads a project's `nuxt.config`.
|
|
68
|
+
*
|
|
69
|
+
* @param rootDir - the project's folder.
|
|
70
|
+
* @returns what the catalogue reads of it, and why it could not be loaded when it could not.
|
|
71
|
+
*/
|
|
72
|
+
export declare const loadNuxtConfig: (rootDir: string) => Promise<{
|
|
73
|
+
readonly config: NuxtProjectConfig;
|
|
74
|
+
readonly problem: string | undefined;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* The public runtime config with the environment's variables over it: `NUXT_PUBLIC_API_BASE` replaces
|
|
78
|
+
* `public.apiBase`, a nested key adding its name (`NUXT_PUBLIC_MAP_KEY` for `public.map.key`). Only a key the config
|
|
79
|
+
* has is replaced, as Nuxt replaces it.
|
|
80
|
+
*
|
|
81
|
+
* @param config - the config's `public` part.
|
|
82
|
+
* @param env - the variables, by name.
|
|
83
|
+
* @param prefix - the variables' prefix for this level; `NUXT_PUBLIC_` at the top.
|
|
84
|
+
*/
|
|
85
|
+
export declare const withEnvironment: (config: Readonly<Record<string, unknown>>, env: Readonly<Record<string, string>>, prefix?: string) => Readonly<Record<string, unknown>>;
|