UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

258 lines (244 loc) 8.85 kB
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); }