@mui/internal-docs-infra 0.12.1-canary.27 → 0.12.1-canary.29

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.27",
3
+ "version": "0.12.1-canary.29",
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": "909caa6fc5ecd4b9324f7b53e4845f52764b1867"
807
+ "gitSha": "2266e66cb9b09ec1f0439b053aee05549f1f5f25"
808
808
  }
@@ -87,6 +87,86 @@ export function isJavaScriptModule(path) {
87
87
  // If no extension, assume it's a JS/TS module (will be resolved to one)
88
88
  return true;
89
89
  }
90
+ const directoryIndexes = new WeakMap();
91
+
92
+ /** Indexes each directory snapshot by basename and extension once. */
93
+ function createDirectoryIndex(entries) {
94
+ const cachedIndex = directoryIndexes.get(entries);
95
+ if (cachedIndex) {
96
+ return cachedIndex;
97
+ }
98
+ const directories = new Set();
99
+ const files = new Map();
100
+ for (const entry of entries) {
101
+ if (entry.isDirectory) {
102
+ directories.add(entry.name);
103
+ }
104
+ if (!entry.isFile) {
105
+ continue;
106
+ }
107
+ const extension = entry.name.endsWith('.d.ts') ? '.d.ts' : getFileNameFromUrl(entry.name).extension;
108
+ const baseName = extension ? entry.name.slice(0, -extension.length) : entry.name;
109
+ const filesByExtension = files.get(baseName) ?? new Map();
110
+ filesByExtension.set(extension, entry);
111
+ files.set(baseName, filesByExtension);
112
+ }
113
+ const index = {
114
+ directories,
115
+ files
116
+ };
117
+ directoryIndexes.set(entries, index);
118
+ return index;
119
+ }
120
+
121
+ /** Selects the first file matching the configured extension priority. */
122
+ function selectFile(index, baseName, extensions) {
123
+ const filesByExtension = index.files.get(baseName);
124
+ if (!filesByExtension) {
125
+ return undefined;
126
+ }
127
+ for (const extension of extensions) {
128
+ const entry = filesByExtension.get(extension);
129
+ if (entry) {
130
+ return entry;
131
+ }
132
+ }
133
+ return undefined;
134
+ }
135
+
136
+ /** Resolves value and type files from one directory index. */
137
+ function selectTypeAwareFiles(index, directory, baseName) {
138
+ const importEntry = selectFile(index, baseName, VALUE_IMPORT_EXTENSIONS);
139
+ const typeImportEntry = selectFile(index, baseName, TYPE_IMPORT_EXTENSIONS);
140
+ const selectedEntry = importEntry ?? typeImportEntry;
141
+ if (!selectedEntry) {
142
+ return undefined;
143
+ }
144
+ const importPath = portablePathToFileUrl(joinPath(directory, selectedEntry.name));
145
+ if (importEntry && typeImportEntry && typeImportEntry !== importEntry) {
146
+ return {
147
+ import: importPath,
148
+ typeImport: portablePathToFileUrl(joinPath(directory, typeImportEntry.name))
149
+ };
150
+ }
151
+ return {
152
+ import: importPath
153
+ };
154
+ }
155
+
156
+ /** Reuses both pending and completed directory reads for one import graph. */
157
+ function cacheDirectoryReader(readDirectory) {
158
+ const cache = new Map();
159
+ return path => {
160
+ const cachedEntries = cache.get(path);
161
+ if (cachedEntries) {
162
+ return cachedEntries;
163
+ }
164
+ const entries = readDirectory(path);
165
+ cache.set(path, entries);
166
+ return entries;
167
+ };
168
+ }
169
+
90
170
  /**
91
171
  * Resolves a module path by reading directory contents to find matching files.
92
172
  * This is more efficient than checking each file individually with stat calls.
@@ -134,172 +214,18 @@ async function resolveWithTypeAwareness(modulePath, readDirectory, _options = {}
134
214
 
135
215
  // Single filesystem read to get directory contents
136
216
  const dirContents = await readDirectory(portablePathToFileUrl(parentDir));
137
-
138
- // Build a map of available files by basename
139
- const filesByBaseName = new Map();
140
- for (const entry of dirContents) {
141
- if (entry.isFile) {
142
- const fileName = entry.name;
143
- let fileBaseName;
144
- let actualExtension;
145
-
146
- // Handle .d.ts files specially since getFileNameFromUrl returns .ts for types.d.ts
147
- if (fileName.endsWith('.d.ts')) {
148
- actualExtension = '.d.ts';
149
- fileBaseName = fileName.substring(0, fileName.length - 5); // Remove .d.ts
150
- } else {
151
- const {
152
- extension: fileExt
153
- } = getFileNameFromUrl(fileName);
154
- actualExtension = fileExt;
155
- fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
156
- }
157
- if (!filesByBaseName.has(fileBaseName)) {
158
- filesByBaseName.set(fileBaseName, []);
159
- }
160
- // Store the entry with its actual extension for later matching
161
- filesByBaseName.get(fileBaseName).push({
162
- ...entry,
163
- actualExtension
164
- });
165
- }
217
+ const directoryIndex = createDirectoryIndex(dirContents);
218
+ const directResult = selectTypeAwareFiles(directoryIndex, parentDir, moduleName);
219
+ if (directResult) {
220
+ return directResult;
166
221
  }
167
-
168
- // Check for the module in both priority orders
169
- const matchingFiles = filesByBaseName.get(moduleName);
170
- if (matchingFiles) {
171
- const entryMap = matchingFiles;
172
-
173
- // Find best match for value imports (VALUE_IMPORT_EXTENSIONS priority)
174
- let importPath = null;
175
- for (const ext of VALUE_IMPORT_EXTENSIONS) {
176
- for (const entry of entryMap) {
177
- if (entry.actualExtension === ext) {
178
- importPath = portablePathToFileUrl(joinPath(parentDir, entry.name));
179
- break;
180
- }
181
- }
182
- if (importPath) {
183
- break;
184
- }
185
- }
186
-
187
- // Find best match for type imports (TYPE_IMPORT_EXTENSIONS priority)
188
- let typeImportPath = null;
189
- for (const ext of TYPE_IMPORT_EXTENSIONS) {
190
- for (const entry of entryMap) {
191
- if (entry.actualExtension === ext) {
192
- typeImportPath = portablePathToFileUrl(joinPath(parentDir, entry.name));
193
- break;
194
- }
195
- }
196
- if (typeImportPath) {
197
- break;
198
- }
199
- }
200
- if (importPath && typeImportPath && importPath !== typeImportPath) {
201
- return {
202
- import: importPath,
203
- typeImport: typeImportPath
204
- };
205
- }
206
- if (importPath) {
207
- return {
208
- import: importPath
209
- };
210
- }
211
- if (typeImportPath) {
212
- return {
213
- import: typeImportPath
214
- };
215
- }
216
- }
217
-
218
- // Try index files with the same single-pass approach
219
- const directoryMatches = dirContents.filter(entry => entry.isDirectory && entry.name === moduleName);
220
- if (directoryMatches.length > 0) {
221
- const moduleDir = joinPath(parentDir, directoryMatches[0].name);
222
+ if (directoryIndex.directories.has(moduleName)) {
223
+ const moduleDir = joinPath(parentDir, moduleName);
222
224
  try {
223
225
  const moduleDirContents = await readDirectory(portablePathToFileUrl(moduleDir));
224
-
225
- // Build a map of available index files by basename
226
- const indexFilesByBaseName = new Map();
227
- for (const moduleFile of moduleDirContents) {
228
- if (moduleFile.isFile) {
229
- const fileName = moduleFile.name;
230
- let fileBaseName;
231
- let actualExtension;
232
-
233
- // Handle .d.ts files specially since getFileNameFromUrl returns .ts for index.d.ts
234
- if (fileName.endsWith('.d.ts')) {
235
- actualExtension = '.d.ts';
236
- fileBaseName = fileName.substring(0, fileName.length - 5); // Remove .d.ts
237
- } else {
238
- const {
239
- extension: fileExt
240
- } = getFileNameFromUrl(fileName);
241
- actualExtension = fileExt;
242
- fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
243
- }
244
- if (!indexFilesByBaseName.has(fileBaseName)) {
245
- indexFilesByBaseName.set(fileBaseName, []);
246
- }
247
- // Store the entry with its actual extension for later matching
248
- indexFilesByBaseName.get(fileBaseName).push({
249
- ...moduleFile,
250
- actualExtension
251
- });
252
- }
253
- }
254
-
255
- // Check for index files in both priority orders
256
- const indexFiles = indexFilesByBaseName.get('index');
257
- if (indexFiles) {
258
- const indexEntryMap = indexFiles;
259
-
260
- // Find best match for value imports
261
- let importPath = null;
262
- for (const ext of VALUE_IMPORT_EXTENSIONS) {
263
- for (const entry of indexEntryMap) {
264
- if (entry.actualExtension === ext) {
265
- importPath = portablePathToFileUrl(joinPath(moduleDir, entry.name));
266
- break;
267
- }
268
- }
269
- if (importPath) {
270
- break;
271
- }
272
- }
273
-
274
- // Find best match for type imports
275
- let typeImportPath = null;
276
- for (const ext of TYPE_IMPORT_EXTENSIONS) {
277
- for (const entry of indexEntryMap) {
278
- if (entry.actualExtension === ext) {
279
- typeImportPath = portablePathToFileUrl(joinPath(moduleDir, entry.name));
280
- break;
281
- }
282
- }
283
- if (typeImportPath) {
284
- break;
285
- }
286
- }
287
- if (importPath && typeImportPath && importPath !== typeImportPath) {
288
- return {
289
- import: importPath,
290
- typeImport: typeImportPath
291
- };
292
- }
293
- if (importPath) {
294
- return {
295
- import: importPath
296
- };
297
- }
298
- if (typeImportPath) {
299
- return {
300
- import: typeImportPath
301
- };
302
- }
226
+ const indexResult = selectTypeAwareFiles(createDirectoryIndex(moduleDirContents), moduleDir, 'index');
227
+ if (indexResult) {
228
+ return indexResult;
303
229
  }
304
230
  } catch {
305
231
  // Could not read module directory, continue
@@ -315,102 +241,18 @@ async function resolveSinglePath(modulePath, parentDir, moduleName, readDirector
315
241
  try {
316
242
  // Read the parent directory contents
317
243
  const dirContents = await readDirectory(portablePathToFileUrl(parentDir));
318
-
319
- // Look for direct file matches in extension priority order
320
- // Create a map of baseName -> files with that basename for efficient lookup
321
- const filesByBaseName = new Map();
322
- for (const entry of dirContents) {
323
- if (entry.isFile) {
324
- const fileName = entry.name;
325
- let fileBaseName;
326
- let actualExtension;
327
-
328
- // Handle .d.ts files specially since getFileNameFromUrl returns .ts for types.d.ts
329
- if (fileName.endsWith('.d.ts')) {
330
- actualExtension = '.d.ts';
331
- fileBaseName = fileName.substring(0, fileName.length - 5); // Remove .d.ts
332
- } else {
333
- const {
334
- extension: fileExt
335
- } = getFileNameFromUrl(fileName);
336
- actualExtension = fileExt;
337
- fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
338
- }
339
- if (!filesByBaseName.has(fileBaseName)) {
340
- filesByBaseName.set(fileBaseName, []);
341
- }
342
- // Store the entry with its actual extension for later matching
343
- filesByBaseName.get(fileBaseName).push({
344
- ...entry,
345
- // Add a custom property to track the actual extension
346
- actualExtension
347
- });
348
- }
349
- }
350
-
351
- // Check for the module in extension priority order
352
- const matchingFiles = filesByBaseName.get(moduleName);
353
- if (matchingFiles) {
354
- for (const ext of extensions) {
355
- for (const entry of matchingFiles) {
356
- const entryWithExt = entry;
357
- if (entryWithExt.actualExtension === ext) {
358
- const resolvedPath = joinPath(parentDir, entry.name);
359
- return portablePathToFileUrl(resolvedPath);
360
- }
361
- }
362
- }
244
+ const directoryIndex = createDirectoryIndex(dirContents);
245
+ const directEntry = selectFile(directoryIndex, moduleName, extensions);
246
+ if (directEntry) {
247
+ return portablePathToFileUrl(joinPath(parentDir, directEntry.name));
363
248
  }
364
-
365
- // Look for directory with index files
366
- const directoryMatches = dirContents.filter(entry => entry.isDirectory && entry.name === moduleName);
367
- if (directoryMatches.length > 0) {
368
- const moduleDir = joinPath(parentDir, directoryMatches[0].name);
249
+ if (directoryIndex.directories.has(moduleName)) {
250
+ const moduleDir = joinPath(parentDir, moduleName);
369
251
  try {
370
252
  const moduleDirContents = await readDirectory(portablePathToFileUrl(moduleDir));
371
-
372
- // Look for index files in extension priority order
373
- // Create a map of baseName -> files for efficient lookup
374
- const indexFilesByBaseName = new Map();
375
- for (const moduleFile of moduleDirContents) {
376
- if (moduleFile.isFile) {
377
- const fileName = moduleFile.name;
378
- let fileBaseName;
379
- let actualExtension;
380
-
381
- // Handle .d.ts files specially since getFileNameFromUrl returns .ts for index.d.ts
382
- if (fileName.endsWith('.d.ts')) {
383
- actualExtension = '.d.ts';
384
- fileBaseName = fileName.substring(0, fileName.length - 5); // Remove .d.ts
385
- } else {
386
- const {
387
- extension: fileExt
388
- } = getFileNameFromUrl(fileName);
389
- actualExtension = fileExt;
390
- fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
391
- }
392
- if (!indexFilesByBaseName.has(fileBaseName)) {
393
- indexFilesByBaseName.set(fileBaseName, []);
394
- }
395
- // Store the entry with its actual extension for later matching
396
- indexFilesByBaseName.get(fileBaseName).push({
397
- ...moduleFile,
398
- actualExtension
399
- });
400
- }
401
- }
402
-
403
- // Check for index files in extension priority order
404
- const indexFiles = indexFilesByBaseName.get('index');
405
- if (indexFiles) {
406
- for (const ext of extensions) {
407
- for (const entry of indexFiles) {
408
- const entryWithExt = entry;
409
- if (entryWithExt.actualExtension === ext) {
410
- return portablePathToFileUrl(joinPath(moduleDir, entry.name));
411
- }
412
- }
413
- }
253
+ const indexEntry = selectFile(createDirectoryIndex(moduleDirContents), 'index', extensions);
254
+ if (indexEntry) {
255
+ return portablePathToFileUrl(joinPath(moduleDir, indexEntry.name));
414
256
  }
415
257
  } catch {
416
258
  // Could not read module directory, continue
@@ -460,124 +302,58 @@ export async function resolveModulePaths(modulePaths, readDirectory, options = {
460
302
  const dirContents = await readDirectory(portablePathToFileUrl(parentDir));
461
303
  const unresolved = [];
462
304
  const resolved = [];
463
-
464
- // Look for direct file matches in extension priority order
465
- // Create a map of baseName -> files for efficient lookup
466
- const filesByBaseName = new Map();
467
- for (const entry of dirContents) {
468
- if (entry.isFile) {
469
- const fileName = entry.name;
470
- const {
471
- extension: fileExt
472
- } = getFileNameFromUrl(fileName);
473
- const fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
474
- if (!filesByBaseName.has(fileBaseName)) {
475
- filesByBaseName.set(fileBaseName, []);
476
- }
477
- filesByBaseName.get(fileBaseName).push(entry);
478
- }
479
- }
480
-
481
- // Check each module path against the file map
305
+ const directoryIndex = createDirectoryIndex(dirContents);
482
306
  for (const {
483
307
  fullPath,
484
308
  moduleName
485
309
  } of pathGroup) {
486
- let foundMatch = false;
487
- const matchingFiles = filesByBaseName.get(moduleName);
488
- if (matchingFiles) {
489
- for (const ext of extensions) {
490
- for (const entry of matchingFiles) {
491
- const {
492
- extension: entryExt
493
- } = getFileNameFromUrl(entry.name);
494
- if (entryExt === ext) {
495
- resolved.push({
496
- fullPath,
497
- resolvedPath: portablePathToFileUrl(joinPath(parentDir, entry.name))
498
- });
499
- foundMatch = true;
500
- break;
501
- }
502
- }
503
- if (foundMatch) {
504
- break;
505
- }
506
- }
507
- }
508
- if (!foundMatch) {
310
+ const entry = selectFile(directoryIndex, moduleName, extensions);
311
+ if (entry) {
312
+ resolved.push({
313
+ fullPath,
314
+ resolvedPath: portablePathToFileUrl(joinPath(parentDir, entry.name))
315
+ });
316
+ } else {
509
317
  unresolved.push({
510
318
  fullPath,
511
319
  moduleName
512
320
  });
513
321
  }
514
322
  }
515
-
516
- // For unresolved paths, check if they are directories with index files
517
- if (unresolved.length > 0) {
518
- const directories = new Set(dirContents.filter(entry => entry.isDirectory).map(entry => entry.name));
519
- const indexResults = await Promise.all(unresolved.map(async ({
520
- fullPath,
521
- moduleName
522
- }) => {
523
- if (directories.has(moduleName)) {
524
- const moduleDir = joinPath(parentDir, moduleName);
525
- try {
526
- const moduleDirContents = await readDirectory(portablePathToFileUrl(moduleDir));
527
-
528
- // Look for index files in extension priority order
529
- // Create a map of baseName -> files for efficient lookup
530
- const indexFilesByBaseName = new Map();
531
- for (const moduleFile of moduleDirContents) {
532
- if (moduleFile.isFile) {
533
- const fileName = moduleFile.name;
534
- const {
535
- extension: fileExt
536
- } = getFileNameFromUrl(fileName);
537
- const fileBaseName = fileName.substring(0, fileName.length - fileExt.length);
538
- if (!indexFilesByBaseName.has(fileBaseName)) {
539
- indexFilesByBaseName.set(fileBaseName, []);
540
- }
541
- indexFilesByBaseName.get(fileBaseName).push(moduleFile);
542
- }
543
- }
544
-
545
- // Check for index files in extension priority order
546
- const indexFiles = indexFilesByBaseName.get('index');
547
- if (indexFiles) {
548
- for (const ext of extensions) {
549
- for (const entry of indexFiles) {
550
- const {
551
- extension: entryExt
552
- } = getFileNameFromUrl(entry.name);
553
- if (entryExt === ext) {
554
- return {
555
- fullPath,
556
- resolvedPath: portablePathToFileUrl(joinPath(moduleDir, entry.name))
557
- };
558
- }
559
- }
560
- }
561
- }
562
- } catch {
563
- // Could not read module directory, leave unresolved
564
- }
565
- }
323
+ const indexResults = await Promise.all(unresolved.map(async ({
324
+ fullPath,
325
+ moduleName
326
+ }) => {
327
+ if (!directoryIndex.directories.has(moduleName)) {
566
328
  return {
567
329
  fullPath,
568
330
  resolvedPath: null
569
331
  };
570
- }));
571
- for (const {
572
- fullPath,
573
- resolvedPath
574
- } of indexResults) {
575
- if (resolvedPath) {
576
- resolved.push({
577
- fullPath,
578
- resolvedPath
579
- });
580
- }
332
+ }
333
+ const moduleDir = joinPath(parentDir, moduleName);
334
+ try {
335
+ const moduleDirContents = await readDirectory(portablePathToFileUrl(moduleDir));
336
+ const entry = selectFile(createDirectoryIndex(moduleDirContents), 'index', extensions);
337
+ return {
338
+ fullPath,
339
+ resolvedPath: entry ? portablePathToFileUrl(joinPath(moduleDir, entry.name)) : null
340
+ };
341
+ } catch {
342
+ return {
343
+ fullPath,
344
+ resolvedPath: null
345
+ };
346
+ }
347
+ }));
348
+ for (const {
349
+ fullPath,
350
+ resolvedPath
351
+ } of indexResults) {
352
+ if (resolvedPath) {
353
+ resolved.push({
354
+ fullPath,
355
+ resolvedPath
356
+ });
581
357
  }
582
358
  }
583
359
  return resolved;
@@ -650,12 +426,13 @@ export async function resolveImportResult(importResult, readDirectory, options =
650
426
 
651
427
  // Resolve JS modules without extensions
652
428
  if (jsModulesToResolve.length > 0) {
429
+ const cachedReadDirectory = cacheDirectoryReader(readDirectory);
653
430
  const resolutionPromises = jsModulesToResolve.map(async ({
654
431
  url,
655
432
  includeTypeDefs
656
433
  }) => {
657
434
  try {
658
- const resolved = await resolveModulePath(url, readDirectory, options, includeTypeDefs);
435
+ const resolved = await resolveModulePath(url, cachedReadDirectory, options, includeTypeDefs);
659
436
  if (typeof resolved === 'string') {
660
437
  // Simple string result
661
438
  return {