Skip to content

Commit

Permalink
fix: Missing default exports
Browse files Browse the repository at this point in the history
My dislike of default exports has grown even more...
Also: Make the sidebar navigation show "Exports" if there is a single module
  • Loading branch information
Gerrit0 committed Oct 31, 2020
1 parent 82042e4 commit 017fad1
Show file tree
Hide file tree
Showing 8 changed files with 513 additions and 106 deletions.
6 changes: 5 additions & 1 deletion src/lib/converter/context.ts
Expand Up @@ -189,10 +189,14 @@ export class Context {
expectSymbolAtLocation(node: ts.Node): ts.Symbol {
const symbol = this.getSymbolAtLocation(node);
if (!symbol) {
const { line } = ts.getLineAndCharacterOfPosition(
node.getSourceFile(),
node.pos
);
throw new Error(
`Expected a symbol for node with kind ${
ts.SyntaxKind[node.kind]
}`
} at ${node.getSourceFile().fileName}:${line + 1}`
);
}
return symbol;
Expand Down
10 changes: 9 additions & 1 deletion src/lib/converter/nodes/block.ts
Expand Up @@ -102,7 +102,7 @@ export class BlockConverter extends ConverterNodeComponent<
result = createDeclaration(
context,
node,
ReflectionKind.Module,
ReflectionKind.Namespace,
this.getModuleName(node.fileName)
);
context.withScope(result, () => {
Expand Down Expand Up @@ -156,6 +156,14 @@ export class BlockConverter extends ConverterNodeComponent<
if (!symbol && ts.isModuleBlock(node)) {
symbol = context.checker.getSymbolAtLocation(node.parent.name);
}

// The generated docs aren't great, but you really ought not be using
// this in the first place... so it's better than nothing.
const exportEq = symbol?.exports?.get("export=" as ts.__String);
if (exportEq) {
return [exportEq];
}

if (symbol) {
return context.checker.getExportsOfModule(symbol);
}
Expand Down
81 changes: 45 additions & 36 deletions src/lib/converter/nodes/export.ts
@@ -1,56 +1,65 @@
import * as ts from "typescript";
import * as assert from "assert";

import {
Reflection,
ReflectionFlag,
ContainerReflection,
DeclarationReflection,
Reflection,
ReflectionKind,
} from "../../models/index";
import { Context } from "../context";
import { Component, ConverterNodeComponent } from "../components";
import { createReferenceOrDeclarationReflection } from "../factories/reference";

@Component({ name: "node:export" })
// Either a default export or export=
@Component({ name: "node:export-assignment" })
export class ExportConverter extends ConverterNodeComponent<
ts.ExportAssignment
> {
/**
* List of supported TypeScript syntax kinds.
*/
supports: ts.SyntaxKind[] = [ts.SyntaxKind.ExportAssignment];

convert(context: Context, node: ts.ExportAssignment): Reflection {
let symbol: ts.Symbol | undefined;
convert(
context: Context,
node: ts.ExportAssignment
): Reflection | undefined {
assert(context.scope instanceof ContainerReflection);
const name = node.isExportEquals ? "export =" : "default";

// We might not have a symbol if someone does `export default 1`
const expressionSymbol = context.getSymbolAtLocation(node.expression);

if (expressionSymbol) {
const reflection = createReferenceOrDeclarationReflection(
context,
expressionSymbol,
context.resolveAliasedSymbol(expressionSymbol)
);

// default export
if (
node.symbol &&
(node.symbol.flags & ts.SymbolFlags.Alias) === ts.SymbolFlags.Alias
) {
symbol = context.checker.getAliasedSymbol(node.symbol);
if (reflection) {
reflection.name = name;
}

return reflection;
} else {
const type = context.getTypeAtLocation(node.expression);
symbol = type ? type.symbol : undefined;
}
if (symbol && symbol.declarations) {
const project = context.project;
symbol.declarations.forEach((declaration) => {
if (!declaration.symbol) {
return;
}

const reflection = project.getReflectionFromSymbol(
declaration.symbol
);
if (
node.isExportEquals &&
reflection instanceof DeclarationReflection
) {
reflection.setFlag(ReflectionFlag.ExportAssignment, true);
}
});
}
// We can't use createDeclaration because it expects a ts.Declaration, which we don't have.
const reflection = new DeclarationReflection(
name,
ReflectionKind.Variable,
context.scope
);

return context.scope;
const exportSymbol =
node.symbol ?? context.expectSymbolAtLocation(node);
reflection.type = this.owner.convertType(
context,
context.checker.getTypeOfSymbolAtLocation(exportSymbol, node)
);

context.scope.children ??= [];
context.scope.children.push(reflection);

return reflection;
}
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/lib/converter/nodes/function.ts
Expand Up @@ -36,7 +36,14 @@ export class FunctionConverter extends ConverterNodeComponent<
? ReflectionKind.Method
: ReflectionKind.Function;
const hasBody = !!node.body;
const method = createDeclaration(context, node, kind);
const method = createDeclaration(
context,
node,
kind,
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)
? "default"
: void 0
);

if (
method && // child inheriting will return null on createDeclaration
Expand Down
24 changes: 13 additions & 11 deletions src/lib/output/themes/DefaultTheme.ts
Expand Up @@ -157,7 +157,11 @@ export class DefaultTheme extends Theme {
* @returns The root navigation item.
*/
getNavigation(project: ProjectReflection): NavigationItem {
const builder = new NavigationBuilder(project, project);
const builder = new NavigationBuilder(
project,
project,
this.application.options.getValue("entryPoints").length > 1
);
return builder.build(
this.application.options.getValue("readme") !== "none"
);
Expand Down Expand Up @@ -393,7 +397,8 @@ export class DefaultTheme extends Theme {
export class NavigationBuilder {
constructor(
private project: ProjectReflection,
private entryPoint: ContainerReflection
private entryPoint: ContainerReflection,
private multipleEntryPoints: boolean
) {}

/**
Expand All @@ -404,15 +409,12 @@ export class NavigationBuilder {
*/
build(hasReadmeFile: boolean): NavigationItem {
const root = new NavigationItem("Index", "index.html");

if (this.entryPoint === this.project) {
const modules = new NavigationItem(
"Modules",
hasReadmeFile ? "modules.html" : "index.html",
root
);
modules.isModules = true;
}
const sidebarRoot = new NavigationItem(
this.multipleEntryPoints ? "Modules" : "Exports",
hasReadmeFile ? "modules.html" : "index.html",
root
);
sidebarRoot.isModules = true;

const modules: DeclarationReflection[] = [];
this.project
Expand Down

0 comments on commit 017fad1

Please sign in to comment.