@forgebook/svelte 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.
@@ -0,0 +1,249 @@
1
+ /**
2
+ * The component of a Svelte story file, read from its types and comments: its props, snippets and callback props. The
3
+ * docs module of `@forgebook/svelte`: the plugin's worker and `forgebook-docs` load it through the adapter
4
+ * (`forgebook/vite/adapter`).
5
+ *
6
+ * @remarks
7
+ * The component is the meta's `component`, found by the type checker, so an import through an alias, a package or a
8
+ * barrel leads to its file. The checker reads a `.svelte` file as the TypeScript `svelte2tsx` writes for it
9
+ * (`svelteSources.ts`), in the core's programs, so what it takes is read as any component's (`componentProps.ts`). A
10
+ * component describes itself as Svelte's language server reads it, in its `<!-- @component -->` comment, tags included
11
+ * (`@useWhen`, `@deprecated`); the comment that opens its script, with `@module`, is the summary.
12
+ *
13
+ * @module
14
+ */
15
+ import { basename, extname } from "node:path";
16
+ import { chosenExport, programFor, summaryOf } from "forgebook/vite/docs-program";
17
+ import { moduleSummaryOf } from "forgebook/vite/module-summary";
18
+ import { declaredSymbol } from "forgebook/vite/prop-docs";
19
+ import { signatureDocs } from "forgebook/vite/signature-docs";
20
+ import { metaComponent, metaSubcomponents } from "forgebook/vite/story-meta";
21
+ import { guidanceOf } from "forgebook/vite/usage-guidance";
22
+ import ts from "typescript-6";
23
+ import { componentProps, sourceFileOf } from "./componentProps.js";
24
+ import { scriptOf } from "./svelteScript.js";
25
+ import { COMPONENT_EXTENSION, componentScriptFile, SVELTE_SOURCES } from "./svelteSources.js";
26
+ /** The module a Svelte story file imports its types from (`Meta`, `Story`), for the stories `forgebook new` writes. */
27
+ export const storyTypesModule = "@forgebook/svelte";
28
+ /** A story file's or a component's program, with its components read as TypeScript. */
29
+ const svelteProgram = (file) => programFor(file, { sources: SVELTE_SOURCES });
30
+ /**
31
+ * The file that declares a symbol, a component as its `.svelte` file.
32
+ *
33
+ * @param symbol - the declared symbol.
34
+ * @param fallback - the file when it has no declaration.
35
+ */
36
+ const fileOf = (symbol, fallback) => sourceFileOf(symbol.declarations?.[0]?.getSourceFile().fileName ?? fallback);
37
+ /**
38
+ * What a component says of itself: its `@component` comment, its tags, and its script's `@module` comment.
39
+ *
40
+ * @param symbol - its declared symbol.
41
+ * @param file - its file.
42
+ * @param program - the program.
43
+ */
44
+ const componentProse = (symbol, file, program) => {
45
+ const isComponentFile = extname(file) === COMPONENT_EXTENSION;
46
+ const source = isComponentFile
47
+ ? ts.createSourceFile(file, scriptOf(ts.sys.readFile(file) ?? ""), ts.ScriptTarget.Latest, true)
48
+ : program.program.getSourceFile(file);
49
+ return {
50
+ description: summaryOf(symbol, program.checker),
51
+ summary: source === undefined ? "" : moduleSummaryOf(source.text),
52
+ ...guidanceOf(symbol, program.checker)
53
+ };
54
+ };
55
+ /**
56
+ * A component's docs and the files they come from.
57
+ *
58
+ * @param name - its name.
59
+ * @param symbol - its declared symbol.
60
+ * @param node - where the code names it.
61
+ * @param program - the program.
62
+ * @param subcomponents - the subcomponents' docs and files; none outside a story file.
63
+ */
64
+ const componentOf = (name, symbol, node, program, subcomponents) => {
65
+ const file = fileOf(symbol, node.getSourceFile().fileName);
66
+ const read = componentProps(symbol, program.checker, node, file);
67
+ return {
68
+ docs: {
69
+ name,
70
+ ...componentProse(symbol, file, program),
71
+ props: read.props,
72
+ parts: read.parts,
73
+ subcomponents: subcomponents.docs
74
+ },
75
+ file,
76
+ sources: [...new Set([...read.sources, ...subcomponents.sources])]
77
+ };
78
+ };
79
+ /**
80
+ * Whether a symbol stands for a function rather than a component: a name in camelCase that can be called, declared
81
+ * outside a component's file. A Svelte component is a function too, so its name and its file tell.
82
+ */
83
+ const isLogic = (symbol, checker, node) => /^[a-z]/.test(symbol.getName()) &&
84
+ symbol.getName() !== "default" &&
85
+ extname(fileOf(symbol, "")) !== COMPONENT_EXTENSION &&
86
+ checker.getTypeOfSymbolAtLocation(symbol, node).getCallSignatures().length > 0;
87
+ /**
88
+ * A function's docs: its signature, which a story about logic shows first.
89
+ *
90
+ * @param symbol - the function's symbol.
91
+ * @param expression - where the code names it.
92
+ * @param program - the program.
93
+ */
94
+ const logicOf = (symbol, expression, program) => {
95
+ const file = fileOf(symbol, expression.getSourceFile().fileName);
96
+ const signature = signatureDocs(symbol, expression, program.checker);
97
+ return {
98
+ docs: {
99
+ name: symbol.getName(),
100
+ ...componentProse(symbol, file, program),
101
+ props: [],
102
+ parts: [],
103
+ subcomponents: [],
104
+ ...(signature === undefined ? {} : { signature })
105
+ },
106
+ file,
107
+ sources: [file]
108
+ };
109
+ };
110
+ /**
111
+ * The docs of the subcomponents a story file's meta names, with the files they come from.
112
+ *
113
+ * @param source - the story file, parsed.
114
+ * @param program - its program.
115
+ * @returns the docs and their files, or a message naming the subcomponent that cannot be resolved.
116
+ */
117
+ const subcomponentsOf = (source, program) => {
118
+ const docs = [];
119
+ const sources = [];
120
+ for (const subcomponent of metaSubcomponents(source, program.checker)) {
121
+ const symbol = declaredSymbol(subcomponent.symbol, program.checker);
122
+ if (symbol === undefined) {
123
+ return `${source.fileName}: the meta's subcomponent ${subcomponent.name} cannot be resolved`;
124
+ }
125
+ const file = fileOf(symbol, source.fileName);
126
+ const read = componentProps(symbol, program.checker, subcomponent.expression, file);
127
+ docs.push({
128
+ name: subcomponent.name,
129
+ description: componentProse(symbol, file, program).description,
130
+ props: read.props
131
+ });
132
+ sources.push(...read.sources);
133
+ }
134
+ return { docs, sources };
135
+ };
136
+ /**
137
+ * What a read gave, or why it threw as a message: one file the checker cannot read must not end `forgebook docs` for a
138
+ * whole package.
139
+ *
140
+ * @param file - the file read, for the message.
141
+ * @param read - the read.
142
+ */
143
+ const orMessage = (file, read) => {
144
+ try {
145
+ return read();
146
+ }
147
+ catch (error) {
148
+ return `${file}: ${error instanceof Error ? error.message : String(error)}`;
149
+ }
150
+ };
151
+ /** Reads a story file's component; `extractStoryComponent` says what can go wrong. */
152
+ const readStoryComponent = (storyFile) => {
153
+ const program = svelteProgram(storyFile);
154
+ if (typeof program === "string")
155
+ return program;
156
+ const source = program.program.getSourceFile(storyFile);
157
+ if (source === undefined)
158
+ return `${storyFile} is not in its program`;
159
+ const expression = metaComponent(source);
160
+ if (expression === undefined)
161
+ return { docs: { name: null } };
162
+ const symbol = declaredSymbol(program.checker.getSymbolAtLocation(expression), program.checker);
163
+ if (symbol === undefined)
164
+ return `${storyFile}: the meta's component cannot be resolved`;
165
+ if (isLogic(symbol, program.checker, expression))
166
+ return logicOf(symbol, expression, program);
167
+ const subcomponents = subcomponentsOf(source, program);
168
+ if (typeof subcomponents === "string")
169
+ return subcomponents;
170
+ return componentOf(expression.getText(source), symbol, expression, program, subcomponents);
171
+ };
172
+ /**
173
+ * A story file's component with the file that declares it.
174
+ *
175
+ * @param storyFile - the story file's absolute path.
176
+ * @returns the component, `{ docs: { name: null } }` when the meta names none, or a message saying why the file
177
+ * cannot be read.
178
+ */
179
+ export const extractStoryComponent = (storyFile) => orMessage(storyFile, () => readStoryComponent(storyFile));
180
+ /**
181
+ * The docs of a story file's component.
182
+ *
183
+ * @param storyFile - the story file's absolute path.
184
+ * @returns the component's docs, `{ name: null }` when the meta names no component, or a message saying why the file
185
+ * cannot be read.
186
+ */
187
+ export const extractComponentDocs = (storyFile) => {
188
+ const component = extractStoryComponent(storyFile);
189
+ return typeof component === "string" ? component : component.docs;
190
+ };
191
+ /** A file's name as a component's (`my-button` → `MyButton`). */
192
+ const pascalCase = (name) => name
193
+ .split(/[^A-Za-z0-9]+/)
194
+ .filter((part) => part !== "")
195
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
196
+ .join("");
197
+ /** Reads a component of a file; `extractFileComponent` says what can go wrong. */
198
+ const readFileComponent = (file, exportName) => {
199
+ const isComponentFile = extname(file) === COMPONENT_EXTENSION;
200
+ // a component is read through the TypeScript written for it
201
+ const read = isComponentFile ? componentScriptFile(file) : file;
202
+ const program = svelteProgram(read);
203
+ if (typeof program === "string")
204
+ return program;
205
+ const source = program.program.getSourceFile(read);
206
+ if (source === undefined)
207
+ return `${file} cannot be read as a component`;
208
+ const module = program.checker.getSymbolAtLocation(source);
209
+ if (module === undefined)
210
+ return `${file} exports nothing`;
211
+ const exported = program.checker.getExportsOfModule(module);
212
+ const components = exported
213
+ .filter((each) => ((declaredSymbol(each, program.checker) ?? each).flags & ts.SymbolFlags.Value) !== 0)
214
+ .map((each) => each.getName())
215
+ .filter((name) => (isComponentFile ? name === "default" : /^[A-Z]/.test(name)));
216
+ const chosen = chosenExport(components, isComponentFile ? "default" : exportName, file);
217
+ if (typeof chosen !== "string")
218
+ return chosen.problem;
219
+ const symbol = exported.find((each) => each.getName() === chosen);
220
+ if (symbol === undefined)
221
+ return `${file} exports no component ${chosen}`;
222
+ const name = isComponentFile ? pascalCase(basename(file, COMPONENT_EXTENSION)) : chosen;
223
+ return componentOf(name, declaredSymbol(symbol, program.checker) ?? symbol, source, program, {
224
+ docs: [],
225
+ sources: []
226
+ });
227
+ };
228
+ /**
229
+ * A component a file exports, for writing its first stories (`forgebook new`): a `.svelte` file is its default export,
230
+ * named after its file.
231
+ *
232
+ * @param file - the component's file, absolute.
233
+ * @param exportName - the export to read; the file's only component when none.
234
+ * @returns the component, or a message saying why it cannot be read (no component, several, a name not exported).
235
+ */
236
+ export const extractFileComponent = (file, exportName) => orMessage(file, () => readFileComponent(file, exportName));
237
+ /**
238
+ * How `forgebook new` writes a Svelte component's stories: `.ts`, since a story needs no markup, a `.svelte` file
239
+ * imported by its default export and with its extension.
240
+ *
241
+ * @param name - the component's name.
242
+ * @param file - the component's file, absolute.
243
+ */
244
+ export const storyFileShape = (name, file) => ({
245
+ extension: ".stories.ts",
246
+ importStatement: extname(file) === COMPONENT_EXTENSION
247
+ ? `import ${name} from ${JSON.stringify(`./${basename(file)}`)};`
248
+ : `import { ${name} } from ${JSON.stringify(`./${basename(file).replace(/\.[cm]?[jt]s$/, "")}`)};`
249
+ });
@@ -0,0 +1,20 @@
1
+ import type { Options as SveltePluginOptions } from "@sveltejs/vite-plugin-svelte";
2
+ import type { FrameworkAdapter } from "forgebook/vite/adapter";
3
+ /** The options of `svelte()`. */
4
+ export type SvelteOptions = {
5
+ /**
6
+ * The options of Svelte's Vite plugin (`preprocess`, `compilerOptions`, `extensions`…), over what it reads from the
7
+ * project's `svelte.config.js`; `configFile: false` reads none. For a project that keeps them elsewhere (SvelteKit 3
8
+ * writes them in `vite.config`); none by default.
9
+ */
10
+ readonly plugin?: Partial<SveltePluginOptions>;
11
+ };
12
+ /**
13
+ * Svelte, as the plugin reads its components: the docs module the docs' worker loads is this package's `extractDocs.ts`
14
+ * (the props, snippets and callback props through `svelte2tsx` and TypeScript); the generated pages draw with its renderer
15
+ * and write code with its markup writer; Vite gets Svelte's plugin, which reads the project's `svelte.config.js`.
16
+ *
17
+ * @param options - the options of Svelte's Vite plugin.
18
+ * @returns the adapter.
19
+ */
20
+ export declare const svelte: (options?: SvelteOptions) => FrameworkAdapter;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Forgebook's adapter for Svelte, for the Vite plugin: `forgebookPlugin({ framework: svelte() })`.
3
+ *
4
+ * @module
5
+ */
6
+ import { extname } from "node:path";
7
+ import { scriptOf } from "./svelteScript.js";
8
+ /** This package, whose renderer draws with a component of its own. */
9
+ const PACKAGE = "@forgebook/svelte";
10
+ /**
11
+ * The Vite plugin that has Vite compile this package's component wherever the catalogue's modules run: a test in Node
12
+ * would otherwise load the package as it is published, and Node cannot load a `.svelte` file.
13
+ *
14
+ * @returns the plugin.
15
+ */
16
+ const ownComponent = () => ({
17
+ name: "forgebook:svelte-host",
18
+ config: () => ({ ssr: { noExternal: [PACKAGE] } })
19
+ });
20
+ /**
21
+ * Svelte, as the plugin reads its components: the docs module the docs' worker loads is this package's `extractDocs.ts`
22
+ * (the props, snippets and callback props through `svelte2tsx` and TypeScript); the generated pages draw with its renderer
23
+ * and write code with its markup writer; Vite gets Svelte's plugin, which reads the project's `svelte.config.js`.
24
+ *
25
+ * @param options - the options of Svelte's Vite plugin.
26
+ * @returns the adapter.
27
+ */
28
+ export const svelte = (options = {}) => ({
29
+ name: "svelte",
30
+ // beside this module, with its extension: `.ts` in the sources, `.js` once built for npm
31
+ docsModule: new URL(`./extractDocs${extname(import.meta.url)}`, import.meta.url).href,
32
+ pages: { frame: `${PACKAGE}/frame`, code: `${PACKAGE}/code` },
33
+ // the server render is named too: the page never runs it, but Vite meets its import in the renderer and would reload
34
+ browserDependencies: ["svelte/server"],
35
+ componentFiles: { extension: ".svelte", scriptOf },
36
+ // components and their hot updates: an edited component updates in its frame
37
+ vitePlugins: async () => {
38
+ const { svelte: sveltePlugin } = await import("@sveltejs/vite-plugin-svelte");
39
+ return [sveltePlugin(options.plugin), ownComponent()];
40
+ }
41
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The first script of a component: a file with a `<script module>` and a `<script>` opens with either.
3
+ *
4
+ * @param text - the file's text.
5
+ * @returns the script's text; empty when the file has none.
6
+ */
7
+ export declare const scriptOf: (text: string) => string;
8
+ /**
9
+ * Whether a component's scripts are TypeScript (`<script lang="ts">`).
10
+ *
11
+ * @param text - the file's text.
12
+ */
13
+ export declare const isTypeScript: (text: string) => boolean;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The scripts of a Svelte component, read by Svelte's own parser: the one its opening comment and its `@module` sit in,
3
+ * and the language it is written in.
4
+ *
5
+ * @module
6
+ */
7
+ import { parse } from "svelte/compiler";
8
+ /**
9
+ * A component's parsed file, or none when Svelte cannot read it.
10
+ *
11
+ * @param text - the file's text.
12
+ */
13
+ const parsed = (text) => {
14
+ try {
15
+ return parse(text, { modern: true });
16
+ }
17
+ catch {
18
+ // a component being edited may not parse for a moment: it has no script to read until it does
19
+ return undefined;
20
+ }
21
+ };
22
+ /**
23
+ * The scripts of a component, in the order the file writes them: `<script module>` and the instance's `<script>`.
24
+ *
25
+ * @param root - the parsed file.
26
+ */
27
+ const scriptsOf = (root) => [root?.module, root?.instance]
28
+ .filter((script) => script !== undefined && script !== null)
29
+ .sort((left, right) => left.start - right.start);
30
+ /**
31
+ * The first script of a component: a file with a `<script module>` and a `<script>` opens with either.
32
+ *
33
+ * @param text - the file's text.
34
+ * @returns the script's text; empty when the file has none.
35
+ */
36
+ export const scriptOf = (text) => {
37
+ const [first] = scriptsOf(parsed(text));
38
+ if (first === undefined)
39
+ return "";
40
+ // Svelte's parser places the script's program in the file, which ESTree's type does not say
41
+ const { content } = first;
42
+ return "start" in content &&
43
+ "end" in content &&
44
+ typeof content.start === "number" &&
45
+ typeof content.end === "number"
46
+ ? text.slice(content.start, content.end)
47
+ : "";
48
+ };
49
+ /** The value an attribute writes, as text; none for an expression or a bare attribute. */
50
+ const attributeText = (attribute) => {
51
+ const { value } = attribute;
52
+ if (value === true || !Array.isArray(value))
53
+ return undefined;
54
+ return value.map((part) => (part.type === "Text" ? part.data : "")).join("");
55
+ };
56
+ /**
57
+ * Whether a component's scripts are TypeScript (`<script lang="ts">`).
58
+ *
59
+ * @param text - the file's text.
60
+ */
61
+ export const isTypeScript = (text) => scriptsOf(parsed(text)).some((script) => script.attributes.some((attribute) => attribute.name === "lang" && /^(ts|typescript)$/.test(attributeText(attribute) ?? "")));
@@ -0,0 +1,18 @@
1
+ import type { ProgramSources } from "forgebook/vite/docs-program";
2
+ /** A component's extension. */
3
+ export declare const COMPONENT_EXTENSION = ".svelte";
4
+ /**
5
+ * The file TypeScript reads in a component's place: beside it, `.ts`, or `.js` for one written without TypeScript.
6
+ *
7
+ * @param component - the component's file.
8
+ */
9
+ export declare const componentScriptFile: (component: string) => string;
10
+ /**
11
+ * The component a file TypeScript asks for stands for, when it does.
12
+ *
13
+ * @param fileName - the file TypeScript asks for.
14
+ * @returns the component's file, or none for a file of its own.
15
+ */
16
+ export declare const componentOfScriptFile: (fileName: string) => string | undefined;
17
+ /** Svelte's components, as the core's programs read them. */
18
+ export declare const SVELTE_SOURCES: ProgramSources;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Svelte components as TypeScript reads them: each `.svelte` file written as the TypeScript it amounts to, by
3
+ * `svelte2tsx`, the converter Svelte's language server and `svelte-check` use, so a component's props have the types
4
+ * the editor shows.
5
+ *
6
+ * @remarks
7
+ * The core's programs read these in a component's place (`ProgramSources` of `forgebook/vite/docs-program`): an import
8
+ * of `./Badge.svelte` looks for `./Badge.svelte.ts` first, and finds the component's TypeScript there; a component
9
+ * without TypeScript is `./Badge.svelte.js`, so its JSDoc types count. A real `.svelte.ts` module (one with runes) and
10
+ * the declarations a package publishes beside a component (`Badge.svelte.d.ts`) are read as they are.
11
+ *
12
+ * `svelte2tsx` loads `typescript` itself, which a project on TypeScript 7 answers with a package that has no compiler
13
+ * API: its import is pointed at the TypeScript 6 the core reads with, for that package only, through Node's module hooks.
14
+ * Its output is text, so which TypeScript wrote it does not matter to the program that reads it.
15
+ *
16
+ * @module
17
+ */
18
+ import { readFileSync, statSync } from "node:fs";
19
+ import { createRequire, registerHooks } from "node:module";
20
+ import { dirname } from "node:path";
21
+ import { pathToFileURL } from "node:url";
22
+ import { parse, VERSION } from "svelte/compiler";
23
+ import { isTypeScript } from "./svelteScript.js";
24
+ /** Resolves what this module depends on, as Node resolves it from here. */
25
+ const resolveHere = createRequire(import.meta.url).resolve;
26
+ /** The folder `svelte2tsx` is installed in, as a URL with its slash. */
27
+ const SVELTE2TSX_FOLDER = `${pathToFileURL(dirname(resolveHere("svelte2tsx"))).href}/`;
28
+ /** The TypeScript 6 the core reads with. */
29
+ const TYPESCRIPT_6 = pathToFileURL(resolveHere("typescript-6")).href;
30
+ // registered before `svelte2tsx` is first loaded, which the docs' worker does in a thread of its own: a copy loaded
31
+ // earlier in the same thread would keep the TypeScript it found
32
+ registerHooks({
33
+ resolve: (specifier, context, nextResolve) => specifier === "typescript" && context.parentURL?.startsWith(SVELTE2TSX_FOLDER) === true
34
+ ? { url: TYPESCRIPT_6, format: "commonjs", shortCircuit: true }
35
+ : nextResolve(specifier, context)
36
+ });
37
+ const { svelte2tsx } = await import("svelte2tsx");
38
+ /** A component's extension. */
39
+ export const COMPONENT_EXTENSION = ".svelte";
40
+ /** The components written so far, by their file: a program renewed after an edit writes only what changed. */
41
+ const written = new Map();
42
+ /**
43
+ * Whether a file is on disk.
44
+ *
45
+ * @param file - the file.
46
+ */
47
+ const isFile = (file) => {
48
+ try {
49
+ return statSync(file).isFile();
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ };
55
+ /**
56
+ * A component's TypeScript, written again only when its text changed.
57
+ *
58
+ * @param component - the component's file.
59
+ * @returns it, or none when there is no such file.
60
+ */
61
+ const writtenComponent = (component) => {
62
+ if (!isFile(component))
63
+ return undefined;
64
+ const text = readFileSync(component, "utf8");
65
+ const kept = written.get(component);
66
+ if (kept?.text === text)
67
+ return kept;
68
+ const isTs = isTypeScript(text);
69
+ let code;
70
+ try {
71
+ code = svelte2tsx(text, {
72
+ filename: component,
73
+ isTsFile: isTs,
74
+ mode: "ts",
75
+ parse,
76
+ version: VERSION
77
+ }).code;
78
+ }
79
+ catch {
80
+ // a component that does not parse has no TypeScript: an import of it stays unresolved until it does
81
+ code = undefined;
82
+ }
83
+ const entry = { text, isTypeScript: isTs, code };
84
+ written.set(component, entry);
85
+ return entry;
86
+ };
87
+ /**
88
+ * The file TypeScript reads in a component's place: beside it, `.ts`, or `.js` for one written without TypeScript.
89
+ *
90
+ * @param component - the component's file.
91
+ */
92
+ export const componentScriptFile = (component) => `${component}${writtenComponent(component)?.isTypeScript === false ? ".js" : ".ts"}`;
93
+ /**
94
+ * The component a file TypeScript asks for stands for, when it does.
95
+ *
96
+ * @param fileName - the file TypeScript asks for.
97
+ * @returns the component's file, or none for a file of its own.
98
+ */
99
+ export const componentOfScriptFile = (fileName) => {
100
+ const match = /\.svelte\.([jt]s)$/.exec(fileName);
101
+ if (match === null)
102
+ return undefined;
103
+ const component = fileName.slice(0, -".ts".length);
104
+ if (isFile(fileName) || isFile(`${component}.d.ts`))
105
+ return undefined;
106
+ const entry = writtenComponent(component);
107
+ if (entry?.code === undefined || entry.isTypeScript !== (match[1] === "ts"))
108
+ return undefined;
109
+ return component;
110
+ };
111
+ /**
112
+ * The text of a file that stands for a component.
113
+ *
114
+ * @param fileName - the file TypeScript asks for.
115
+ */
116
+ const textOf = (fileName) => {
117
+ const component = componentOfScriptFile(fileName);
118
+ return component === undefined ? undefined : writtenComponent(component)?.code;
119
+ };
120
+ /** Svelte's components, as the core's programs read them. */
121
+ export const SVELTE_SOURCES = {
122
+ name: "svelte",
123
+ textOf,
124
+ // what the written TypeScript refers to: `svelte2tsx`'s helpers and the elements' attributes
125
+ roots: [resolveHere("svelte2tsx/svelte-shims-v4.d.ts"), resolveHere("svelte2tsx/svelte-jsx-v4.d.ts")],
126
+ options: { allowJs: true }
127
+ };
package/package.json CHANGED
@@ -1,6 +1,87 @@
1
1
  {
2
2
  "name": "@forgebook/svelte",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Forgebook for Svelte 5: the story types and the context helpers, the renderer the frame mounts a story with, and the adapter that reads a Svelte component's props, snippets and callback props.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ensiria/forgebook.git",
9
+ "directory": "packages/svelte"
10
+ },
11
+ "type": "module",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "engines": {
16
+ "node": ">=22.18"
17
+ },
18
+ "sideEffects": [
19
+ "**/vite/svelteSources.*"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/src/stories.d.ts",
24
+ "default": "./dist/src/stories.js"
25
+ },
26
+ "./clock": {
27
+ "types": "./dist/src/clock.d.ts",
28
+ "default": "./dist/src/clock.js"
29
+ },
30
+ "./texts": {
31
+ "types": "./dist/src/texts.d.ts",
32
+ "default": "./dist/src/texts.js"
33
+ },
34
+ "./renderer": {
35
+ "types": "./dist/src/svelteRenderer.d.ts",
36
+ "default": "./dist/src/svelteRenderer.js"
37
+ },
38
+ "./frame": {
39
+ "types": "./dist/src/mountFrame.d.ts",
40
+ "default": "./dist/src/mountFrame.js"
41
+ },
42
+ "./code": {
43
+ "types": "./dist/src/storyMarkup.d.ts",
44
+ "default": "./dist/src/storyMarkup.js"
45
+ },
46
+ "./vite": {
47
+ "types": "./dist/src/vite/svelte.d.ts",
48
+ "default": "./dist/src/vite/svelte.js"
49
+ },
50
+ "./docs": {
51
+ "types": "./dist/src/vite/extractDocs.d.ts",
52
+ "default": "./dist/src/vite/extractDocs.js"
53
+ }
54
+ },
55
+ "dependencies": {
56
+ "@sveltejs/vite-plugin-svelte": "7.3.1",
57
+ "svelte2tsx": "0.7.63",
58
+ "typescript-6": "npm:typescript@6.0.3",
59
+ "@forgebook/story-format": "0.2.0"
60
+ },
61
+ "peerDependencies": {
62
+ "svelte": "^5.46.4",
63
+ "forgebook": "^0.2.0"
64
+ },
65
+ "devDependencies": {
66
+ "@types/node": "24.19.2",
67
+ "happy-dom": "21.0.4",
68
+ "svelte": "5.57.3",
69
+ "svelte-check": "4.7.8",
70
+ "typescript": "npm:typescript@6.0.3",
71
+ "vite": "8.3.5",
72
+ "vitest": "5.0.5",
73
+ "@forgebook/testing": "0.2.0",
74
+ "forgebook": "0.2.0",
75
+ "@forgebook/tools": "0.0.0",
76
+ "@forgebook/typescript-config": "0.0.0"
77
+ },
78
+ "publishConfig": {
79
+ "access": "public"
80
+ },
81
+ "scripts": {
82
+ "build": "package-build build",
83
+ "manifest": "package-build manifest",
84
+ "test": "vitest run",
85
+ "typecheck": "svelte-check --tsconfig src/tsconfig.json --fail-on-warnings && tsc -p src/vite && svelte-check --tsconfig test/tsconfig.json --fail-on-warnings"
86
+ }
6
87
  }