@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
258 lines (244 loc) • 8.85 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _typeof from "@babel/runtime/helpers/esm/typeof";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
/**
* Shared path utilities for CodeHighlighter components
*
* Back navigation counting functions:
* - resolveRelativePath().backSteps: Net back navigation after path resolution (recommended for most cases)
* - countConsecutiveBackNavigation(): Raw consecutive '../' at start (for trimming leading patterns)
* - countBackNavigationOccurrences(): Total raw '../' count anywhere (for metadata analysis)
*/
/**
* Minimal file representation for path utilities
*/
/**
* Resolves a relative path by handling .. and . segments properly
* This mimics path.resolve() behavior for relative paths
* Returns the net back navigation steps after path resolution
*/
export function resolveRelativePath(relativePath) {
// Split the path into segments
var segments = relativePath.split('/');
var resolved = [];
var backSteps = 0;
var _iterator = _createForOfIteratorHelper(segments),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var segment = _step.value;
if (segment === '' || segment === '.') {
// Skip empty and current directory segments
continue;
} else if (segment === '..') {
if (resolved.length > 0) {
// Remove the last segment (go back one directory)
resolved.pop();
} else {
// Count back steps that go beyond the current directory
backSteps += 1;
}
} else {
// Regular directory or file segment
resolved.push(segment);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return {
resolvedPath: resolved.join('/'),
backSteps: backSteps
};
}
/**
* Split a path into components, filtering out empty strings
*/
export function splitPath(path) {
return path.split('/').filter(Boolean);
}
/**
* Extract URL path components, filtering out empty strings
*/
export function getUrlParts(url) {
return splitPath(new URL(url).pathname);
}
/**
* Remove trailing slash from a path string
*/
export function removeTrailingSlash(path) {
return path.endsWith('/') ? path.slice(0, -1) : path;
}
/**
* Remove a specific number of back navigation prefixes from a path
*/
export function removeBackNavigationPrefix(path, count) {
var result = path;
for (var i = 0; i < count; i += 1) {
if (result.startsWith('../')) {
result = result.slice(3);
} else {
break;
}
}
return result;
}
/**
* Calculate the maximum back navigation levels from a collection of file paths
*
* This function analyzes all file paths in the collection and determines:
* 1. The maximum back navigation steps needed to reach any file (including metadata)
* 2. The maximum back navigation steps needed to reach any non-metadata file
*
* @param files - Record of relative file paths to file content (string) or file objects with optional metadata flag
* @returns Object containing:
* - maxBackNavigation: Maximum '../' steps needed to reach any file in the collection
* - maxSourceBackNavigation: Maximum '../' steps needed to reach any non-metadata file
*
* @example
* ```typescript
* const files = {
* 'component.tsx': 'url',
* '../shared/utils.ts': 'url',
* '../../docs/readme.md': { metadata: true }
* };
*
* const result = calculateMaxBackNavigation(files);
* // result: { maxBackNavigation: 2, maxSourceBackNavigation: 1 }
* ```
*/
export function calculateMaxBackNavigation(files) {
var maxBackNavigation = 0;
var maxSourceBackNavigation = 0;
for (var _i = 0, _Object$entries = Object.entries(files); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
relativePath = _Object$entries$_i[0],
fileContent = _Object$entries$_i[1];
// Check if this is a metadata file
var isMetadata = _typeof(fileContent) === 'object' && fileContent.metadata;
var _resolveRelativePath = resolveRelativePath(relativePath),
backSteps = _resolveRelativePath.backSteps;
if (!isMetadata) {
maxSourceBackNavigation = Math.max(maxSourceBackNavigation, backSteps);
}
maxBackNavigation = Math.max(maxBackNavigation, backSteps);
}
return {
maxBackNavigation: maxBackNavigation,
maxSourceBackNavigation: maxSourceBackNavigation
};
}
/**
* Calculate the maximum back navigation level from a collection of file paths
*
* This function analyzes file paths and determines the maximum number of back navigation
* steps needed to reach any non-metadata file. It ignores metadata files completely,
* focusing only on source code and other content files.
*
* @param files - Record of relative file paths to file content (string) or file objects with optional metadata flag
* @returns The maximum number of `../` steps needed to reach any non-metadata file
*
* @example
* ```typescript
* const files = {
* 'component.tsx': 'url',
* '../shared/utils.ts': 'url',
* '../../docs/readme.md': { metadata: true }, // ignored
* '../../../deep/source.js': 'url'
* };
*
* const maxSteps = calculateMaxSourceBackNavigation(files);
* // maxSteps: 3 (from '../../../deep/source.js')
* ```
*/
export function calculateMaxSourceBackNavigation(files) {
var maxSourceBackNavigation = 0;
for (var _i2 = 0, _Object$entries2 = Object.entries(files); _i2 < _Object$entries2.length; _i2++) {
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
relativePath = _Object$entries2$_i[0],
fileContent = _Object$entries2$_i[1];
// Check if this is a metadata file
var isMetadata = _typeof(fileContent) === 'object' && fileContent.metadata;
// Skip metadata files - only consider non-metadata files for maxSourceBackNavigation
if (isMetadata) {
continue;
}
// Use path resolution to get the net back steps (most accurate)
var _resolveRelativePath2 = resolveRelativePath(relativePath),
backSteps = _resolveRelativePath2.backSteps;
maxSourceBackNavigation = Math.max(maxSourceBackNavigation, backSteps);
}
return maxSourceBackNavigation;
}
/**
* Build a path from multiple components, filtering out empty parts
*/
export function buildPath() {
var parts = [];
for (var _len = arguments.length, segments = new Array(_len), _key = 0; _key < _len; _key++) {
segments[_key] = arguments[_key];
}
for (var _i3 = 0, _segments = segments; _i3 < _segments.length; _i3++) {
var segment = _segments[_i3];
if (segment === undefined) {
continue;
}
if (Array.isArray(segment)) {
parts.push.apply(parts, _toConsumableArray(segment));
} else {
parts.push(segment);
}
}
return parts.filter(Boolean).map(removeTrailingSlash).join('/');
}
/**
* Create synthetic directory names for path structure
* Generates alphabetic names: 'a', 'b', 'c', ..., 'z', 'aa', 'ab', 'ac', etc.
* @param count - Number of directory names to generate
* @returns Array of alphabetic directory names
*/
export function createSyntheticDirectories(count) {
return Array.from({
length: count
}, function (_, i) {
var result = '';
var num = i + 1; // 1-based for Excel-style naming
while (num > 0) {
num -= 1; // Adjust for 0-based indexing
result = String.fromCharCode(97 + num % 26) + result;
num = Math.floor(num / 26);
}
return result;
});
}
/**
* Calculate the required back navigation pattern for metadata files positioning.
* This combines maxSourceBackNavigation from files with additional levels from metadataPrefix.
*
* @param files - Record of extraFiles to analyze for source back navigation
* @param metadataPrefix - Optional prefix path (e.g., 'src/', 'src/app/') that adds additional back navigation levels
* @returns A string of '../' patterns representing the back navigation needed
*
* @example
* ```typescript
* const files = { '../utils.ts': 'url', '../../shared.ts': 'url' };
* const result = calculateMetadataBackNavigation(files, 'src/');
* // result: '../../../' (maxSourceBackNavigation=2 + metadataPrefix=1)
* ```
*/
export function calculateMetadataBackNavigation(files, metadataPrefix) {
// Get the maxSourceBackNavigation from the file structure
var backLevels = 0;
if (files) {
backLevels = calculateMaxSourceBackNavigation(files);
}
if (metadataPrefix) {
// When a prefix is provided, add additional back navigation based on prefix depth
var prefixSegments = metadataPrefix.split('/').filter(Boolean);
backLevels += prefixSegments.length;
}
return '../'.repeat(backLevels);
}