Skip to content

Commit

Permalink
Defer conversion of aliases when possible
Browse files Browse the repository at this point in the history
Resolves #2856
  • Loading branch information
Gerrit0 committed Feb 16, 2025
1 parent d81c071 commit de7d448
Show file tree
Hide file tree
Showing 9 changed files with 594 additions and 554 deletions.
81 changes: 42 additions & 39 deletions src/lib/converter/converter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ts from "typescript";
import { ok } from "assert";

import type { Application } from "../application.js";
import {
Expand All @@ -23,7 +24,7 @@ import { convertType } from "./types.js";
import { ConverterEvents } from "./converter-events.js";
import { convertSymbol } from "./symbols.js";
import { MinimatchSet, nicePath } from "../utils/paths.js";
import { type GlobString, hasAllFlags, hasAnyFlag, unique } from "#utils";
import { type GlobString, hasAllFlags, hasAnyFlag, partition, unique } from "#utils";
import type { DocumentationEntryPoint } from "../utils/entry-point.js";
import type { CommentParserConfig } from "./comments/index.js";
import type { CommentStyle, ValidationOptions } from "../utils/options/declaration.js";
Expand Down Expand Up @@ -132,6 +133,12 @@ export class Converter extends AbstractComponent<Application, ConverterEvents> {

private _config?: CommentParserConfig;
private _externalSymbolResolvers: Array<ExternalSymbolResolver> = [];
// We try to document symbols which are exported from multiple locations
// in modules/namespaces which declare them, rather than those which re-export them.
// To do this, when converting a symbol, that might be re-exported, we first defer it
// to the second conversion pass.
private _deferPermitted = false;
private _defer: Array<() => void> = [];

get config(): CommentParserConfig {
return this._config || this._buildCommentParserConfig();
Expand Down Expand Up @@ -434,6 +441,14 @@ export class Converter extends AbstractComponent<Application, ConverterEvents> {
}
}

/**
* Defer a conversion step until later. This may only be called during conversion.
*/
deferConversion(cb: () => void): void {
ok(this._deferPermitted, "Attempted to defer conversion when not permitted");
this._defer.push(cb);
}

/**
* Compile the files within the given context and convert the compiler symbols to reflections.
*
Expand All @@ -444,14 +459,10 @@ export class Converter extends AbstractComponent<Application, ConverterEvents> {
entryPoints: readonly DocumentationEntryPoint[],
context: Context,
) {
const entries = entryPoints.map((e) => {
return {
entryPoint: e,
context: undefined as Context | undefined,
};
});

let createModuleReflections = entries.length > 1;
ok(!this._deferPermitted);
this._deferPermitted = true;

let createModuleReflections = entryPoints.length > 1;
if (!createModuleReflections) {
const opts = this.application.options;
createModuleReflections = opts.isSet("alwaysCreateEntryPointModule")
Expand All @@ -467,22 +478,18 @@ export class Converter extends AbstractComponent<Application, ConverterEvents> {
);
}

entries.forEach((e) => {
context.setActiveProgram(e.entryPoint.program);
e.context = this.convertExports(
context,
e.entryPoint,
createModuleReflections,
);
});
for (const { entryPoint, context } of entries) {
// active program is already set on context
// if we don't have a context, then this entry point is being ignored
if (context) {
this.convertReExports(context, entryPoint.sourceFile);
}
for (const entry of entryPoints) {
// Clone context in case deferred conversion uses different programs
const entryContext = context.withScope(context.scope);
entryContext.setActiveProgram(entry.program);
this.convertExports(entryContext, entry, createModuleReflections);
}
context.setActiveProgram(undefined);

while (this._defer.length) {
const first = this._defer.shift()!;
first();
}
this._deferPermitted = false;
}

private convertExports(
Expand Down Expand Up @@ -530,24 +537,20 @@ export class Converter extends AbstractComponent<Application, ConverterEvents> {
}

const allExports = getExports(context, node, symbol);
for (const exp of allExports.filter((exp) => isDirectExport(context.resolveAliasedSymbol(exp), node))) {
const [directExport, indirectExports] = partition(
allExports,
exp => isDirectExport(context.resolveAliasedSymbol(exp), node),
);
for (const exp of directExport) {
this.convertSymbol(moduleContext, exp);
}

return moduleContext;
}

private convertReExports(moduleContext: Context, node: ts.SourceFile) {
for (
const exp of getExports(
moduleContext,
node,
moduleContext.project.getSymbolFromReflection(moduleContext.scope),
).filter(
(exp) => !isDirectExport(moduleContext.resolveAliasedSymbol(exp), node),
)
) {
this.convertSymbol(moduleContext, exp);
if (indirectExports.length) {
this.deferConversion(() => {
for (const exp of indirectExports) {
this.convertSymbol(moduleContext, exp);
}
});
}
}

Expand Down
62 changes: 40 additions & 22 deletions src/lib/converter/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,7 @@ assert(
"conversionOrder contains a symbol flag that converters do not.",
);

export function convertSymbol(
context: Context,
symbol: ts.Symbol,
exportSymbol?: ts.Symbol,
): void {
function _convertSymbolNow(context: Context, symbol: ts.Symbol, exportSymbol: ts.Symbol | undefined) {
if (context.shouldIgnore(symbol)) {
return;
}
Expand Down Expand Up @@ -183,6 +179,23 @@ export function convertSymbol(
}
}

export function convertSymbol(
context: Context,
symbol: ts.Symbol,
exportSymbol?: ts.Symbol,
): void {
// #1795, defer conversion of symbols named `default` so that if a function
// is default exported and also exported with a name, the name takes precedence
if ((exportSymbol?.name ?? symbol.name) === "default") {
context.converter.deferConversion(() => {
_convertSymbolNow(context, symbol, exportSymbol);
});
return;
}

_convertSymbolNow(context, symbol, exportSymbol);
}

function convertSymbols(context: Context, symbols: readonly ts.Symbol[]) {
for (const symbol of symbols) {
convertSymbol(context, symbol);
Expand Down Expand Up @@ -890,25 +903,30 @@ function convertAlias(
symbol: ts.Symbol,
exportSymbol?: ts.Symbol,
): undefined {
const reflection = context.project.getReflectionFromSymbol(
context.resolveAliasedSymbol(symbol),
);
if (
!reflection ||
(reflection &&
!reflection.parent?.kindOf(
ReflectionKind.Project | ReflectionKind.SomeModule,
))
) {
// We don't have this, convert it.
convertSymbol(
context,
// Defer conversion of aliases so that if the original module/namespace
// containing them is included in the docs, we will point to that namespace
// rather than pointing that namespace to the first namespace encountered, #2856.
context.converter.deferConversion(() => {
const reflection = context.project.getReflectionFromSymbol(
context.resolveAliasedSymbol(symbol),
exportSymbol ?? symbol,
);
} else {
createAlias(reflection, context, symbol, exportSymbol);
}
if (
!reflection ||
(reflection &&
!reflection.parent?.kindOf(
ReflectionKind.Project | ReflectionKind.SomeModule,
))
) {
// We don't have this, convert it.
convertSymbol(
context,
context.resolveAliasedSymbol(symbol),
exportSymbol ?? symbol,
);
} else {
createAlias(reflection, context, symbol, exportSymbol);
}
});
}

function createAlias(
Expand Down
Loading

0 comments on commit de7d448

Please sign in to comment.