@mui/internal-docs-infra 0.3.1-canary.3 → 0.3.1-canary.4

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 (102) hide show
  1. package/esm/CodeControllerContext/CodeControllerContext.js +2 -2
  2. package/esm/CodeExternalsContext/CodeExternalsContext.js +1 -1
  3. package/esm/CodeHighlighter/CodeHighlighter.js +247 -329
  4. package/esm/CodeHighlighter/CodeHighlighterClient.js +447 -653
  5. package/esm/CodeHighlighter/CodeHighlighterContext.js +2 -2
  6. package/esm/CodeHighlighter/CodeHighlighterFallbackContext.js +2 -2
  7. package/esm/CodeHighlighter/codeToFallbackProps.js +21 -37
  8. package/esm/CodeHighlighter/errors.js +248 -400
  9. package/esm/CodeHighlighter/parseControlledCode.js +12 -20
  10. package/esm/CodeProvider/CodeContext.js +3 -3
  11. package/esm/CodeProvider/CodeProvider.js +31 -40
  12. package/esm/abstractCreateDemo/abstractCreateDemo.js +13 -17
  13. package/esm/abstractCreateDemoClient/abstractCreateDemoClient.js +12 -12
  14. package/esm/cli/index.js +1 -1
  15. package/esm/cli/runValidate.js +160 -264
  16. package/esm/createDemoData/createDemoData.js +11 -12
  17. package/esm/createSitemap/createSitemap.js +2 -2
  18. package/esm/pipeline/getFileConventions/fileConventions.js +1 -1
  19. package/esm/pipeline/getFileConventions/getFileConventions.js +2 -15
  20. package/esm/pipeline/hastUtils/hastUtils.js +16 -17
  21. package/esm/pipeline/loadCodeVariant/addCodeVariantPaths.js +24 -24
  22. package/esm/pipeline/loadCodeVariant/applyCodeTransform.js +12 -22
  23. package/esm/pipeline/loadCodeVariant/calculateMainFilePath.js +30 -37
  24. package/esm/pipeline/loadCodeVariant/computeHastDeltas.js +107 -185
  25. package/esm/pipeline/loadCodeVariant/diffHast.js +18 -53
  26. package/esm/pipeline/loadCodeVariant/examineCodeVariant.js +24 -27
  27. package/esm/pipeline/loadCodeVariant/flattenCodeVariant.js +9 -10
  28. package/esm/pipeline/loadCodeVariant/hasAllCodeVariants.js +5 -5
  29. package/esm/pipeline/loadCodeVariant/loadCodeFallback.js +516 -731
  30. package/esm/pipeline/loadCodeVariant/loadCodeVariant.js +679 -1079
  31. package/esm/pipeline/loadCodeVariant/maybeCodeInitialData.js +14 -20
  32. package/esm/pipeline/loadCodeVariant/mergeCodeMetadata.js +53 -63
  33. package/esm/pipeline/loadCodeVariant/parseCode.js +40 -48
  34. package/esm/pipeline/loadCodeVariant/pathUtils.js +43 -64
  35. package/esm/pipeline/loadCodeVariant/transformSource.js +55 -125
  36. package/esm/pipeline/loadPrecomputedCodeHighlighter/loadPrecomputedCodeHighlighter.js +160 -221
  37. package/esm/pipeline/loadPrecomputedCodeHighlighter/parseCreateFactoryCall.js +377 -479
  38. package/esm/pipeline/loadPrecomputedCodeHighlighter/parseFunctionArguments.js +171 -173
  39. package/esm/pipeline/loadPrecomputedCodeHighlighter/performanceLogger.js +14 -30
  40. package/esm/pipeline/loadPrecomputedCodeHighlighter/replacePrecomputeValue.js +19 -21
  41. package/esm/pipeline/loadPrecomputedCodeHighlighter/serializeFunctionArguments.js +37 -71
  42. package/esm/pipeline/loadPrecomputedCodeHighlighterClient/filterRuntimeExternals.js +3 -9
  43. package/esm/pipeline/loadPrecomputedCodeHighlighterClient/generateImportStatements.js +54 -80
  44. package/esm/pipeline/loadPrecomputedCodeHighlighterClient/generateResolvedExternals.js +71 -98
  45. package/esm/pipeline/loadPrecomputedCodeHighlighterClient/injectImportsIntoSource.js +5 -5
  46. package/esm/pipeline/loadPrecomputedCodeHighlighterClient/loadPrecomputedCodeHighlighterClient.js +161 -211
  47. package/esm/pipeline/loadPrecomputedSitemap/loadPrecomputedSitemap.js +159 -207
  48. package/esm/pipeline/loadServerCodeMeta/loadServerCodeMeta.js +42 -64
  49. package/esm/pipeline/loadServerCodeMeta/resolveModulePathWithFs.js +20 -96
  50. package/esm/pipeline/loadServerPageIndex/loadServerPageIndex.js +66 -85
  51. package/esm/pipeline/loadServerSitemap/loadServerSitemap.js +71 -118
  52. package/esm/pipeline/loadServerSource/loadServerSource.js +121 -148
  53. package/esm/pipeline/loaderUtils/externalsToPackages.js +7 -7
  54. package/esm/pipeline/loaderUtils/extractNameAndSlugFromUrl.js +8 -12
  55. package/esm/pipeline/loaderUtils/fileUrlToPortablePath.js +5 -5
  56. package/esm/pipeline/loaderUtils/getFileNameFromUrl.js +19 -29
  57. package/esm/pipeline/loaderUtils/getLanguageFromExtension.js +3 -4
  58. package/esm/pipeline/loaderUtils/mergeExternals.js +15 -35
  59. package/esm/pipeline/loaderUtils/parseImportsAndComments.js +413 -433
  60. package/esm/pipeline/loaderUtils/processRelativeImports.js +153 -239
  61. package/esm/pipeline/loaderUtils/resolveModulePath.js +544 -1303
  62. package/esm/pipeline/loaderUtils/rewriteImports.js +73 -111
  63. package/esm/pipeline/parseSource/addLineGutters.js +33 -45
  64. package/esm/pipeline/parseSource/grammars.js +3 -3
  65. package/esm/pipeline/parseSource/parseSource.js +13 -31
  66. package/esm/pipeline/syncPageIndex/createMarkdownNodes.js +32 -55
  67. package/esm/pipeline/syncPageIndex/mergeMetadataMarkdown.js +107 -160
  68. package/esm/pipeline/syncPageIndex/metadataToMarkdown.js +846 -1033
  69. package/esm/pipeline/syncPageIndex/syncPageIndex.js +291 -438
  70. package/esm/pipeline/transformHtmlCodePrecomputed/transformHtmlCodePrecomputed.js +213 -311
  71. package/esm/pipeline/transformMarkdownBlockquoteCallouts/transformMarkdownBlockquoteCallouts.js +10 -10
  72. package/esm/pipeline/transformMarkdownCode/transformMarkdownCode.js +133 -193
  73. package/esm/pipeline/transformMarkdownDemoLinks/transformMarkdownDemoLinks.js +25 -27
  74. package/esm/pipeline/transformMarkdownMetadata/transformMarkdownMetadata.js +572 -717
  75. package/esm/pipeline/transformMarkdownRelativePaths/transformMarkdownRelativePaths.js +8 -8
  76. package/esm/pipeline/transformTypescriptToJavascript/removeTypes.js +84 -113
  77. package/esm/pipeline/transformTypescriptToJavascript/transformTypescriptToJavascript.js +10 -26
  78. package/esm/useCode/Pre.js +58 -62
  79. package/esm/useCode/useCode.js +59 -61
  80. package/esm/useCode/useCodeUtils.js +54 -63
  81. package/esm/useCode/useCopyFunctionality.js +10 -9
  82. package/esm/useCode/useFileNavigation.js +150 -212
  83. package/esm/useCode/useSourceEditing.js +17 -14
  84. package/esm/useCode/useTransformManagement.js +23 -26
  85. package/esm/useCode/useUIState.js +12 -20
  86. package/esm/useCode/useVariantSelection.js +62 -79
  87. package/esm/useCopier/index.js +29 -56
  88. package/esm/useDemo/createCodeSandbox.js +12 -15
  89. package/esm/useDemo/createStackBlitz.js +14 -20
  90. package/esm/useDemo/exportVariant.js +200 -180
  91. package/esm/useDemo/exportVariantAsCra.js +22 -25
  92. package/esm/useDemo/useDemo.js +80 -84
  93. package/esm/useErrors/ErrorsContext.js +1 -1
  94. package/esm/useErrors/useErrors.js +3 -3
  95. package/esm/useLocalStorageState/useLocalStorageState.js +23 -39
  96. package/esm/usePreference/PreferencesProvider.js +1 -1
  97. package/esm/usePreference/usePreference.js +9 -11
  98. package/esm/useSearch/useSearch.js +290 -387
  99. package/esm/useUrlHashState/useUrlHashState.js +11 -14
  100. package/esm/withDocsInfra/withDeploymentConfig.js +26 -21
  101. package/esm/withDocsInfra/withDocsInfra.js +99 -101
  102. package/package.json +7 -4
@@ -1,8 +1,3 @@
1
- import _regeneratorValues from "@babel/runtime/helpers/esm/regeneratorValues";
2
- import _regenerator from "@babel/runtime/helpers/esm/regenerator";
3
- import _extends from "@babel/runtime/helpers/esm/extends";
4
- import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
5
- import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
6
1
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
7
2
  import { basename, dirname, resolve, relative, join } from 'node:path';
8
3
  import * as lockfile from 'proper-lockfile';
@@ -14,9 +9,7 @@ import { markdownToMetadata } from "./metadataToMarkdown.js";
14
9
  * @example kebabToTitleCase('hello-world') -> 'Hello World'
15
10
  */
16
11
  function kebabToTitleCase(str) {
17
- return str.split('-').map(function (word) {
18
- return word.charAt(0).toUpperCase() + word.slice(1);
19
- }).join(' ');
12
+ return str.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
20
13
  }
21
14
 
22
15
  /**
@@ -33,9 +26,8 @@ function isRouteGroup(dirName) {
33
26
  * @example getParentDir('/app/(public)/(content)/react') -> '/app/(public)/(content)'
34
27
  * When recursing, skips route groups: '/app/(public)/(content)' -> '/app'
35
28
  */
36
- function getParentDir(path) {
37
- var skipRouteGroups = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
38
- var parent = dirname(path);
29
+ function getParentDir(path, skipRouteGroups = false) {
30
+ let parent = dirname(path);
39
31
 
40
32
  // If we should skip route groups, keep going up until we find a non-route-group directory
41
33
  if (skipRouteGroups) {
@@ -55,24 +47,15 @@ function getParentDir(path) {
55
47
  */
56
48
  function shouldIncludePath(path, include, exclude) {
57
49
  // Normalize path separators to forward slashes and remove Next.js route groups
58
- var normalizedPath = path.replace(/\\/g, '/').replace(/\/\([^)]+\)/g, '');
50
+ const normalizedPath = path.replace(/\\/g, '/').replace(/\/\([^)]+\)/g, '');
59
51
 
60
52
  // Check exclude patterns first
61
53
  if (exclude && exclude.length > 0) {
62
- var _iterator = _createForOfIteratorHelper(exclude),
63
- _step;
64
- try {
65
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
66
- var pattern = _step.value;
67
- var normalizedPattern = pattern.replace(/\\/g, '/');
68
- if (normalizedPath.startsWith(normalizedPattern)) {
69
- return false;
70
- }
54
+ for (const pattern of exclude) {
55
+ const normalizedPattern = pattern.replace(/\\/g, '/');
56
+ if (normalizedPath.startsWith(normalizedPattern)) {
57
+ return false;
71
58
  }
72
- } catch (err) {
73
- _iterator.e(err);
74
- } finally {
75
- _iterator.f();
76
59
  }
77
60
  }
78
61
 
@@ -82,23 +65,14 @@ function shouldIncludePath(path, include, exclude) {
82
65
  }
83
66
 
84
67
  // Check if path matches any include pattern
85
- var _iterator2 = _createForOfIteratorHelper(include),
86
- _step2;
87
- try {
88
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
89
- var _pattern = _step2.value;
90
- var _normalizedPattern = _pattern.replace(/\\/g, '/');
91
- if (normalizedPath.startsWith(_normalizedPattern)) {
92
- return true;
93
- }
68
+ for (const pattern of include) {
69
+ const normalizedPattern = pattern.replace(/\\/g, '/');
70
+ if (normalizedPath.startsWith(normalizedPattern)) {
71
+ return true;
94
72
  }
95
-
96
- // Path doesn't match any include pattern
97
- } catch (err) {
98
- _iterator2.e(err);
99
- } finally {
100
- _iterator2.f();
101
73
  }
74
+
75
+ // Path doesn't match any include pattern
102
76
  return false;
103
77
  }
104
78
  /**
@@ -126,369 +100,265 @@ function shouldIncludePath(path, include, exclude) {
126
100
  * });
127
101
  * ```
128
102
  */
129
- export function syncPageIndex(_x) {
130
- return _syncPageIndex.apply(this, arguments);
131
- }
132
- function _syncPageIndex() {
133
- _syncPageIndex = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(options) {
134
- var _options$indexTitle;
135
- var pagePath, metadata, metadataList, _options$indexFileNam, indexFileName, _options$lockOptions, lockOptions, baseDir, _options$updateParent, updateParents, include, exclude, _options$onlyUpdateIn, onlyUpdateIndexes, _options$markerDir, markerDir, _options$errorIfOutOf, errorIfOutOfDate, indexWrapperComponent, isBatchUpdate, metadataArray, indexPath, parentDir, relativePath, shouldStop, indexTitle, existingContent, fileExists, relativeIndexPath, hasMarker, existingMarkdown, existingPages, parsed, needsUpdate, _iterator3, _step3, _loop, _ret, _relativeIndexPath, release, mergedPages, currentContent, currentMarkdown, currentPages, _parsed, updatedPagesMap, _iterator4, _step4, page, _iterator5, _step5, metaItem, allPages, _relativeIndexPath2, finalMarkdown, markerRelativePath, markerDirResolved, markerPath, markerDirPath, grandParentDir, relativePathFromGrandparent, indexMetadata, sections, _iterator6, _step6, childPage, _t, _t2, _t3, _t4;
136
- return _regenerator().w(function (_context2) {
137
- while (1) switch (_context2.p = _context2.n) {
138
- case 0:
139
- pagePath = options.pagePath, metadata = options.metadata, metadataList = options.metadataList, _options$indexFileNam = options.indexFileName, indexFileName = _options$indexFileNam === void 0 ? 'page.mdx' : _options$indexFileNam, _options$lockOptions = options.lockOptions, lockOptions = _options$lockOptions === void 0 ? {} : _options$lockOptions, baseDir = options.baseDir, _options$updateParent = options.updateParents, updateParents = _options$updateParent === void 0 ? false : _options$updateParent, include = options.include, exclude = options.exclude, _options$onlyUpdateIn = options.onlyUpdateIndexes, onlyUpdateIndexes = _options$onlyUpdateIn === void 0 ? false : _options$onlyUpdateIn, _options$markerDir = options.markerDir, markerDir = _options$markerDir === void 0 ? false : _options$markerDir, _options$errorIfOutOf = options.errorIfOutOfDate, errorIfOutOfDate = _options$errorIfOutOf === void 0 ? false : _options$errorIfOutOf, indexWrapperComponent = options.indexWrapperComponent; // Validate that either metadata or metadataList is provided
140
- if (!(!metadata && (!metadataList || metadataList.length === 0))) {
141
- _context2.n = 1;
142
- break;
143
- }
144
- throw new Error('Either metadata or metadataList must be provided');
145
- case 1:
146
- // Determine if we're doing a batch update
147
- isBatchUpdate = !!metadataList;
148
- metadataArray = isBatchUpdate ? metadataList : [metadata]; // Resolve the index file path
149
- // For batch updates, pagePath is the index file itself
150
- // For single updates, pagePath is a child page and we need the parent's index
151
- indexPath = isBatchUpdate ? resolve(pagePath) : resolve(getParentDir(dirname(pagePath), true), indexFileName);
152
- parentDir = dirname(indexPath); // Check if this index path should be processed based on include/exclude filters
153
- if (!baseDir) {
154
- _context2.n = 2;
155
- break;
156
- }
157
- relativePath = relative(resolve(baseDir), resolve(parentDir));
158
- if (shouldIncludePath(relativePath, include, exclude)) {
159
- _context2.n = 2;
160
- break;
161
- }
162
- return _context2.a(2);
163
- case 2:
164
- // Check if we've reached the base directory
165
- shouldStop = baseDir && resolve(parentDir) === resolve(baseDir); // Derive index title from directory name if not provided
166
- indexTitle = (_options$indexTitle = options.indexTitle) != null ? _options$indexTitle : kebabToTitleCase(basename(parentDir)); // Step 1: Read the file without acquiring a lock to check if we need to make changes
167
- existingContent = '';
168
- fileExists = true;
169
- _context2.p = 3;
170
- _context2.n = 4;
171
- return readFile(indexPath, 'utf-8');
172
- case 4:
173
- existingContent = _context2.v;
174
- _context2.n = 8;
175
- break;
176
- case 5:
177
- _context2.p = 5;
178
- _t = _context2.v;
179
- if (!(_t.code !== 'ENOENT')) {
180
- _context2.n = 6;
181
- break;
182
- }
183
- throw _t;
184
- case 6:
185
- // File doesn't exist
186
- fileExists = false;
187
-
188
- // If onlyUpdateIndexes is true and file doesn't exist, skip this update
189
- if (!onlyUpdateIndexes) {
190
- _context2.n = 7;
191
- break;
192
- }
193
- return _context2.a(2);
194
- case 7:
195
- if (!errorIfOutOfDate) {
196
- _context2.n = 8;
197
- break;
198
- }
199
- relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
200
- throw new Error("Index file is missing: ".concat(relativeIndexPath, "\n") + "Please run next build locally and commit the updated index files.\n" + "Don't forget to add it to the `app/sitemap/index.ts` to list it publicly.");
201
- case 8:
202
- if (!(fileExists && existingContent)) {
203
- _context2.n = 9;
204
- break;
205
- }
206
- hasMarker = existingContent.includes("[//]: # 'This file is autogenerated");
207
- if (hasMarker) {
208
- _context2.n = 9;
209
- break;
210
- }
211
- return _context2.a(2);
212
- case 9:
213
- // Step 2: Parse existing content and check if our specific page needs updating
214
- existingMarkdown = existingContent.trim() ? existingContent : undefined;
215
- existingPages = [];
216
- if (!existingMarkdown) {
217
- _context2.n = 11;
218
- break;
219
- }
220
- _context2.n = 10;
221
- return markdownToMetadata(existingMarkdown);
222
- case 10:
223
- parsed = _context2.v;
224
- if (parsed) {
225
- existingPages = parsed.pages;
226
- }
227
- case 11:
228
- // Step 3: Check if any of our metadata items need updating
229
- needsUpdate = false;
230
- _iterator3 = _createForOfIteratorHelper(metadataArray);
231
- _context2.p = 12;
232
- _loop = /*#__PURE__*/_regenerator().m(function _loop() {
233
- var metaItem, existingPageIndex, existingPage, existingPageJson, newPageJson;
234
- return _regenerator().w(function (_context) {
235
- while (1) switch (_context.n) {
236
- case 0:
237
- metaItem = _step3.value;
238
- existingPageIndex = existingPages.findIndex(function (p) {
239
- return p.slug === metaItem.slug;
240
- });
241
- if (!(existingPageIndex >= 0)) {
242
- _context.n = 2;
243
- break;
244
- }
245
- existingPage = existingPages[existingPageIndex]; // Compare metadata - if different, we need to update
246
- existingPageJson = JSON.stringify(existingPage);
247
- newPageJson = JSON.stringify(metaItem);
248
- if (!(existingPageJson !== newPageJson)) {
249
- _context.n = 1;
250
- break;
251
- }
252
- needsUpdate = true;
253
- return _context.a(2, 0);
254
- case 1:
255
- _context.n = 3;
256
- break;
257
- case 2:
258
- // Page doesn't exist, we need to add it
259
- needsUpdate = true;
260
- return _context.a(2, 0);
261
- case 3:
262
- return _context.a(2);
263
- }
264
- }, _loop);
265
- });
266
- _iterator3.s();
267
- case 13:
268
- if ((_step3 = _iterator3.n()).done) {
269
- _context2.n = 16;
270
- break;
271
- }
272
- return _context2.d(_regeneratorValues(_loop()), 14);
273
- case 14:
274
- _ret = _context2.v;
275
- if (!(_ret === 0)) {
276
- _context2.n = 15;
277
- break;
278
- }
279
- return _context2.a(3, 16);
280
- case 15:
281
- _context2.n = 13;
282
- break;
283
- case 16:
284
- _context2.n = 18;
285
- break;
286
- case 17:
287
- _context2.p = 17;
288
- _t2 = _context2.v;
289
- _iterator3.e(_t2);
290
- case 18:
291
- _context2.p = 18;
292
- _iterator3.f();
293
- return _context2.f(18);
294
- case 19:
295
- if (needsUpdate) {
296
- _context2.n = 20;
297
- break;
298
- }
299
- return _context2.a(2);
300
- case 20:
301
- if (!errorIfOutOfDate) {
302
- _context2.n = 21;
303
- break;
304
- }
305
- _relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
306
- throw new Error("Index file is out of date: ".concat(_relativeIndexPath, "\n") + "Please run the validation command (or next build) locally and commit the updated index files.");
307
- case 21:
308
- if (fileExists) {
309
- _context2.n = 22;
310
- break;
311
- }
312
- _context2.n = 22;
313
- return writeFile(indexPath, '', 'utf-8');
314
- case 22:
315
- mergedPages = []; // Store merged pages for parent update
316
- _context2.p = 23;
317
- _context2.n = 24;
318
- return lockfile.lock(indexPath, _extends({
319
- retries: {
320
- retries: 300,
321
- minTimeout: 1,
322
- // Start with 1ms for fast retries
323
- maxTimeout: 150,
324
- randomize: true
325
- },
326
- stale: 30000
327
- }, lockOptions));
328
- case 24:
329
- release = _context2.v;
330
- // Step 6: Re-read and re-merge to catch any concurrent updates from other processes
331
- // This ensures we don't lose updates from other pages being processed in parallel
332
- currentContent = '';
333
- _context2.p = 25;
334
- _context2.n = 26;
335
- return readFile(indexPath, 'utf-8');
336
- case 26:
337
- currentContent = _context2.v;
338
- _context2.n = 28;
339
- break;
340
- case 27:
341
- _context2.p = 27;
342
- _t3 = _context2.v;
343
- if (!(_t3.code !== 'ENOENT')) {
344
- _context2.n = 28;
345
- break;
346
- }
347
- throw _t3;
348
- case 28:
349
- currentMarkdown = currentContent.trim() ? currentContent : undefined;
350
- currentPages = [];
351
- if (!currentMarkdown) {
352
- _context2.n = 30;
353
- break;
354
- }
355
- _context2.n = 29;
356
- return markdownToMetadata(currentMarkdown);
357
- case 29:
358
- _parsed = _context2.v;
359
- if (_parsed) {
360
- currentPages = _parsed.pages;
361
- }
362
- case 30:
363
- // For batch updates, merge the metadata items with existing pages
364
- // Build a map keyed by path (not slug) to match mergeMetadataMarkdown's logic
365
- updatedPagesMap = new Map(); // First, add all current pages
366
- _iterator4 = _createForOfIteratorHelper(currentPages);
367
- try {
368
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
369
- page = _step4.value;
370
- updatedPagesMap.set(page.path, page);
371
- }
372
-
373
- // Then update/add the new metadata items
374
- } catch (err) {
375
- _iterator4.e(err);
376
- } finally {
377
- _iterator4.f();
378
- }
379
- _iterator5 = _createForOfIteratorHelper(metadataArray);
380
- try {
381
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
382
- metaItem = _step5.value;
383
- updatedPagesMap.set(metaItem.path, metaItem);
384
- }
385
-
386
- // Convert back to array - this is the COMPLETE list of pages that should exist
387
- } catch (err) {
388
- _iterator5.e(err);
389
- } finally {
390
- _iterator5.f();
391
- }
392
- allPages = Array.from(updatedPagesMap.values()); // Store for parent update
393
- mergedPages = allPages;
394
-
395
- // Re-merge with the latest content, passing the COMPLETE list of pages
396
- // mergeMetadataMarkdown will preserve the order from currentMarkdown
397
- // Only include path in the comment when baseDir is set (otherwise it's an absolute path)
398
- _relativeIndexPath2 = baseDir ? relative(resolve(baseDir), indexPath) : undefined;
399
- _context2.n = 31;
400
- return mergeMetadataMarkdown(currentMarkdown, {
401
- title: indexTitle,
402
- pages: allPages
403
- }, {
404
- indexWrapperComponent: indexWrapperComponent,
405
- path: _relativeIndexPath2
406
- });
407
- case 31:
408
- finalMarkdown = _context2.v;
409
- if (!(!finalMarkdown || !finalMarkdown.trim())) {
410
- _context2.n = 32;
411
- break;
412
- }
413
- throw new Error("Cannot write empty content to ".concat(indexPath));
414
- case 32:
415
- if (!(currentContent !== finalMarkdown)) {
416
- _context2.n = 35;
417
- break;
418
- }
419
- _context2.n = 33;
420
- return writeFile(indexPath, finalMarkdown, 'utf-8');
421
- case 33:
422
- if (!markerDir) {
423
- _context2.n = 35;
424
- break;
425
- }
426
- // Compute relative path for marker (always compute, not used for comments)
427
- markerRelativePath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath; // Resolve markerDir relative to baseDir (if baseDir is provided)
428
- markerDirResolved = baseDir ? resolve(baseDir, markerDir) : markerDir;
429
- markerPath = join(markerDirResolved, markerRelativePath);
430
- markerDirPath = dirname(markerPath); // Ensure the marker directory exists
431
- _context2.n = 34;
432
- return mkdir(markerDirPath, {
433
- recursive: true
434
- });
435
- case 34:
436
- _context2.n = 35;
437
- return writeFile(markerPath, '', 'utf-8');
438
- case 35:
439
- _context2.p = 35;
440
- if (!release) {
441
- _context2.n = 36;
442
- break;
443
- }
444
- _context2.n = 36;
445
- return release();
446
- case 36:
447
- return _context2.f(35);
448
- case 37:
449
- if (!(updateParents && !shouldStop)) {
450
- _context2.n = 47;
451
- break;
452
- }
453
- // Get the grandparent directory, skipping over Next.js route groups
454
- grandParentDir = getParentDir(parentDir, true); // Only continue if we're not at the filesystem root
455
- if (!(grandParentDir !== parentDir)) {
456
- _context2.n = 47;
457
- break;
458
- }
459
- // CRITICAL: Use the merged pages from Step 6, not a re-read of the file
460
- // Re-reading could get a stale version if other processes are still writing
461
- // mergedPages already contains ALL pages after the merge in Step 6
462
- // Calculate the relative path from grandparent to this index, preserving route groups
463
- relativePathFromGrandparent = relative(grandParentDir, parentDir); // Extract metadata for the current index to add to its parent
464
- indexMetadata = {
465
- slug: basename(parentDir),
466
- path: "./".concat(relativePathFromGrandparent, "/").concat(indexFileName),
467
- title: indexTitle,
468
- description: 'No description available'
469
- }; // Convert child pages to sections format (no subsections, just page names)
470
- // Use mergedPages which contains the complete merged state
471
- // Skip single-link entries (external links) as they don't have detail sections
472
- if (!(mergedPages.length > 0)) {
473
- _context2.n = 46;
474
- break;
475
- }
476
- sections = {};
477
- _iterator6 = _createForOfIteratorHelper(mergedPages);
478
- _context2.p = 38;
479
- _iterator6.s();
480
- case 39:
481
- if ((_step6 = _iterator6.n()).done) {
482
- _context2.n = 42;
483
- break;
484
- }
485
- childPage = _step6.value;
486
- if (!childPage.skipDetailSection) {
487
- _context2.n = 40;
488
- break;
103
+ export async function syncPageIndex(options) {
104
+ const {
105
+ pagePath,
106
+ metadata,
107
+ metadataList,
108
+ indexFileName = 'page.mdx',
109
+ lockOptions = {},
110
+ baseDir,
111
+ updateParents = false,
112
+ include,
113
+ exclude,
114
+ onlyUpdateIndexes = false,
115
+ markerDir = false,
116
+ errorIfOutOfDate = false,
117
+ indexWrapperComponent
118
+ } = options;
119
+
120
+ // Validate that either metadata or metadataList is provided
121
+ if (!metadata && (!metadataList || metadataList.length === 0)) {
122
+ throw new Error('Either metadata or metadataList must be provided');
123
+ }
124
+
125
+ // Determine if we're doing a batch update
126
+ const isBatchUpdate = !!metadataList;
127
+ const metadataArray = isBatchUpdate ? metadataList : [metadata];
128
+
129
+ // Resolve the index file path
130
+ // For batch updates, pagePath is the index file itself
131
+ // For single updates, pagePath is a child page and we need the parent's index
132
+ const indexPath = isBatchUpdate ? resolve(pagePath) : resolve(getParentDir(dirname(pagePath), true), indexFileName);
133
+ const parentDir = dirname(indexPath);
134
+
135
+ // Check if this index path should be processed based on include/exclude filters
136
+ if (baseDir) {
137
+ const relativePath = relative(resolve(baseDir), resolve(parentDir));
138
+ if (!shouldIncludePath(relativePath, include, exclude)) {
139
+ // This index is outside the configured paths - skip it
140
+ return;
141
+ }
142
+ }
143
+
144
+ // Check if we've reached the base directory
145
+ const shouldStop = baseDir && resolve(parentDir) === resolve(baseDir);
146
+
147
+ // Derive index title from directory name if not provided
148
+ const indexTitle = options.indexTitle ?? kebabToTitleCase(basename(parentDir));
149
+
150
+ // Step 1: Read the file without acquiring a lock to check if we need to make changes
151
+ let existingContent = '';
152
+ let fileExists = true;
153
+ try {
154
+ existingContent = await readFile(indexPath, 'utf-8');
155
+ } catch (error) {
156
+ if (error.code !== 'ENOENT') {
157
+ throw error;
158
+ }
159
+ // File doesn't exist
160
+ fileExists = false;
161
+
162
+ // If onlyUpdateIndexes is true and file doesn't exist, skip this update
163
+ if (onlyUpdateIndexes) {
164
+ return;
165
+ }
166
+
167
+ // If errorIfOutOfDate is true, throw an error for missing index
168
+ if (errorIfOutOfDate) {
169
+ const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
170
+ throw new Error(`Index file is missing: ${relativeIndexPath}\n` + `Please run next build locally and commit the updated index files.\n` + `Don't forget to add it to the \`app/sitemap/index.ts\` to list it publicly.`);
171
+ }
172
+ }
173
+
174
+ // Step 1.5: Verify the file has the autogeneration marker if it exists
175
+ if (fileExists && existingContent) {
176
+ const hasMarker = existingContent.includes("[//]: # 'This file is autogenerated");
177
+ if (!hasMarker) {
178
+ // File exists but doesn't have the autogeneration marker - skip updating it
179
+ return;
180
+ }
181
+ }
182
+
183
+ // Step 2: Parse existing content and check if our specific page needs updating
184
+ const existingMarkdown = existingContent.trim() ? existingContent : undefined;
185
+ let existingPages = [];
186
+ if (existingMarkdown) {
187
+ const parsed = await markdownToMetadata(existingMarkdown);
188
+ if (parsed) {
189
+ existingPages = parsed.pages;
190
+ }
191
+ }
192
+
193
+ // Step 3: Check if any of our metadata items need updating
194
+ let needsUpdate = false;
195
+ for (const metaItem of metadataArray) {
196
+ const existingPageIndex = existingPages.findIndex(p => p.slug === metaItem.slug);
197
+ if (existingPageIndex >= 0) {
198
+ const existingPage = existingPages[existingPageIndex];
199
+ // Compare metadata - if different, we need to update
200
+ const existingPageJson = JSON.stringify(existingPage);
201
+ const newPageJson = JSON.stringify(metaItem);
202
+ if (existingPageJson !== newPageJson) {
203
+ needsUpdate = true;
204
+ break;
205
+ }
206
+ } else {
207
+ // Page doesn't exist, we need to add it
208
+ needsUpdate = true;
209
+ break;
210
+ }
211
+ }
212
+ if (!needsUpdate) {
213
+ // All pages are already up-to-date, no need to acquire lock or write
214
+ return;
215
+ }
216
+
217
+ // If errorIfOutOfDate is true, throw an error instead of updating
218
+ if (errorIfOutOfDate) {
219
+ const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
220
+ 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.`);
221
+ }
222
+
223
+ // Step 4: Ensure the file exists before locking (proper-lockfile requires an existing file)
224
+ if (!fileExists) {
225
+ await writeFile(indexPath, '', 'utf-8');
226
+ }
227
+ let release;
228
+ let mergedPages = []; // Store merged pages for parent update
229
+
230
+ try {
231
+ // Step 5: Acquire lock on the index file
232
+ release = await lockfile.lock(indexPath, {
233
+ retries: {
234
+ retries: 300,
235
+ minTimeout: 1,
236
+ // Start with 1ms for fast retries
237
+ maxTimeout: 150,
238
+ randomize: true
239
+ },
240
+ stale: 30000,
241
+ ...lockOptions
242
+ });
243
+
244
+ // Step 6: Re-read and re-merge to catch any concurrent updates from other processes
245
+ // This ensures we don't lose updates from other pages being processed in parallel
246
+ let currentContent = '';
247
+ try {
248
+ currentContent = await readFile(indexPath, 'utf-8');
249
+ } catch (error) {
250
+ if (error.code !== 'ENOENT') {
251
+ throw error;
252
+ }
253
+ // File was deleted while waiting - that's okay, we'll create it
254
+ }
255
+ const currentMarkdown = currentContent.trim() ? currentContent : undefined;
256
+ let currentPages = [];
257
+ if (currentMarkdown) {
258
+ const parsed = await markdownToMetadata(currentMarkdown);
259
+ if (parsed) {
260
+ currentPages = parsed.pages;
261
+ }
262
+ }
263
+
264
+ // For batch updates, merge the metadata items with existing pages
265
+ // Build a map keyed by path (not slug) to match mergeMetadataMarkdown's logic
266
+ const updatedPagesMap = new Map();
267
+
268
+ // First, add all current pages
269
+ for (const page of currentPages) {
270
+ updatedPagesMap.set(page.path, page);
271
+ }
272
+
273
+ // Then update/add the new metadata items
274
+ for (const metaItem of metadataArray) {
275
+ updatedPagesMap.set(metaItem.path, metaItem);
276
+ }
277
+
278
+ // Convert back to array - this is the COMPLETE list of pages that should exist
279
+ const allPages = Array.from(updatedPagesMap.values());
280
+
281
+ // Store for parent update
282
+ mergedPages = allPages;
283
+
284
+ // Re-merge with the latest content, passing the COMPLETE list of pages
285
+ // mergeMetadataMarkdown will preserve the order from currentMarkdown
286
+ // Only include path in the comment when baseDir is set (otherwise it's an absolute path)
287
+ const relativeIndexPath = baseDir ? relative(resolve(baseDir), indexPath) : undefined;
288
+ const finalMarkdown = await mergeMetadataMarkdown(currentMarkdown, {
289
+ title: indexTitle,
290
+ pages: allPages
291
+ }, {
292
+ indexWrapperComponent,
293
+ path: relativeIndexPath
294
+ });
295
+
296
+ // Defensive check
297
+ if (!finalMarkdown || !finalMarkdown.trim()) {
298
+ throw new Error(`Cannot write empty content to ${indexPath}`);
299
+ }
300
+
301
+ // Step 7: Write only if the final content differs from what's currently on disk
302
+ if (currentContent !== finalMarkdown) {
303
+ await writeFile(indexPath, finalMarkdown, 'utf-8');
304
+
305
+ // Create a marker file unless explicitly disabled
306
+ if (markerDir) {
307
+ // Compute relative path for marker (always compute, not used for comments)
308
+ const markerRelativePath = baseDir ? relative(resolve(baseDir), indexPath) : indexPath;
309
+ // Resolve markerDir relative to baseDir (if baseDir is provided)
310
+ const markerDirResolved = baseDir ? resolve(baseDir, markerDir) : markerDir;
311
+ const markerPath = join(markerDirResolved, markerRelativePath);
312
+ const markerDirPath = dirname(markerPath);
313
+
314
+ // Ensure the marker directory exists
315
+ await mkdir(markerDirPath, {
316
+ recursive: true
317
+ });
318
+
319
+ // Create an empty marker file
320
+ await writeFile(markerPath, '', 'utf-8');
321
+ }
322
+ }
323
+ } finally {
324
+ // Always release the lock
325
+ if (release) {
326
+ await release();
327
+ }
328
+ }
329
+
330
+ // After releasing the lock, update the parent index if needed
331
+ if (updateParents && !shouldStop) {
332
+ // Get the grandparent directory, skipping over Next.js route groups
333
+ const grandParentDir = getParentDir(parentDir, true);
334
+
335
+ // Only continue if we're not at the filesystem root
336
+ if (grandParentDir !== parentDir) {
337
+ // CRITICAL: Use the merged pages from Step 6, not a re-read of the file
338
+ // Re-reading could get a stale version if other processes are still writing
339
+ // mergedPages already contains ALL pages after the merge in Step 6
340
+
341
+ // Calculate the relative path from grandparent to this index, preserving route groups
342
+ const relativePathFromGrandparent = relative(grandParentDir, parentDir);
343
+
344
+ // Extract metadata for the current index to add to its parent
345
+ const indexMetadata = {
346
+ slug: basename(parentDir),
347
+ path: `./${relativePathFromGrandparent}/${indexFileName}`,
348
+ title: indexTitle,
349
+ description: 'No description available'
350
+ };
351
+
352
+ // Convert child pages to sections format (no subsections, just page names)
353
+ // Use mergedPages which contains the complete merged state
354
+ // Skip single-link entries (external links) as they don't have detail sections
355
+ if (mergedPages.length > 0) {
356
+ const sections = {};
357
+ for (const childPage of mergedPages) {
358
+ // Skip entries that don't have detail sections (external links, etc.)
359
+ if (childPage.skipDetailSection) {
360
+ continue;
489
361
  }
490
- return _context2.a(3, 41);
491
- case 40:
492
362
  sections[childPage.slug] = {
493
363
  title: childPage.title || childPage.slug,
494
364
  titleMarkdown: childPage.title ? [{
@@ -500,41 +370,24 @@ function _syncPageIndex() {
500
370
  }],
501
371
  children: {} // Don't include any subsections in parent index
502
372
  };
503
- case 41:
504
- _context2.n = 39;
505
- break;
506
- case 42:
507
- _context2.n = 44;
508
- break;
509
- case 43:
510
- _context2.p = 43;
511
- _t4 = _context2.v;
512
- _iterator6.e(_t4);
513
- case 44:
514
- _context2.p = 44;
515
- _iterator6.f();
516
- return _context2.f(44);
517
- case 45:
518
- indexMetadata.sections = sections;
519
- case 46:
520
- _context2.n = 47;
521
- return syncPageIndex({
522
- pagePath: indexPath,
523
- metadata: indexMetadata,
524
- indexFileName: indexFileName,
525
- lockOptions: lockOptions,
526
- baseDir: baseDir,
527
- updateParents: true,
528
- include: include,
529
- exclude: exclude,
530
- onlyUpdateIndexes: onlyUpdateIndexes,
531
- markerDir: markerDir,
532
- errorIfOutOfDate: errorIfOutOfDate
533
- });
534
- case 47:
535
- return _context2.a(2);
373
+ }
374
+ indexMetadata.sections = sections;
536
375
  }
537
- }, _callee, null, [[38, 43, 44, 45], [25, 27], [23,, 35, 37], [12, 17, 18, 19], [3, 5]]);
538
- }));
539
- return _syncPageIndex.apply(this, arguments);
376
+
377
+ // Recursively update the parent index (will create it if it doesn't exist)
378
+ await syncPageIndex({
379
+ pagePath: indexPath,
380
+ metadata: indexMetadata,
381
+ indexFileName,
382
+ lockOptions,
383
+ baseDir,
384
+ updateParents: true,
385
+ include,
386
+ exclude,
387
+ onlyUpdateIndexes,
388
+ markerDir,
389
+ errorIfOutOfDate
390
+ });
391
+ }
392
+ }
540
393
  }