sdocs 0.0.22 → 0.0.25
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/CHANGELOG.md +77 -0
- package/dist/server/config.d.ts +4 -0
- package/dist/server/config.js +13 -5
- package/dist/server/prop-parser.js +75 -2
- package/dist/server/prop-parser.test.d.ts +1 -0
- package/dist/server/prop-parser.test.js +148 -0
- package/dist/server/snippet-compiler.js +6 -2
- package/dist/ui/Button/Button.sdoc +3 -2
- package/dist/vite.js +4 -7
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `sdocs` package are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.25] - 2026-07-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **JSDoc prop extraction for plain-JS components.** Both `@type {{ ... }}`
|
|
15
|
+
object annotations and `@typedef {Object} Props` with `@property` tags on
|
|
16
|
+
the `$props()` declaration are parsed — types, optionality (bracketed
|
|
17
|
+
names), and descriptions all flow into the docs and interactive controls.
|
|
18
|
+
- A prop-parser test suite; `npm test` now runs it.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Props typed as `import('svelte').Snippet` are classified as snippets.
|
|
23
|
+
|
|
24
|
+
## [0.0.24] - 2026-07-02
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Snippets receive `args` as a real snippet parameter: write
|
|
29
|
+
`{#snippet Default(args)}`. Previews render snippets through `{@render}`,
|
|
30
|
+
so `args` is an explicit, editor-visible binding instead of a value injected
|
|
31
|
+
into scope. Parameterless snippets keep working unchanged.
|
|
32
|
+
|
|
33
|
+
## [0.0.23] - 2026-04-16
|
|
34
|
+
|
|
35
|
+
First documented release. `sdocs` is a lightweight documentation tool for Svelte 5
|
|
36
|
+
components; this entry describes its capabilities as of 0.0.23.
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **CLI** — `sdocs dev`, `sdocs build`, `sdocs preview`, and `sdocs init`.
|
|
41
|
+
- **Vite plugin** (`sdocs/vite`) for embedding the explorer in an existing Vite or
|
|
42
|
+
SvelteKit app, exposing discovered docs through the `virtual:sdocs` module.
|
|
43
|
+
- **Three `.sdoc` kinds** — component docs (`.sdoc`), standalone pages
|
|
44
|
+
(`.page.sdoc`), and full-page layouts (`.layout.sdoc`).
|
|
45
|
+
- **Prop extraction** from component types, rendered as a props table.
|
|
46
|
+
- **Interactive controls** for live-editing component props and CSS custom
|
|
47
|
+
properties.
|
|
48
|
+
- **Theming** with light and dark modes and named-stylesheet switching.
|
|
49
|
+
- **Configurable sidebar** ordering and hash-based routing.
|
|
50
|
+
- **Syntax highlighting** via Shiki.
|
|
51
|
+
- Package entry points: `sdocs`, `sdocs/vite`, `sdocs/client`, and `sdocs/ui`.
|
|
52
|
+
|
|
53
|
+
## [0.0.20] - 2026-04-15
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Redesigned the component view and added snippet-based preview code.
|
|
58
|
+
|
|
59
|
+
## [0.0.18] - 2026-02-27
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- Scoped the explorer's CSS under `.sdocs-app` to keep its styles from leaking
|
|
64
|
+
into component previews.
|
|
65
|
+
|
|
66
|
+
## [0.0.17] - 2026-02-27
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Corrected the `Icon` component imports.
|
|
71
|
+
|
|
72
|
+
## [0.0.16] - 2026-02-27
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- Rewritten codebase: the current architecture for `.sdoc` discovery, preview
|
|
77
|
+
rendering, and the component explorer UI.
|
package/dist/server/config.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { SdocsConfig, ResolvedSdocsConfig } from '../types.js';
|
|
2
2
|
/** Find the config file path in the given directory */
|
|
3
3
|
export declare function findConfigFile(root: string): string | null;
|
|
4
|
+
/** Load the raw config from file (unresolved) */
|
|
5
|
+
export declare function loadRawConfig(root: string): Promise<SdocsConfig>;
|
|
4
6
|
/** Load and resolve the sdocs config with defaults */
|
|
5
7
|
export declare function loadConfig(root: string): Promise<ResolvedSdocsConfig>;
|
|
8
|
+
/** Resolve config and finalize paths */
|
|
9
|
+
export declare function resolveAndFinalize(userConfig: SdocsConfig, root: string): ResolvedSdocsConfig;
|
|
6
10
|
/** Merge user config with defaults */
|
|
7
11
|
export declare function resolveConfig(userConfig: SdocsConfig): ResolvedSdocsConfig;
|
package/dist/server/config.js
CHANGED
|
@@ -39,18 +39,26 @@ function resolveCssPaths(css, root) {
|
|
|
39
39
|
}
|
|
40
40
|
return resolved;
|
|
41
41
|
}
|
|
42
|
-
/** Load
|
|
43
|
-
export async function
|
|
42
|
+
/** Load the raw config from file (unresolved) */
|
|
43
|
+
export async function loadRawConfig(root) {
|
|
44
44
|
const configPath = findConfigFile(root);
|
|
45
45
|
if (!configPath)
|
|
46
|
-
return {
|
|
47
|
-
|
|
46
|
+
return {};
|
|
47
|
+
return importConfig(configPath);
|
|
48
|
+
}
|
|
49
|
+
/** Load and resolve the sdocs config with defaults */
|
|
50
|
+
export async function loadConfig(root) {
|
|
51
|
+
const rawConfig = await loadRawConfig(root);
|
|
52
|
+
return resolveAndFinalize(rawConfig, root);
|
|
53
|
+
}
|
|
54
|
+
/** Resolve config and finalize paths */
|
|
55
|
+
export function resolveAndFinalize(userConfig, root) {
|
|
48
56
|
const resolved = resolveConfig(userConfig);
|
|
49
57
|
resolved.include = resolveIncludePatterns(resolved.include, root);
|
|
50
58
|
resolved.css = resolveCssPaths(resolved.css, root);
|
|
51
59
|
return resolved;
|
|
52
60
|
}
|
|
53
|
-
/** Import a config file (
|
|
61
|
+
/** Import a config file (.js/.mjs; .ts only on Node with native type stripping) */
|
|
54
62
|
async function importConfig(configPath) {
|
|
55
63
|
// Use dynamic import with file:// URL for ESM compatibility
|
|
56
64
|
const mod = await import(pathToFileURL(configPath).href);
|
|
@@ -15,9 +15,16 @@ export function parseComponentSource(source) {
|
|
|
15
15
|
if (scriptContent) {
|
|
16
16
|
const tsAst = ts.createSourceFile('component.ts', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
17
17
|
const interfaceProps = parseInterfaceProps(tsAst);
|
|
18
|
+
const jsdocTypeProps = parseJsdocTypeProps(tsAst);
|
|
18
19
|
const destructuredProps = parsePropsDestructuring(tsAst);
|
|
19
20
|
const jsdocData = parseJsdocComments(tsAst);
|
|
20
|
-
|
|
21
|
+
// TS interface wins over JSDoc types when both exist (they shouldn't).
|
|
22
|
+
const interfaceNames = new Set(interfaceProps.map((p) => p.name));
|
|
23
|
+
const typedProps = [
|
|
24
|
+
...interfaceProps,
|
|
25
|
+
...jsdocTypeProps.filter((p) => !interfaceNames.has(p.name)),
|
|
26
|
+
];
|
|
27
|
+
props = mergeProps(typedProps, destructuredProps, jsdocData);
|
|
21
28
|
methods = parseExportedFunctions(tsAst);
|
|
22
29
|
state = parseExportedState(tsAst);
|
|
23
30
|
}
|
|
@@ -52,6 +59,71 @@ function parseInterfaceProps(sourceFile) {
|
|
|
52
59
|
});
|
|
53
60
|
return props;
|
|
54
61
|
}
|
|
62
|
+
// ─── JSDoc-typed props (plain JS components) ───
|
|
63
|
+
/** Props typed via JSDoc: `@type {{ ... }}` on the $props() declaration, or
|
|
64
|
+
* `@type {Props}` referencing a `@typedef {Object} Props` with `@property` tags. */
|
|
65
|
+
function parseJsdocTypeProps(sourceFile) {
|
|
66
|
+
const props = [];
|
|
67
|
+
function fromTypeLiteral(literal) {
|
|
68
|
+
for (const member of literal.members) {
|
|
69
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
70
|
+
props.push({
|
|
71
|
+
name: member.name.getText(sourceFile),
|
|
72
|
+
type: member.type ? member.type.getText(sourceFile) : 'unknown',
|
|
73
|
+
optional: !!member.questionToken,
|
|
74
|
+
description: null,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function fromTypedef(name) {
|
|
80
|
+
function visit(node) {
|
|
81
|
+
// ts.getJSDocTags only surfaces the last JSDoc comment on a node;
|
|
82
|
+
// a standalone @typedef block above the @type one needs the raw list.
|
|
83
|
+
const jsDocs = node.jsDoc ?? [];
|
|
84
|
+
for (const tag of jsDocs.flatMap((jd) => [...(jd.tags ?? [])])) {
|
|
85
|
+
if (ts.isJSDocTypedefTag(tag) &&
|
|
86
|
+
tag.name?.getText(sourceFile) === name &&
|
|
87
|
+
tag.typeExpression &&
|
|
88
|
+
ts.isJSDocTypeLiteral(tag.typeExpression)) {
|
|
89
|
+
for (const propTag of tag.typeExpression.jsDocPropertyTags ?? []) {
|
|
90
|
+
const comment = ts.getTextOfJSDocComment(propTag.comment);
|
|
91
|
+
props.push({
|
|
92
|
+
name: propTag.name.getText(sourceFile),
|
|
93
|
+
type: propTag.typeExpression
|
|
94
|
+
? propTag.typeExpression.type.getText(sourceFile)
|
|
95
|
+
: 'unknown',
|
|
96
|
+
optional: propTag.isBracketed,
|
|
97
|
+
description: comment ? comment.replace(/^-\s*/, '').trim() : null,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
ts.forEachChild(node, visit);
|
|
103
|
+
}
|
|
104
|
+
visit(sourceFile);
|
|
105
|
+
}
|
|
106
|
+
function visit(node) {
|
|
107
|
+
if (ts.isVariableDeclaration(node) &&
|
|
108
|
+
node.initializer &&
|
|
109
|
+
ts.isCallExpression(node.initializer) &&
|
|
110
|
+
node.initializer.expression.getText(sourceFile) === '$props' &&
|
|
111
|
+
ts.isObjectBindingPattern(node.name)) {
|
|
112
|
+
// The @type tag sits on the enclosing statement; getJSDocType finds it.
|
|
113
|
+
const typeNode = ts.getJSDocType(node) ?? ts.getJSDocType(node.parent?.parent ?? node);
|
|
114
|
+
if (typeNode) {
|
|
115
|
+
if (ts.isTypeLiteralNode(typeNode))
|
|
116
|
+
fromTypeLiteral(typeNode);
|
|
117
|
+
else if (ts.isTypeReferenceNode(typeNode)) {
|
|
118
|
+
fromTypedef(typeNode.typeName.getText(sourceFile));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
ts.forEachChild(node, visit);
|
|
123
|
+
}
|
|
124
|
+
visit(sourceFile);
|
|
125
|
+
return props;
|
|
126
|
+
}
|
|
55
127
|
function parsePropsDestructuring(sourceFile) {
|
|
56
128
|
const props = [];
|
|
57
129
|
function visit(node) {
|
|
@@ -159,7 +231,8 @@ function mergeProps(interfaceProps, destructuredProps, jsdocData) {
|
|
|
159
231
|
function classifyProp(name, type) {
|
|
160
232
|
if (name.startsWith('on') && type?.includes('=>'))
|
|
161
233
|
return 'event';
|
|
162
|
-
|
|
234
|
+
// Matches `Snippet`, `Snippet<[...]>`, and `import('svelte').Snippet`
|
|
235
|
+
if (type && /(^|\.)Snippet\b/.test(type))
|
|
163
236
|
return 'snippet';
|
|
164
237
|
return 'prop';
|
|
165
238
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseComponentSource } from './prop-parser.js';
|
|
3
|
+
function byName(list, name) {
|
|
4
|
+
const found = list.find((item) => item.name === name);
|
|
5
|
+
expect(found, `expected to find "${name}"`).toBeDefined();
|
|
6
|
+
return found;
|
|
7
|
+
}
|
|
8
|
+
describe('TypeScript components (interface Props)', () => {
|
|
9
|
+
const source = `<script lang="ts">
|
|
10
|
+
import type { Snippet } from 'svelte';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
/** The button label text */
|
|
14
|
+
label?: string;
|
|
15
|
+
/** Visual style variant */
|
|
16
|
+
variant?: 'primary' | 'secondary';
|
|
17
|
+
count: number;
|
|
18
|
+
/** Click handler */
|
|
19
|
+
onclick?: (e: MouseEvent) => void;
|
|
20
|
+
oneTime?: boolean;
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
item?: Snippet<[value: string]>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { label = 'Button', variant = 'primary', count, onclick, oneTime, children, item }: Props = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<button>{label}</button>`;
|
|
29
|
+
const data = parseComponentSource(source);
|
|
30
|
+
it('extracts types, defaults, descriptions, and requiredness', () => {
|
|
31
|
+
const label = byName(data.props, 'label');
|
|
32
|
+
expect(label.type).toBe('string');
|
|
33
|
+
expect(label.default).toBe('Button');
|
|
34
|
+
expect(label.description).toBe('The button label text');
|
|
35
|
+
expect(label.required).toBe(false);
|
|
36
|
+
const count = byName(data.props, 'count');
|
|
37
|
+
expect(count.type).toBe('number');
|
|
38
|
+
expect(count.default).toBe(null);
|
|
39
|
+
expect(count.required).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it('classifies events, snippets, and on-prefixed non-functions', () => {
|
|
42
|
+
expect(byName(data.props, 'onclick').category).toBe('event');
|
|
43
|
+
expect(byName(data.props, 'oneTime').category).toBe('prop');
|
|
44
|
+
expect(byName(data.props, 'children').category).toBe('snippet');
|
|
45
|
+
expect(byName(data.props, 'item').category).toBe('snippet');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('JS components with @type {{ ... }} annotation', () => {
|
|
49
|
+
const source = `<script>
|
|
50
|
+
/** @type {{ label?: string, size?: 'sm' | 'lg', onclick?: (e: MouseEvent) => void }} */
|
|
51
|
+
let { label = 'Badge', size = 'sm', onclick } = $props();
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<span>{label}</span>`;
|
|
55
|
+
const data = parseComponentSource(source);
|
|
56
|
+
it('extracts types and optionality from the annotation', () => {
|
|
57
|
+
const label = byName(data.props, 'label');
|
|
58
|
+
expect(label.type).toBe('string');
|
|
59
|
+
expect(label.default).toBe('Badge');
|
|
60
|
+
expect(label.required).toBe(false);
|
|
61
|
+
expect(byName(data.props, 'size').type).toBe("'sm' | 'lg'");
|
|
62
|
+
});
|
|
63
|
+
it('classifies events from JSDoc function types', () => {
|
|
64
|
+
expect(byName(data.props, 'onclick').category).toBe('event');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('JS components with @typedef/@property', () => {
|
|
68
|
+
const source = `<script>
|
|
69
|
+
/**
|
|
70
|
+
* @typedef {Object} Props
|
|
71
|
+
* @property {string} [label] - The badge label
|
|
72
|
+
* @property {number} count - How many
|
|
73
|
+
* @property {() => void} [ondismiss] - Called when dismissed
|
|
74
|
+
* @property {import('svelte').Snippet} [children] - Body content
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/** @type {Props} */
|
|
78
|
+
let { label = 'Badge', count, ondismiss, children } = $props();
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<span>{label}{count}</span>`;
|
|
82
|
+
const data = parseComponentSource(source);
|
|
83
|
+
it('extracts types, descriptions, and optionality from @property tags', () => {
|
|
84
|
+
const label = byName(data.props, 'label');
|
|
85
|
+
expect(label.type).toBe('string');
|
|
86
|
+
expect(label.description).toBe('The badge label');
|
|
87
|
+
expect(label.required).toBe(false);
|
|
88
|
+
expect(label.default).toBe('Badge');
|
|
89
|
+
const count = byName(data.props, 'count');
|
|
90
|
+
expect(count.type).toBe('number');
|
|
91
|
+
expect(count.description).toBe('How many');
|
|
92
|
+
expect(count.required).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it('classifies events and imported Snippet types', () => {
|
|
95
|
+
expect(byName(data.props, 'ondismiss').category).toBe('event');
|
|
96
|
+
expect(byName(data.props, 'children').category).toBe('snippet');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('untyped JS components', () => {
|
|
100
|
+
it('still extracts names and defaults from destructuring', () => {
|
|
101
|
+
const data = parseComponentSource(`<script>
|
|
102
|
+
let { label = 'x', flag } = $props();
|
|
103
|
+
</script>`);
|
|
104
|
+
expect(byName(data.props, 'label').default).toBe('x');
|
|
105
|
+
expect(byName(data.props, 'label').type).toBe(null);
|
|
106
|
+
expect(byName(data.props, 'flag').required).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('methods, state, and CSS custom properties', () => {
|
|
110
|
+
const source = `<script lang="ts">
|
|
111
|
+
/** Clears the value */
|
|
112
|
+
export function clear(): void {}
|
|
113
|
+
|
|
114
|
+
/** Current count */
|
|
115
|
+
export const count = $state(0);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @cssvar {color} --bg - Background color
|
|
119
|
+
* @cssvar {dimension} --radius - Corner radius
|
|
120
|
+
*/
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<style>
|
|
124
|
+
.x {
|
|
125
|
+
background: var(--bg, #333);
|
|
126
|
+
border-radius: var(--radius, 4px);
|
|
127
|
+
padding: var(--pad, 8px);
|
|
128
|
+
}
|
|
129
|
+
</style>`;
|
|
130
|
+
const data = parseComponentSource(source);
|
|
131
|
+
it('extracts exported functions with descriptions', () => {
|
|
132
|
+
const clear = byName(data.methods, 'clear');
|
|
133
|
+
expect(clear.returnType).toBe('void');
|
|
134
|
+
expect(clear.description).toBe('Clears the value');
|
|
135
|
+
});
|
|
136
|
+
it('extracts exported state', () => {
|
|
137
|
+
expect(byName(data.state, 'count').description).toBe('Current count');
|
|
138
|
+
});
|
|
139
|
+
it('extracts CSS vars, merging @cssvar type/description with var() defaults', () => {
|
|
140
|
+
const bg = byName(data.cssProps, '--bg');
|
|
141
|
+
expect(bg.type).toBe('color');
|
|
142
|
+
expect(bg.default).toBe('#333');
|
|
143
|
+
expect(bg.description).toBe('Background color');
|
|
144
|
+
const pad = byName(data.cssProps, '--pad');
|
|
145
|
+
expect(pad.type).toBe(null);
|
|
146
|
+
expect(pad.default).toBe('8px');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -25,7 +25,8 @@ export function generateIframeComponent(absoluteImports, snippetBody) {
|
|
|
25
25
|
const importBlock = absoluteImports.length > 0
|
|
26
26
|
? absoluteImports.join('\n') + '\n'
|
|
27
27
|
: '';
|
|
28
|
-
|
|
28
|
+
// lang="ts" so lifted imports may carry type-only syntax (Svelte 5 erases it natively)
|
|
29
|
+
return `<script lang="ts">
|
|
29
30
|
${importBlock}import { onMount } from 'svelte';
|
|
30
31
|
|
|
31
32
|
let args = $state({});
|
|
@@ -68,7 +69,10 @@ export function generateIframeComponent(absoluteImports, snippetBody) {
|
|
|
68
69
|
</script>
|
|
69
70
|
|
|
70
71
|
<div id="sdocs-preview">
|
|
71
|
-
|
|
72
|
+
{#snippet SdocsPreview(args)}
|
|
73
|
+
${snippetBody}
|
|
74
|
+
{/snippet}
|
|
75
|
+
{@render SdocsPreview(args)}
|
|
72
76
|
</div>`;
|
|
73
77
|
}
|
|
74
78
|
/** Convert a CSS path to a Vite-servable URL */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ComponentProps } from 'svelte';
|
|
2
3
|
import Button from './Button.svelte';
|
|
3
4
|
import { Icon } from '../Icon/index.js';
|
|
4
5
|
|
|
@@ -9,8 +10,8 @@
|
|
|
9
10
|
};
|
|
10
11
|
</script>
|
|
11
12
|
|
|
12
|
-
{#snippet Default()}
|
|
13
|
-
<Button>Click me</Button>
|
|
13
|
+
{#snippet Default(args: ComponentProps<typeof Button>)}
|
|
14
|
+
<Button {...args}>Click me</Button>
|
|
14
15
|
{/snippet}
|
|
15
16
|
|
|
16
17
|
{#snippet WithLeftIcon()}
|
package/dist/vite.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { loadRawConfig, resolveAndFinalize } from './server/config.js';
|
|
3
3
|
import { discoverDocFiles, getSdocKind } from './server/discovery.js';
|
|
4
4
|
import { parseDocSource } from './server/meta-parser.js';
|
|
5
5
|
import { parseComponent } from './server/prop-parser.js';
|
|
@@ -22,12 +22,9 @@ export function sdocsPlugin(userConfig) {
|
|
|
22
22
|
name: 'sdocs',
|
|
23
23
|
async configResolved(resolvedConfig) {
|
|
24
24
|
root = resolvedConfig.root;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else {
|
|
29
|
-
config = await loadConfig(root);
|
|
30
|
-
}
|
|
25
|
+
const fileConfig = await loadRawConfig(root);
|
|
26
|
+
const merged = { ...fileConfig, ...userConfig };
|
|
27
|
+
config = resolveAndFinalize(merged, root);
|
|
31
28
|
},
|
|
32
29
|
configureServer(devServer) {
|
|
33
30
|
server = devServer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdocs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "A lightweight documentation tool for Svelte 5 components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
-
"bin"
|
|
29
|
+
"bin",
|
|
30
|
+
"CHANGELOG.md"
|
|
30
31
|
],
|
|
31
32
|
"peerDependencies": {
|
|
32
33
|
"@sveltejs/vite-plugin-svelte": "*",
|