sdocs 0.0.40 → 0.0.42

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 (35) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/explorer/tree-builder.js +2 -4
  3. package/dist/explorer/views/ComponentView.svelte +85 -29
  4. package/dist/explorer/views/LayoutView.svelte +1 -1
  5. package/dist/explorer/views/PageView.svelte +1 -1
  6. package/dist/grammar/sdoc.tmLanguage.json +245 -0
  7. package/dist/index.d.ts +1 -1
  8. package/dist/language/index.d.ts +2 -0
  9. package/dist/language/index.js +2 -0
  10. package/dist/language/parser.d.ts +82 -0
  11. package/dist/language/parser.js +293 -0
  12. package/dist/language/parser.test.d.ts +1 -0
  13. package/dist/language/parser.test.js +158 -0
  14. package/dist/language/scanner.d.ts +82 -0
  15. package/dist/language/scanner.js +529 -0
  16. package/dist/language/scanner.test.d.ts +1 -0
  17. package/dist/language/scanner.test.js +146 -0
  18. package/dist/server/app-gen.js +18 -21
  19. package/dist/server/discovery.d.ts +0 -2
  20. package/dist/server/discovery.js +0 -9
  21. package/dist/server/doc-model.d.ts +26 -0
  22. package/dist/server/doc-model.js +58 -0
  23. package/dist/server/page-markdown.d.ts +15 -0
  24. package/dist/server/page-markdown.js +104 -0
  25. package/dist/server/snippet-compiler.d.ts +18 -18
  26. package/dist/server/snippet-compiler.js +32 -29
  27. package/dist/types.d.ts +35 -19
  28. package/dist/vite.js +168 -99
  29. package/package.json +7 -1
  30. package/dist/server/meta-parser.d.ts +0 -11
  31. package/dist/server/meta-parser.js +0 -109
  32. package/dist/server/snippet-extractor.d.ts +0 -11
  33. package/dist/server/snippet-extractor.js +0 -83
  34. package/dist/server/toc-extractor.d.ts +0 -3
  35. package/dist/server/toc-extractor.js +0 -23
package/CHANGELOG.md CHANGED
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.42] - 2026-07-04
11
+
12
+ ### Changed
13
+
14
+ - **Breaking: `.sdoc` files are the block format.** A doc file is now
15
+ `<script>` on top, `[DOCS]`/`[PAGE]`/`[LAYOUT]` entity blocks in the
16
+ middle, `<style>` at the bottom — the language documented in the site's
17
+ Language section. Each entity is its own sidebar entry, so one file can
18
+ hold several. The `export const meta` convention, `{#snippet}` extraction,
19
+ auto-generated `Default` previews, and the `.page.sdoc`/`.layout.sdoc`
20
+ filename kinds are gone.
21
+ - **`[DOCS]` holds any number of previews.** Each
22
+ `[preview component={X} args={{…}}]` is self-contained; several render as
23
+ tabs, each a fully live panel with its own controls, extracted API tables,
24
+ and component source. `[example title="…"]` blocks are frozen and render
25
+ page-level below the tabs.
26
+ - **`[PAGE]` bodies are markdown** — GFM with `{expression}` interpolation
27
+ and Svelte component islands; code fences and inline code are inert. The
28
+ table of contents comes from the markdown headings.
29
+ - **Parsing never crashes the dev server.** Doc files parse through the
30
+ `sdocs/language` scanner; mistakes surface as file:line warnings and the
31
+ rest of the file keeps working.
32
+ - **Breaking for embedders:** `DocEntry` is reshaped (`previews`/`examples`/
33
+ `content` replace `snippets`/`componentData`), and preview URLs address
34
+ entities as `path#slug`.
35
+
36
+ ## [0.0.41] - 2026-07-04
37
+
38
+ ### Added
39
+
40
+ - **`sdocs/language`** — a scanner and parser for the block-based sdoc
41
+ format: line-anchored `[DOCS]`/`[PAGE]`/`[LAYOUT]` entities with
42
+ `[preview]`/`[example]` sub-blocks, Svelte-style attributes, precise
43
+ source spans, and recoverable diagnostics (it never throws, and keeps
44
+ scanning past mistakes). Groundwork for the format switch — the runtime
45
+ still reads the current `.sdoc` format.
46
+ - **A TextMate grammar for sdoc** at `sdocs/grammar/sdoc.tmLanguage.json`,
47
+ usable by any TextMate-compatible highlighter; the documentation site
48
+ uses it to highlight ```` ```sdoc ```` fences.
49
+
10
50
  ## [0.0.40] - 2026-07-04
11
51
 
12
52
  ### Fixed
@@ -44,9 +44,7 @@ export function buildTree(docs, sidebar) {
44
44
  // Create the item node
45
45
  const itemPath = [...currentPath, itemName];
46
46
  if (kind === 'component') {
47
- const examples = doc.snippets
48
- ?.filter((s) => s.name !== 'Default')
49
- .map((s) => s.name) ?? [];
47
+ const examples = doc.examples?.map((s) => s.name) ?? [];
50
48
  // Check if a folder node with this name already exists (created by a child doc)
51
49
  const existing = parent.find((n) => n.name === itemName);
52
50
  const componentNode = existing ?? {
@@ -195,7 +193,7 @@ export function findDocByPath(docs, path) {
195
193
  // One extra segment → example
196
194
  if (segments.length === path.length - 1 && segments.every((s, i) => s === path[i])) {
197
195
  const snippetName = path[path.length - 1];
198
- const hasSnippet = doc.snippets?.some((s) => s.name === snippetName);
196
+ const hasSnippet = doc.examples?.some((s) => s.name === snippetName);
199
197
  if (hasSnippet) {
200
198
  return { doc, snippetName };
201
199
  }
@@ -18,28 +18,40 @@
18
18
  let { doc, snippetName, activeStylesheet }: Props = $props();
19
19
 
20
20
  const meta = $derived(doc.meta);
21
- const cd = $derived(doc.componentData);
21
+ const previews = $derived(doc.previews ?? []);
22
+
23
+ // Active preview tab (one full live panel per preview)
24
+ let activeIndex = $state(0);
25
+ $effect(() => {
26
+ doc;
27
+ activeIndex = 0;
28
+ });
29
+ const activePreview = $derived(
30
+ previews.length > 0 ? previews[Math.min(activeIndex, previews.length - 1)] : undefined,
31
+ );
32
+
33
+ const cd = $derived(activePreview?.componentData ?? null);
22
34
  const componentName = $derived(
23
- typeof meta.component === 'string'
24
- ? meta.component
25
- : (meta.title ?? '').split('/').pop()?.trim() ?? 'Component',
35
+ activePreview?.componentName ??
36
+ ((meta.title ?? '').split('/').pop()?.trim() || 'Component'),
37
+ );
38
+ const exampleSnippets = $derived(doc.examples ?? []);
39
+ const focusedSnippet = $derived(
40
+ snippetName ? exampleSnippets.find((s) => s.name === snippetName) : null,
26
41
  );
27
- const snippets = $derived(doc.snippets ?? []);
28
- const defaultSnippet = $derived(snippets.find((s) => s.name === 'Default'));
29
- const exampleSnippets = $derived(snippets.filter((s) => s.name !== 'Default'));
30
- const focusedSnippet = $derived(snippetName ? snippets.find((s) => s.name === snippetName) : null);
31
42
 
32
43
  // Props/CSS controls state
33
44
  let propValues = $state<Record<string, unknown>>({});
34
45
  let cssValues = $state<Record<string, string>>({});
35
46
 
36
- // Live wiring to the default preview: method invocation + exported state values
47
+ // Live wiring to the active preview: method invocation + exported state values
37
48
  let defaultPreview = $state<{ callMethod: (name: string) => void }>();
38
49
  let liveStateValues = $state<Record<string, unknown>>({});
39
50
 
40
- // Initialize from meta.args (build new objects to avoid read+write loop)
51
+ // Initialize from the active preview's args (build new objects to avoid
52
+ // read+write loop); re-runs on doc and tab change.
41
53
  $effect(() => {
42
- propValues = { ...(meta.args ?? {}) };
54
+ propValues = { ...(activePreview?.args ?? {}) };
43
55
  const newCss: Record<string, string> = {};
44
56
  if (cd?.cssProps) {
45
57
  for (const cp of cd.cssProps) {
@@ -47,6 +59,7 @@
47
59
  }
48
60
  }
49
61
  cssValues = newCss;
62
+ liveStateValues = {};
50
63
  });
51
64
 
52
65
  function handlePropChange(name: string, value: unknown) {
@@ -159,8 +172,8 @@
159
172
  return snippetBody.slice(0, attrsStart) + attrs + closing + snippetBody.slice(tagEnd + 1);
160
173
  }
161
174
 
162
- // Initial values for diffing (computed once per doc change)
163
- const initialProps = $derived(meta.args ?? {});
175
+ // Initial values for diffing (computed once per doc/tab change)
176
+ const initialProps = $derived(activePreview?.args ?? {});
164
177
  const initialCss = $derived.by(() => {
165
178
  const css: Record<string, string> = {};
166
179
  if (cd?.cssProps) {
@@ -172,8 +185,8 @@
172
185
  });
173
186
 
174
187
  const usageCode = $derived.by(() => {
175
- if (defaultSnippet?.body) {
176
- return patchSnippetCode(defaultSnippet.body, componentName, propValues, cssValues, initialProps, initialCss);
188
+ if (activePreview?.snippet.body) {
189
+ return patchSnippetCode(activePreview.snippet.body, componentName, propValues, cssValues, initialProps, initialCss);
177
190
  }
178
191
  return generateFallbackCode(componentName, propValues, cssValues);
179
192
  });
@@ -198,7 +211,7 @@
198
211
  });
199
212
 
200
213
  function handleReset() {
201
- propValues = { ...(meta.args ?? {}) };
214
+ propValues = { ...(activePreview?.args ?? {}) };
202
215
  const newCss: Record<string, string> = {};
203
216
  if (cd?.cssProps) {
204
217
  for (const cp of cd.cssProps) {
@@ -290,19 +303,37 @@
290
303
  {/if}
291
304
  </div>
292
305
 
306
+ {#if previews.length > 1}
307
+ <div class="sdocs-preview-tabs" role="tablist">
308
+ {#each previews as preview, i (preview.snippet.slug)}
309
+ <button
310
+ class="sdocs-preview-tab"
311
+ class:active={i === activeIndex}
312
+ role="tab"
313
+ aria-selected={i === activeIndex}
314
+ onclick={() => (activeIndex = i)}
315
+ >
316
+ {preview.label}
317
+ </button>
318
+ {/each}
319
+ </div>
320
+ {/if}
321
+
293
322
  <div class="sdocs-panels">
294
323
  <!-- Showcase -->
295
- {#if defaultSnippet}
296
- <div class="sdocs-preview-wrapper">
297
- <PreviewFrame
298
- bind:this={defaultPreview}
299
- src={defaultSnippet.previewUrl ?? ''}
300
- props={propValues}
301
- cssVars={cssValues}
302
- {activeStylesheet}
303
- onStateValues={(values) => (liveStateValues = values)}
304
- />
305
- </div>
324
+ {#if activePreview}
325
+ {#key activePreview.snippet.slug}
326
+ <div class="sdocs-preview-wrapper">
327
+ <PreviewFrame
328
+ bind:this={defaultPreview}
329
+ src={activePreview.snippet.previewUrl ?? ''}
330
+ props={propValues}
331
+ cssVars={cssValues}
332
+ {activeStylesheet}
333
+ onStateValues={(values) => (liveStateValues = values)}
334
+ />
335
+ </div>
336
+ {/key}
306
337
 
307
338
  <CollapsiblePanel title="Preview Code" defaultExpanded={false}>
308
339
  <div class="sdocs-code-block">
@@ -414,11 +445,11 @@
414
445
  {/each}
415
446
  {/if}
416
447
 
417
- {#if doc.highlightedSource}
448
+ {#if activePreview?.highlightedSource}
418
449
  <hr class="sdocs-divider" />
419
450
  <div class="sdocs-panels">
420
451
  <CollapsiblePanel title="Component Source" defaultExpanded={false}>
421
- <div class="sdocs-code-block">{@html doc.highlightedSource}</div>
452
+ <div class="sdocs-code-block">{@html activePreview.highlightedSource}</div>
422
453
  </CollapsiblePanel>
423
454
  </div>
424
455
  {/if}
@@ -476,6 +507,31 @@
476
507
  opacity: 0.4;
477
508
  cursor: default;
478
509
  }
510
+ .sdocs-preview-tabs {
511
+ display: flex;
512
+ gap: 4px;
513
+ margin-bottom: 12px;
514
+ border-bottom: 1px solid var(--color-base-200);
515
+ }
516
+ .sdocs-preview-tab {
517
+ padding: 7px 14px;
518
+ border: none;
519
+ border-bottom: 2px solid transparent;
520
+ background: none;
521
+ font: inherit;
522
+ font-size: 13px;
523
+ font-weight: 500;
524
+ color: var(--color-base-500);
525
+ cursor: pointer;
526
+ }
527
+ .sdocs-preview-tab:hover {
528
+ color: var(--color-base-800);
529
+ }
530
+ .sdocs-preview-tab.active {
531
+ color: var(--color-base-900);
532
+ border-bottom-color: var(--color-accent-500, var(--color-base-900));
533
+ font-weight: 600;
534
+ }
479
535
  .sdocs-view-header {
480
536
  margin-bottom: 24px;
481
537
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  let { doc, activeStylesheet }: Props = $props();
11
11
 
12
- const contentSnippet = $derived(doc.snippets?.[0]);
12
+ const contentSnippet = $derived(doc.content);
13
13
  </script>
14
14
 
15
15
  <div class="sdocs-layout-view">
@@ -10,7 +10,7 @@
10
10
  let { doc, activeStylesheet }: Props = $props();
11
11
 
12
12
  const meta = $derived(doc.meta);
13
- const contentSnippet = $derived(doc.snippets?.[0]);
13
+ const contentSnippet = $derived(doc.content);
14
14
  const toc = $derived(doc.toc ?? []);
15
15
 
16
16
  function scrollToHeading(id: string) {
@@ -0,0 +1,245 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
+ "name": "sdoc",
4
+ "scopeName": "source.sdoc",
5
+ "fileTypes": ["sdoc"],
6
+ "patterns": [
7
+ { "include": "#comment" },
8
+ { "include": "#script-ts" },
9
+ { "include": "#script-js" },
10
+ { "include": "#style" },
11
+ { "include": "#docs" },
12
+ { "include": "#page" },
13
+ { "include": "#layout" }
14
+ ],
15
+ "repository": {
16
+ "comment": {
17
+ "name": "comment.block.html.sdoc",
18
+ "begin": "<!--",
19
+ "end": "-->"
20
+ },
21
+ "script-ts": {
22
+ "begin": "^\\s*(<)(script)(?=[^>]*lang=([\"'])ts\\3)",
23
+ "beginCaptures": {
24
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
25
+ "2": { "name": "entity.name.tag.script.sdoc" }
26
+ },
27
+ "end": "(</)(script)\\s*(>)",
28
+ "endCaptures": {
29
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
30
+ "2": { "name": "entity.name.tag.script.sdoc" },
31
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
32
+ },
33
+ "patterns": [
34
+ { "include": "#tag-rest" },
35
+ {
36
+ "begin": "(?<=>)",
37
+ "end": "(?=</script)",
38
+ "contentName": "source.ts",
39
+ "patterns": [{ "include": "source.ts" }]
40
+ }
41
+ ]
42
+ },
43
+ "script-js": {
44
+ "begin": "^\\s*(<)(script)(?![^>]*lang=([\"'])ts\\3)(?=[\\s>])",
45
+ "beginCaptures": {
46
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
47
+ "2": { "name": "entity.name.tag.script.sdoc" }
48
+ },
49
+ "end": "(</)(script)\\s*(>)",
50
+ "endCaptures": {
51
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
52
+ "2": { "name": "entity.name.tag.script.sdoc" },
53
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
54
+ },
55
+ "patterns": [
56
+ { "include": "#tag-rest" },
57
+ {
58
+ "begin": "(?<=>)",
59
+ "end": "(?=</script)",
60
+ "contentName": "source.js",
61
+ "patterns": [{ "include": "source.js" }]
62
+ }
63
+ ]
64
+ },
65
+ "style": {
66
+ "begin": "^\\s*(<)(style)(?=[\\s>])",
67
+ "beginCaptures": {
68
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
69
+ "2": { "name": "entity.name.tag.style.sdoc" }
70
+ },
71
+ "end": "(</)(style)\\s*(>)",
72
+ "endCaptures": {
73
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
74
+ "2": { "name": "entity.name.tag.style.sdoc" },
75
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
76
+ },
77
+ "patterns": [
78
+ { "include": "#tag-rest" },
79
+ {
80
+ "begin": "(?<=>)",
81
+ "end": "(?=</style)",
82
+ "contentName": "source.css",
83
+ "patterns": [{ "include": "source.css" }]
84
+ }
85
+ ]
86
+ },
87
+ "tag-rest": {
88
+ "begin": "\\G",
89
+ "end": "(>)",
90
+ "endCaptures": {
91
+ "1": { "name": "punctuation.definition.tag.end.sdoc" }
92
+ },
93
+ "patterns": [{ "include": "#attributes" }]
94
+ },
95
+ "docs": {
96
+ "name": "meta.block.docs.sdoc",
97
+ "begin": "^\\s*(\\[)(DOCS)\\b",
98
+ "beginCaptures": {
99
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
100
+ "2": { "name": "keyword.control.entity.sdoc" }
101
+ },
102
+ "end": "^\\s*(\\[/)(DOCS)(\\])\\s*$",
103
+ "endCaptures": {
104
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
105
+ "2": { "name": "keyword.control.entity.sdoc" },
106
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
107
+ },
108
+ "patterns": [
109
+ { "include": "#opener-attrs" },
110
+ { "include": "#comment" },
111
+ { "include": "#preview" },
112
+ { "include": "#example" }
113
+ ]
114
+ },
115
+ "page": {
116
+ "name": "meta.block.page.sdoc",
117
+ "begin": "^\\s*(\\[)(PAGE)\\b",
118
+ "beginCaptures": {
119
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
120
+ "2": { "name": "keyword.control.entity.sdoc" }
121
+ },
122
+ "end": "^\\s*(\\[/)(PAGE)(\\])\\s*$",
123
+ "endCaptures": {
124
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
125
+ "2": { "name": "keyword.control.entity.sdoc" },
126
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
127
+ },
128
+ "patterns": [
129
+ { "include": "#opener-attrs" },
130
+ { "include": "source.svelte" }
131
+ ]
132
+ },
133
+ "layout": {
134
+ "name": "meta.block.layout.sdoc",
135
+ "begin": "^\\s*(\\[)(LAYOUT)\\b",
136
+ "beginCaptures": {
137
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
138
+ "2": { "name": "keyword.control.entity.sdoc" }
139
+ },
140
+ "end": "^\\s*(\\[/)(LAYOUT)(\\])\\s*$",
141
+ "endCaptures": {
142
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
143
+ "2": { "name": "keyword.control.entity.sdoc" },
144
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
145
+ },
146
+ "patterns": [
147
+ { "include": "#opener-attrs" },
148
+ { "include": "source.svelte" }
149
+ ]
150
+ },
151
+ "preview": {
152
+ "name": "meta.block.preview.sdoc",
153
+ "begin": "^\\s*(\\[)(preview)\\b",
154
+ "beginCaptures": {
155
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
156
+ "2": { "name": "entity.name.tag.sdoc" }
157
+ },
158
+ "end": "^\\s*(\\[/)(preview)(\\])\\s*$",
159
+ "endCaptures": {
160
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
161
+ "2": { "name": "entity.name.tag.sdoc" },
162
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
163
+ },
164
+ "patterns": [
165
+ { "include": "#opener-attrs" },
166
+ { "include": "source.svelte" }
167
+ ]
168
+ },
169
+ "example": {
170
+ "name": "meta.block.example.sdoc",
171
+ "begin": "^\\s*(\\[)(example)\\b",
172
+ "beginCaptures": {
173
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
174
+ "2": { "name": "entity.name.tag.sdoc" }
175
+ },
176
+ "end": "^\\s*(\\[/)(example)(\\])\\s*$",
177
+ "endCaptures": {
178
+ "1": { "name": "punctuation.definition.tag.begin.sdoc" },
179
+ "2": { "name": "entity.name.tag.sdoc" },
180
+ "3": { "name": "punctuation.definition.tag.end.sdoc" }
181
+ },
182
+ "patterns": [
183
+ { "include": "#opener-attrs" },
184
+ { "include": "source.svelte" }
185
+ ]
186
+ },
187
+ "opener-attrs": {
188
+ "begin": "\\G",
189
+ "end": "(\\])",
190
+ "endCaptures": {
191
+ "1": { "name": "punctuation.definition.tag.end.sdoc" }
192
+ },
193
+ "patterns": [{ "include": "#attributes" }]
194
+ },
195
+ "attributes": {
196
+ "patterns": [
197
+ {
198
+ "match": "([A-Za-z_][\\w-]*)\\s*(=)",
199
+ "captures": {
200
+ "1": { "name": "entity.other.attribute-name.sdoc" },
201
+ "2": { "name": "punctuation.separator.key-value.sdoc" }
202
+ }
203
+ },
204
+ {
205
+ "match": "([A-Za-z_][\\w-]*)",
206
+ "name": "entity.other.attribute-name.sdoc"
207
+ },
208
+ {
209
+ "name": "string.quoted.double.sdoc",
210
+ "begin": "\"",
211
+ "end": "\"",
212
+ "beginCaptures": {
213
+ "0": { "name": "punctuation.definition.string.begin.sdoc" }
214
+ },
215
+ "endCaptures": {
216
+ "0": { "name": "punctuation.definition.string.end.sdoc" }
217
+ }
218
+ },
219
+ { "include": "#expression" }
220
+ ]
221
+ },
222
+ "expression": {
223
+ "name": "meta.embedded.expression.sdoc",
224
+ "begin": "\\{",
225
+ "end": "\\}",
226
+ "beginCaptures": {
227
+ "0": { "name": "punctuation.section.embedded.begin.sdoc" }
228
+ },
229
+ "endCaptures": {
230
+ "0": { "name": "punctuation.section.embedded.end.sdoc" }
231
+ },
232
+ "patterns": [{ "include": "#expression-inner" }]
233
+ },
234
+ "expression-inner": {
235
+ "patterns": [
236
+ {
237
+ "begin": "\\{",
238
+ "end": "\\}",
239
+ "patterns": [{ "include": "#expression-inner" }]
240
+ },
241
+ { "include": "source.ts" }
242
+ ]
243
+ }
244
+ }
245
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { sdocsPlugin } from './vite.js';
2
- export type { SdocsConfig, ResolvedSdocsConfig, SdocMeta, DocEntry, ParsedProp, ParsedMethod, ParsedState, ParsedCssProp, ComponentData, ExtractedSnippet, } from './types.js';
2
+ export type { SdocsConfig, ResolvedSdocsConfig, SdocMeta, DocEntry, ParsedProp, ParsedMethod, ParsedState, ParsedCssProp, ComponentData, ExtractedSnippet, PreviewEntry, TocHeading, } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { scanSdoc, offsetToPosition, ENTITY_KINDS, SUB_BLOCK_KINDS, type Span, type EntityKind, type SubBlockKind, type AttrValue, type Attrs, type SubBlock, type Entity, type TagBlock, type ScanError, type SdocFile, } from './scanner.js';
2
+ export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, type ArgValue, type PreviewBlock, type ExampleBlock, type DocsEntity, type PageEntity, type LayoutEntity, type SdocEntity, type SdocDocument, } from './parser.js';
@@ -0,0 +1,2 @@
1
+ export { scanSdoc, offsetToPosition, ENTITY_KINDS, SUB_BLOCK_KINDS, } from './scanner.js';
2
+ export { parseSdoc, parseArgsLiteral, slugifyTitle, normalizeBody, } from './parser.js';
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Semantic layer over the syntactic scanner: validates attributes against
3
+ * the sdoc language rules and produces typed entities ready for the Vite
4
+ * plugin, the CLI app-gen, and the editor tooling.
5
+ */
6
+ import { type ScanError, type Span, type TagBlock } from './scanner.js';
7
+ export type ArgValue = string | number | boolean;
8
+ export interface PreviewBlock {
9
+ /** The identifier from component={X}; null when invalid/missing (already reported) */
10
+ componentName: string | null;
11
+ /** Parsed args literal; null when absent or invalid */
12
+ args: Record<string, ArgValue> | null;
13
+ /** Exact source of the args expression (inner object), for code display */
14
+ argsRaw: string | null;
15
+ /** Explicit title="…" override, when present */
16
+ title: string | null;
17
+ /** Tab label: the title override or the component name */
18
+ label: string;
19
+ body: string;
20
+ bodySpan: Span;
21
+ span: Span;
22
+ }
23
+ export interface ExampleBlock {
24
+ title: string;
25
+ body: string;
26
+ bodySpan: Span;
27
+ span: Span;
28
+ }
29
+ export interface DocsEntity {
30
+ kind: 'DOCS';
31
+ title: string;
32
+ slug: string;
33
+ description: string | null;
34
+ previews: PreviewBlock[];
35
+ examples: ExampleBlock[];
36
+ openerSpan: Span;
37
+ span: Span;
38
+ }
39
+ export interface PageEntity {
40
+ kind: 'PAGE';
41
+ title: string;
42
+ slug: string;
43
+ body: string;
44
+ bodySpan: Span;
45
+ openerSpan: Span;
46
+ span: Span;
47
+ }
48
+ export interface LayoutEntity {
49
+ kind: 'LAYOUT';
50
+ title: string;
51
+ slug: string;
52
+ padding: string | null;
53
+ body: string;
54
+ bodySpan: Span;
55
+ openerSpan: Span;
56
+ span: Span;
57
+ }
58
+ export type SdocEntity = DocsEntity | PageEntity | LayoutEntity;
59
+ export interface SdocDocument {
60
+ script: TagBlock | null;
61
+ style: TagBlock | null;
62
+ entities: SdocEntity[];
63
+ diagnostics: ScanError[];
64
+ source: string;
65
+ }
66
+ /**
67
+ * Normalize a raw block body for consumption: strip the common leading
68
+ * indentation (bodies sit one level deep inside their block), drop a
69
+ * trailing carriage return per line, and unescape lines that start with
70
+ * `\[` (the escape for body lines that would otherwise read as tags).
71
+ * The scanner keeps the raw text and spans; this is the display/runtime form.
72
+ */
73
+ export declare function normalizeBody(raw: string): string;
74
+ /** Slug used for entity addressing: relPath + '#' + slug. */
75
+ export declare function slugifyTitle(title: string): string;
76
+ /**
77
+ * Parse a preview args expression: a flat object literal whose values are
78
+ * plain literals (strings, numbers, booleans). Anything richer belongs in
79
+ * the block body, so it is rejected here.
80
+ */
81
+ export declare function parseArgsLiteral(raw: string, span: Span, diagnostics: ScanError[]): Record<string, ArgValue> | null;
82
+ export declare function parseSdoc(source: string): SdocDocument;