|
12 | 12 | * Each per-example mirror includes fenced Svelte source copied from |
13 | 13 | * `src/lib/examples/<slug>/demos/*.svelte`, so coding agents can fetch |
14 | 14 | * runnable examples without scraping the interactive UI. |
| 15 | + * |
| 16 | + * The examples landing page may expose a curated `const examples = [{ slug, |
| 17 | + * title, tag, description }]` array to control index ordering and tags. When |
| 18 | + * it doesn't — a missing landing page, or one that builds its list |
| 19 | + * dynamically — the plugin discovers examples from the `examplesDir` route |
| 20 | + * folder and derives the index from each page's SEO copy and primary section |
| 21 | + * tag. |
15 | 22 | */ |
16 | 23 | import { existsSync } from 'node:fs' |
17 | 24 | import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises' |
@@ -165,8 +172,18 @@ async function parseExamplePages({ |
165 | 172 | ) |
166 | 173 | } |
167 | 174 |
|
| 175 | +/** |
| 176 | + * Parse the optional curated `const examples = [{ slug, title, tag, |
| 177 | + * description }]` index from the examples landing page. Returns `[]` when the |
| 178 | + * page declares no such array — many sites build their index dynamically |
| 179 | + * (`$derived`, a sitemap walk, etc.), in which case the plugin discovers |
| 180 | + * examples straight from the route folder instead. A curated array, when |
| 181 | + * present, drives ordering and the advertised tags. |
| 182 | + */ |
168 | 183 | function parseExampleIndex(source: string): ExampleIndexItem[] { |
169 | | - const arraySource = extractConstArray(source, 'examples') |
| 184 | + const arraySource = findConstArray(source, 'examples') |
| 185 | + if (arraySource === null) return [] |
| 186 | + |
170 | 187 | const objects = splitTopLevelObjects(arraySource) |
171 | 188 |
|
172 | 189 | return objects.map((objectSource) => ({ |
@@ -377,13 +394,16 @@ function renderExampleIndexMarkdown({ |
377 | 394 | pages: ExamplePageMirror[] |
378 | 395 | opts: ResolvedOptions |
379 | 396 | }): string { |
| 397 | + // When no curated index array is present, build the list from the |
| 398 | + // folder-discovered pages, advertising each example's primary section |
| 399 | + // tag (falling back to a generic label only when a page has no sections). |
380 | 400 | const indexItems = |
381 | 401 | items.length > 0 |
382 | 402 | ? items |
383 | 403 | : pages.map((page) => ({ |
384 | 404 | slug: page.slug, |
385 | 405 | title: page.title, |
386 | | - tag: 'EXAMPLE', |
| 406 | + tag: page.sections[0]?.tag ?? 'EXAMPLE', |
387 | 407 | description: page.description |
388 | 408 | })) |
389 | 409 | const pageDescriptions = new Map(pages.map((page) => [page.slug, page.description])) |
@@ -428,15 +448,29 @@ function codeFence(source: string, language: string): string { |
428 | 448 | return `${fence}${language}\n${source.trimEnd()}\n${fence}` |
429 | 449 | } |
430 | 450 |
|
431 | | -function extractConstArray(source: string, constName: string): string { |
| 451 | +/** |
| 452 | + * Locate a `const <name> = [ … ]` array literal and return its inner source, |
| 453 | + * or `null` when the file declares no such array (e.g. the index page builds |
| 454 | + * its list dynamically). Callers that require the array use |
| 455 | + * {@link extractConstArray}; callers that can fall back to folder discovery |
| 456 | + * use this nullable form directly. |
| 457 | + */ |
| 458 | +function findConstArray(source: string, constName: string): string | null { |
432 | 459 | const pattern = new RegExp(`const\\s+${constName}\\b[^=]*=\\s*\\[`) |
433 | 460 | const match = pattern.exec(source) |
434 | | - if (!match) |
435 | | - throw new Error(`[docs-kit:example-mirrors] Could not find const ${constName} array`) |
| 461 | + if (!match) return null |
436 | 462 |
|
437 | 463 | return readBalanced(source, match.index + match[0].length - 1, '[', ']') |
438 | 464 | } |
439 | 465 |
|
| 466 | +function extractConstArray(source: string, constName: string): string { |
| 467 | + const arraySource = findConstArray(source, constName) |
| 468 | + if (arraySource === null) |
| 469 | + throw new Error(`[docs-kit:example-mirrors] Could not find const ${constName} array`) |
| 470 | + |
| 471 | + return arraySource |
| 472 | +} |
| 473 | + |
440 | 474 | function splitTopLevelObjects(source: string): string[] { |
441 | 475 | const objects: string[] = [] |
442 | 476 | let index = 0 |
|
0 commit comments