@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
119 lines (117 loc) • 4.24 kB
JavaScript
/**
* Pure function to parse controlled code and convert it to regular Code format.
* Handles the conversion from ControlledCode (string|null sources) to Code (HAST nodes).
*
* When `preParsedCache` is supplied, each file's source is first looked up in
* the cache. If the cached entry's source string matches byte-for-byte, the
* cached HAST is reused and `parseSource` is skipped for that file. On a
* mismatch the stale entry is evicted before re-parsing so the cache cannot
* grow stale across rapid edits.
*/
export function parseControlledCode(controlledCode, parseSource, preParsedCache) {
/**
* Try the cache for `fileName`/`source`. Returns the cached HAST on an
* exact match; evicts and returns `undefined` on a mismatch.
*/
const tryCache = (fileName, source) => {
if (!preParsedCache) {
return undefined;
}
const entry = preParsedCache.get(fileName);
if (!entry) {
return undefined;
}
if (entry.source === source) {
return entry.hast;
}
preParsedCache.delete(fileName);
return undefined;
};
const parsed = {};
for (const [variant, variantCode] of Object.entries(controlledCode)) {
if (variantCode === null) {
// Explicitly deleted - skip this variant
continue;
}
if (variantCode && typeof variantCode === 'object') {
let mainSource;
// Convert null to empty string, then parse
const sourceToProcess = variantCode.source === null ? '' : variantCode.source;
if (typeof sourceToProcess === 'string' && variantCode.fileName) {
const cached = tryCache(variantCode.fileName, sourceToProcess);
if (cached) {
mainSource = cached;
} else {
try {
mainSource = parseSource(sourceToProcess, variantCode.fileName);
} catch (error) {
// Keep original string if parsing fails
mainSource = sourceToProcess;
}
}
} else if (typeof sourceToProcess === 'string' && !variantCode.fileName) {
// Return a basic HAST root node with the source text for unsupported file types
// This indicates that the source has at least passed through the parsing pipeline
const source = {
type: 'root',
children: [{
type: 'text',
value: sourceToProcess
}]
};
mainSource = source;
} else {
// Handle undefined or other non-string values
mainSource = sourceToProcess;
}
// Parse extraFiles if present
let extraFiles;
if (variantCode.extraFiles) {
const parsedExtraFiles = {};
for (const [fileName, fileData] of Object.entries(variantCode.extraFiles)) {
// Convert null to empty string, then parse
const fileSourceToProcess = fileData.source === null ? '' : fileData.source;
if (typeof fileSourceToProcess === 'string') {
const cached = tryCache(fileName, fileSourceToProcess);
if (cached) {
parsedExtraFiles[fileName] = {
source: cached,
comments: fileData.comments
};
} else {
try {
const parsedSource = parseSource(fileSourceToProcess, fileName);
parsedExtraFiles[fileName] = {
source: parsedSource,
comments: fileData.comments
};
} catch (error) {
// Keep original if parsing fails
parsedExtraFiles[fileName] = {
source: fileSourceToProcess,
comments: fileData.comments
};
}
}
} else {
// Keep other values as-is
parsedExtraFiles[fileName] = {
source: fileSourceToProcess,
comments: fileData.comments
};
}
}
extraFiles = parsedExtraFiles;
}
parsed[variant] = {
fileName: variantCode.fileName,
url: variantCode.url,
source: mainSource,
extraFiles,
filesOrder: variantCode.filesOrder,
comments: variantCode.comments
};
}
}
return parsed;
}