Skip to content

Commit

Permalink
feat: Improved support for global files
Browse files Browse the repository at this point in the history
Lift requirement of only one global file per project
If a value is declared in more than one global file entry point, this will result in it being included in both... which isn't great, but seems to produce mostly reasonable results.
Resolves #1424
  • Loading branch information
Gerrit0 committed Dec 29, 2020
1 parent 56158e2 commit 5882e44
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 55 deletions.
8 changes: 4 additions & 4 deletions src/lib/converter/context.ts
Expand Up @@ -160,12 +160,12 @@ export class Context {

createDeclarationReflection(
kind: ReflectionKind,
symbol: ts.Symbol,
name = getHumanName(symbol.name)
symbol: ts.Symbol | undefined,
name = getHumanName(symbol?.name ?? "unknown")
) {
const reflection = new DeclarationReflection(name, kind, this.scope);
this.addChild(reflection);
if (this.converter.isExternal(symbol)) {
if (symbol && this.converter.isExternal(symbol)) {
reflection.setFlag(ReflectionFlag.External);
}
this.registerReflection(reflection, symbol);
Expand All @@ -175,7 +175,7 @@ export class Context {
this,
reflection,
// FIXME this isn't good enough.
this.converter.getNodesForSymbol(symbol, kind)[0]
symbol && this.converter.getNodesForSymbol(symbol, kind)[0]
);

return reflection;
Expand Down
95 changes: 44 additions & 51 deletions src/lib/converter/converter.ts
Expand Up @@ -4,13 +4,7 @@ import * as assert from "assert";
import { resolve } from "path";

import { Application } from "../application";
import {
Type,
ProjectReflection,
ReflectionKind,
ContainerReflection,
DeclarationReflection,
} from "../models/index";
import { Type, ProjectReflection, ReflectionKind } from "../models/index";
import { Context } from "./context";
import { ConverterComponent } from "./components";
import { Component, ChildableComponent } from "../utils/component";
Expand Down Expand Up @@ -249,34 +243,40 @@ export class Converter extends ChildableComponent<
*/
private compile(entryPoints: readonly string[], context: Context) {
const baseDir = getCommonDirectory(entryPoints);
const entries: [string, ts.SourceFile, ts.Program][] = [];

entryLoop: for (const entry of entryPoints.map(normalizePath)) {
const entries: {
file: string;
sourceFile: ts.SourceFile;
program: ts.Program;
context?: Context;
}[] = [];

entryLoop: for (const file of entryPoints.map(normalizePath)) {
for (const program of context.programs) {
const sourceFile = program.getSourceFile(entry);
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
entries.push([entry, sourceFile, program]);
entries.push({ file, sourceFile, program });
continue entryLoop;
}
}
this.application.logger.warn(
`Unable to locate entry point: ${entry}`
`Unable to locate entry point: ${file}`
);
}

for (const [entry, file, program] of entries) {
context.setActiveProgram(program);
this.convertExports(
for (const entry of entries) {
context.setActiveProgram(entry.program);
entry.context = this.convertExports(
context,
file,
entry.sourceFile,
entryPoints,
getModuleName(resolve(entry), baseDir)
getModuleName(resolve(entry.file), baseDir)
);
}

for (const [, file, program] of entries) {
context.setActiveProgram(program);
this.convertReExports(context, file);
for (const { sourceFile, context } of entries) {
// active program is already set on context
assert(context);
this.convertReExports(context, sourceFile);
}

context.setActiveProgram(undefined);
Expand All @@ -296,49 +296,28 @@ export class Converter extends ChildableComponent<
// create modules for each entry. Register the project as this module.
context.project.registerReflection(context.project, symbol);
moduleContext = context;
} else if (symbol) {
} else {
const reflection = context.createDeclarationReflection(
ReflectionKind.Module,
symbol,
entryName
);
moduleContext = context.withScope(reflection);
} else {
this.application.logger.warn(
`If specifying a global file as an entry point, only one entry point may be specified. (${node.fileName})`
);
return;
}

for (const exp of getExports(context, node).filter((exp) =>
context
.resolveAliasedSymbol(exp)
.getDeclarations()
?.every((d) => d.getSourceFile() === node.getSourceFile())
isDirectExport(context.resolveAliasedSymbol(exp), node)
)) {
convertSymbol(moduleContext, exp);
}
}

private convertReExports(context: Context, node: ts.SourceFile) {
const symbol = context.checker.getSymbolAtLocation(node) ?? node.symbol;
// Was a global "module"... no re exports.
if (symbol == null) return;

const moduleReflection = context.project.getReflectionFromSymbol(
symbol
);
assert(
moduleReflection instanceof ContainerReflection ||
moduleReflection instanceof DeclarationReflection
);
return moduleContext;
}

const moduleContext = context.withScope(moduleReflection);
for (const exp of getExports(context, node).filter((exp) =>
context
.resolveAliasedSymbol(exp)
.getDeclarations()
?.some((d) => d.getSourceFile() !== node.getSourceFile())
private convertReExports(moduleContext: Context, node: ts.SourceFile) {
for (const exp of getExports(moduleContext, node).filter(
(exp) =>
!isDirectExport(moduleContext.resolveAliasedSymbol(exp), node)
)) {
convertSymbol(moduleContext, exp);
}
Expand Down Expand Up @@ -437,10 +416,24 @@ function getExports(
// and lift that up one level
if (
globalSymbols.length === 1 &&
globalSymbols[0].getDeclarations()?.every(ts.isModuleDeclaration)
globalSymbols[0]
.getDeclarations()
?.every(
(declaration) =>
ts.isModuleDeclaration(declaration) &&
ts.isStringLiteral(declaration.name)
)
) {
return context.checker.getExportsOfModule(globalSymbols[0]);
}

return globalSymbols;
}

function isDirectExport(symbol: ts.Symbol, file: ts.SourceFile): boolean {
return (
symbol
.getDeclarations()
?.every((decl) => decl.getSourceFile() === file) ?? false
);
}

0 comments on commit 5882e44

Please sign in to comment.