@poveste/plugin-svelte 0.19.1 → 0.21.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/README.md +2 -0
- package/dist/client/util.js +1 -1
- package/dist/collect/Story.js +1 -1
- package/dist/collect/Story.svelte +5 -0
- package/dist/collect/index.js +1 -1
- package/dist/docgen/defaults.d.ts +14 -0
- package/dist/docgen/defaults.js +57 -0
- package/dist/docgen/extractor.d.ts +8 -0
- package/dist/docgen/extractor.js +172 -0
- package/dist/helpers.d.ts +5 -4
- package/dist/index.node.js +30 -0
- package/dist/util/auto-props-vite.d.ts +3 -0
- package/dist/util/auto-props-vite.js +29 -4
- package/package.json +19 -14
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ and your content reads it from the snippet:
|
|
|
55
55
|
>
|
|
56
56
|
> [How, and why](https://poveste.dev/guide/migration-from-histoire#svelte-story-state-moves-to-initstate)
|
|
57
57
|
|
|
58
|
+
A story's Docs tab shows a props, slots and events table for each component it imports, read from the type on the component's `$props()`. It needs `typescript` 6 or earlier and `svelte2tsx` in the book: `npm i -D typescript@6 svelte2tsx`. See [Component docs](https://poveste.dev/guide/svelte/getting-started#component-docs).
|
|
59
|
+
|
|
58
60
|
[Svelte guide](https://poveste.dev/guide/svelte/getting-started) ·
|
|
59
61
|
[SvelteKit](https://poveste.dev/guide/sveltekit/getting-started) ·
|
|
60
62
|
[Documentation](https://poveste.dev)
|
package/dist/client/util.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{watch as e}from"@poveste/vendors/vue";import{applyState as t,clone as n}from"@poveste/shared";function r(e){let t={};for(let n in e){if(n===`Hst`)continue;let r=e[n];typeof r!=`function`&&r!==void 0&&(r instanceof HTMLElement||typeof r==`object`&&r?.$$||(t[n]=r))}return t}function i(i,a){let o=!1,s=e(()=>i,e=>{
|
|
1
|
+
import{watch as e}from"@poveste/vendors/vue";import{applyState as t,clone as n}from"@poveste/shared";function r(e){let t={};for(let n in e){if(n===`Hst`)continue;let r=e[n];typeof r!=`function`&&r!==void 0&&(r instanceof HTMLElement||typeof r==`object`&&r?.$$||(t[n]=r))}return t}function i(i,a){let o=!1,s=e(()=>i,e=>{e!=null&&(o?o=!1:(o=!0,a(r(e))))},{deep:!0,immediate:!0});function c(e){e!=null&&(o=!o&&t(i,n(r(e))))}return{apply:c,stop(){s()}}}export{i as syncState};
|
package/dist/collect/Story.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{addStoryContext as e,addVariantContext as t,collectStoryContext as n,storyFileContext as r}from"../contexts.js";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";function a(a,o){i.push(o,!1);let s=i.prop(o,`title`,8,null),c=i.prop(o,`id`,8,null),l=i.prop(o,`group`,8,null),u=i.prop(o,`layout`,8,null),d=i.prop(o,`icon`,8,null),f=i.prop(o,`iconColor`,8,null),p=i.prop(o,`docsOnly`,8,!1),m=i.prop(o,`autoPropsDisabled`,8,null),
|
|
1
|
+
import{addStoryContext as e,addVariantContext as t,collectStoryContext as n,storyFileContext as r}from"../contexts.js";import"svelte/internal/disclose-version";import"svelte/internal/flags/legacy";import*as i from"svelte/internal/client";function a(a,o){i.push(o,!1);let s=i.prop(o,`title`,8,null),c=i.prop(o,`id`,8,null),l=i.prop(o,`group`,8,null),u=i.prop(o,`layout`,8,null),d=i.prop(o,`icon`,8,null),f=i.prop(o,`iconColor`,8,null),p=i.prop(o,`docsOnly`,8,!1),m=i.prop(o,`autoDocs`,8);i.prop(o,`component`,8);let h=i.prop(o,`autoPropsDisabled`,8,null),g=i.prop(o,`initState`,8,null),_=i.prop(o,`meta`,8,null),v=e.getOptional(),y=r.getOptional(),b=c()??y?.id,x=s()??y?.fileName;if(!b||x===void 0)throw Error("[poveste] a <Story> collected outside a story file needs an `id` and a `title`");let S={id:b,title:x,group:l(),layout:u(),icon:d(),iconColor:f(),docsOnly:p(),...m()===void 0?{}:{autoDocs:m()},autoPropsDisabled:h(),..._()?{meta:_()}:{},variants:[]};v?.(S);let C=g()?g()():{};n.set(S),t.set(e=>{S.variants.push(e)}),i.init();var w=i.comment(),T=i.first_child(w);i.slot(T,o,`default`,{get state(){return C}},null),i.append(a,w),i.pop()}export{a as default};
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
export let icon = null
|
|
9
9
|
export let iconColor = null
|
|
10
10
|
export let docsOnly = false
|
|
11
|
+
export let autoDocs
|
|
12
|
+
// Read from the source by auto-docs, not here (#1432).
|
|
13
|
+
// svelte-ignore export_let_unused
|
|
14
|
+
export let component
|
|
11
15
|
export let autoPropsDisabled = null
|
|
12
16
|
export let initState = null
|
|
13
17
|
export let meta = null
|
|
@@ -33,6 +37,7 @@
|
|
|
33
37
|
icon,
|
|
34
38
|
iconColor,
|
|
35
39
|
docsOnly,
|
|
40
|
+
...autoDocs !== undefined ? { autoDocs } : {},
|
|
36
41
|
// Collected rather than inherited down: `MountStory` forwards story props
|
|
37
42
|
// only to an implicit variant (#466). Left undefined when unset, or the
|
|
38
43
|
// collector's `??=` merge could never apply `defaultStoryProps`.
|
package/dist/collect/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{addStoryContext as e,storyFileContext as t}from"../contexts.js";import{callSetupFunctions as n,mountSvelteComponent as r}from"../util/svelte.js";import{collectHst as i}from"./hst.js";import*as a from"virtual:$poveste-generated-global-setup";import*as o from"virtual:$poveste-setup";import{tick as s}from"svelte";async function c({file:c,el:l,storyData:u}){let{default:d}=await import(/* @vite-ignore */c.moduleId),f=await r(d,{target:l,props:{Hst:i()},context:new Map([[e.key,e=>{u.push(e)}],[t.key,c]])},`client`),p={app:f.app,story:void 0,variant:void 0};
|
|
1
|
+
import{addStoryContext as e,storyFileContext as t}from"../contexts.js";import{callSetupFunctions as n,mountSvelteComponent as r}from"../util/svelte.js";import{collectHst as i}from"./hst.js";import*as a from"virtual:$poveste-generated-global-setup";import*as o from"virtual:$poveste-setup";import{tick as s}from"svelte";async function c({file:c,el:l,storyData:u}){let{default:d}=await import(/* @vite-ignore */c.moduleId),f=await r(d,{target:l,props:{Hst:i()},context:new Map([[e.key,e=>{u.push(e)}],[t.key,c]])},`client`),p={app:f.app,story:void 0,variant:void 0};await n(a,o,p),await s(),u[0]&&(u[0].variants.length||u[0].variants.push({id:`_default`,title:`default`})),f.destroy()}export{c as run};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Parse = typeof import('svelte/compiler').parse;
|
|
2
|
+
export interface ScriptReading {
|
|
3
|
+
/** The defaults the component's own code applies, as written. */
|
|
4
|
+
defaults: Map<string, string>;
|
|
5
|
+
/** The props its `$props()` destructuring names. */
|
|
6
|
+
taken: Set<string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* What a component's script says about its props: a `$props()` destructuring
|
|
10
|
+
* default, through `$bindable(...)`, or a Svelte 4 `export let`, and which props
|
|
11
|
+
* the destructuring takes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readScript(parse: Parse, source: string): ScriptReading;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function keyOf(property) {
|
|
2
|
+
return property.key?.name ?? property.key?.value;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* What a component's script says about its props: a `$props()` destructuring
|
|
6
|
+
* default, through `$bindable(...)`, or a Svelte 4 `export let`, and which props
|
|
7
|
+
* the destructuring takes.
|
|
8
|
+
*/
|
|
9
|
+
export function readScript(parse, source) {
|
|
10
|
+
const defaults = new Map();
|
|
11
|
+
const taken = new Set();
|
|
12
|
+
const ast = parse(source, { modern: true });
|
|
13
|
+
const text = (node) => source.slice(node.start, node.end);
|
|
14
|
+
function visit(node) {
|
|
15
|
+
if (!node || typeof node !== 'object') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (node.type === 'VariableDeclarator' && node.init?.type === 'CallExpression' && node.init.callee?.name === '$props' && node.id?.type === 'ObjectPattern') {
|
|
19
|
+
for (const property of node.id.properties ?? []) {
|
|
20
|
+
const name = keyOf(property);
|
|
21
|
+
if (property.type === 'Property' && name) {
|
|
22
|
+
taken.add(name);
|
|
23
|
+
}
|
|
24
|
+
if (property.type !== 'Property' || property.value?.type !== 'AssignmentPattern' || !name) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
let value = property.value.right;
|
|
28
|
+
if (value?.type === 'CallExpression' && value.callee?.name === '$bindable') {
|
|
29
|
+
value = value.arguments?.[0];
|
|
30
|
+
}
|
|
31
|
+
if (value) {
|
|
32
|
+
defaults.set(name, text(value));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (node.type === 'ExportNamedDeclaration' && node.declaration?.type === 'VariableDeclaration' && node.declaration.kind === 'let') {
|
|
37
|
+
for (const declarator of node.declaration.declarations ?? []) {
|
|
38
|
+
if (declarator.id?.type === 'Identifier' && declarator.id.name && declarator.init) {
|
|
39
|
+
defaults.set(declarator.id.name, text(declarator.init));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const [key, value] of Object.entries(node)) {
|
|
44
|
+
if (key === 'parent') {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
value.forEach(visit);
|
|
49
|
+
}
|
|
50
|
+
else if (value && typeof value === 'object' && 'type' in value) {
|
|
51
|
+
visit(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
visit(ast.instance?.content);
|
|
56
|
+
return { defaults, taken };
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DocgenExtractor, DocgenExtractorContext } from '@poveste/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Runs in the docgen worker. One language service over the book's tsconfig, in
|
|
4
|
+
* which a `.svelte` file is its `svelte2tsx` output; a probe file names each
|
|
5
|
+
* requested component's `ComponentProps`, which the checker resolves through
|
|
6
|
+
* imported and extended types the way the book's editor does (#501, #1160).
|
|
7
|
+
*/
|
|
8
|
+
export declare function createExtractor(context: DocgenExtractorContext): DocgenExtractor;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'pathe';
|
|
4
|
+
import { ambientFiles, documentProps, loadFromBook, readTsconfig } from 'poveste/docgen';
|
|
5
|
+
import { readScript } from './defaults.js';
|
|
6
|
+
// A Svelte 5 `Snippet<...>`, and Svelte 4's slot shape as `svelte2tsx` types it.
|
|
7
|
+
const SLOT_TYPE = /\bSnippet\b/;
|
|
8
|
+
// Svelte 5's DOM-style `onclick` as well as a library's `onCheckedChange`.
|
|
9
|
+
const EVENT_NAME = /^on[a-z]/i;
|
|
10
|
+
// Each `.svelte` file is read as its `svelte2tsx` output under this name. Nothing on
|
|
11
|
+
// disk carries it, unlike `x.svelte.ts`, which is a rune module a book may have.
|
|
12
|
+
const VIRTUAL = '.tsx';
|
|
13
|
+
function isVirtual(file) {
|
|
14
|
+
return file.endsWith(`.svelte${VIRTUAL}`) && !file.includes('/node_modules/');
|
|
15
|
+
}
|
|
16
|
+
function realOf(file) {
|
|
17
|
+
return isVirtual(file) ? file.slice(0, -VIRTUAL.length) : file;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Runs in the docgen worker. One language service over the book's tsconfig, in
|
|
21
|
+
* which a `.svelte` file is its `svelte2tsx` output; a probe file names each
|
|
22
|
+
* requested component's `ComponentProps`, which the checker resolves through
|
|
23
|
+
* imported and extended types the way the book's editor does (#501, #1160).
|
|
24
|
+
*/
|
|
25
|
+
export function createExtractor(context) {
|
|
26
|
+
const root = context.root;
|
|
27
|
+
const options = (context.options ?? {});
|
|
28
|
+
const typescript = loadFromBook(root, 'typescript', 'reads Svelte components\' types');
|
|
29
|
+
const { VERSION, parse } = loadFromBook(root, 'svelte/compiler', 'reads Svelte components\' source');
|
|
30
|
+
const { svelte2tsx } = loadFromBook(root, 'svelte2tsx', 'reads Svelte components\' types through it');
|
|
31
|
+
const shims = ['svelte-shims-v4.d.ts', 'svelte-jsx-v4.d.ts'].map(file => join(dirname(createRequire(join(root, 'package.json')).resolve('svelte2tsx/package.json')), file));
|
|
32
|
+
const parsed = readTsconfig(typescript, root);
|
|
33
|
+
// The book's `app.d.ts` and other globals, which the probe's imports would not reach.
|
|
34
|
+
const ambient = ambientFiles(parsed);
|
|
35
|
+
const compilerOptions = {
|
|
36
|
+
module: typescript.ModuleKind.ESNext,
|
|
37
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
|
38
|
+
target: typescript.ScriptTarget.ESNext,
|
|
39
|
+
strict: true,
|
|
40
|
+
skipLibCheck: true,
|
|
41
|
+
...parsed ? parsed.options : {},
|
|
42
|
+
// What `svelte2tsx` output needs whatever the book says: it is TSX, and the probe is never emitted.
|
|
43
|
+
jsx: typescript.JsxEmit.Preserve,
|
|
44
|
+
allowJs: true,
|
|
45
|
+
noEmit: true,
|
|
46
|
+
};
|
|
47
|
+
const probe = join(root, '__poveste_docgen__.ts');
|
|
48
|
+
const requested = [];
|
|
49
|
+
const versions = new Map();
|
|
50
|
+
const generated = new Map();
|
|
51
|
+
let projectVersion = 0;
|
|
52
|
+
function bump(file) {
|
|
53
|
+
versions.set(file, (versions.get(file) ?? 0) + 1);
|
|
54
|
+
projectVersion++;
|
|
55
|
+
}
|
|
56
|
+
function probeText() {
|
|
57
|
+
return requested.map((file, index) => `import C${index} from ${JSON.stringify(file)}\nexport type P${index} = import('svelte').ComponentProps<typeof C${index}>\n`).join('');
|
|
58
|
+
}
|
|
59
|
+
// Whether a component's script is TypeScript. One without `lang="ts"` comes out of
|
|
60
|
+
// `svelte2tsx` typed in JSDoc, which only a JS source file reads.
|
|
61
|
+
const typed = new Map();
|
|
62
|
+
function tsx(file) {
|
|
63
|
+
let code = generated.get(file);
|
|
64
|
+
if (code === undefined) {
|
|
65
|
+
const source = readFileSync(file, 'utf8');
|
|
66
|
+
const isTsFile = /<script\s[^>]*lang=["']ts["']/.test(source);
|
|
67
|
+
typed.set(file, isTsFile);
|
|
68
|
+
code = svelte2tsx(source, { filename: file, isTsFile, mode: 'ts', version: VERSION }).code;
|
|
69
|
+
generated.set(file, code);
|
|
70
|
+
}
|
|
71
|
+
return code;
|
|
72
|
+
}
|
|
73
|
+
function scriptKind(file) {
|
|
74
|
+
if (!isVirtual(file)) {
|
|
75
|
+
return typescript.ScriptKind.Unknown;
|
|
76
|
+
}
|
|
77
|
+
tsx(realOf(file));
|
|
78
|
+
return typed.get(realOf(file)) ? typescript.ScriptKind.TSX : typescript.ScriptKind.JSX;
|
|
79
|
+
}
|
|
80
|
+
function read(file) {
|
|
81
|
+
if (file === probe) {
|
|
82
|
+
return probeText();
|
|
83
|
+
}
|
|
84
|
+
if (isVirtual(file) && existsSync(realOf(file))) {
|
|
85
|
+
return tsx(realOf(file));
|
|
86
|
+
}
|
|
87
|
+
return typescript.sys.readFile(file);
|
|
88
|
+
}
|
|
89
|
+
function exists(file) {
|
|
90
|
+
return file === probe || (isVirtual(file) ? existsSync(realOf(file)) : typescript.sys.fileExists(file));
|
|
91
|
+
}
|
|
92
|
+
const moduleHost = { ...typescript.sys, fileExists: exists, readFile: read };
|
|
93
|
+
const cache = typescript.createModuleResolutionCache(root, name => name, compilerOptions);
|
|
94
|
+
/**
|
|
95
|
+
* A `.svelte` specifier goes to the virtual file when the book has the component,
|
|
96
|
+
* through the book's own `paths`, and otherwise resolves as TypeScript would: to a
|
|
97
|
+
* package's `.svelte.d.ts`, or to the ambient `*.svelte` module.
|
|
98
|
+
*/
|
|
99
|
+
function resolveOne(specifier, containingFile) {
|
|
100
|
+
if (specifier.endsWith('.svelte')) {
|
|
101
|
+
const direct = specifier.startsWith('.') || isAbsolute(specifier) ? resolve(dirname(containingFile), specifier) + VIRTUAL : undefined;
|
|
102
|
+
// Through `paths` (`$lib/x.svelte`), the mapped location is all that is kept: TypeScript marks
|
|
103
|
+
// that resolution as made with a `.tsx` extension, which the specifier does not have, and the
|
|
104
|
+
// checker then fails an assertion looking for it.
|
|
105
|
+
const target = direct ?? typescript.resolveModuleName(specifier + VIRTUAL, containingFile, compilerOptions, moduleHost, cache).resolvedModule?.resolvedFileName;
|
|
106
|
+
if (target && isVirtual(target) && existsSync(realOf(target))) {
|
|
107
|
+
return { resolvedModule: { resolvedFileName: target, extension: typescript.Extension.Tsx, isExternalLibraryImport: false } };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return typescript.resolveModuleName(specifier, containingFile, compilerOptions, moduleHost, cache);
|
|
111
|
+
}
|
|
112
|
+
const service = typescript.createLanguageService({
|
|
113
|
+
getProjectVersion: () => String(projectVersion),
|
|
114
|
+
getCompilationSettings: () => compilerOptions,
|
|
115
|
+
getScriptFileNames: () => [probe, ...shims, ...ambient],
|
|
116
|
+
getScriptVersion: file => String(versions.get(file) ?? 0),
|
|
117
|
+
getScriptSnapshot(file) {
|
|
118
|
+
const text = read(file);
|
|
119
|
+
return text === undefined ? undefined : typescript.ScriptSnapshot.fromString(text);
|
|
120
|
+
},
|
|
121
|
+
getScriptKind: scriptKind,
|
|
122
|
+
getCurrentDirectory: () => root,
|
|
123
|
+
getDefaultLibFileName: settings => typescript.getDefaultLibFilePath(settings),
|
|
124
|
+
fileExists: exists,
|
|
125
|
+
readFile: read,
|
|
126
|
+
readDirectory: typescript.sys.readDirectory,
|
|
127
|
+
directoryExists: typescript.sys.directoryExists,
|
|
128
|
+
getDirectories: typescript.sys.getDirectories,
|
|
129
|
+
...typescript.sys.realpath ? { realpath: typescript.sys.realpath } : {},
|
|
130
|
+
resolveModuleNameLiterals: (literals, containingFile) => literals.map(literal => resolveOne(literal.text, containingFile)),
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
extract(file) {
|
|
134
|
+
let index = requested.indexOf(file);
|
|
135
|
+
if (index === -1) {
|
|
136
|
+
index = requested.push(file) - 1;
|
|
137
|
+
bump(probe);
|
|
138
|
+
}
|
|
139
|
+
const program = service.getProgram();
|
|
140
|
+
const alias = program?.getSourceFile(probe)?.statements.find((statement) => typescript.isTypeAliasDeclaration(statement) && statement.name.text === `P${index}`);
|
|
141
|
+
if (!program || !alias) {
|
|
142
|
+
throw new Error('TypeScript built no program for the book');
|
|
143
|
+
}
|
|
144
|
+
const checker = program.getTypeChecker();
|
|
145
|
+
const props = checker.getTypeAtLocation(alias.name);
|
|
146
|
+
if (props.flags & typescript.TypeFlags.Any) {
|
|
147
|
+
throw new Error('its props type did not resolve');
|
|
148
|
+
}
|
|
149
|
+
const script = readScript(parse, readFileSync(file, 'utf8'));
|
|
150
|
+
return documentProps(typescript, checker, props, {
|
|
151
|
+
location: alias,
|
|
152
|
+
name: basename(file).replace(/\.svelte$/, ''),
|
|
153
|
+
defaults: script.defaults,
|
|
154
|
+
slotType: SLOT_TYPE,
|
|
155
|
+
eventName: EVENT_NAME,
|
|
156
|
+
takesChildren: script.taken.has('children'),
|
|
157
|
+
}, options);
|
|
158
|
+
},
|
|
159
|
+
update(file) {
|
|
160
|
+
if (file.endsWith('.svelte')) {
|
|
161
|
+
generated.delete(file);
|
|
162
|
+
typed.delete(file);
|
|
163
|
+
bump(file + VIRTUAL);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
bump(file);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
sources: () => service.getProgram()?.getSourceFiles().map(sourceFile => realOf(sourceFile.fileName)).filter(file => file !== probe && !file.includes('/node_modules/')) ?? [],
|
|
170
|
+
dispose: () => service.dispose(),
|
|
171
|
+
};
|
|
172
|
+
}
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HstControlOption } from '@poveste/controls';
|
|
2
|
-
import type { Story, StoryMeta, StoryProps, Variant, VariantMeta, VariantProps } from '@poveste/shared';
|
|
2
|
+
import type { Awaitable, Story, StoryComponentOptions, StoryMeta, StoryProps, Variant, VariantMeta, VariantProps } from '@poveste/shared';
|
|
3
3
|
import type { Component, Snippet } from 'svelte';
|
|
4
4
|
export interface SvelteStorySetupApi {
|
|
5
5
|
app: any;
|
|
@@ -44,7 +44,7 @@ type WithState<P> = Omit<P, 'source'> & {
|
|
|
44
44
|
source?: string | ((state: StoryState) => string);
|
|
45
45
|
};
|
|
46
46
|
export interface Hst {
|
|
47
|
-
Story: Component<WithState<WithChildren<StoryProps>> & {
|
|
47
|
+
Story: Component<WithState<WithChildren<StoryProps>> & StoryComponentOptions<Component<any> | Component<any>[]> & {
|
|
48
48
|
meta?: StoryMeta;
|
|
49
49
|
}>;
|
|
50
50
|
Variant: Component<WithState<WithChildren<VariantProps>> & {
|
|
@@ -108,7 +108,7 @@ export interface Hst {
|
|
|
108
108
|
value: unknown;
|
|
109
109
|
title: string;
|
|
110
110
|
}>;
|
|
111
|
-
|
|
111
|
+
ColorShades: Component<{
|
|
112
112
|
shades: Record<string, any>;
|
|
113
113
|
getName?: (key: string, color: string) => string;
|
|
114
114
|
search?: string;
|
|
@@ -123,7 +123,8 @@ export interface Hst {
|
|
|
123
123
|
colSize?: number;
|
|
124
124
|
}>;
|
|
125
125
|
CopyIcon: Component<{
|
|
126
|
-
content: string;
|
|
126
|
+
content: string | undefined | (() => Awaitable<string | undefined>);
|
|
127
|
+
loading?: boolean;
|
|
127
128
|
}>;
|
|
128
129
|
ColorSelect: Component<{
|
|
129
130
|
value?: string;
|
package/dist/index.node.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { dirname, join } from 'pathe';
|
|
5
6
|
import { defaultColors } from 'poveste';
|
|
6
7
|
import generateStoryCommand from './commands/generate-story.server.js';
|
|
@@ -11,9 +12,38 @@ import { withoutSvelteKitServer } from './util/kit-server.js';
|
|
|
11
12
|
import { syncSvelteKit } from './util/kit-sync.js';
|
|
12
13
|
import { listComponentFiles } from './util/list-components.js';
|
|
13
14
|
import { disableStoryComponentHmr } from './util/story-hmr.js';
|
|
15
|
+
/**
|
|
16
|
+
* `svelte2tsx` is an optional peer, loaded from the book like TypeScript, so a book
|
|
17
|
+
* with auto-docs off installs neither. Looked for up the book's own `node_modules`,
|
|
18
|
+
* where a check that passes means the extractor's load finds it, or resolved from
|
|
19
|
+
* the book under Yarn PnP, which has none (#1508).
|
|
20
|
+
*/
|
|
21
|
+
function svelte2tsxProblem(root) {
|
|
22
|
+
if (process.versions['pnp']) {
|
|
23
|
+
try {
|
|
24
|
+
createRequire(join(root, 'package.json')).resolve('svelte2tsx/package.json');
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
}
|
|
29
|
+
for (let dir = root; dir !== dirname(dir); dir = dirname(dir)) {
|
|
30
|
+
if (existsSync(join(dir, 'node_modules', 'svelte2tsx', 'package.json'))) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return 'Auto-docs is off: it reads Svelte components\' types through `svelte2tsx`, which this book does not have. Install `svelte2tsx` in the book to turn it on, or set `autoDocs: false` to turn it off without this message.';
|
|
35
|
+
}
|
|
14
36
|
export function HstSvelte() {
|
|
15
37
|
return {
|
|
16
38
|
name: '@poveste/plugin-svelte',
|
|
39
|
+
// Props through `svelte2tsx` and the book's own TypeScript, read in the docgen worker (#501).
|
|
40
|
+
docgen: {
|
|
41
|
+
match: file => file.endsWith('.svelte') && !file.endsWith('.story.svelte'),
|
|
42
|
+
module: fileURLToPath(new URL('./docgen/extractor.js', import.meta.url)),
|
|
43
|
+
// `svelte2tsx`'s output is read with the book's TypeScript (#1401).
|
|
44
|
+
typescript: 'book',
|
|
45
|
+
unavailable: svelte2tsxProblem,
|
|
46
|
+
},
|
|
17
47
|
async defaultConfig() {
|
|
18
48
|
const syncProblem = syncSvelteKit(process.cwd());
|
|
19
49
|
if (syncProblem) {
|
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
2
3
|
import { transformStoryAutoProps } from './auto-props.js';
|
|
3
4
|
import { extractPropDefs } from './props.js';
|
|
4
5
|
import { isSvelteStory } from './story-source.js';
|
|
5
6
|
const IMPORTS_A_COMPONENT = /from\s*['"][^'"]*\.svelte['"]/;
|
|
6
7
|
/** Runs before `vite-plugin-svelte`: after it compiles there is nothing left to rewrite (#233). */
|
|
7
8
|
export function svelteAutoProps() {
|
|
9
|
+
// Shared by every story and by both passes a build makes: each story re-resolved
|
|
10
|
+
// and re-parsed the components it imports, 30 times for 12 files in the Svelte
|
|
11
|
+
// example (#476). A resolution holds until a file is created or deleted; props
|
|
12
|
+
// hold while the component's source is unchanged.
|
|
13
|
+
const resolutions = new Map();
|
|
14
|
+
const parsed = new Map();
|
|
8
15
|
return {
|
|
9
16
|
name: 'poveste:svelte-auto-props',
|
|
10
17
|
enforce: 'pre',
|
|
18
|
+
watchChange(_id, change) {
|
|
19
|
+
if (change.event !== 'update') {
|
|
20
|
+
resolutions.clear();
|
|
21
|
+
}
|
|
22
|
+
},
|
|
11
23
|
async transform(code, id) {
|
|
12
24
|
if (!isSvelteStory(id, code) || !IMPORTS_A_COMPONENT.test(code)) {
|
|
13
25
|
return undefined;
|
|
14
26
|
}
|
|
15
27
|
const result = await transformStoryAutoProps(code, async (specifier) => {
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
28
|
+
const key = `${dirname(id)}\0${specifier}`;
|
|
29
|
+
if (!resolutions.has(key)) {
|
|
30
|
+
resolutions.set(key, this.resolve(specifier, id).then((resolved) => resolved?.id.split('?')[0], (error) => {
|
|
31
|
+
resolutions.delete(key);
|
|
32
|
+
throw error;
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
const file = await resolutions.get(key);
|
|
36
|
+
if (!file) {
|
|
18
37
|
return undefined;
|
|
19
38
|
}
|
|
20
|
-
const file = resolved.id.split('?')[0];
|
|
21
39
|
// Baked into this module, so Vite has to know it depends on the source.
|
|
22
40
|
this.addWatchFile(file);
|
|
23
41
|
try {
|
|
24
|
-
|
|
42
|
+
const source = await readFile(file, 'utf8');
|
|
43
|
+
const cached = parsed.get(file);
|
|
44
|
+
if (cached?.source === source) {
|
|
45
|
+
return cached.props;
|
|
46
|
+
}
|
|
47
|
+
const props = extractPropDefs(source);
|
|
48
|
+
parsed.set(file, { source, props });
|
|
49
|
+
return props;
|
|
25
50
|
}
|
|
26
51
|
catch (error) {
|
|
27
52
|
// Not a failed build, but silence would look like a component that
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poveste/plugin-svelte",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.21.0",
|
|
5
5
|
"description": "Poveste plugin for Svelte 5 and SvelteKit support",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sorin Gitlan",
|
|
@@ -69,36 +69,41 @@
|
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@sveltejs/kit": "^2.53.0 || ^3.0.0",
|
|
71
71
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
72
|
-
"poveste": "^0.
|
|
73
|
-
"svelte": "^5.46.4"
|
|
72
|
+
"poveste": "^0.21.0",
|
|
73
|
+
"svelte": "^5.46.4",
|
|
74
|
+
"svelte2tsx": "^0.7.61"
|
|
74
75
|
},
|
|
75
76
|
"peerDependenciesMeta": {
|
|
76
77
|
"@sveltejs/kit": {
|
|
77
78
|
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"svelte2tsx": {
|
|
81
|
+
"optional": true
|
|
78
82
|
}
|
|
79
83
|
},
|
|
80
84
|
"dependencies": {
|
|
81
|
-
"@poveste/controls": "^0.
|
|
82
|
-
"@poveste/shared": "^0.
|
|
83
|
-
"@poveste/vendors": "^0.
|
|
85
|
+
"@poveste/controls": "^0.21.0",
|
|
86
|
+
"@poveste/shared": "^0.21.0",
|
|
87
|
+
"@poveste/vendors": "^0.21.0",
|
|
84
88
|
"change-case": "^5.4.4",
|
|
85
89
|
"ignore": "^7.0.10",
|
|
86
|
-
"launch-editor": "^2.
|
|
90
|
+
"launch-editor": "^2.14.2",
|
|
87
91
|
"pathe": "^2.0.3",
|
|
88
92
|
"tinyglobby": "^0.2.17"
|
|
89
93
|
},
|
|
90
94
|
"devDependencies": {
|
|
91
95
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
92
|
-
"@types/node": "^26.6.
|
|
93
|
-
"concurrently": "^10.0.
|
|
96
|
+
"@types/node": "^26.6.5",
|
|
97
|
+
"concurrently": "^10.0.6",
|
|
94
98
|
"fs-extra": "^11.4.1",
|
|
95
|
-
"jsdom": "^30.1.
|
|
96
|
-
"poveste": "0.
|
|
97
|
-
"svelte": "5.57.
|
|
99
|
+
"jsdom": "^30.1.3",
|
|
100
|
+
"poveste": "0.21.0",
|
|
101
|
+
"svelte": "5.57.2",
|
|
98
102
|
"svelte-preprocess": "^6.0.5",
|
|
103
|
+
"svelte2tsx": "^0.7.62",
|
|
99
104
|
"typescript": "6.0.3",
|
|
100
|
-
"vite": "^8.3.
|
|
101
|
-
"vitest": "^5.0.
|
|
105
|
+
"vite": "^8.3.4",
|
|
106
|
+
"vitest": "^5.0.5"
|
|
102
107
|
},
|
|
103
108
|
"scripts": {
|
|
104
109
|
"build": "rimraf dist && vite build && tsc -d -P tsconfig.build.json && pnpm run build:types",
|