@mui/internal-docs-infra 0.12.1-canary.19 → 0.12.1-canary.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-docs-infra",
3
- "version": "0.12.1-canary.19",
3
+ "version": "0.12.1-canary.20",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI Infra - internal documentation creation tools.",
6
6
  "license": "MIT",
@@ -804,5 +804,5 @@
804
804
  "bin": {
805
805
  "docs-infra": "./cli/index.mjs"
806
806
  },
807
- "gitSha": "2543f4364e265ee3ed091cb080536009835ec614"
807
+ "gitSha": "d812bc24a3f4f38926ffe69e171df9c72254f78a"
808
808
  }
@@ -97,6 +97,55 @@ async function savePageIndexCache({
97
97
  }, markdown, enrichPageIndex(cacheMetadata, indexPath, rootContext));
98
98
  }
99
99
 
100
+ /**
101
+ * Merges the incoming metadata into the source pages (keyed by path, matching
102
+ * mergeMetadataMarkdown's logic) and renders the index markdown exactly as it is
103
+ * persisted. Shared by the writer and the `errorIfOutOfDate` validation path so the two
104
+ * always agree on what the index should contain - otherwise validation could render
105
+ * differently from the writer and flag an up-to-date file (or miss a stale one).
106
+ */
107
+ async function renderIndexPages(sourceMarkdown, sourcePages, metadataArray, {
108
+ indexTitle,
109
+ indexWrapperComponent,
110
+ preserveExistingTitleAndSlug,
111
+ relativeIndexPath
112
+ }) {
113
+ // Build a map keyed by path (not slug) to match mergeMetadataMarkdown's logic.
114
+ const updatedPagesMap = new Map();
115
+ // First, add all source pages.
116
+ for (const page of sourcePages) {
117
+ updatedPagesMap.set(page.path, page);
118
+ }
119
+ // Then update/add the new metadata items.
120
+ for (const metaItem of metadataArray) {
121
+ updatedPagesMap.set(metaItem.path, metaItem);
122
+ }
123
+ // The COMPLETE list of pages that should exist.
124
+ const allPages = Array.from(updatedPagesMap.values());
125
+
126
+ // mergeMetadataPages preserves the order from sourceMarkdown and returns the normalized
127
+ // metadata that renders to finalMarkdown - reused by the writer to populate the cache
128
+ // without re-parsing what it just wrote.
129
+ const merged = await mergeMetadataPages(sourceMarkdown, {
130
+ title: indexTitle,
131
+ pages: allPages
132
+ }, {
133
+ indexWrapperComponent,
134
+ preserveExistingTitleAndSlug
135
+ });
136
+ const finalMarkdown = await metadataToMarkdown(merged.metadata, {
137
+ editableMarker: merged.editableMarker,
138
+ indexWrapperComponent: merged.indexWrapperComponent,
139
+ // Only include path in the comment when baseDir is set (otherwise it's an absolute path).
140
+ path: relativeIndexPath
141
+ });
142
+ return {
143
+ allPages,
144
+ merged,
145
+ finalMarkdown
146
+ };
147
+ }
148
+
100
149
  /**
101
150
  * Updates the parent directory's index file with metadata from a page.
102
151
  *
@@ -218,6 +267,21 @@ export async function syncPageIndex(options) {
218
267
  }
219
268
  }
220
269
 
270
+ // Refresh the page-index cache from the committed markdown for the "nothing to write"
271
+ // exits, so the next cold loadServerPageIndex read stays warm without re-parsing.
272
+ const saveExistingIndexCache = async () => {
273
+ if (cacheDir && existingMetadata) {
274
+ await savePageIndexCache({
275
+ cacheDir,
276
+ indexPath,
277
+ rootContext,
278
+ markdown: existingContent,
279
+ metadata: existingMetadata,
280
+ defaultPageMetadata: false
281
+ });
282
+ }
283
+ };
284
+
221
285
  // Step 3: Check if any of our metadata items need updating
222
286
  let needsUpdate = false;
223
287
  for (const metaItem of metadataArray) {
@@ -238,25 +302,36 @@ export async function syncPageIndex(options) {
238
302
  }
239
303
  }
240
304
  if (!needsUpdate) {
241
- if (cacheDir && existingMetadata) {
242
- await savePageIndexCache({
243
- cacheDir,
244
- indexPath,
245
- rootContext,
246
- markdown: existingContent,
247
- metadata: existingMetadata,
248
- defaultPageMetadata: false
249
- });
250
- }
305
+ await saveExistingIndexCache();
251
306
 
252
307
  // All pages are already up-to-date, no need to acquire lock or write the index.
253
308
  return;
254
309
  }
255
310
 
256
- // If errorIfOutOfDate is true, throw an error instead of updating
311
+ // If errorIfOutOfDate is true, the writer would refuse to touch the file. The coarse
312
+ // per-page comparison above compares the raw in-memory metadata, which contains fields
313
+ // that never reach the persisted index (volatile AST node positions, or metadata that
314
+ // mergeMetadataPages fills in from the existing markdown). Those produce false positives.
315
+ // Determine whether the index is genuinely out of date by rendering the exact markdown
316
+ // the writer would produce and comparing it to what is committed on disk. Only fail when
317
+ // the rendered output actually differs. This is validation only - it never writes, so it
318
+ // works from the pre-lock read (existingMarkdown/existingPages) rather than re-reading
319
+ // under a lock like the writer does.
257
320
  if (errorIfOutOfDate) {
258
- const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
259
- throw new Error(`Index file is out of date: ${relativeIndexPath}\n` + `Please run the validation command (or next build) locally and commit the updated index files.`);
321
+ const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : undefined;
322
+ const {
323
+ finalMarkdown
324
+ } = await renderIndexPages(existingMarkdown, existingPages, metadataArray, {
325
+ indexTitle,
326
+ indexWrapperComponent,
327
+ preserveExistingTitleAndSlug,
328
+ relativeIndexPath
329
+ });
330
+ if (existingContent !== finalMarkdown) {
331
+ throw new Error(`Index file is out of date: ${relativeIndexPath ?? indexPath}\n` + `Please run the validation command (or next build) locally and commit the updated index files.`);
332
+ }
333
+ await saveExistingIndexCache();
334
+ return;
260
335
  }
261
336
 
262
337
  // Step 4: Ensure the file exists before locking (proper-lockfile requires an existing file)
@@ -302,45 +377,24 @@ export async function syncPageIndex(options) {
302
377
  }
303
378
  }
304
379
 
305
- // For batch updates, merge the metadata items with existing pages
306
- // Build a map keyed by path (not slug) to match mergeMetadataMarkdown's logic
307
- const updatedPagesMap = new Map();
308
-
309
- // First, add all current pages
310
- for (const page of currentPages) {
311
- updatedPagesMap.set(page.path, page);
312
- }
313
-
314
- // Then update/add the new metadata items
315
- for (const metaItem of metadataArray) {
316
- updatedPagesMap.set(metaItem.path, metaItem);
317
- }
318
-
319
- // Convert back to array - this is the COMPLETE list of pages that should exist
320
- const allPages = Array.from(updatedPagesMap.values());
321
-
322
- // Store for parent update
323
- mergedPages = allPages;
324
-
325
- // Re-merge with the latest content, passing the COMPLETE list of pages.
326
- // mergeMetadataPages preserves the order from currentMarkdown and returns the
327
- // normalized metadata that renders to finalMarkdown - reused below to populate
328
- // the cache without re-parsing what we just wrote.
329
- // Only include path in the comment when baseDir is set (otherwise it's an absolute path)
380
+ // Re-merge with the latest content re-read under the lock, passing the COMPLETE list
381
+ // of pages. The returned normalized metadata is reused below to populate the cache
382
+ // without re-parsing what we just wrote.
330
383
  const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : undefined;
331
- const merged = await mergeMetadataPages(currentMarkdown, {
332
- title: indexTitle,
333
- pages: allPages
334
- }, {
384
+ const {
385
+ allPages,
386
+ merged,
387
+ finalMarkdown
388
+ } = await renderIndexPages(currentMarkdown, currentPages, metadataArray, {
389
+ indexTitle,
335
390
  indexWrapperComponent,
336
- preserveExistingTitleAndSlug
337
- });
338
- const finalMarkdown = await metadataToMarkdown(merged.metadata, {
339
- editableMarker: merged.editableMarker,
340
- indexWrapperComponent: merged.indexWrapperComponent,
341
- path: relativeIndexPath
391
+ preserveExistingTitleAndSlug,
392
+ relativeIndexPath
342
393
  });
343
394
 
395
+ // Store for parent update
396
+ mergedPages = allPages;
397
+
344
398
  // Defensive check
345
399
  if (!finalMarkdown || !finalMarkdown.trim()) {
346
400
  throw new Error(`Cannot write empty content to ${indexPath}`);