@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
169 lines (153 loc) • 6.37 kB
JavaScript
/**
* Utility function for serializing structured function arguments back to string format
* This is the inverse of parseFunctionArguments - it takes structured data and creates
* valid JavaScript/TypeScript function call arguments.
*/
import { isArray, isFunction, isGeneric, isArrowFunction, isObjectLiteral, isTypeAssertion } from "./parseFunctionArguments.mjs";
/**
* Serialize structured arguments back to a string representation
* Uses JSON.stringify for object values for performance and reliability
*/
export function serializeFunctionArguments(args) {
return args.map(arg => serializeArgument(arg)).join(', ');
}
/**
* Serialize a single argument based on its type
*/
function serializeArgument(arg) {
if (typeof arg === 'string') {
return arg;
}
if (typeof arg === 'number' || typeof arg === 'boolean' || arg === null) {
return String(arg);
}
// Check structured types using type guards
const arrayCheck = isArray(arg);
if (arrayCheck) {
return `[${arrayCheck.items.map(item => serializeArgument(item)).join(', ')}]`;
}
const functionCheck = isFunction(arg);
if (functionCheck) {
const args = functionCheck.arguments.map(p => serializeArgument(p)).join(', ');
return `${functionCheck.name}(${args})`;
}
const genericCheck = isGeneric(arg);
if (genericCheck) {
const generics = genericCheck.generics.map(g => serializeArgument(g)).join(', ');
if (genericCheck.arguments === null) {
// Type generic like Theme<"dark" | "light">
return `${genericCheck.name}<${generics}>`;
}
// Function generic like Component<Props>(args) - only add () if there are actual arguments
if (genericCheck.arguments.length > 0) {
const args = genericCheck.arguments.map(p => serializeArgument(p)).join(', ');
return `${genericCheck.name}<${generics}>(${args})`;
}
// Generic without function call - like Component<Props>
return `${genericCheck.name}<${generics}>`;
}
const arrowCheck = isArrowFunction(arg);
if (arrowCheck) {
const args = arrowCheck.args.map(p => serializeArgument(p)).join(', ');
const argStr = arrowCheck.args.length === 1 ? args : `(${args})`;
if (arrowCheck.types) {
// Typed arrow function
const [inputTypes, outputTypes] = arrowCheck.types;
const inputTypeStr = Array.isArray(inputTypes) ? inputTypes.join(', ') : inputTypes;
return `(${args}: ${inputTypeStr}): ${outputTypes} => ${serializeArgument(arrowCheck.returnValue)}`;
}
// Simple arrow function
return `${argStr} => ${serializeArgument(arrowCheck.returnValue)}`;
}
const typeAssertionCheck = isTypeAssertion(arg);
if (typeAssertionCheck) {
return `${serializeArgument(typeAssertionCheck.expression)} as ${typeAssertionCheck.type}`;
}
const objectCheck = isObjectLiteral(arg);
if (objectCheck) {
return serializeObject(objectCheck.properties);
}
// Array but not a structured type - serialize as array literal
if (Array.isArray(arg)) {
// Check if this is a double-wrapped array literal from parseArrayLiteral
// parseArrayLiteral calls parseArgumentsRecursive which returns an array,
// so we get [[item1, item2]] instead of [item1, item2]
const doubleWrappedCheck = isArray(arg);
if (doubleWrappedCheck) {
// This is a double-wrapped array literal - unwrap it
return `[${arg[0].map(item => serializeArgument(item)).join(', ')}]`;
}
// Regular array
return `[${arg.map(item => serializeArgument(item)).join(', ')}]`;
}
// Object but not a structured type - serialize as object literal
if (typeof arg === 'object' && arg !== null) {
return serializeObject(arg);
}
// Fallback to string representation
return String(arg);
}
/**
* Serialize an object to JavaScript object literal syntax
*/
function serializeObject(obj) {
if (Object.keys(obj).length === 0) {
return '{}';
}
const entries = Object.entries(obj).map(([key, value]) => {
const serializedValue = serializeObjectValue(value);
// Handle shorthand properties (key === value)
if (typeof value === 'string' && key === value) {
return key;
}
return `${key}: ${serializedValue}`;
});
return `{ ${entries.join(', ')} }`;
} /**
* Serialize an object value, using JSON.stringify when appropriate
*/
function serializeObjectValue(value) {
// Special case: array literals in objects are double-wrapped by the parser
// Check if this is a double-wrapped array literal: [[item1, item2, item3]]
// This MUST come before the structured type check to prevent it being treated as a structured array
if (isArray(value)) {
// This is a double-wrapped array literal - unwrap it
return `[${value[0].map(item => serializeObjectValue(item)).join(', ')}]`;
}
// For structured types, recursively serialize first
if (typeof value === 'object' && value !== null && isStructuredType(value)) {
return serializeArgument(value);
}
// For primitive values, just convert to string
if (typeof value === 'number' || typeof value === 'boolean' || value === null) {
return String(value);
}
// For strings, preserve original quotes from the parser
if (typeof value === 'string') {
// The parser preserves quotes for string literals and returns identifiers without quotes
// So we should return exactly what the parser gave us
return value;
}
// For plain objects and arrays, serialize keys/values individually
if (typeof value === 'object' && value !== null && !isStructuredType(value)) {
if (Array.isArray(value)) {
// Check if this is a double-wrapped array literal
if (isArray(value)) {
// This is a double-wrapped array literal - unwrap it
return `[${value[0].map(item => serializeObjectValue(item)).join(', ')}]`;
}
// Regular array
return `[${value.map(item => serializeObjectValue(item)).join(', ')}]`;
}
const pairs = Object.entries(value).map(([key, val]) => `${key}: ${serializeObjectValue(val)}`);
return `{ ${pairs.join(', ')} }`;
}
// Fallback
return serializeArgument(value);
}
/**
* Check if a value is one of our structured types that needs special handling
*/
function isStructuredType(value) {
return !!(isArray(value) || isFunction(value) || isGeneric(value) || isArrowFunction(value) || isTypeAssertion(value));
}