@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,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The modules of a SvelteKit project's environment, written for a catalogue: the public variables as SvelteKit gives
|
|
3
|
+
* them to the browser, and a module that refuses to load for every one only a server may read.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* SvelteKit 2 gives the variables whose name has the public prefix (`PUBLIC_`) through `$env/static/public` and
|
|
7
|
+
* `$env/dynamic/public`; SvelteKit 3 the variables `src/env.ts` declares `public`, checked by their schema, through
|
|
8
|
+
* `$app/env/public`. Nothing else of the environment reaches the page: `$env/static/private`, `$app/env/private` and
|
|
9
|
+
* `$app/server` throw as they are loaded, so a component that reads a secret fails in its frame rather than shipping it.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { codeOfJson } from "forgebook/code-literal";
|
|
16
|
+
import { runnerImport } from "vite";
|
|
17
|
+
/** The modules only a server may load, with what each is. */
|
|
18
|
+
export const SERVER_ONLY = {
|
|
19
|
+
"$env/static/private": "the private environment",
|
|
20
|
+
"$env/dynamic/private": "the private environment",
|
|
21
|
+
"$app/env/private": "the private environment",
|
|
22
|
+
"$app/server": "the server's own functions (getRequestEvent, read, the remote functions)",
|
|
23
|
+
"$service-worker": "the service worker's own module"
|
|
24
|
+
};
|
|
25
|
+
/** The public environment modules, by the name a component imports. */
|
|
26
|
+
export const PUBLIC_ENV = ["$env/static/public", "$env/dynamic/public", "$app/env/public"];
|
|
27
|
+
/** A name JavaScript takes as an export's. */
|
|
28
|
+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
29
|
+
/** The words a `const` cannot be named, which a variable or an import may still be called. */
|
|
30
|
+
const RESERVED = new Set("await break case catch class const continue debugger default delete do else enum export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield".split(" "));
|
|
31
|
+
/** The names `src/env` may have, in the order SvelteKit 3 looks for them. */
|
|
32
|
+
const ENV_FILES = ["env.ts", "env.js", "env.mts", "env.mjs"];
|
|
33
|
+
/** Whether a value is a plain object. */
|
|
34
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
|
+
/**
|
|
36
|
+
* A value written in a module: a big integer as its literal, a value JSON writes nothing for as `undefined`.
|
|
37
|
+
*
|
|
38
|
+
* @param value - the value.
|
|
39
|
+
*/
|
|
40
|
+
const written = (value) => {
|
|
41
|
+
if (typeof value === "bigint")
|
|
42
|
+
return `${value}n`;
|
|
43
|
+
const json = JSON.stringify(value);
|
|
44
|
+
return typeof json === "string" ? codeOfJson(json) : "undefined";
|
|
45
|
+
};
|
|
46
|
+
/** The query a server-only import is rewritten with, naming what it imports (`?names=SECRET,TOKEN`). */
|
|
47
|
+
const NAMES_QUERY = "?names=";
|
|
48
|
+
/** An import of named exports: its names and its module. */
|
|
49
|
+
const NAMED_IMPORT = /import\s*(?:[\w$]+\s*,\s*)?\{([^}]*)\}\s*from\s*(["'])([^"']+)\2/g;
|
|
50
|
+
/**
|
|
51
|
+
* A module that refuses to load in a story: it declares the names its importer asked for, so the browser links it and
|
|
52
|
+
* runs it, and its error, which says why, is the one the frame shows.
|
|
53
|
+
*
|
|
54
|
+
* @param id - the module's name, with the names it is imported with (`$app/env/private?names=TOKEN`).
|
|
55
|
+
* @param reason - the error's message.
|
|
56
|
+
*/
|
|
57
|
+
export const refusedModule = (id, reason) => {
|
|
58
|
+
const [, names = ""] = id.split(NAMES_QUERY);
|
|
59
|
+
const declared = names.split(",").filter((item) => IDENTIFIER.test(item) && !RESERVED.has(item));
|
|
60
|
+
return [
|
|
61
|
+
`throw new Error(${written(reason)});`,
|
|
62
|
+
...declared.map((item) => `export const ${item} = undefined;`),
|
|
63
|
+
"export default undefined;",
|
|
64
|
+
""
|
|
65
|
+
].join("\n");
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* A server-only module, refusing to load with why.
|
|
69
|
+
*
|
|
70
|
+
* @param id - the module's name, with the names it is imported with.
|
|
71
|
+
*/
|
|
72
|
+
export const serverOnlyModule = (id) => {
|
|
73
|
+
const name = kitModuleName(id) ?? id;
|
|
74
|
+
return refusedModule(id, `${name} is ${SERVER_ONLY[name] ?? "server code"}, which only a server may load: a component drawn in a catalogue cannot import it. Give the story what it reads through its args or its page's data.`);
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* A module's code with its named imports of SvelteKit's environment and server modules naming what they import, so a
|
|
78
|
+
* module that refuses to load (`refusedModule`) can declare them.
|
|
79
|
+
*
|
|
80
|
+
* @param code - the module's code.
|
|
81
|
+
* @returns the code, or none when it imports none of those modules by name.
|
|
82
|
+
*/
|
|
83
|
+
export const namedServerImports = (code) => {
|
|
84
|
+
let isChanged = false;
|
|
85
|
+
const rewritten = code.replace(NAMED_IMPORT, (statement, list, quote, source) => {
|
|
86
|
+
if (kitModuleName(source) === undefined)
|
|
87
|
+
return statement;
|
|
88
|
+
isChanged = true;
|
|
89
|
+
const names = list
|
|
90
|
+
.split(",")
|
|
91
|
+
.map((item) => item
|
|
92
|
+
.trim()
|
|
93
|
+
.split(/\s+as\s+/)[0]
|
|
94
|
+
?.replace(/^type\s+/, "") ?? "")
|
|
95
|
+
.filter((item) => item !== "");
|
|
96
|
+
return statement.replace(`${quote}${source}${quote}`, `${quote}${source}${NAMES_QUERY}${names.join(",")}${quote}`);
|
|
97
|
+
});
|
|
98
|
+
return isChanged ? rewritten : undefined;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The environment or server module of SvelteKit a name stands for, its query left out; none for another module.
|
|
102
|
+
*
|
|
103
|
+
* @param source - an import's module.
|
|
104
|
+
*/
|
|
105
|
+
export const kitModuleName = (source) => {
|
|
106
|
+
const [name = source] = source.split(NAMES_QUERY);
|
|
107
|
+
return name in SERVER_ONLY || PUBLIC_ENV.some((known) => known === name) ? name : undefined;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Whether a name is a server-only module, its query left out.
|
|
111
|
+
*
|
|
112
|
+
* @param source - an import's module.
|
|
113
|
+
*/
|
|
114
|
+
export const isServerOnly = (source) => {
|
|
115
|
+
const [name = source] = source.split(NAMES_QUERY);
|
|
116
|
+
return name in SERVER_ONLY;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The variables SvelteKit 2 gives the browser: those with the public prefix, and not the private one.
|
|
120
|
+
*
|
|
121
|
+
* @param env - every variable, by name.
|
|
122
|
+
* @param publicPrefix - `env.publicPrefix` (`PUBLIC_`).
|
|
123
|
+
* @param privatePrefix - `env.privatePrefix` (empty by default).
|
|
124
|
+
*/
|
|
125
|
+
export const publicVariables = (env, publicPrefix, privatePrefix) => Object.fromEntries(Object.entries(env).filter(([name]) => name.startsWith(publicPrefix) && (privatePrefix === "" || !name.startsWith(privatePrefix))));
|
|
126
|
+
/**
|
|
127
|
+
* A module of named exports, one per variable: what a name JavaScript cannot export is left out.
|
|
128
|
+
*
|
|
129
|
+
* @param values - the values, by name.
|
|
130
|
+
*/
|
|
131
|
+
export const namedExports = (values) => Object.entries(values)
|
|
132
|
+
.filter(([name]) => IDENTIFIER.test(name) && !RESERVED.has(name))
|
|
133
|
+
.map(([name, value]) => `export const ${name} = ${written(value)};\n`)
|
|
134
|
+
.join("");
|
|
135
|
+
/**
|
|
136
|
+
* A value checked by a variable's schema: a function, or a Standard Schema (`zod`, `valibot`).
|
|
137
|
+
*
|
|
138
|
+
* @param schema - the variable's `schema`.
|
|
139
|
+
* @param value - the variable's text, or none.
|
|
140
|
+
* @param name - the variable's name, for the error.
|
|
141
|
+
*/
|
|
142
|
+
const checked = async (schema, value, name) => {
|
|
143
|
+
if (typeof schema === "function")
|
|
144
|
+
return Reflect.apply(schema, undefined, [value]);
|
|
145
|
+
const standard = isRecord(schema) ? schema["~standard"] : undefined;
|
|
146
|
+
if (!isRecord(standard) || typeof standard["validate"] !== "function") {
|
|
147
|
+
// SvelteKit wants a variable with no schema set, an empty text included
|
|
148
|
+
if (value === undefined)
|
|
149
|
+
throw new Error(`The public variable ${name} is missing: set it, or give it a schema that makes it optional`);
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
const result = await Reflect.apply(standard["validate"], standard, [value]);
|
|
153
|
+
if (isRecord(result) && Array.isArray(result["issues"]))
|
|
154
|
+
throw new Error(`The public variable ${name} is not valid: ${JSON.stringify(result["issues"])}`);
|
|
155
|
+
return isRecord(result) ? result["value"] : value;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* The public variables a SvelteKit 3 project declares in `src/env.ts`, checked by their schemas.
|
|
159
|
+
*
|
|
160
|
+
* @param srcDir - the project's source folder (`src`).
|
|
161
|
+
* @param env - every variable, by name.
|
|
162
|
+
* @param aliases - the project's aliases.
|
|
163
|
+
* @returns the values by name, and the files read; none without an env file.
|
|
164
|
+
*/
|
|
165
|
+
export const declaredPublicVariables = async (srcDir, env, aliases = []) => {
|
|
166
|
+
const file = ENV_FILES.map((name) => join(srcDir, name)).find((path) => existsSync(path));
|
|
167
|
+
if (file === undefined)
|
|
168
|
+
return undefined;
|
|
169
|
+
// the project's aliases, as SvelteKit gives them to its env file (`$lib/…`)
|
|
170
|
+
const { module } = await runnerImport(file, {
|
|
171
|
+
configFile: false,
|
|
172
|
+
logLevel: "silent",
|
|
173
|
+
resolve: { alias: [...aliases] }
|
|
174
|
+
});
|
|
175
|
+
const variables = module["variables"];
|
|
176
|
+
const values = {};
|
|
177
|
+
for (const [name, declared] of Object.entries(isRecord(variables) ? variables : {}))
|
|
178
|
+
if (isRecord(declared) && declared["public"] === true)
|
|
179
|
+
values[name] = await checked(declared["schema"], env[name], name);
|
|
180
|
+
return { values, file };
|
|
181
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
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/sveltekit";
|
|
4
|
+
/** Reads a story file's component, as Svelte's adapter reads it. */
|
|
5
|
+
export declare const extractStoryComponent: ExtractStoryComponent;
|
|
6
|
+
/** Reads a component a file exports, as Svelte's adapter reads it. */
|
|
7
|
+
export declare const extractFileComponent: ExtractFileComponent;
|
|
8
|
+
/**
|
|
9
|
+
* How a component's stories file is written, as Svelte's adapter writes it.
|
|
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;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The docs module of a SvelteKit catalogue: Svelte's, since a SvelteKit project's components are Svelte components,
|
|
3
|
+
* with the story types a new story file imports being this package's (`forgebook new` writes `@forgebook/sveltekit`).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { extractFileComponent as extractSvelteFileComponent, extractStoryComponent as extractSvelteStoryComponent, storyFileShape as svelteStoryFileShape } from "@forgebook/svelte/docs";
|
|
8
|
+
/** The module a new story file imports its types from. */
|
|
9
|
+
export const storyTypesModule = "@forgebook/sveltekit";
|
|
10
|
+
/** Reads a story file's component, as Svelte's adapter reads it. */
|
|
11
|
+
export const extractStoryComponent = (storyFile) => extractSvelteStoryComponent(storyFile);
|
|
12
|
+
/** Reads a component a file exports, as Svelte's adapter reads it. */
|
|
13
|
+
export const extractFileComponent = (file, exportName) => extractSvelteFileComponent(file, exportName);
|
|
14
|
+
/**
|
|
15
|
+
* How a component's stories file is written, as Svelte's adapter writes it.
|
|
16
|
+
*
|
|
17
|
+
* @param name - the component's name.
|
|
18
|
+
* @param file - the component's file, absolute.
|
|
19
|
+
*/
|
|
20
|
+
export const storyFileShape = (name, file) => svelteStoryFileShape(name, file);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type VitePaths } from "forgebook/vite/paths";
|
|
2
|
+
import { type Alias, type Plugin } from "vite";
|
|
3
|
+
import { type KitAppSettings } from "../appSettings.ts";
|
|
4
|
+
import type { KitProject } from "./kitProject.ts";
|
|
5
|
+
/**
|
|
6
|
+
* The aliases of SvelteKit's `alias` option and of `$lib`, as SvelteKit writes them: a name for itself and what is
|
|
7
|
+
* below it, a name ending in `/*` for what is below it. Each leads to a path in Vite's form, with forward slashes on
|
|
8
|
+
* every system: what a name leads to is written after it (`$lib/site` to `<lib>/site`), so a Windows path would come
|
|
9
|
+
* out half in backslashes.
|
|
10
|
+
*
|
|
11
|
+
* @param project - the project.
|
|
12
|
+
* @param paths - the system's path functions; the running system's by default.
|
|
13
|
+
*/
|
|
14
|
+
export declare const projectAliases: (project: Pick<KitProject, "kit" | "dir">, paths?: VitePaths) => Alias[];
|
|
15
|
+
/**
|
|
16
|
+
* The script the frame's page runs before any module: the settings the stand-ins read. Set inline in the page, so
|
|
17
|
+
* every value is a `codeLiteral`: no text in it can close the script.
|
|
18
|
+
*
|
|
19
|
+
* @param settings - the settings.
|
|
20
|
+
*/
|
|
21
|
+
export declare const pageScript: (settings: KitAppSettings) => string;
|
|
22
|
+
/**
|
|
23
|
+
* The plugin of a SvelteKit catalogue.
|
|
24
|
+
*
|
|
25
|
+
* @param project - the project, read.
|
|
26
|
+
* @returns the plugin.
|
|
27
|
+
*/
|
|
28
|
+
export declare const kitPlugin: (project: KitProject) => Plugin;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Vite plugin that lets a SvelteKit project's components draw outside SvelteKit: `$app/*` resolved to stand-ins
|
|
3
|
+
* that work in a story, `$lib` and the project's aliases, the public environment, and the project's routes written in
|
|
4
|
+
* the frame's page.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* What a stand-in does and why is in each module of `src/app`. The settings the stand-ins read (the base path, the
|
|
8
|
+
* version, the routes) are written in the frame's page before any module runs (`appSettings.ts`), read again when a
|
|
9
|
+
* route or a static file is added or removed. The environment modules are written from the project's `.env` files and
|
|
10
|
+
* the environment as SvelteKit would give them to the browser (`envModules.ts`).
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { codeLiteral } from "forgebook/code-literal";
|
|
18
|
+
import { VITE_PATHS } from "forgebook/vite/paths";
|
|
19
|
+
import { loadEnv } from "vite";
|
|
20
|
+
import { APP_SETTINGS_KEY, SETTINGS_CONSTANT } from "../appSettings.js";
|
|
21
|
+
import { declaredPublicVariables, isServerOnly, kitModuleName, namedExports, namedServerImports, PUBLIC_ENV, publicVariables, refusedModule, serverOnlyModule } from "./envModules.js";
|
|
22
|
+
import { projectRoutes, projectStaticFiles } from "./projectFiles.js";
|
|
23
|
+
/** The modules a story gets Forgebook's stand-in for, by the stand-in's export of this package. */
|
|
24
|
+
const STAND_INS = {
|
|
25
|
+
"$app/env": "app/env",
|
|
26
|
+
"$app/environment": "app/env",
|
|
27
|
+
"$app/forms": "app/forms",
|
|
28
|
+
"$app/manifest": "app/manifest",
|
|
29
|
+
"$app/navigation": "app/navigation",
|
|
30
|
+
"$app/paths": "app/paths",
|
|
31
|
+
"$app/state": "app/state",
|
|
32
|
+
"$app/stores": "app/stores"
|
|
33
|
+
};
|
|
34
|
+
/** The prefix of the modules this plugin writes. */
|
|
35
|
+
const VIRTUAL = "\0forgebook-sveltekit:";
|
|
36
|
+
/** Whether a value is a plain object. */
|
|
37
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38
|
+
/**
|
|
39
|
+
* A text of SvelteKit's options, or a default.
|
|
40
|
+
*
|
|
41
|
+
* @param options - an object of the options.
|
|
42
|
+
* @param key - its key.
|
|
43
|
+
* @param fallback - the default.
|
|
44
|
+
*/
|
|
45
|
+
const textOption = (options, key, fallback) => {
|
|
46
|
+
const value = isRecord(options) ? options[key] : undefined;
|
|
47
|
+
return typeof value === "string" ? value : fallback;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A text written as a regular expression that matches it alone.
|
|
51
|
+
*
|
|
52
|
+
* @param name - the text.
|
|
53
|
+
*/
|
|
54
|
+
const exactly = (name) => new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}$`);
|
|
55
|
+
/**
|
|
56
|
+
* A module this package exports, as a file.
|
|
57
|
+
*
|
|
58
|
+
* @param subpath - the export (`app/state`).
|
|
59
|
+
*/
|
|
60
|
+
const ownExport = (subpath) => fileURLToPath(import.meta.resolve(`@forgebook/sveltekit/${subpath}`));
|
|
61
|
+
/**
|
|
62
|
+
* The aliases of SvelteKit's `alias` option and of `$lib`, as SvelteKit writes them: a name for itself and what is
|
|
63
|
+
* below it, a name ending in `/*` for what is below it. Each leads to a path in Vite's form, with forward slashes on
|
|
64
|
+
* every system: what a name leads to is written after it (`$lib/site` to `<lib>/site`), so a Windows path would come
|
|
65
|
+
* out half in backslashes.
|
|
66
|
+
*
|
|
67
|
+
* @param project - the project.
|
|
68
|
+
* @param paths - the system's path functions; the running system's by default.
|
|
69
|
+
*/
|
|
70
|
+
export const projectAliases = (project, paths = VITE_PATHS) => {
|
|
71
|
+
const { kit, dir } = project;
|
|
72
|
+
const files = isRecord(kit["files"]) ? kit["files"] : {};
|
|
73
|
+
const aliases = [
|
|
74
|
+
// SvelteKit 3 has no `$lib`, which a project's own `#lib` import replaces: giving it costs nothing
|
|
75
|
+
["$lib", textOption(files, "lib", "src/lib")],
|
|
76
|
+
...(isRecord(kit["alias"])
|
|
77
|
+
? Object.entries(kit["alias"]).filter((entry) => typeof entry[1] === "string")
|
|
78
|
+
: [])
|
|
79
|
+
];
|
|
80
|
+
return aliases.flatMap(([name, target]) => {
|
|
81
|
+
const path = paths.isAbsolute(target) ? paths.normalize(target) : paths.resolve(dir, target);
|
|
82
|
+
if (name.endsWith("/*")) {
|
|
83
|
+
const prefix = name.slice(0, -2);
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
find: new RegExp(`^${exactly(prefix).source.slice(1, -1)}/`),
|
|
87
|
+
replacement: `${path.replace(/\/\*$/, "")}/`
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
return [
|
|
92
|
+
{ find: exactly(name), replacement: path },
|
|
93
|
+
{ find: new RegExp(`^${exactly(name).source.slice(1, -1)}/`), replacement: `${path}/` }
|
|
94
|
+
];
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Why a public environment module is not this SvelteKit's: each has its own, and the other's does not exist, so it
|
|
99
|
+
* gives nothing (SvelteKit 3 gives no variable by its prefix alone).
|
|
100
|
+
*
|
|
101
|
+
* @param module - the module.
|
|
102
|
+
* @param version - the project's SvelteKit.
|
|
103
|
+
* @returns the reason, or none for one of this SvelteKit's.
|
|
104
|
+
*/
|
|
105
|
+
const otherKitsModule = (module, version) => {
|
|
106
|
+
if (module === "$app/env/public")
|
|
107
|
+
return version === 2
|
|
108
|
+
? `${module} is SvelteKit 3's: SvelteKit 2 gives the public variables through $env/static/public and $env/dynamic/public`
|
|
109
|
+
: undefined;
|
|
110
|
+
return version === 3
|
|
111
|
+
? `${module} is SvelteKit 2's: SvelteKit 3 gives the variables src/env.ts declares public through $app/env/public`
|
|
112
|
+
: undefined;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* The `.env` files Vite reads for a mode that exist, so an edit to one writes the environment modules again.
|
|
116
|
+
*
|
|
117
|
+
* @param envDir - the folder they are in.
|
|
118
|
+
* @param mode - the mode.
|
|
119
|
+
*/
|
|
120
|
+
const envFiles = (envDir, mode) => [".env", ".env.local", `.env.${mode}`, `.env.${mode}.local`]
|
|
121
|
+
.map((name) => join(envDir, name))
|
|
122
|
+
// a missing one is not watched: Vite would take it for an import the module makes
|
|
123
|
+
.filter((file) => existsSync(file));
|
|
124
|
+
/**
|
|
125
|
+
* The script the frame's page runs before any module: the settings the stand-ins read. Set inline in the page, so
|
|
126
|
+
* every value is a `codeLiteral`: no text in it can close the script.
|
|
127
|
+
*
|
|
128
|
+
* @param settings - the settings.
|
|
129
|
+
*/
|
|
130
|
+
export const pageScript = (settings) => `globalThis[Symbol.for(${codeLiteral(APP_SETTINGS_KEY)})] = ${codeLiteral(settings)};`;
|
|
131
|
+
/**
|
|
132
|
+
* The plugin of a SvelteKit catalogue.
|
|
133
|
+
*
|
|
134
|
+
* @param project - the project, read.
|
|
135
|
+
* @returns the plugin.
|
|
136
|
+
*/
|
|
137
|
+
export const kitPlugin = (project) => {
|
|
138
|
+
const { kit, dir } = project;
|
|
139
|
+
const files = isRecord(kit["files"]) ? kit["files"] : {};
|
|
140
|
+
const paths = isRecord(kit["paths"]) ? kit["paths"] : {};
|
|
141
|
+
const env = isRecord(kit["env"]) ? kit["env"] : {};
|
|
142
|
+
const srcDir = resolve(dir, textOption(files, "src", "src"));
|
|
143
|
+
const routesDir = resolve(dir, textOption(files, "routes", join(textOption(files, "src", "src"), "routes")));
|
|
144
|
+
const staticDir = resolve(dir, textOption(files, "assets", "static"));
|
|
145
|
+
const { extensions, version } = project;
|
|
146
|
+
let resolved;
|
|
147
|
+
let settings;
|
|
148
|
+
const settingsNow = () => {
|
|
149
|
+
settings ??= {
|
|
150
|
+
base: textOption(paths, "base", ""),
|
|
151
|
+
assets: textOption(paths, "assets", ""),
|
|
152
|
+
version: textOption(kit["version"], "name", ""),
|
|
153
|
+
routes: projectRoutes(routesDir, extensions),
|
|
154
|
+
staticFiles: projectStaticFiles(staticDir)
|
|
155
|
+
};
|
|
156
|
+
return settings;
|
|
157
|
+
};
|
|
158
|
+
const allVariables = () => resolved === undefined ? {} : loadEnv(resolved.mode, resolve(dir, textOption(env, "dir", ".")), "");
|
|
159
|
+
/**
|
|
160
|
+
* A public environment module: the variables this SvelteKit gives the browser, or a refusal for the other's module.
|
|
161
|
+
*
|
|
162
|
+
* @param name - the module's name, with the names it is imported with.
|
|
163
|
+
* @param module - the module.
|
|
164
|
+
* @param watch - adds a file whose edit writes the module again.
|
|
165
|
+
*/
|
|
166
|
+
const publicEnvModule = async (name, module, watch) => {
|
|
167
|
+
const refusal = otherKitsModule(module, version);
|
|
168
|
+
if (refusal !== undefined)
|
|
169
|
+
return refusedModule(name, refusal);
|
|
170
|
+
for (const file of envFiles(resolve(dir, textOption(env, "dir", ".")), resolved?.mode ?? "development"))
|
|
171
|
+
watch(file);
|
|
172
|
+
const variables = allVariables();
|
|
173
|
+
if (module === "$app/env/public") {
|
|
174
|
+
const declared = await declaredPublicVariables(srcDir, variables, projectAliases(project));
|
|
175
|
+
if (declared === undefined)
|
|
176
|
+
return "";
|
|
177
|
+
watch(declared.file);
|
|
178
|
+
return namedExports(declared.values);
|
|
179
|
+
}
|
|
180
|
+
const values = publicVariables(variables, textOption(env, "publicPrefix", "PUBLIC_"), textOption(env, "privatePrefix", ""));
|
|
181
|
+
return module === "$env/static/public"
|
|
182
|
+
? namedExports(values)
|
|
183
|
+
: `export const env = ${codeLiteral(values)};\n`;
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
name: "forgebook:sveltekit",
|
|
187
|
+
enforce: "pre",
|
|
188
|
+
config: () => ({
|
|
189
|
+
// a server render has no page to write the settings in: the code this package compiles has them instead
|
|
190
|
+
define: { [SETTINGS_CONSTANT]: codeLiteral(settingsNow()) },
|
|
191
|
+
// compiled by Vite, so the settings are in it, rather than loaded by Node as it is published
|
|
192
|
+
ssr: { noExternal: ["@forgebook/sveltekit"] },
|
|
193
|
+
resolve: {
|
|
194
|
+
alias: [
|
|
195
|
+
...Object.entries(STAND_INS).map(([name, subpath]) => ({
|
|
196
|
+
find: exactly(name),
|
|
197
|
+
replacement: ownExport(subpath)
|
|
198
|
+
})),
|
|
199
|
+
...projectAliases(project)
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
}),
|
|
203
|
+
configResolved: (config) => {
|
|
204
|
+
resolved = config;
|
|
205
|
+
if (project.problem !== undefined)
|
|
206
|
+
config.logger.warn(`[forgebook] ${project.problem}`);
|
|
207
|
+
},
|
|
208
|
+
configureServer: (server) => {
|
|
209
|
+
// a route or a static file added or removed is in the next frame's settings
|
|
210
|
+
const renew = (path) => {
|
|
211
|
+
if (path.startsWith(routesDir) || path.startsWith(staticDir))
|
|
212
|
+
settings = undefined;
|
|
213
|
+
};
|
|
214
|
+
for (const event of ["add", "unlink", "addDir", "unlinkDir"])
|
|
215
|
+
server.watcher.on(event, renew);
|
|
216
|
+
},
|
|
217
|
+
transformIndexHtml: (_html, context) => (context.path.split("?")[0] ?? "").endsWith("frame.html")
|
|
218
|
+
? [{ tag: "script", children: pageScript(settingsNow()), injectTo: "head-prepend" }]
|
|
219
|
+
: [],
|
|
220
|
+
// an import of a server-only module names what it imports, so the module that refuses to load can declare it
|
|
221
|
+
transform: {
|
|
222
|
+
filter: { code: /\$(app|env|service-worker)/ },
|
|
223
|
+
handler: (code) => {
|
|
224
|
+
const rewritten = namedServerImports(code);
|
|
225
|
+
return rewritten === undefined ? undefined : { code: rewritten, map: null };
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
resolveId: (source) => kitModuleName(source) === undefined ? undefined : `${VIRTUAL}${source}`,
|
|
229
|
+
async load(id) {
|
|
230
|
+
if (!id.startsWith(VIRTUAL))
|
|
231
|
+
return undefined;
|
|
232
|
+
const name = id.slice(VIRTUAL.length);
|
|
233
|
+
if (isServerOnly(name))
|
|
234
|
+
return serverOnlyModule(name);
|
|
235
|
+
const module = PUBLIC_ENV.find((known) => known === kitModuleName(name));
|
|
236
|
+
if (module === undefined)
|
|
237
|
+
return undefined;
|
|
238
|
+
return publicEnvModule(name, module, (file) => this.addWatchFile(file));
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Options as SveltePluginOptions } from "@sveltejs/vite-plugin-svelte";
|
|
2
|
+
import { type Plugin, type PluginOption } from "vite";
|
|
3
|
+
/** What the catalogue reads of a SvelteKit project. */
|
|
4
|
+
export type KitProject = {
|
|
5
|
+
/** The project's folder. */
|
|
6
|
+
readonly dir: string;
|
|
7
|
+
/** SvelteKit's options, as SvelteKit 3 writes them (SvelteKit 2's `kit` key, read the same). */
|
|
8
|
+
readonly kit: Readonly<Record<string, unknown>>;
|
|
9
|
+
/** Svelte's options (`preprocess`, `compilerOptions`, `extensions`) for Svelte's Vite plugin; none to read `svelte.config.js`. */
|
|
10
|
+
readonly svelte: Partial<SveltePluginOptions> | undefined;
|
|
11
|
+
/** SvelteKit's major version, as its options say: 2 holds them under `kit`, 3 has them at the top. */
|
|
12
|
+
readonly version: 2 | 3;
|
|
13
|
+
/** The extensions of the project's Svelte components (`.svelte`, `.svx`): its routes' pages may have any. */
|
|
14
|
+
readonly extensions: readonly string[];
|
|
15
|
+
/** The project's other Vite plugins. */
|
|
16
|
+
readonly plugins: readonly PluginOption[];
|
|
17
|
+
/** Why the project's configuration could not be read; none when it was. */
|
|
18
|
+
readonly problem: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Reads a SvelteKit project.
|
|
22
|
+
*
|
|
23
|
+
* @param dir - the project's folder.
|
|
24
|
+
* @param viteConfig - its Vite configuration, from the folder; the one Vite would find by default; `false` reads none.
|
|
25
|
+
* @param keepPlugin - whether to keep one of the project's own plugins; every one by default.
|
|
26
|
+
*/
|
|
27
|
+
export declare const loadKitProject: (dir: string, viteConfig: string | false | undefined, keepPlugin?: (plugin: Plugin) => boolean) => Promise<KitProject>;
|