@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
158 lines (142 loc) • 6.17 kB
JavaScript
/**
* Interface for rewrite replacement operations.
*/
/**
* Converts import statements to const declarations set to null.
* This preserves variable names while removing the actual imports.
* Useful when precomputing data that makes the imports unnecessary.
*
* @param source - The source code to process
* @param importPathsToRewrite - Set of import paths whose import statements should be rewritten
* @param importResult - Import result with position and name data
* @returns The source code with import statements rewritten to const declarations
*/
export function rewriteImportsToNull(source, importPathsToRewrite, importResult) {
const replacements = [];
// Find all import statements to rewrite
importPathsToRewrite.forEach(importPath => {
const importData = importResult[importPath];
if (importData && importData.positions.length > 0) {
// For each position (there should typically be one per import statement)
for (const position of importData.positions) {
// Parse backwards from the quote to find 'import' keyword
let importStart = position.start;
const targetWord = 'import';
while (importStart >= targetWord.length) {
const slice = source.slice(importStart - targetWord.length, importStart);
const prevChar = importStart > targetWord.length ? source[importStart - targetWord.length - 1] : '';
if (slice === targetWord && (!prevChar || /\s/.test(prevChar))) {
importStart -= targetWord.length;
break;
}
importStart -= 1;
}
// Parse forwards from after the closing quote to find semicolon/newline
// position.end points to the character AFTER the closing quote
let importEnd = position.end;
// Check for optional semicolon
if (importEnd < source.length && source[importEnd] === ';') {
importEnd += 1;
}
// Check for newline after the statement
let hasTrailingNewline = false;
if (importEnd < source.length && source[importEnd] === '\n') {
hasTrailingNewline = true;
importEnd += 1;
}
// Generate const declarations from import names
const constDeclarations = importData.names.map(nameInfo => {
const varName = nameInfo.alias || nameInfo.name;
return `const ${varName} = null;`;
}).join('\n');
// Add newline if original import had one
const newText = constDeclarations + (hasTrailingNewline ? '\n' : '');
replacements.push({
start: importStart,
end: importEnd,
newText
});
}
}
});
// Sort replacements by position (descending) to avoid position shifts
replacements.sort((a, b) => b.start - a.start);
// Apply replacements from right to left (using same logic as rewriteImports)
let result = source;
for (const replacement of replacements) {
result = result.slice(0, replacement.start) + replacement.newText + result.slice(replacement.end);
}
return result;
}
/**
* Removes entire import statements for the specified import paths.
* This removes the full import line, not just the path.
*
* @param source - The source code to process
* @param importPathsToRemove - Set of import paths whose entire import statements should be removed
* @param importResult - Import result with position data
* @returns The source code with import statements removed
*/
export function removeImports(source, importPathsToRemove, importResult) {
const linesToRemove = new Set();
// Find all line numbers that contain imports to remove
importPathsToRemove.forEach(importPath => {
const positions = importResult[importPath]?.positions;
if (positions && positions.length > 0) {
for (const position of positions) {
// Find which line this position is on
const beforePosition = source.slice(0, position.start);
const lineNumber = (beforePosition.match(/\n/g) || []).length;
linesToRemove.add(lineNumber);
}
}
});
// Split source into lines and filter out lines to remove
const lines = source.split('\n');
const filteredLines = lines.filter((_, index) => !linesToRemove.has(index));
return filteredLines.join('\n');
}
/**
* Efficiently rewrites import paths using position data.
* This avoids regex parsing and uses precise position information for replacement.
* Works for both JavaScript/TypeScript and CSS imports.
*
* @param source - The source code to process
* @param importPathMapping - Map from original import paths to new import paths
* @param importResult - Import result with position data
* @returns The source code with rewritten import paths
*/
export function rewriteImports(source, importPathMapping, importResult) {
const replacements = [];
// Use precise position-based replacement
importPathMapping.forEach((newPath, originalPath) => {
const positions = importResult[originalPath]?.positions;
if (positions && positions.length > 0) {
// Process all positions where this import path appears
for (const position of positions) {
// Validate position bounds
if (position.start >= 0 && position.end <= source.length && position.start < position.end) {
// The positions include the quotes, so we need to preserve them
const originalText = source.slice(position.start, position.end);
if (originalText.length > 0) {
const quote = originalText.charAt(0); // Get the original quote character
const newText = `${quote}${newPath}${quote}`;
replacements.push({
start: position.start,
end: position.end,
newText
});
}
}
}
}
});
// Sort replacements by position (descending) to avoid position shifts
replacements.sort((a, b) => b.start - a.start);
// Apply replacements from right to left
let result = source;
for (const replacement of replacements) {
result = result.slice(0, replacement.start) + replacement.newText + result.slice(replacement.end);
}
return result;
}