@forgebook/sveltekit 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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -2
  3. package/dist/src/app/env.d.ts +8 -0
  4. package/dist/src/app/env.js +15 -0
  5. package/dist/src/app/forms.d.ts +68 -0
  6. package/dist/src/app/forms.js +125 -0
  7. package/dist/src/app/manifest.d.ts +21 -0
  8. package/dist/src/app/manifest.js +17 -0
  9. package/dist/src/app/navigation.d.ts +97 -0
  10. package/dist/src/app/navigation.js +147 -0
  11. package/dist/src/app/paths.d.ts +32 -0
  12. package/dist/src/app/paths.js +56 -0
  13. package/dist/src/app/state.d.ts +27 -0
  14. package/dist/src/app/state.js +60 -0
  15. package/dist/src/app/stores.d.ts +31 -0
  16. package/dist/src/app/stores.js +36 -0
  17. package/dist/src/appSettings.d.ts +44 -0
  18. package/dist/src/appSettings.js +55 -0
  19. package/dist/src/kitParameters.d.ts +272 -0
  20. package/dist/src/kitParameters.js +291 -0
  21. package/dist/src/kitRenderer.d.ts +3 -0
  22. package/dist/src/kitRenderer.js +276 -0
  23. package/dist/src/kitStore.d.ts +90 -0
  24. package/dist/src/kitStore.js +301 -0
  25. package/dist/src/loadFetch.d.ts +33 -0
  26. package/dist/src/loadFetch.js +171 -0
  27. package/dist/src/mountFrame.d.ts +14 -0
  28. package/dist/src/mountFrame.js +15 -0
  29. package/dist/src/routeIds.d.ts +44 -0
  30. package/dist/src/routeIds.js +150 -0
  31. package/dist/src/routeOrder.d.ts +18 -0
  32. package/dist/src/routeOrder.js +187 -0
  33. package/dist/src/stories.d.ts +48 -0
  34. package/dist/src/stories.js +11 -0
  35. package/dist/src/storyCode.d.ts +3 -0
  36. package/dist/src/storyCode.js +9 -0
  37. package/dist/src/storyPage.d.ts +35 -0
  38. package/dist/src/storyPage.js +19 -0
  39. package/dist/src/vite/envModules.d.ts +67 -0
  40. package/dist/src/vite/envModules.js +181 -0
  41. package/dist/src/vite/extractDocs.d.ts +14 -0
  42. package/dist/src/vite/extractDocs.js +20 -0
  43. package/dist/src/vite/kitPlugin.d.ts +28 -0
  44. package/dist/src/vite/kitPlugin.js +241 -0
  45. package/dist/src/vite/kitProject.d.ts +27 -0
  46. package/dist/src/vite/kitProject.js +176 -0
  47. package/dist/src/vite/projectFiles.d.ts +14 -0
  48. package/dist/src/vite/projectFiles.js +70 -0
  49. package/dist/src/vite/sveltekit.d.ts +26 -0
  50. package/dist/src/vite/sveltekit.js +39 -0
  51. package/package.json +120 -3
@@ -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>;
@@ -0,0 +1,176 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ /**
10
+ * A SvelteKit project, as the catalogue reads it: SvelteKit's options, from the project's own Vite configuration
11
+ * (SvelteKit 3 keeps them there) or from `svelte.config.js` (SvelteKit 2), and the project's other Vite plugins.
12
+ *
13
+ * @remarks
14
+ * The project's `vite.config` is loaded, not run as a server: SvelteKit's plugin, made there, holds the options it read
15
+ * and checked (`api.options`), whichever version wrote them, so the catalogue reads what SvelteKit itself does. Its own
16
+ * plugins are left out, since they build an app; the project's other plugins (Tailwind CSS, an i18n compiler) are kept,
17
+ * so its components are drawn as the app draws them.
18
+ *
19
+ * @module
20
+ */
21
+ import { existsSync } from "node:fs";
22
+ import { join, resolve } from "node:path";
23
+ import { pathToFileURL } from "node:url";
24
+ import { loadConfigFromFile } from "vite";
25
+ /** SvelteKit's plugin that holds the options it read. */
26
+ const SETUP_PLUGIN = "vite-plugin-sveltekit-setup";
27
+ /**
28
+ * The plugins SvelteKit's `sveltekit()` makes: its own (any whose name says SvelteKit, so one it adds later is left out
29
+ * too, its environment's among them), and Svelte's, which the catalogue makes itself.
30
+ */
31
+ const KIT_PLUGIN = /sveltekit|^vite-plugin-svelte([-:]|$)/;
32
+ /** The names `svelte.config` may have, in the order SvelteKit 2 looks for them. */
33
+ const SVELTE_CONFIGS = ["svelte.config.js", "svelte.config.mjs", "svelte.config.ts", "svelte.config.mts"];
34
+ /** The options of Svelte's Vite plugin read from SvelteKit's. */
35
+ const SVELTE_KEYS = ["preprocess", "compilerOptions", "extensions"];
36
+ /** Whether a value is a plain object. */
37
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
38
+ /** Whether a value is one of Vite's plugins. */
39
+ const isPlugin = (value) => isRecord(value) && typeof value["name"] === "string";
40
+ /**
41
+ * A configuration's plugins as one list: Vite takes nested lists and promises of plugins.
42
+ *
43
+ * @param plugins - the plugins, as the configuration writes them.
44
+ */
45
+ const flatPlugins = async (plugins) => {
46
+ const value = await plugins;
47
+ if (Array.isArray(value))
48
+ return (await Promise.all(value.map(flatPlugins))).flat();
49
+ return isPlugin(value) ? [value] : [];
50
+ };
51
+ /**
52
+ * SvelteKit's options held by its setup plugin.
53
+ *
54
+ * @param plugins - the project's plugins.
55
+ */
56
+ const setupOptions = (plugins) => {
57
+ const setup = plugins.find((plugin) => plugin.name === SETUP_PLUGIN);
58
+ const api = setup?.api;
59
+ return isRecord(api) && isRecord(api["options"]) ? api["options"] : undefined;
60
+ };
61
+ /**
62
+ * The options of Svelte's Vite plugin a SvelteKit configuration gives, read as they are: Svelte's plugin reads no
63
+ * `svelte.config.js` then, since the configuration was it.
64
+ *
65
+ * @param options - SvelteKit's options, or `svelte.config.js`'s default export.
66
+ */
67
+ const svelteOptionsOf = (options) => ({
68
+ configFile: false,
69
+ ...Object.fromEntries(SVELTE_KEYS.filter((key) => options[key] !== undefined).map((key) => [key, options[key]]))
70
+ });
71
+ /**
72
+ * SvelteKit's options from a SvelteKit 2 `svelte.config.js`, loaded as Node loads a module.
73
+ *
74
+ * @param dir - the project's folder.
75
+ */
76
+ const svelteConfigOptions = async (dir) => {
77
+ const file = SVELTE_CONFIGS.map((name) => join(dir, name)).find((path) => existsSync(path));
78
+ if (file === undefined)
79
+ return { kit: {}, config: {}, problem: undefined };
80
+ try {
81
+ const loaded = await import(__rewriteRelativeImportExtension(pathToFileURL(file).href));
82
+ const config = isRecord(loaded) && isRecord(loaded["default"]) ? loaded["default"] : {};
83
+ return { kit: isRecord(config["kit"]) ? config["kit"] : {}, config, problem: undefined };
84
+ }
85
+ catch (error) {
86
+ return {
87
+ kit: {},
88
+ config: {},
89
+ problem: `${file} could not be loaded: ${error instanceof Error ? error.message : String(error)}`
90
+ };
91
+ }
92
+ };
93
+ /**
94
+ * The extensions of a project's Svelte components, from Svelte's options: `.svelte` by default.
95
+ *
96
+ * @param options - SvelteKit's options, or `svelte.config.js`'s default export.
97
+ */
98
+ const extensionsOf = (options) => {
99
+ const { extensions } = options;
100
+ const listed = Array.isArray(extensions) ? extensions.filter((item) => typeof item === "string") : [];
101
+ return listed.length === 0 ? [".svelte"] : listed;
102
+ };
103
+ /**
104
+ * What is wrong with a SvelteKit 3 project `svelte-kit sync` has not run in: its `tsconfig.json` extends
105
+ * `$app/tsconfig`, which the command writes, so every module of the catalogue fails to compile without it.
106
+ *
107
+ * @param root - the project's folder.
108
+ * @param version - SvelteKit's major version.
109
+ * @param problem - what was found wrong before.
110
+ */
111
+ const syncedProblem = (root, version, problem) => {
112
+ const isSynced = version === 2 || existsSync(join(root, "node_modules", "$app", "tsconfig.json"));
113
+ return {
114
+ problem: problem ??
115
+ (isSynced
116
+ ? undefined
117
+ : "SvelteKit has not written its types: run `svelte-kit sync` first (SvelteKit's templates run it on install, as `prepare`), since the project's tsconfig.json extends $app/tsconfig")
118
+ };
119
+ };
120
+ /**
121
+ * The plugins a project's Vite configuration writes, loaded as `vite dev` loads it.
122
+ *
123
+ * @param root - the project's folder.
124
+ * @param viteConfig - the configuration, from the folder; the one Vite would find by default; `false` reads none.
125
+ * @returns the plugins as written, and why the configuration could not be loaded.
126
+ */
127
+ const viteConfigPlugins = async (root, viteConfig) => {
128
+ if (viteConfig === false)
129
+ return { plugins: [], problem: undefined };
130
+ try {
131
+ const loaded = await loadConfigFromFile({ command: "serve", mode: "development" }, viteConfig === undefined ? undefined : resolve(root, viteConfig), root, "silent");
132
+ return { plugins: loaded?.config.plugins, problem: undefined };
133
+ }
134
+ catch (error) {
135
+ const reason = error instanceof Error ? error.message : String(error);
136
+ return { plugins: [], problem: `The project's Vite configuration could not be loaded: ${reason}` };
137
+ }
138
+ };
139
+ /**
140
+ * Reads a SvelteKit project.
141
+ *
142
+ * @param dir - the project's folder.
143
+ * @param viteConfig - its Vite configuration, from the folder; the one Vite would find by default; `false` reads none.
144
+ * @param keepPlugin - whether to keep one of the project's own plugins; every one by default.
145
+ */
146
+ export const loadKitProject = async (dir, viteConfig, keepPlugin = () => true) => {
147
+ const root = resolve(dir);
148
+ const { plugins: written, problem } = await viteConfigPlugins(root, viteConfig);
149
+ const plugins = await flatPlugins(written);
150
+ const options = setupOptions(plugins);
151
+ const others = plugins.filter((plugin) => !KIT_PLUGIN.test(plugin.name) && keepPlugin(plugin));
152
+ // SvelteKit 2's options hold SvelteKit's under `kit`, beside Svelte's; SvelteKit 3's are SvelteKit's alone
153
+ if (options !== undefined) {
154
+ const kit = options["kit"];
155
+ const version = isRecord(kit) ? 2 : 3;
156
+ return {
157
+ ...syncedProblem(root, version, problem),
158
+ dir: root,
159
+ kit: isRecord(kit) ? kit : options,
160
+ version,
161
+ extensions: extensionsOf(options),
162
+ svelte: svelteOptionsOf(options),
163
+ plugins: others
164
+ };
165
+ }
166
+ const fromSvelteConfig = await svelteConfigOptions(root);
167
+ return {
168
+ dir: root,
169
+ kit: fromSvelteConfig.kit,
170
+ version: 2,
171
+ extensions: extensionsOf(fromSvelteConfig.config),
172
+ svelte: undefined,
173
+ plugins: others,
174
+ problem: problem ?? fromSvelteConfig.problem
175
+ };
176
+ };
@@ -0,0 +1,14 @@
1
+ import type { AppRoute } from "../appSettings.ts";
2
+ /**
3
+ * The routes of a project's routes folder: every folder with a page or an endpoint.
4
+ *
5
+ * @param routesDir - the routes folder (`src/routes`).
6
+ * @param extensions - the extensions of the project's Svelte components (`.svelte`).
7
+ */
8
+ export declare const projectRoutes: (routesDir: string, extensions: readonly string[]) => AppRoute[];
9
+ /**
10
+ * The files of a project's static folder, as paths from it.
11
+ *
12
+ * @param staticDir - the folder (`static`).
13
+ */
14
+ export declare const projectStaticFiles: (staticDir: string) => string[];
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The routes and static files of a SvelteKit project, read from its folders: what `$app/state` matches a story's
3
+ * address against, and what `$app/manifest` lists.
4
+ *
5
+ * @module
6
+ */
7
+ import { existsSync, readdirSync } from "node:fs";
8
+ import { join, relative, sep } from "node:path";
9
+ /** A route's page: `+page.svelte`, or another extension the project compiles as Svelte. */
10
+ const PAGE_FILE = /^\+page(@[^.]*)?\.[^.]+$/;
11
+ /** A route's endpoint: `+server.js` or `+server.ts`. */
12
+ const SERVER_FILE = /^\+server\.[cm]?[jt]s$/;
13
+ /** The most static files listed: a folder of thousands of images is listed no further. */
14
+ const MOST_STATIC_FILES = 5000;
15
+ /**
16
+ * A folder's path from another, written with `/`.
17
+ *
18
+ * @param from - the folder it is relative to.
19
+ * @param path - the path.
20
+ */
21
+ const slashed = (from, path) => relative(from, path).split(sep).join("/");
22
+ /**
23
+ * The routes of a project's routes folder: every folder with a page or an endpoint.
24
+ *
25
+ * @param routesDir - the routes folder (`src/routes`).
26
+ * @param extensions - the extensions of the project's Svelte components (`.svelte`).
27
+ */
28
+ export const projectRoutes = (routesDir, extensions) => {
29
+ if (!existsSync(routesDir))
30
+ return [];
31
+ const routes = [];
32
+ const visit = (dir) => {
33
+ const entries = readdirSync(dir, { withFileTypes: true });
34
+ const files = entries.filter((entry) => entry.isFile()).map((entry) => entry.name);
35
+ const page = files.some((file) => PAGE_FILE.test(file) && extensions.some((extension) => file.endsWith(extension)));
36
+ const endpoint = files.some((file) => SERVER_FILE.test(file));
37
+ if (page || endpoint) {
38
+ const path = slashed(routesDir, dir);
39
+ routes.push({ id: path === "" ? "/" : `/${path}`, page, endpoint });
40
+ }
41
+ for (const entry of entries)
42
+ if (entry.isDirectory())
43
+ visit(join(dir, entry.name));
44
+ };
45
+ visit(routesDir);
46
+ return routes.sort((first, second) => first.id.localeCompare(second.id));
47
+ };
48
+ /**
49
+ * The files of a project's static folder, as paths from it.
50
+ *
51
+ * @param staticDir - the folder (`static`).
52
+ */
53
+ export const projectStaticFiles = (staticDir) => {
54
+ if (!existsSync(staticDir))
55
+ return [];
56
+ const files = [];
57
+ const visit = (dir) => {
58
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
59
+ if (files.length >= MOST_STATIC_FILES)
60
+ return;
61
+ const path = join(dir, entry.name);
62
+ if (entry.isDirectory())
63
+ visit(path);
64
+ else if (entry.isFile())
65
+ files.push(slashed(staticDir, path));
66
+ }
67
+ };
68
+ visit(staticDir);
69
+ return files.sort();
70
+ };
@@ -0,0 +1,26 @@
1
+ import type { FrameworkAdapter } from "forgebook/vite/adapter";
2
+ import type { Plugin } from "vite";
3
+ /** The options of `sveltekit()`. */
4
+ export type SveltekitOptions = {
5
+ /** The SvelteKit project's folder; the folder the command runs in by default. */
6
+ readonly dir?: string;
7
+ /**
8
+ * The project's Vite configuration, from its folder, where SvelteKit 3 keeps its options; the one Vite would find
9
+ * by default; `false` reads none (SvelteKit 2's `svelte.config.js` is read then, and the project's other plugins
10
+ * are left out).
11
+ */
12
+ readonly viteConfig?: string | false;
13
+ /**
14
+ * Whether to keep one of the app's own Vite plugins (SvelteKit's are always left out); every one by default. A
15
+ * catalogue runs them as `vite dev` would, its build too: leave out one that only an app's build needs (a source-map
16
+ * upload, a service-worker generator).
17
+ */
18
+ readonly keepPlugin?: (plugin: Plugin) => boolean;
19
+ };
20
+ /**
21
+ * SvelteKit, as the plugin reads and draws its components.
22
+ *
23
+ * @param options - the project's folder and its Vite configuration.
24
+ * @returns the adapter.
25
+ */
26
+ export declare const sveltekit: (options?: SveltekitOptions) => FrameworkAdapter;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Forgebook's adapter for SvelteKit, for the Vite plugin: `defineConfig({ framework: sveltekit() })`.
3
+ *
4
+ * @remarks
5
+ * SvelteKit is Svelte: the props are read as Svelte's, and the stories drawn with Svelte's renderer, wrapped so each
6
+ * has its page, its data and its links (`@forgebook/sveltekit/frame`). What SvelteKit adds is Vite's work: Svelte's
7
+ * plugin with the project's own Svelte options, the project's other plugins, and this package's plugin for SvelteKit's
8
+ * modules, aliases and environment (`kitPlugin.ts`). SvelteKit's own plugins are left out: they build an app, and a
9
+ * catalogue draws components.
10
+ *
11
+ * @module
12
+ */
13
+ import { extname } from "node:path";
14
+ import { svelte } from "@forgebook/svelte/vite";
15
+ import { kitPlugin } from "./kitPlugin.js";
16
+ import { loadKitProject } from "./kitProject.js";
17
+ /**
18
+ * SvelteKit, as the plugin reads and draws its components.
19
+ *
20
+ * @param options - the project's folder and its Vite configuration.
21
+ * @returns the adapter.
22
+ */
23
+ export const sveltekit = (options = {}) => {
24
+ const base = svelte();
25
+ return {
26
+ name: "sveltekit",
27
+ // beside this module, with its extension: `.ts` in the sources, `.js` once built for npm
28
+ docsModule: new URL(`./extractDocs${extname(import.meta.url)}`, import.meta.url).href,
29
+ // the catalogue imports this package only: its own pages, which use Svelte's
30
+ pages: { frame: "@forgebook/sveltekit/frame", code: "@forgebook/sveltekit/code" },
31
+ browserDependencies: base.browserDependencies ?? [],
32
+ ...(base.componentFiles === undefined ? {} : { componentFiles: base.componentFiles }),
33
+ vitePlugins: async () => {
34
+ const project = await loadKitProject(options.dir ?? process.cwd(), options.viteConfig, options.keepPlugin);
35
+ const withSvelte = project.svelte === undefined ? base : svelte({ plugin: project.svelte });
36
+ return [...((await withSvelte.vitePlugins?.()) ?? []), ...project.plugins, kitPlugin(project)];
37
+ }
38
+ };
39
+ };