@mui/internal-docs-infra 0.12.1-canary.45 → 0.12.1-canary.47

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 (37) hide show
  1. package/createSitemap/index.d.mts +2 -1
  2. package/createSitemap/types.d.mts +27 -0
  3. package/package.json +22 -2
  4. package/pipeline/loadPrecomputedSitemap/loadPrecomputedSitemap.d.mts +1 -1
  5. package/pipeline/loadServerPageIndex/enrichPageIndex.mjs +12 -2
  6. package/pipeline/loadServerPageIndex/extractPrefixAndTitle.mjs +2 -1
  7. package/pipeline/loadServerPageIndex/loadServerPageIndex.d.mts +1 -1
  8. package/pipeline/loadServerPageIndex/resolvePageIndexCacheKey.d.mts +9 -2
  9. package/pipeline/loadServerPageIndex/resolvePageIndexCacheKey.mjs +9 -2
  10. package/pipeline/loaderUtils/extractNameAndSlugFromUrl.mjs +1 -31
  11. package/pipeline/loaderUtils/index.d.mts +2 -1
  12. package/pipeline/loaderUtils/index.mjs +2 -1
  13. package/pipeline/loaderUtils/kebabToTitleCase.d.mts +11 -0
  14. package/pipeline/loaderUtils/kebabToTitleCase.mjs +34 -0
  15. package/pipeline/loaderUtils/stripRouteGroups.d.mts +36 -0
  16. package/pipeline/loaderUtils/stripRouteGroups.mjs +44 -0
  17. package/pipeline/parseSource/index.d.mts +1 -1
  18. package/pipeline/parseSource/index.mjs +1 -1
  19. package/pipeline/parseSource/parseSource.d.mts +11 -1
  20. package/pipeline/parseSource/parseSource.mjs +15 -0
  21. package/pipeline/syncPageIndex/mergeMetadataMarkdown.d.mts +7 -0
  22. package/pipeline/syncPageIndex/mergeMetadataMarkdown.mjs +156 -18
  23. package/pipeline/syncPageIndex/metadataToMarkdown.d.mts +147 -1
  24. package/pipeline/syncPageIndex/metadataToMarkdown.mjs +446 -71
  25. package/pipeline/syncPageIndex/syncPageIndex.d.mts +14 -1
  26. package/pipeline/syncPageIndex/syncPageIndex.mjs +21 -19
  27. package/pipeline/syncTypes/syncTypes.mjs +6 -4
  28. package/pipeline/transformHtmlCodeInline/transformHtmlCodeInline.mjs +18 -50
  29. package/pipeline/transformMarkdownMetadata/transformMarkdownMetadata.mjs +19 -31
  30. package/pipeline/transformMarkdownMetadata/types.d.mts +1 -1
  31. package/pipeline/transformMarkdownRelativePaths/transformMarkdownRelativePaths.mjs +5 -2
  32. package/resolvePageUrl/index.d.mts +1 -0
  33. package/resolvePageUrl/index.mjs +1 -0
  34. package/resolvePageUrl/resolvePageUrl.d.mts +12 -0
  35. package/resolvePageUrl/resolvePageUrl.mjs +27 -0
  36. package/useSearch/useSearch.mjs +3 -1
  37. package/withDocsInfra/withDocsInfra.d.mts +2 -2
@@ -1 +1,2 @@
1
- export * from "./createSitemap.mjs";
1
+ export * from "./createSitemap.mjs";
2
+ export type * from "./types.mjs";
@@ -41,11 +41,30 @@ export interface SitemapPage {
41
41
  skipDetailSection?: boolean;
42
42
  audience?: Audience;
43
43
  index?: boolean;
44
+ /**
45
+ * The title of the route-group section this page is listed under in a grouped index
46
+ * (e.g. `Components`), resolved from the page's route group. `null` for a page in a flat
47
+ * index or with no route group, so the field is always present. Useful for
48
+ * grouping/faceting search results.
49
+ */
50
+ section: string | null;
44
51
  image?: {
45
52
  url: string;
46
53
  alt?: string;
47
54
  };
48
55
  }
56
+ /**
57
+ * A route-group section heading in a grouped index (see `SitemapSectionData.sections`).
58
+ * Maps a Next.js route group to the human-editable heading its pages are listed under.
59
+ */
60
+ export interface PageIndexSection {
61
+ /** The route group this section collects (e.g. `(components)`). */
62
+ group: string;
63
+ /** The (human-editable) heading text shown for the section. */
64
+ title: string;
65
+ /** Heading depth for the section title. Defaults to 2 (`##`). */
66
+ depth?: number;
67
+ }
49
68
  /**
50
69
  * Section data from sitemap
51
70
  */
@@ -53,6 +72,14 @@ export interface SitemapSectionData {
53
72
  title: string;
54
73
  prefix: string;
55
74
  pages: SitemapPage[];
75
+ /**
76
+ * Ordered route-group sections when the index is grouped (its `##` subtitles), so
77
+ * search can recover the sections a page belongs to. An empty array for a flat index,
78
+ * so the field is always present.
79
+ */
80
+ sections: PageIndexSection[];
81
+ /** Heading of the detail-region wrapper in a grouped index (defaults to `Details`). */
82
+ detailsSectionTitle?: string;
56
83
  }
57
84
  /**
58
85
  * Orama schema property types
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-docs-infra",
3
- "version": "0.12.1-canary.45",
3
+ "version": "0.12.1-canary.47",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI Infra - internal documentation creation tools.",
6
6
  "license": "MIT",
@@ -281,6 +281,26 @@
281
281
  "default": "./createSitemap/types.mjs"
282
282
  }
283
283
  },
284
+ "./resolvePageUrl": {
285
+ "import": {
286
+ "types": "./resolvePageUrl/index.d.mts",
287
+ "default": "./resolvePageUrl/index.mjs"
288
+ },
289
+ "default": {
290
+ "types": "./resolvePageUrl/index.d.mts",
291
+ "default": "./resolvePageUrl/index.mjs"
292
+ }
293
+ },
294
+ "./routeGroups": {
295
+ "import": {
296
+ "types": "./pipeline/loaderUtils/stripRouteGroups.d.mts",
297
+ "default": "./pipeline/loaderUtils/stripRouteGroups.mjs"
298
+ },
299
+ "default": {
300
+ "types": "./pipeline/loaderUtils/stripRouteGroups.d.mts",
301
+ "default": "./pipeline/loaderUtils/stripRouteGroups.mjs"
302
+ }
303
+ },
284
304
  "./useStream": {
285
305
  "import": {
286
306
  "types": "./useStream/index.d.mts",
@@ -795,5 +815,5 @@
795
815
  "bin": {
796
816
  "docs-infra": "./cli/index.mjs"
797
817
  },
798
- "gitSha": "1c5627440adc8bc5990c9b1c95fb4b74d375345e"
818
+ "gitSha": "81765a8f621d2dc77469da42ae427c0b7e680e4f"
799
819
  }
@@ -8,7 +8,7 @@ export type LoaderOptions = {
8
8
  };
9
9
  /**
10
10
  * Directory for the sha256-validated JSON cache of parsed page indexes. When set,
11
- * unchanged indexes are read from `{cacheDir}/pages-index/{route}.json` instead of
11
+ * unchanged indexes are read from `{cacheDir}/pages-index-v2/{route}.json` instead of
12
12
  * being re-parsed.
13
13
  */
14
14
  cacheDir?: string;
@@ -1,5 +1,5 @@
1
1
  import { extractPrefixAndTitle, stripTitleMarkdown } from "./extractPrefixAndTitle.mjs";
2
- import { collapseInlineWhitespace } from "../syncPageIndex/metadataToMarkdown.mjs";
2
+ import { collapseInlineWhitespace, createPageSectionResolver } from "../syncPageIndex/metadataToMarkdown.mjs";
3
3
  /**
4
4
  * Converts parsed page-index metadata into the `SitemapSectionData` read-model.
5
5
  *
@@ -18,10 +18,15 @@ export function enrichPageIndex(metadata, absolutePath, rootContext) {
18
18
  prefix,
19
19
  title
20
20
  } = extractPrefixAndTitle(absolutePath, rootContext);
21
+
22
+ // Resolve each page's section title from its route group (grouped indexes only).
23
+ const resolveSection = createPageSectionResolver(metadata.sections);
21
24
  return {
22
25
  ...metadata,
23
26
  prefix,
24
27
  title,
28
+ // Always present so consumers never branch on absence: an empty array for a flat index.
29
+ sections: metadata.sections ?? [],
25
30
  // Normalize the index's own (leaked) description like the parser. Spread instead of an explicit
26
31
  // key because SitemapSectionData does not declare `description`.
27
32
  ...(metadata.description !== undefined ? {
@@ -35,6 +40,7 @@ export function enrichPageIndex(metadata, absolutePath, rootContext) {
35
40
  const {
36
41
  descriptionMarkdown,
37
42
  sections,
43
+ sectionGroup,
38
44
  ...pageWithoutMarkdown
39
45
  } = page;
40
46
  return {
@@ -43,7 +49,11 @@ export function enrichPageIndex(metadata, absolutePath, rootContext) {
43
49
  keywords: page.keywords?.map(collapseInlineWhitespace),
44
50
  types: page.types?.map(collapseInlineWhitespace),
45
51
  // Strip titleMarkdown from the sections hierarchy.
46
- sections: sections ? stripTitleMarkdown(sections) : undefined
52
+ sections: sections ? stripTitleMarkdown(sections) : undefined,
53
+ // Resolve the route-group section title for grouped indexes (search faceting),
54
+ // honoring the header a human filed an otherwise-ungrouped page under. Always present
55
+ // (`null` when the page has no section) so consumers never branch on absence.
56
+ section: resolveSection(page.path, sectionGroup) ?? null
47
57
  };
48
58
  })
49
59
  };
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { isRouteGroup } from "../loaderUtils/stripRouteGroups.mjs";
2
3
  /**
3
4
  * Converts a path segment to a title
4
5
  * e.g., "docs-infra" -> "Docs Infra", "components" -> "Components"
@@ -58,7 +59,7 @@ export function extractPrefixAndTitle(absolutePath, rootContext) {
58
59
  return false;
59
60
  }
60
61
  // Filter out Next.js route groups (e.g., '(public)', '(content)')
61
- if (seg.startsWith('(') && seg.endsWith(')')) {
62
+ if (isRouteGroup(seg)) {
62
63
  return false;
63
64
  }
64
65
  // Filter out current directory markers
@@ -11,7 +11,7 @@ export interface CreateLoadServerPageIndexOptions {
11
11
  /**
12
12
  * Directory for the sha256-validated JSON cache of parsed page indexes.
13
13
  *
14
- * When set, a read first checks `{cacheDir}/pages-index/{route}.json`; on a hash
14
+ * When set, a read first checks `{cacheDir}/pages-index-v2/{route}.json`; on a hash
15
15
  * match it returns the cached data and skips the markdown parse, and on a miss it
16
16
  * parses, writes the cache, and returns. When unset, no cache is read or written.
17
17
  */
@@ -1,5 +1,12 @@
1
- /** Cache namespace (subdirectory) for page-index entries. */
2
- export declare const PAGE_INDEX_CACHE_NAMESPACE = "pages-index";
1
+ /**
2
+ * Cache namespace (subdirectory) for page-index entries. The `-v2` suffix is a schema version:
3
+ * grouped indexes added always-present `sections` and per-page `section` fields to the cached
4
+ * read-model, so a pre-existing `pages-index` entry (keyed only by content hash) could otherwise
5
+ * survive a docs-infra upgrade and serve the old shape for an unchanged index file. Bumping the
6
+ * namespace forces a clean miss the first time the new code runs. Bump again on any future
7
+ * breaking change to the cached page-index shape.
8
+ */
9
+ export declare const PAGE_INDEX_CACHE_NAMESPACE = "pages-index-v2";
3
10
  /**
4
11
  * Derives the cache key for a page-index file from its path, reusing the same
5
12
  * route derivation as the loaded data so a writer and reader of the same index
@@ -1,7 +1,14 @@
1
1
  import { extractPrefixAndTitle } from "./extractPrefixAndTitle.mjs";
2
2
 
3
- /** Cache namespace (subdirectory) for page-index entries. */
4
- export const PAGE_INDEX_CACHE_NAMESPACE = 'pages-index';
3
+ /**
4
+ * Cache namespace (subdirectory) for page-index entries. The `-v2` suffix is a schema version:
5
+ * grouped indexes added always-present `sections` and per-page `section` fields to the cached
6
+ * read-model, so a pre-existing `pages-index` entry (keyed only by content hash) could otherwise
7
+ * survive a docs-infra upgrade and serve the old shape for an unchanged index file. Bumping the
8
+ * namespace forces a clean miss the first time the new code runs. Bump again on any future
9
+ * breaking change to the cached page-index shape.
10
+ */
11
+ export const PAGE_INDEX_CACHE_NAMESPACE = 'pages-index-v2';
5
12
 
6
13
  /**
7
14
  * Derives the cache key for a page-index file from its path, reusing the same
@@ -1,4 +1,5 @@
1
1
  import { fileUrlToPortablePath } from "./fileUrlToPortablePath.mjs";
2
+ import { kebabToTitleCase } from "./kebabToTitleCase.mjs";
2
3
 
3
4
  /**
4
5
  * Extracts and formats a name and slug from a URL path.
@@ -42,37 +43,6 @@ function camelToTitleCase(camelCase) {
42
43
  .replace(/^./, str => str.toUpperCase());
43
44
  }
44
45
 
45
- /**
46
- * Known brand names and acronyms that have a canonical capitalization.
47
- * Keys are lowercase, values are the canonical form to use in titles.
48
- */
49
- const BRAND_NAME_OVERRIDES = {
50
- javascript: 'JavaScript',
51
- typescript: 'TypeScript'
52
- };
53
-
54
- /**
55
- * Capitalizes a single word, applying brand-name overrides when applicable.
56
- */
57
- function capitalizeWord(word) {
58
- const lower = word.toLowerCase();
59
- const override = BRAND_NAME_OVERRIDES[lower];
60
- if (override) {
61
- return override;
62
- }
63
- return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
64
- }
65
-
66
- /**
67
- * Converts a kebab-case string to Title Case
68
- * @param kebabCase - The kebab-case string to convert
69
- * @returns Title case string
70
- */
71
- function kebabToTitleCase(kebabCase) {
72
- return kebabCase.split(/[-_]/) // Split on both hyphens and underscores
73
- .map(capitalizeWord).join(' ');
74
- }
75
-
76
46
  /**
77
47
  * Detects if a string is camelCase or PascalCase
78
48
  * @param str - The string to check
@@ -7,4 +7,5 @@ export * from "./extractNameAndSlugFromUrl.mjs";
7
7
  export * from "./externalsToPackages.mjs";
8
8
  export * from "./fileUrlToPortablePath.mjs";
9
9
  export * from "./getLanguageFromExtension.mjs";
10
- export * from "./generateFileSlug.mjs";
10
+ export * from "./generateFileSlug.mjs";
11
+ export * from "./stripRouteGroups.mjs";
@@ -7,4 +7,5 @@ export * from "./extractNameAndSlugFromUrl.mjs";
7
7
  export * from "./externalsToPackages.mjs";
8
8
  export * from "./fileUrlToPortablePath.mjs";
9
9
  export * from "./getLanguageFromExtension.mjs";
10
- export * from "./generateFileSlug.mjs";
10
+ export * from "./generateFileSlug.mjs";
11
+ export * from "./stripRouteGroups.mjs";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Converts a kebab-case (or snake_case) string to Title Case, honoring brand-name overrides
3
+ * (e.g. `javascript` -> `JavaScript`). This is the single title-casing rule shared by the
4
+ * producers that turn a slug/folder name into a display title — directory-derived index titles,
5
+ * URL-segment names, and route-group section headings — so those titles never diverge.
6
+ *
7
+ * @example kebabToTitleCase('alert-dialog') -> 'Alert Dialog'
8
+ * @example kebabToTitleCase('getting_started') -> 'Getting Started'
9
+ * @example kebabToTitleCase('typescript') -> 'TypeScript'
10
+ */
11
+ export declare function kebabToTitleCase(kebabCase: string): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Known brand names and acronyms that have a canonical capitalization.
3
+ * Keys are lowercase, values are the canonical form to use in titles.
4
+ */
5
+ const BRAND_NAME_OVERRIDES = {
6
+ javascript: 'JavaScript',
7
+ typescript: 'TypeScript'
8
+ };
9
+
10
+ /**
11
+ * Capitalizes a single word, applying brand-name overrides when applicable.
12
+ */
13
+ function capitalizeWord(word) {
14
+ const lower = word.toLowerCase();
15
+ const override = BRAND_NAME_OVERRIDES[lower];
16
+ if (override) {
17
+ return override;
18
+ }
19
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
20
+ }
21
+
22
+ /**
23
+ * Converts a kebab-case (or snake_case) string to Title Case, honoring brand-name overrides
24
+ * (e.g. `javascript` -> `JavaScript`). This is the single title-casing rule shared by the
25
+ * producers that turn a slug/folder name into a display title — directory-derived index titles,
26
+ * URL-segment names, and route-group section headings — so those titles never diverge.
27
+ *
28
+ * @example kebabToTitleCase('alert-dialog') -> 'Alert Dialog'
29
+ * @example kebabToTitleCase('getting_started') -> 'Getting Started'
30
+ * @example kebabToTitleCase('typescript') -> 'TypeScript'
31
+ */
32
+ export function kebabToTitleCase(kebabCase) {
33
+ return kebabCase.split(/[-_]/).map(capitalizeWord).join(' ');
34
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Whether a single path segment is a Next.js route group: a directory name wrapped in
3
+ * parentheses (e.g. `(overview)`). Route groups organize files without appearing in the public
4
+ * URL. This is the one shared definition used by page grouping, parent-index resolution, and
5
+ * path-to-URL resolution.
6
+ *
7
+ * Because it tests a whole segment, a segment that merely contains parentheses (e.g.
8
+ * `(draft)notes`) is not a route group and is kept.
9
+ *
10
+ * @example isRouteGroup('(overview)') -> true
11
+ * @example isRouteGroup('components') -> false
12
+ * @example isRouteGroup('(draft)notes') -> false
13
+ */
14
+ export declare function isRouteGroup(segment: string): boolean;
15
+ /**
16
+ * The name inside a route-group segment (e.g. `(overview)` -> `overview`), or `undefined` when the
17
+ * segment is not a whole route group. Uses the same whole-segment rule as {@link isRouteGroup}, so
18
+ * callers get one definition of the parenthesis convention instead of re-deriving it. Useful for
19
+ * mapping a route group back to a section (e.g. resolving a section's default landing page).
20
+ *
21
+ * @example routeGroupName('(overview)') -> 'overview'
22
+ * @example routeGroupName('components') -> undefined
23
+ * @example routeGroupName('(draft)notes') -> undefined
24
+ */
25
+ export declare function routeGroupName(segment: string): string | undefined;
26
+ /**
27
+ * Removes whole Next.js route-group segments (`(group)`) from a `/`-separated path or URL, since
28
+ * they do not appear in the public URL, keeping every other segment intact. It splits on `/` and
29
+ * filters whole segments with {@link isRouteGroup}, so a folder that merely contains parentheses
30
+ * (e.g. `(draft)notes`) stays in the path. This is the one whole-segment rule the grouped index,
31
+ * rendered links, and search URL resolution all share.
32
+ *
33
+ * @example stripRouteGroupSegments('/components/(inputs)/checkbox') -> '/components/checkbox'
34
+ * @example stripRouteGroupSegments('/(draft)notes/guide') -> '/(draft)notes/guide'
35
+ */
36
+ export declare function stripRouteGroupSegments(pathOrUrl: string): string;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Whether a single path segment is a Next.js route group: a directory name wrapped in
3
+ * parentheses (e.g. `(overview)`). Route groups organize files without appearing in the public
4
+ * URL. This is the one shared definition used by page grouping, parent-index resolution, and
5
+ * path-to-URL resolution.
6
+ *
7
+ * Because it tests a whole segment, a segment that merely contains parentheses (e.g.
8
+ * `(draft)notes`) is not a route group and is kept.
9
+ *
10
+ * @example isRouteGroup('(overview)') -> true
11
+ * @example isRouteGroup('components') -> false
12
+ * @example isRouteGroup('(draft)notes') -> false
13
+ */
14
+ export function isRouteGroup(segment) {
15
+ return segment.startsWith('(') && segment.endsWith(')');
16
+ }
17
+
18
+ /**
19
+ * The name inside a route-group segment (e.g. `(overview)` -> `overview`), or `undefined` when the
20
+ * segment is not a whole route group. Uses the same whole-segment rule as {@link isRouteGroup}, so
21
+ * callers get one definition of the parenthesis convention instead of re-deriving it. Useful for
22
+ * mapping a route group back to a section (e.g. resolving a section's default landing page).
23
+ *
24
+ * @example routeGroupName('(overview)') -> 'overview'
25
+ * @example routeGroupName('components') -> undefined
26
+ * @example routeGroupName('(draft)notes') -> undefined
27
+ */
28
+ export function routeGroupName(segment) {
29
+ return isRouteGroup(segment) ? segment.slice(1, -1) : undefined;
30
+ }
31
+
32
+ /**
33
+ * Removes whole Next.js route-group segments (`(group)`) from a `/`-separated path or URL, since
34
+ * they do not appear in the public URL, keeping every other segment intact. It splits on `/` and
35
+ * filters whole segments with {@link isRouteGroup}, so a folder that merely contains parentheses
36
+ * (e.g. `(draft)notes`) stays in the path. This is the one whole-segment rule the grouped index,
37
+ * rendered links, and search URL resolution all share.
38
+ *
39
+ * @example stripRouteGroupSegments('/components/(inputs)/checkbox') -> '/components/checkbox'
40
+ * @example stripRouteGroupSegments('/(draft)notes/guide') -> '/(draft)notes/guide'
41
+ */
42
+ export function stripRouteGroupSegments(pathOrUrl) {
43
+ return pathOrUrl.split('/').filter(segment => !isRouteGroup(segment)).join('/');
44
+ }
@@ -1,2 +1,2 @@
1
- export * from "./parseSource.mjs";
1
+ export { createParseSource, parsePlainText, parseSource, registerAllGrammars, registerGrammars, resetStarryNight } from "./parseSource.mjs";
2
2
  export { getGrammarFromLanguage, languageToGrammarMap, extensionMap } from "./grammarMaps.mjs";
@@ -1,2 +1,2 @@
1
- export * from "./parseSource.mjs";
1
+ export { createParseSource, parsePlainText, parseSource, registerAllGrammars, registerGrammars, resetStarryNight } from "./parseSource.mjs";
2
2
  export { getGrammarFromLanguage, languageToGrammarMap, extensionMap } from "./grammarMaps.mjs";
@@ -1,4 +1,6 @@
1
+ import { createStarryNight } from '@wooorm/starry-night';
1
2
  import type { ParseSource } from "../../CodeHighlighter/types.mjs";
3
+ type StarryNight = Awaited<ReturnType<typeof createStarryNight>>;
2
4
  /**
3
5
  * Parses source into a line-guttered HAST **without** syntax highlighting — the
4
6
  * raw text wrapped in the same `.line`/`.frame` structure `parseSource` produces,
@@ -51,8 +53,16 @@ export declare function registerAllGrammars(): Promise<void>;
51
53
  * @returns A Promise that resolves to the initialized `parseSource` function
52
54
  */
53
55
  export declare const createParseSource: (initialScopes?: string[]) => Promise<ParseSource>;
56
+ /**
57
+ * Returns the shared Starry Night instance after registering every grammar.
58
+ *
59
+ * Inline highlighting can follow code that initialized a lazy subset, so it must
60
+ * initialize the complete registry before using the shared instance.
61
+ */
62
+ export declare function getStarryNightInstance(): Promise<StarryNight>;
54
63
  /**
55
64
  * Clears the global Starry Night singleton and registration state. Intended for
56
65
  * tests exercising lazy registration from a known-empty registry.
57
66
  */
58
- export declare function resetStarryNight(): void;
67
+ export declare function resetStarryNight(): void;
68
+ export {};
@@ -232,6 +232,21 @@ export const createParseSource = async initialScopes => {
232
232
  return parseSource;
233
233
  };
234
234
 
235
+ /**
236
+ * Returns the shared Starry Night instance after registering every grammar.
237
+ *
238
+ * Inline highlighting can follow code that initialized a lazy subset, so it must
239
+ * initialize the complete registry before using the shared instance.
240
+ */
241
+ export async function getStarryNightInstance() {
242
+ await createParseSource();
243
+ const instance = getInstance();
244
+ if (!instance) {
245
+ throw new Error('Starry Night failed to initialize.');
246
+ }
247
+ return instance;
248
+ }
249
+
235
250
  /**
236
251
  * Clears the global Starry Night singleton and registration state. Intended for
237
252
  * tests exercising lazy registration from a known-empty registry.
@@ -59,6 +59,13 @@ export declare function mergeMetadataPages(existingMarkdown: string | undefined,
59
59
  * This allows multiple pages to have the same slug (anchor) while still being treated
60
60
  * as distinct pages.
61
61
  *
62
+ * Known limitation: matching is by exact path, so a page whose stored path changes format —
63
+ * notably an index first regenerated after route groups became path-preserving, where an old
64
+ * entry `./accordion/page.mdx` no longer matches the new `./(components)/accordion/page.mdx` —
65
+ * is not reconciled with its old entry and can briefly duplicate. This is accepted rather than
66
+ * matched by route-group-stripped path (two distinct pages `./(a)/x` and `./(b)/x` share one
67
+ * stripped path), since grouped indexes are net-new; a stale entry clears once removed by hand.
68
+ *
62
69
  * @param existingMarkdown - The existing markdown content (or undefined if none exists)
63
70
  * @param newMetadata - The new metadata to merge in
64
71
  * @param options - Optional configuration