Warning
This is an internal project, and is not intended for public use. No support or stability guarantees are provided.
The loader utilities provide a comprehensive set of functions for resolving, parsing, and processing import statements in JavaScript/TypeScript code. These utilities are essential for building systems that need to analyze and transform code imports, such as code highlighters, bundlers, and documentation generators.
The loader utilities consist of several interconnected modules:
parseImportsAndComments - Parses import statements and optionally processes commentsresolveModulePath - Resolves module paths to actual file pathsresolveImportResult - Resolves all imports from a parsed resultprocessRelativeImports - Transforms and processes resolved importsgetFileNameFromUrl - Extracts file names and extensions from URLs/pathsextractNameAndSlugFromUrl - Extracts human-readable names and slugs from URLsrewriteImports - Rewrites import statements in source codefileUrlToPortablePath / portablePathToFileUrl - Path conversion utilitiesgetLanguageFromExtension / normalizeLanguage - Language detection utilitiesexternalsToPackages - Converts external imports to package namesgraph TD
A[Source Code] --> B[parseImportsAndComments]
B --> C[ImportsAndComments]
C --> D[resolveImportResult]
D --> E[Resolved Paths Map]
C --> F[processRelativeImports]
E --> F
F --> G[Processed Result]
H[File URLs] --> I[getFileNameFromUrl]
I --> J[File Metadata]
K[Source + Import Map] --> L[rewriteImports]
L --> M[Transformed Source]
Parses import statements from JavaScript/TypeScript/CSS source code and extracts import information. Optionally processes and removes comments.
import { parseImportsAndComments } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const source = `
import React from 'react';
import { Component } from '../components/Component';
import type { Props } from '../types';
import '../styles.css';
`;
const result = await parseImportsAndComments(source, 'file:///src/current/file.tsx');
console.log(result);
// {
// relative: {
// '../components/Component': {
// url: 'file:///src/components/Component',
// names: [{ name: 'Component', type: 'named' }],
// positions: [{ start: 45, end: 72 }]
// },
// '../types': {
// url: 'file:///src/types',
// names: [{ name: 'Props', type: 'named', isType: true }],
// includeTypeDefs: true,
// positions: [{ start: 100, end: 112 }]
// },
// '../styles.css': {
// url: 'file:///src/styles.css',
// names: [],
// positions: [{ start: 130, end: 145 }]
// }
// },
// externals: {
// 'react': {
// names: [{ name: 'React', type: 'default' }],
// positions: [{ start: 15, end: 22 }]
// }
// }
// }
With comment processing:
const result = await parseImportsAndComments(source, fileUrl, {
removeCommentsWithPrefix: ['@internal'],
notableCommentsPrefix: ['TODO'],
});
// Returns: { relative, externals, code, comments }
// - code: source with matching comments removed
// - comments: Record<lineNumber, commentContent[]>
Features:
import type statements and marks them with includeTypeDefs: truefile:// URLs@import statementsResolves module paths to actual file paths using filesystem directory reading.
import { resolveModulePath } from '@mui/internal-docs-infra/pipeline/loaderUtils';
import type { DirectoryReader } from '@mui/internal-docs-infra/pipeline/loaderUtils';
// directoryReader returns directory entries for a given file:// URL
const directoryReader: DirectoryReader = async (path) => {
// Implementation that returns DirectoryEntry[]
};
// Basic resolution - returns file:// URL
const resolved = await resolveModulePath('file:///src/components/Button', directoryReader);
console.log(resolved); // 'file:///src/components/Button.tsx'
// Type-aware resolution - returns object with both paths
const typeAware = await resolveModulePath(
'file:///src/components/Button',
directoryReader,
{},
true, // includeTypeDefs
);
console.log(typeAware);
// {
// import: 'file:///src/components/Button.tsx',
// typeImport: 'file:///src/components/Button.d.ts'
// }
Features:
Button to Button/index.tsxfile:// URLs and portable pathsExtension Priorities:
.ts, .tsx, .js, .jsx, .mdx, .d.ts.d.ts, .ts, .tsx, .js, .jsx, .mdxResolves multiple module paths efficiently by grouping them by directory.
import { resolveModulePaths } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const paths = ['/src/components/Button', '/src/components/Input', '/src/utils/helpers'];
const resolved = await resolveModulePaths(paths, directoryReader);
// Map {
// '/src/components/Button' => 'file:///src/components/Button.tsx',
// '/src/components/Input' => 'file:///src/components/Input.tsx',
// '/src/utils/helpers' => 'file:///src/utils/helpers/index.ts'
// }
Resolves all imports from a parseImportsAndComments result, handling JavaScript modules and static assets differently.
import {
parseImportsAndComments,
resolveImportResult,
} from '@mui/internal-docs-infra/pipeline/loaderUtils';
const { relative } = await parseImportsAndComments(source, fileUrl);
const resolved = await resolveImportResult(relative, directoryReader);
// Map from import URL to resolved file:// URL
Resolves variant paths from a variants object (useful for demo systems).
import { resolveVariantPaths } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const variants = {
TypeScript: '/src/demos/Button',
JavaScript: '/src/demos/Button.js',
};
const resolved = await resolveVariantPaths(variants, directoryReader);
// Map {
// 'TypeScript' => 'file:///src/demos/Button.tsx',
// 'JavaScript' => 'file:///src/demos/Button.js'
// }
Transforms resolved imports based on different storage strategies.
import { processRelativeImports } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const result = processRelativeImports(
source,
importResult.relative, // from parseImportsAndComments
'flat', // storeAt mode: 'flat' | 'canonical' | 'import'
true, // isJsFile: true for JS/TS, false for CSS/JSON
resolvedPathsMap, // from resolveImportResult (required for JS files)
);
console.log(result);
// {
// processedSource: "import Component from './Component';",
// extraFiles: {
// './Component.tsx': 'file:///src/components/Component.tsx'
// }
// }
Storage Strategies:
flat - Flattens all imports to current directory level
// import Component from '../components/Component';
// File stored at: ../components/Component/index.tsx
// File Displayed As: Component.tsx
import - Preserves original import path without rewriting
// import Component from '../components/Component';
// File Stored at: ../components/Component/index.tsx
// File Displayed As: ../components/Component.tsx
canonical - Preserves the original file structure
// import Component from '../components/Component';
// File Stored at: ../components/Component/index.tsx
// File Displayed As: ../components/Component/index.tsx
Extracts file names and extensions from URLs and file paths.
import { getFileNameFromUrl } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const meta = getFileNameFromUrl('file:///src/components/Button.tsx');
console.log(meta); // { fileName: 'Button.tsx', extension: '.tsx' }
// Handles compound extensions
const types = getFileNameFromUrl('file:///src/types.d.ts');
console.log(types); // { fileName: 'types.d.ts', extension: '.d.ts' }
// Handles CSS modules
const cssModule = getFileNameFromUrl('file:///src/styles.module.css');
console.log(cssModule); // { fileName: 'styles.module.css', extension: '.module.css' }
// Works with URLs
const url = getFileNameFromUrl('https://example.com/file.js');
console.log(url); // { fileName: 'file.js', extension: '.js' }
Supported Compound Extensions:
.module.css, .module.scss, .d.ts, .test.ts, .test.tsx, .spec.ts, .config.ts, .stories.tsx, and more.
Extracts and formats human-readable names and URL-friendly slugs from file paths.
import { extractNameAndSlugFromUrl } from '@mui/internal-docs-infra/pipeline/loaderUtils';
// Handles index files (uses parent directory name)
const demo = extractNameAndSlugFromUrl('file:///app/components/demos/advanced-keyboard/index.ts');
console.log(demo); // { name: 'Advanced Keyboard', slug: 'advanced-keyboard' }
// Handles camelCase filenames
const camel = extractNameAndSlugFromUrl('/src/components/customButton.tsx');
console.log(camel); // { name: 'Custom Button', slug: 'custom-button' }
// Handles kebab-case filenames
const kebab = extractNameAndSlugFromUrl('/src/components/button-group.tsx');
console.log(kebab); // { name: 'Button Group', slug: 'button-group' }
Features:
index.* files.js, .d.ts, .module.css, etc.)customButton to Custom Button / custom-buttonbutton-group to Button Group / button-groupfile:// URLs and regular pathsConverts an array of external import paths to a packages object for dependency tracking.
import { externalsToPackages } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const externals = [
'react',
'react-dom',
'@mui/internal-docs-infra/CodeHighlighter',
'@mui/internal-docs-infra/pipeline/parseSource',
'lodash/get',
'some-package/submodule/deep',
'@/components/Button', // path alias - filtered out
];
const packages = externalsToPackages(externals);
console.log(packages);
// {
// 'react': true,
// 'react-dom': true,
// '@mui/internal-docs-infra': true,
// 'lodash': true,
// 'some-package': true
// }
Features:
@scope/package from longer pathslodash/get to lodash@/Rewrites import statements in source code based on a mapping, using precise position data.
import { rewriteImports } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const source = `
import Component from '../components/Component';
import { helper } from '../utils/helper';
`;
const importPathMapping = new Map([
['../components/Component', './Component'],
['../utils/helper', './helper'],
]);
// importResult contains position data from parseImportsAndComments
const rewritten = rewriteImports(source, importPathMapping, importResult.relative);
console.log(rewritten);
// import Component from './Component';
// import { helper } from './helper';
Converts import statements to const declarations set to null. Useful when precomputing data that makes imports unnecessary.
import { rewriteImportsToNull } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const source = `import { Component } from './Component';`;
const importPathsToRewrite = new Set(['./Component']);
const rewritten = rewriteImportsToNull(source, importPathsToRewrite, importResult.relative);
console.log(rewritten);
// const Component = null;
Removes entire import statements for specified import paths.
import { removeImports } from '@mui/internal-docs-infra/pipeline/loaderUtils';
const source = `
import React from 'react';
import { helper } from './helper';
`;
const importPathsToRemove = new Set(['./helper']);
const result = removeImports(source, importPathsToRemove, importResult.relative);
console.log(result);
// import React from 'react';
Converts a file:// URL to a portable POSIX-style path for cross-platform path manipulation.
import { fileUrlToPortablePath } from '@mui/internal-docs-infra/pipeline/loaderUtils';
// Unix
fileUrlToPortablePath('file:///home/user/file.ts');
// => '/home/user/file.ts'
// Windows
fileUrlToPortablePath('file:///C:/Users/file.ts');
// => '/C:/Users/file.ts'
// Already a portable path (passthrough)
fileUrlToPortablePath('/home/user/file.ts');
// => '/home/user/file.ts'
Converts a portable path back to a file:// URL.
import { portablePathToFileUrl } from '@mui/internal-docs-infra/pipeline/loaderUtils';
portablePathToFileUrl('/home/user/file.ts');
// => 'file:///home/user/file.ts'
portablePathToFileUrl('/C:/Users/file.ts');
// => 'file:///C:/Users/file.ts'
// Already a URL (passthrough)
portablePathToFileUrl('file:///home/user/file.ts');
// => 'file:///home/user/file.ts'
Gets the language name from a file extension.
import { getLanguageFromExtension } from '@mui/internal-docs-infra/pipeline/loaderUtils';
getLanguageFromExtension('.tsx'); // => 'tsx'
getLanguageFromExtension('.ts'); // => 'typescript'
getLanguageFromExtension('.css'); // => 'css'
getLanguageFromExtension('.mdx'); // => 'mdx'
Normalizes language aliases to canonical names.
import { normalizeLanguage } from '@mui/internal-docs-infra/pipeline/loaderUtils';
normalizeLanguage('js'); // => 'javascript'
normalizeLanguage('ts'); // => 'typescript'
normalizeLanguage('bash'); // => 'shell'
normalizeLanguage('yml'); // => 'yaml'
Constants for extension-to-language and alias-to-language mappings.
import { languageMap, languageAliasMap } from '@mui/internal-docs-infra/pipeline/loaderUtils';
// languageMap: { '.js': 'javascript', '.ts': 'typescript', ... }
// languageAliasMap: { 'js': 'javascript', 'bash': 'shell', ... }
Checks if a path represents a JavaScript/TypeScript module.
import { isJavaScriptModule } from '@mui/internal-docs-infra/pipeline/loaderUtils';
isJavaScriptModule('./component.tsx'); // => true
isJavaScriptModule('./styles.css'); // => false
isJavaScriptModule('./data.json'); // => false
isJavaScriptModule('./module'); // => true (no extension = assumed JS module)
Here's how to use all the utilities together for a complete import processing pipeline:
import {
parseImportsAndComments,
resolveImportResult,
processRelativeImports,
} from '@mui/internal-docs-infra/pipeline/loaderUtils';
import type { DirectoryReader } from '@mui/internal-docs-infra/pipeline/loaderUtils';
// Create a directory reader (implementation depends on your environment)
const directoryReader: DirectoryReader = async (dirUrl) => {
// Return DirectoryEntry[] for the given file:// URL
};
async function processCodeImports(source: string, fileUrl: string) {
// 1. Parse imports from source
const { relative, externals } = await parseImportsAndComments(source, fileUrl);
// 2. Resolve import paths to actual files
const resolvedPathsMap = await resolveImportResult(relative, directoryReader);
// 3. Process and transform the imports
const result = processRelativeImports(
source,
relative,
'flat',
true, // isJsFile
resolvedPathsMap,
);
return { result, externals };
}
// Usage
const source = `
import React from 'react';
import { Button } from '../components/Button';
import type { Props } from '../types';
import '../styles.css';
`;
const { result, externals } = await processCodeImports(source, 'file:///src/demo/example.tsx');
console.log(result.processedSource);
// import React from 'react';
// import { Button } from './Button';
// import type { Props } from './types';
// import './styles.css';
console.log(result.extraFiles);
// {
// './Button.tsx': 'file:///src/components/Button.tsx',
// './types.d.ts': 'file:///src/types.d.ts',
// './styles.css': 'file:///src/styles.css'
// }
console.log(externals);
// {
// 'react': {
// names: [{ name: 'React', type: 'default' }],
// positions: [...]
// }
// }
type DirectoryEntry = { name: string; isFile: boolean; isDirectory: boolean };Represents an import from an external package (node_modules).
type ExternalImport = { names: ImportName[]; positions: ImportPathPosition[] };Represents a single import name with its properties.
type ImportName = {
name: string;
alias?: string;
type: 'default' | 'named' | 'namespace';
isType?: boolean;
};Represents the position of an import path in the source code.
type ImportPathPosition = { start: number; end: number };The result of parsing import statements from source code.
type ImportsAndComments = {
relative: Record<string, RelativeImport>;
externals: Record<string, ExternalImport>;
code?: string;
comments?: Record<number, string[]>;
};Default file extensions for JavaScript/TypeScript modules that can be resolved
type JAVASCRIPT_MODULE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mdx', '.d.ts'];Maps language aliases to canonical language names. Used to normalize short language names (e.g., from className like ‘language-js’) to their full names.
type languageAliasMap = Record<string, string>;Maps file extensions to language names.
These are user-friendly names that can be used in the language prop.
type languageMap = Record<string, string>;type ProcessImportsResult = { processedSource: string; extraFiles: Record<string, string> };Represents an import from a relative path (starts with ./ or ../).
type RelativeImport = {
url: string;
names: ImportName[];
includeTypeDefs?: true;
positions: ImportPathPosition[];
};type ResolveModulePathOptions = { extensions?: string[] };type StoreAtMode = 'canonical' | 'import' | 'flat';Extension priority for type-only imports — prioritize .d.ts first
type TYPE_IMPORT_EXTENSIONS = ['.d.ts', '.ts', '.tsx', '.js', '.jsx', '.mdx'];type TypeAwareResolveResult = { import: string; typeImport?: string };Extension priority for value imports — standard priority with .d.ts last
type VALUE_IMPORT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mdx', '.d.ts'];// Default extensions for JavaScript/TypeScript modules
const JAVASCRIPT_MODULE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mdx', '.d.ts'];
// Type import extensions (prioritize .d.ts first)
const TYPE_IMPORT_EXTENSIONS = ['.d.ts', '.ts', '.tsx', '.js', '.jsx', '.mdx'];
// Value import extensions (standard priority)
const VALUE_IMPORT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mdx', '.d.ts'];
The utilities are optimized to minimize filesystem calls:
includeTypeDefs is enabledAll functions include comprehensive error handling:
try {
const result = await resolveModulePath('/nonexistent/path', directoryReader);
} catch (error) {
console.error(`Module resolution failed: ${error.message}`);
// Error: Could not resolve module at path "/nonexistent/path".
// Tried extensions: .ts, .tsx, .js, .jsx, .d.ts
}
The utilities include comprehensive test coverage: