@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
504 lines • 21.1 kB
text/typescript
import type { Element, ElementContent } from 'hast';
/**
* A proven binding in a scope — links a variable name to its type origin.
* Discriminated on `refKind` so that each variant carries exactly the
* metadata needed to produce the correct link element.
*/
export type ScopeBinding = {
refKind: 'type';
href: string;
typeName: string;
declKind?: 'const' | 'let' | 'var';
} | {
refKind: 'prop';
href: string;
ownerName: string;
propPath: string;
} | {
refKind: 'param';
href: string;
paramOwnerName: string;
paramName: string;
} | {
refKind: 'value';
value: string;
varName: string;
refs?: Record<string, string>;
declKind?: 'const' | 'let' | 'var';
} | {
refKind: 'value-object';
properties: Map<string, string>;
varName: string;
declKind?: 'const' | 'let' | 'var';
} | {
refKind: 'shadow';
} | {
refKind: 'module';
href: string;
defaultHref?: string;
exports: Record<string, {
slug: string;
title?: string;
}>;
};
/**
* Entry in the moduleLinkMap configuration.
*/
export interface ModuleLinkMapEntry {
/** The page URL for this module (e.g., '/docs-infra/pipeline/enhanceCodeTypes'). */
href: string;
/** Per-module override for the anchor slug used by default/namespace imports. */
defaultSlug?: string;
/** The kind of the default export, when known (e.g., 'function', 'class', 'object'). */
defaultKind?: ResolvedExportKind;
/**
* Structured properties for the default export when `defaultKind` is `'object'`.
* Keys are property names; values are the literal type strings (e.g., `"'.root'"`).
*/
defaultProperties?: Record<string, string>;
/** Maps exported names to their slug and optional title. */
exports?: Record<string, {
slug: string;
title?: string;
kind?: ResolvedExportKind;
/**
* Structured properties when `kind` is `'object'`.
* Keys are property names; values are the literal type strings.
*/
properties?: Record<string, string>;
}>;
}
/**
* Resolved import collected during the scan.
* Used to build the `data-imports` attribute on the `<code>` element.
*/
export interface ResolvedImport {
link: string;
exports: Array<{
slug: string;
title: string;
}>;
}
/**
* Resolved export collected during the scan.
* Used to build the `data-exports` attribute on the `<code>` element.
*/
export type ResolvedExportKind = 'function' | 'const' | 'let' | 'var' | 'type' | 'interface' | 'class' | 'unknown' | 'enum' | 'object';
interface BaseResolvedExport {
/** The exported name (or "default" for default exports) */
name: string;
/** The kind of export declaration */
kind: ResolvedExportKind;
}
export interface ResolvedValueExport extends BaseResolvedExport {
kind: Exclude<ResolvedExportKind, 'object'>;
/** The type annotation or inferred literal type, when determinable */
type?: string;
/** The resolved href for the type, when available in the linkMap */
typeHref?: string;
}
export interface ResolvedObjectExport extends BaseResolvedExport {
kind: 'object';
/** Structured key/value properties for object-shaped exports */
properties: Record<string, string>;
}
export type ResolvedExport = ResolvedValueExport | ResolvedObjectExport;
/**
* A single lexical scope in the scope stack.
* - `'function'`: function body scope (holds `var` bindings and params)
* - `'block'`: block scope (holds `let`/`const` bindings)
*/
export interface Scope {
bindings: Map<string, ScopeBinding>;
kind: 'function' | 'block';
}
/**
* Owner context for property linking.
* Tracks which type/function/component owns the current block of properties.
*/
export interface OwnerContext {
/** The owner identifier, e.g., "User", "createUser[0]", "Card[0]" */
name: string;
/** The anchor href from the linkMap for this owner */
anchorHref: string;
/** The kind of owner, affecting how the context ends */
kind: 'type-def' | 'type-annotation' | 'func-call' | 'jsx' | 'css-property';
/** Current brace depth within this owner (1 = top-level properties) */
braceDepth: number;
/** Stack of property names for deep nesting */
propPath: string[];
/** Brace depths at which each propPath entry was pushed */
propPathDepths: number[];
/** Parameter index for function calls (0-indexed). Only used for func-call and jsx kinds. */
paramIndex: number;
/**
* Optional param-specific anchor href from the linkMap (e.g., linkMap["makeItem[0]"]).
* When set, prop hrefs use this as the base instead of index-based formatting.
*/
paramAnchorHref: string | null;
}
/**
* Mutable state threaded through the single-pass traversal.
*/
export interface ScanState {
/** Stack of active owner contexts (for nested types) */
ownerStack: OwnerContext[];
/** Set after seeing pl-k("type") keyword */
sawTypeKeyword: boolean;
/** The entity name seen after "type" keyword (potential type definition owner) */
pendingTypeDefName: string | null;
/** Whether we're expecting a brace to start a type def body */
expectingTypeDefBrace: boolean;
/** Type annotation name (from pl-en after pl-k(":") in const declarations) */
pendingAnnotationType: string | null;
/** Whether we saw pl-k("=") after a type annotation, expecting a brace */
expectingAnnotationBrace: boolean;
/** Last seen entity name from pl-en (for function call detection) */
lastEntityName: string | null;
/** Whether we just saw "<" text (for JSX detection) */
sawJsxOpen: boolean;
/** JSX component name (from pl-c1 after "<") */
jsxComponentName: string | null;
/** Last property name that was linked (for deep nesting: detect { after prop) */
lastLinkedProp: string | null;
/** Pending function call context for tracking parentheses and parameter indices */
pendingFuncCall: {
name: string;
anchorHref: string;
parenDepth: number;
paramIndex: number;
} | null;
/** Persisted type def info for multi-brace type definitions (unions, intersections) */
typeDefPersist: {
name: string;
anchorHref: string;
} | null;
/** Paren depth tracking for type def expressions (e.g., `type X = ( | {...} | {...} ) & {...}`) */
typeDefParenDepth: number;
/** Pending CSS property name for owner context (set after a linked pl-c1 span in CSS) */
pendingCssProperty: {
name: string;
anchorHref: string;
} | null;
/** Set after seeing pl-k("function") keyword */
sawFunctionKeyword: boolean;
/** Scope stack for variable reference resolution (innermost last) */
scopeStack: Scope[];
/**
* Set when `)` closes a recognized non-definition funcParamContext.
* The next `{` pushes a function scope and flushes pendingFunctionBindings.
* Cleared on any token that isn't `{` or a `=>` keyword span.
*/
expectingFunctionBody: boolean;
/**
* Transient flag set when `=>` keyword is seen while expectingFunctionBody is true.
* Consumed by text `(` to set expressionArrowBody.
*/
sawArrowForBody: boolean;
/**
* Set when `(` follows `=>` while expectingFunctionBody is true.
* Indicates the arrow has an expression body with a paren-wrapped object literal
* `=> ({...})` — the `{` should push a block scope, not a function scope.
*/
expressionArrowBody: boolean;
/**
* Param bindings saved from funcParamContext at `)` close, waiting for `{`
* to flush into the new function scope. Dropped if expectingFunctionBody is cleared.
*/
pendingFunctionBindings: Map<string, ScopeBinding> | null;
/** Variable name from `const x` / `let x` / `var x`, awaiting type annotation */
lastDeclaredVarName: string | null;
/** Which variable keyword (`const`/`let`/`var`) introduced lastDeclaredVarName */
lastVarKeyword: 'const' | 'let' | 'var' | null;
/**
* Variable name preserved after `=` so that value capture (string literals,
* object literals, array literals) can bind back to the declared variable.
* Only set for `const` declarations (mutable variables are unreliable).
*/
pendingValueVar: string | null;
/**
* Active object literal value collection. Set when `{` follows a `const x =`
* declaration. Collects top-level property name→value pairs and flushes
* a `'value-object'` scope binding on `}`.
*/
pendingObjectValue: {
varName: string;
properties: Map<string, string>;
currentPropName: string | null;
/** Tentative key from a span-tokenized identifier, awaiting `:` confirmation. */
pendingSpanKey: string | null;
braceDepth: number;
/** True when a shorthand property (no `:` value) was seen — the shape is incomplete. */
hasUnresolvedKeys: boolean;
} | null;
/**
* Deferred literal candidate for const value binding.
* Instead of eagerly recording a value binding when we see a literal after
* `const x =`, we store the candidate here. It is flushed (committed) at
* the next `;` statement boundary, and invalidated if an operator or
* non-evaluable expression token appears after the literal.
*/
pendingLiteralCandidate: {
varName: string;
value: string;
/** Index in newChildren where this literal node was pushed. Set by enhanceChildren. */
startChildIndex: number;
/** The child array where this literal node was pushed. */
targetChildren: ElementContent[] | null;
} | null;
/**
* True when a newline was seen while pendingExpression looked syntactically
* complete (last token is not an operator). The next non-whitespace token
* decides the outcome: `.`, `[`, or `(` invalidates the expression
* (continuation syntax), while a syntax span or `;` commits it.
*/
expressionNewlineReady: boolean;
/**
* Active compound expression accumulator. Promoted from pendingLiteralCandidate
* when an evaluable operator (`+`, `-`, `*`, `/`) appears after a literal.
* Tokens are accumulated and evaluated at `;` or ASI boundaries.
*/
pendingExpression: {
varName: string;
tokens: Array<{
kind: 'number' | 'string' | 'operator' | 'variable';
value: string;
/** For `variable` tokens with a type/prop/param binding, the anchor href. */
ref?: string;
}>;
/** Index into newChildren where the expression value nodes begin (for wrapping). */
startChildIndex: number;
/** The child array where the expression definition nodes were collected. */
targetChildren: ElementContent[] | null;
/**
* Index into the child array where the expression value nodes end (exclusive).
* Set when a non-expression element (e.g. comment span) follows the last
* expression token. When unset (-1), wrapping extends to the end of the array.
*/
endChildIndex: number;
} | null;
/**
* Result of the last flushed compound expression. Set by flushPendingExpression
* so enhanceChildren can wrap the expression nodes in a value-ref element.
* Consumed (cleared) by enhanceChildren after wrapping.
*/
lastFlushedExpression: {
value: string;
varName: string;
startChildIndex: number;
endChildIndex: number;
refs?: Record<string, string>;
targetChildren: ElementContent[] | null;
} | null;
/**
* Active array literal value collection. Set when `[` follows a `const x =`
* declaration. Collects element values (literals and resolved variable
* references) and flushes a `'value'` scope binding on `]`.
*/
pendingArrayValue: {
varName: string;
elements: string[];
bracketDepth: number;
/** True when a `...` spread was just seen — the next identifier will be resolved and inlined. */
pendingSpread: boolean;
} | null;
/**
* Active function-parameter context. Set when inside a parenthesised parameter
* list of a known owner (type def arrow, annotation arrow, function decl, or
* callback property in deep mode). `pl-v` spans inside this context are treated
* as function parameters rather than object properties.
*/
funcParamContext: {
/** Owner name for anchor building */
ownerName: string;
/** Base anchor href from the linkMap */
anchorHref: string;
/** Paren nesting depth (1 = top-level params) */
parenDepth: number;
/** Nesting depth of braces/brackets inside the param list (for destructuring) */
nestedBracketDepth: number;
/** Nesting depth of angle brackets inside the param list (for generics) */
nestedAngleDepth: number;
/** 0-indexed position of the current parameter */
paramIndex: number;
/** Whether this is a definition site (type def) or reference site */
isDefinition: boolean;
/** Inherited property path from an outer owner (for deep callback nesting) */
basePropPath: string[];
/** Whether we're inside a default value expression (after `=` in a param slot) */
inDefaultValue: boolean;
/** Most recently linked param name (for upgrading to type-ref when annotation follows) */
lastParamName: string | null;
/** Flat destructured names accumulated inside `{ }` in the param list */
destructuredNames: string[];
/** True when a `:` is seen inside destructuring braces — indicates rename or nesting, bail out of prop-ref bindings */
sawColonInDestructuring: boolean;
/** Pending scope bindings collected from this function's params, flushed into the function scope on `{` */
pendingScopeBindings: Map<string, ScopeBinding>;
} | null;
/** Set after seeing pl-k("import") keyword — JS import statement in progress */
sawJsImportKeyword: boolean;
/** Collected named import identifiers: { localName, exportedName } */
pendingImportNames: Array<{
localName: string;
exportedName: string;
}>;
/** Default import name (before `{` or `from`) */
pendingDefaultImport: string | null;
/** Namespace import name (`import * as X`) */
pendingNamespaceImport: string | null;
/** True when `as` keyword seen inside import — next pl-c1 is alias */
importSawAs: boolean;
/** Set after seeing `from` keyword while import is active */
sawFromKeyword: boolean;
/** True when inside the `{ }` block of a named import */
inImportBraces: boolean;
/** True when `*` was seen in import context (namespace import) */
importSawStar: boolean;
/** Paren depth tracking for dynamic `import()` expressions */
dynamicImportDepth: number;
/**
* Deferred link for a dynamic import string. Set when a string literal
* is seen inside `import(...)` and finalized only when `)` closes the
* expression with no other content. Cleared if computed content is detected.
*/
pendingDynamicImportLink: {
node: Element;
href: string;
rawValue: string;
} | null;
/**
* Deferred `data-import` annotation for a dynamic import string that didn't
* match `moduleLinkMap`. Applied only at finalization when the expression is
* confirmed non-computed. Cleared if computed content is detected.
*/
pendingDynamicImportAnnotation: {
node: Element;
rawValue: string;
} | null;
/** True when non-string content is detected inside `import(...)`, preventing link creation */
dynamicImportIsComputed: boolean;
/** Set after seeing CSS `@import` keyword — CSS import statement in progress */
sawCssImportKeyword: boolean;
/** Resolved imports collected during the scan, keyed by module specifier */
resolvedImports: Map<string, ResolvedImport>;
/** Unresolved module specifiers that didn't match moduleLinkMap */
unresolvedImports: Set<string>;
/** Set after seeing pl-k("export") keyword — JS export statement in progress */
sawExportKeyword: boolean;
/** Set after seeing pl-k("default") following an export keyword */
sawExportDefaultKeyword: boolean;
/** The export kind keyword seen after `export` (e.g., "function", "const", "type") */
pendingExportKind: ResolvedExport['kind'] | null;
/** The export keyword node, for adding `id` attribute once the export name is known */
pendingExportKeywordNode: Element | null;
/** Collected export names for `export { a, b as c }` */
pendingExportNames: Array<{
localName: string;
exportedName: string;
node: Element;
}>;
/** True when inside the `{ }` block of a named export */
inExportBraces: boolean;
/** True when `as` keyword seen inside export — next identifier is the external name */
exportSawAs: boolean;
/** Resolved exports collected during the scan */
resolvedExports: ResolvedExport[];
/** Bare CSS class selectors collected for CSS Modules-style default export objects */
cssModuleExports: Map<string, string>;
/** Index into resolvedExports of the last recorded variable export awaiting type resolution */
pendingExportTypeIndex: number | null;
/** Index into resolvedExports of the last recorded variable export awaiting kind refinement (e.g. arrow → function) */
pendingExportKindIndex: number | null;
/** Tracks parenthesis nesting depth since pendingExportKindIndex was set.
* Only an `=>` at depth 0 should refine the export kind — arrows inside
* function calls (depth > 0) belong to nested expressions. */
exportKindParenDepth: number;
/** For multi-declarator exports (`export const a = 1, b = 2`), remembers the
* declaration keyword so the second declarator can be re-armed on `,`. */
pendingMultiDeclKind: 'const' | 'let' | 'var' | null;
/** Nesting depth for `()`, `[]`, `{}` while inside a multi-declarator export.
* Only a `,` at depth 0 separates declarators. */
multiDeclNestingDepth: number;
/** Recently-recorded export list entries awaiting `from 'module'` enrichment.
* Populated at the closing `}` of `export { ... }` so the module string
* handler can look up each entry against moduleLinkMap and set typeHref. */
pendingReExportEntries: Array<{
localName: string;
index: number;
}>;
/** True when `export *` was recorded and awaits `from 'module'` handling. */
pendingStarReExport: boolean;
}
/**
* Creates a fresh ScanState.
*/
export declare function createScanState(): ScanState;
/**
* Looks up an owner name in the linkMap.
*/
export declare function lookupOwner(name: string, linkMap: Record<string, string>): {
ownerName: string;
anchorHref: string;
} | null;
/**
* Returns the current active owner context, or null if none.
*/
export declare function currentOwner(state: ScanState): OwnerContext | null;
/**
* Builds the property href for the given owner and property path.
* - If a named param anchor exists (e.g., `makeItem[0]` → `#make-item:props`):
* uses `paramAnchorHref:propPath` (e.g., `#make-item:props:label`)
* - For type-def/type-annotation: `#anchor:prop-path`
* - For func-call/jsx param 0: `#anchor::prop-path` (zero omitted)
* - For func-call param N: `#anchor:N:prop-path`
*/
export declare function buildPropHref(owner: OwnerContext, propPathStr: string): string;
/**
* Builds the anchor map lookup key for a deep callback property.
* For JSX/func-call owners with paramAnchorHref, uses the resolved owner key path.
* For type-def/type-annotation, uses `OwnerName:prop.path`.
*/
export declare function buildParamOwnerKey(owner: OwnerContext, propPath: string[]): string;
/**
* Builds the anchor for a function parameter.
*
* At **definition sites** (type defs), checks `linkMap["Owner[N]"]` for a named
* anchor, then falls back to the positional format `#anchor[N]`.
*
* At **reference sites**, resolves through `linkMap["OwnerKey[N]"]` named anchors,
* or falls back to `#anchor[N]`.
*/
export declare function buildParamHref(ctx: NonNullable<ScanState['funcParamContext']>, paramName: string, linkMap: Record<string, string>): string;
/**
* Records a value-object binding for the current pendingObjectValue in the scope stack.
* Clears pendingObjectValue after flushing.
*/
export declare function recordObjectValueBinding(state: ScanState): void;
/**
* Records a value binding for the current pendingArrayValue in the scope stack.
* Formats the elements as `[elem1, elem2, ...]` and clears pendingArrayValue.
*/
/**
* Clears all JS import-related parsing state.
* Used after an import statement is fully consumed, or when we discover the
* `import` keyword was not actually an import statement (e.g. `import.meta`).
*/
export declare function resetImportState(state: ScanState): void;
/**
* Clears all JS export-related parsing state.
* Used after an export statement is fully consumed.
*/
export declare function resetExportState(state: ScanState): void;
/**
* Finalizes an in-progress `export default ...` statement that has reached a
* statement boundary before a named declaration identifier was seen.
*/
export declare function finalizePendingDefaultExport(state: ScanState): boolean;
/**
* Records a resolved export and sets the `id` attribute on the export keyword node.
*/
export declare function recordExport(state: ScanState, name: string, kind: ResolvedValueExport['kind']): number;
export declare function getResolvedValueExportAt(state: ScanState, index: number | null): ResolvedValueExport | null;
export declare function recordArrayValueBinding(state: ScanState): void;
export {};