UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

504 lines 21.1 kB
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 {};