Skip to content

Commit

Permalink
feat: #573 - ModuledNode#getExportedDeclarations() now returns a map.
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ModuledNode#getExportedDeclarations() now returns a map with a key as the export name and the value as an array of declarations that are exported.
  • Loading branch information
dsherret committed Mar 22, 2019
1 parent 967206f commit 752aaf1
Show file tree
Hide file tree
Showing 23 changed files with 362 additions and 197 deletions.
4 changes: 4 additions & 0 deletions breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ These two structures are now differentiated based on their new `.kind` property.

These two structures are now differentiated based on their new `.kind` property. Previously it used the `isSelfClosing` property.

### `ModuledNode#getExportedDeclarations()` now returns a map

So instead of just returning an array of nodes, it now returns a map. The key is the name it was exported on and the value is an array of declarations for that value. This will make it much easier to identify the name a node was exported on.

## Version 1

Renamed library to `ts-morph` and reset version to 1.0.0.
Expand Down
30 changes: 16 additions & 14 deletions docs/details/exports.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,21 @@ Note: This is safe to call even when there is no default export.

### Getting Exported Declarations

The exported declarations of a file or module can be retrieved via `.getExportedDeclarations()`.
The exported declarations of a file or module can be retrieved via `.getExportedDeclarations()`. This will return a map keyed on the export name with a value of the exported declarations for that name.

For example, given the following setup:

```ts
// main.ts
export * from "./classes";
export {Interface1} from "./interfaces";
export { Interface1 as AliasedInterface } from "./interfaces";

class MainClass {}
export default MainClass;
namespace MergedNamespace { let t; }
namespace MergedNamespace { let u; }

export { MergedNamespace };

export default 5;

// classes.ts
export * from "./Class1";
Expand All @@ -102,27 +106,25 @@ export interface Interface2 {}
The following code:

```ts
import { Project, TypeGuards } from "ts-morph";
import { Project, TypeGuards, ExportedDeclarations } from "ts-morph";

const project = new Project();
project.addExistingSourceFiles("**/*.ts");
const mainFile = project.getSourceFileOrThrow("main.ts");

for (const declaration of mainFile.getExportedDeclarations()) {
if (TypeGuards.isClassDeclaration(declaration) || TypeGuards.isInterfaceDeclaration(declaration))
console.log(`Name: ${declaration.getName()}`);
else
throw new Error(`Not expected declaration kind: ${declaration.getKindName()}`);
for (const [name, declarations] of mainFile.getExportedDeclarations().entries()) {
console.log(`${name}: ${declarations.map(d => d.getText()).join(", ")}`)
}
```

Outputs the following:

```
Name: MainClass
Name: Class1
Name: Class2
Name: Interface1
Class1: export class Class1 {}
Class2: export class Class2 {}
AliasedInterface: export interface Interface1 {}
MergedNamespace: namespace MergedNamespace { let t; }, namespace MergedNamespace { let u; }
default: 5
```

## Export Declarations
Expand Down
28 changes: 25 additions & 3 deletions lib/ts-morph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,14 @@ export interface CompilerOptionsFromTsConfigResult {
* @param options - Options.
*/
export declare function getCompilerOptionsFromTsConfig(filePath: string, options?: CompilerOptionsFromTsConfigOptions): CompilerOptionsFromTsConfigResult;
export interface ReadonlyMap<K, V> {
readonly size: number;
get(key: K): V | undefined;
has(key: K): boolean;
entries(): IterableIterator<[K, V]>;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
}

/**
* Type guards for checking the type of a node.
Expand Down Expand Up @@ -1912,6 +1920,18 @@ export declare type TypeElementTypes = PropertySignature | MethodSignature | Con

export declare type TemplateLiteral = TemplateExpression | NoSubstitutionTemplateLiteral;

/**
* Local target declarations.
* @remarks This may be missing some types. Please open an issue if this returns a type not listed here.
*/
export declare type LocalTargetDeclarations = SourceFile | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | FunctionDeclaration | VariableDeclaration | TypeAliasDeclaration | NamespaceDeclaration | ExportAssignment;

/**
* Declarations that can be exported from a module.
* @remarks This may be missing some types. Please open an issue if this returns a type not listed here.
*/
export declare type ExportedDeclarations = ClassDeclaration | InterfaceDeclaration | EnumDeclaration | FunctionDeclaration | VariableDeclaration | TypeAliasDeclaration | NamespaceDeclaration | Expression;

export declare function AmbientableNode<T extends Constructor<AmbientableNodeExtensionType>>(Base: T): Constructor<AmbientableNode> & T;

export interface AmbientableNode {
Expand Down Expand Up @@ -2660,10 +2680,12 @@ export interface ModuledNode {
/**
* Gets all the declarations that are exported from the module.
*
* The key is the name it's exported on and the value is the array of declarations for that name.
*
* This will include declarations that are transitively exported from other modules. If you mean to get the export
* declarations then use `.getExportDeclarations()`.
*/
getExportedDeclarations(): Node[];
getExportedDeclarations(): ReadonlyMap<string, ExportedDeclarations[]>;
/**
* Removes any "export default".
*/
Expand Down Expand Up @@ -5859,7 +5881,7 @@ export declare class ArrowFunction extends ArrowFunctionBase<ts.ArrowFunction> {
getEqualsGreaterThan(): Node<ts.Token<SyntaxKind.EqualsGreaterThanToken>>;
}

declare const FunctionDeclarationBase: Constructor<ChildOrderableNode> & Constructor<UnwrappableNode> & Constructor<TextInsertableNode> & Constructor<OverloadableNode> & Constructor<BodyableNode> & Constructor<AsyncableNode> & Constructor<GeneratorableNode> & Constructor<FunctionLikeDeclaration> & Constructor<AmbientableNode> & Constructor<NamespaceChildableNode> & Constructor<ExportableNode> & Constructor<ModifierableNode> & Constructor<NameableNode> & typeof Node;
declare const FunctionDeclarationBase: Constructor<ChildOrderableNode> & Constructor<UnwrappableNode> & Constructor<TextInsertableNode> & Constructor<OverloadableNode> & Constructor<BodyableNode> & Constructor<AsyncableNode> & Constructor<GeneratorableNode> & Constructor<AmbientableNode> & Constructor<ExportableNode> & Constructor<FunctionLikeDeclaration> & Constructor<NamespaceChildableNode> & Constructor<NameableNode> & typeof Node;

declare const FunctionDeclarationOverloadBase: Constructor<ChildOrderableNode> & Constructor<UnwrappableNode> & Constructor<TextInsertableNode> & Constructor<AsyncableNode> & Constructor<GeneratorableNode> & Constructor<ModifierableNode> & Constructor<SignaturedDeclaration> & Constructor<AmbientableNode> & Constructor<NamespaceChildableNode> & Constructor<JSDocableNode> & Constructor<TypeParameteredNode> & Constructor<ExportableNode> & typeof Node;

Expand Down Expand Up @@ -6973,7 +6995,7 @@ export declare class ExportSpecifier extends ExportSpecifierBase<ts.ExportSpecif
/**
* Gets all the declarations referenced by the export specifier.
*/
getLocalTargetDeclarations(): Node[];
getLocalTargetDeclarations(): LocalTargetDeclarations[];
/**
* Removes the export specifier.
*/
Expand Down
2 changes: 1 addition & 1 deletion scripts/inspectors/TsMorphInspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class TsMorphInspector {
}
}

return Array.from(nodes.values());
return ArrayUtils.from(nodes.values());

function getDependencyNode(node: WrappedNode) {
if (nodes.has(node))
Expand Down
4 changes: 2 additions & 2 deletions src/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class Project {
for (const sourceFile of result.unchangedSourceFiles)
sourceFiles.delete(sourceFile);

return Array.from(sourceFiles.values());
return ArrayUtils.from(sourceFiles.values());
}

/**
Expand Down Expand Up @@ -376,7 +376,7 @@ export class Project {
return ArrayUtils.from(sourceFiles);

function* getFilteredSourceFiles() {
const sourceFilePaths = Array.from(getSourceFilePaths());
const sourceFilePaths = ArrayUtils.from(getSourceFilePaths());
const matchedPaths = matchGlobs(sourceFilePaths, globPatterns!, fileSystemWrapper.getCurrentDirectory());

for (const matchedPath of matchedPaths)
Expand Down
26 changes: 22 additions & 4 deletions src/compiler/ast/aliases.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { AssertTrue, IsExact } from "conditional-type-checks";
import { ts } from "../../typescript";
import { ArrayBindingPattern, ObjectBindingPattern, BindingElement } from "./binding";
import { GetAccessorDeclaration, MethodDeclaration, SetAccessorDeclaration } from "./class";
import { GetAccessorDeclaration, MethodDeclaration, SetAccessorDeclaration, ClassDeclaration } from "./class";
import { ComputedPropertyName, Identifier, Node, QualifiedName } from "./common";
import { Decorator } from "./decorator";
import { PropertyAccessExpression, PropertyAssignment, ShorthandPropertyAssignment, SpreadAssignment,
import { EnumDeclaration } from "./enum";
import { Expression, PropertyAccessExpression, PropertyAssignment, ShorthandPropertyAssignment, SpreadAssignment,
ThisExpression, OmittedExpression, CallExpression, NewExpression } from "./expression";
import { ExternalModuleReference } from "./module";
import { CallSignatureDeclaration, ConstructSignatureDeclaration, IndexSignatureDeclaration, MethodSignature, PropertySignature } from "./interface";
import { FunctionDeclaration } from "./function";
import { SourceFile, ExternalModuleReference, ExportAssignment, NamespaceDeclaration } from "./module";
import { CallSignatureDeclaration, ConstructSignatureDeclaration, IndexSignatureDeclaration, MethodSignature, PropertySignature, InterfaceDeclaration } from "./interface";
import { JsxAttribute, JsxElement, JsxExpression, JsxFragment, JsxSelfClosingElement, JsxSpreadAttribute, JsxText, JsxOpeningElement } from "./jsx";
import { NoSubstitutionTemplateLiteral, NumericLiteral, StringLiteral, TemplateExpression, TaggedTemplateExpression } from "./literal";
import { CaseClause, DefaultClause } from "./statement";
import { VariableDeclaration } from "./variable";
import { TypeAliasDeclaration } from "./type";

type WrappedToCompilerNodeType<T extends Node> = T["compilerNode"];

Expand Down Expand Up @@ -75,3 +79,17 @@ function typeElementTypes() {

export type TemplateLiteral = TemplateExpression | NoSubstitutionTemplateLiteral;
type _TemplateLiteralTest = AssertTrue<IsExact<WrappedToCompilerNodeType<TemplateLiteral>, ts.TemplateLiteral>>;

/**
* Local target declarations.
* @remarks This may be missing some types. Please open an issue if this returns a type not listed here.
*/
export type LocalTargetDeclarations = SourceFile | ClassDeclaration | InterfaceDeclaration | EnumDeclaration | FunctionDeclaration | VariableDeclaration
| TypeAliasDeclaration | NamespaceDeclaration | ExportAssignment;

/**
* Declarations that can be exported from a module.
* @remarks This may be missing some types. Please open an issue if this returns a type not listed here.
*/
export type ExportedDeclarations = ClassDeclaration | InterfaceDeclaration | EnumDeclaration | FunctionDeclaration | VariableDeclaration
| TypeAliasDeclaration | NamespaceDeclaration | Expression;
102 changes: 55 additions & 47 deletions src/compiler/ast/base/ModuledNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { FormattingKind, removeChildrenWithFormatting } from "../../../manipulat
import { ImportDeclarationStructure, ExportDeclarationStructure, ExportAssignmentStructure, OptionalKind } from "../../../structures";
import { Constructor } from "../../../types";
import { ts, SyntaxKind } from "../../../typescript";
import { ArrayUtils, TypeGuards, createHashSet } from "../../../utils";
import { ArrayUtils, TypeGuards, createMap, ReadonlyMap } from "../../../utils";
import { Symbol } from "../../symbols";
import { ExportedDeclarations } from "../aliases";
import { Node } from "../common";
import { ImportDeclaration, ExportDeclaration, ExportAssignment } from "../module";
import { StatementedNode } from "../statement";
Expand Down Expand Up @@ -163,10 +164,12 @@ export interface ModuledNode {
/**
* Gets all the declarations that are exported from the module.
*
* The key is the name it's exported on and the value is the array of declarations for that name.
*
* This will include declarations that are transitively exported from other modules. If you mean to get the export
* declarations then use `.getExportDeclarations()`.
*/
getExportedDeclarations(): Node[];
getExportedDeclarations(): ReadonlyMap<string, ExportedDeclarations[]>;
/**
* Removes any "export default".
*/
Expand Down Expand Up @@ -332,57 +335,62 @@ export function ModuledNode<T extends Constructor<ModuledNodeExtensionType>>(Bas
return symbol == null ? [] : this._context.typeChecker.getExportsOfModule(symbol);
}

getExportedDeclarations(): Node[] {
getExportedDeclarations(): ReadonlyMap<string, ExportedDeclarations[]> {
const result = createMap<string, ExportedDeclarations[]>();
const exportSymbols = this.getExportSymbols();
return ArrayUtils.from(getDeclarationsForSymbols());

function* getDeclarationsForSymbols() {
const handledDeclarations = createHashSet<Node>();
for (const symbol of exportSymbols) {
for (const declaration of symbol.getDeclarations()) {
const declarations = ArrayUtils.from(getDeclarationHandlingImportsAndExports(declaration));
result.set(symbol.getName(), declarations as ExportedDeclarations[]);
}
}

for (const symbol of exportSymbols)
for (const declaration of symbol.getDeclarations())
yield* getDeclarationHandlingImportsAndExports(declaration);
return result;

function* getDeclarationHandlingImportsAndExports(declaration: Node): IterableIterator<Node> {
if (handledDeclarations.has(declaration))
function* getDeclarationHandlingImportsAndExports(declaration: Node): IterableIterator<Node> {
if (TypeGuards.isExportSpecifier(declaration)) {
for (const d of declaration.getLocalTargetDeclarations())
yield* getDeclarationHandlingImportsAndExports(d);
}
else if (TypeGuards.isExportAssignment(declaration)) {
const expression = declaration.getExpression();
if (expression == null || expression.getKind() !== SyntaxKind.Identifier) {
yield expression;
return;
handledDeclarations.add(declaration);

if (TypeGuards.isExportSpecifier(declaration)) {
for (const d of declaration.getLocalTargetDeclarations())
yield* getDeclarationHandlingImportsAndExports(d);
}
else if (TypeGuards.isExportAssignment(declaration)) {
const identifier = declaration.getExpression();
if (identifier == null || identifier.getKind() !== SyntaxKind.Identifier)
return;
yield* getDeclarationsForSymbol(identifier.getSymbol());
}
else if (TypeGuards.isImportSpecifier(declaration)) {
const identifier = declaration.getNameNode();
const symbol = identifier.getSymbol();
if (symbol == null)
return;
yield* getDeclarationsForSymbol(symbol.getAliasedSymbol() || symbol);
}
else if (TypeGuards.isImportClause(declaration)) {
const identifier = declaration.getDefaultImport();
if (identifier == null)
return;
const symbol = identifier.getSymbol();
if (symbol == null)
return;
yield* getDeclarationsForSymbol(symbol.getAliasedSymbol() || symbol);
}
else
yield declaration;

function* getDeclarationsForSymbol(symbol: Symbol | undefined): IterableIterator<Node> {
if (symbol == null)
return;
for (const d of symbol.getDeclarations())
yield* getDeclarationHandlingImportsAndExports(d);
}
yield* getDeclarationsForSymbol(expression.getSymbol());
}
else if (TypeGuards.isImportSpecifier(declaration)) {
const identifier = declaration.getNameNode();
const symbol = identifier.getSymbol();
if (symbol == null)
return;
yield* getDeclarationsForSymbol(symbol.getAliasedSymbol() || symbol);
}
else if (TypeGuards.isImportClause(declaration)) {
const identifier = declaration.getDefaultImport();
if (identifier == null)
return;
const symbol = identifier.getSymbol();
if (symbol == null)
return;
yield* getDeclarationsForSymbol(symbol.getAliasedSymbol() || symbol);
}
else if (TypeGuards.isNamespaceImport(declaration)) {
const symbol = declaration.getNameNode().getSymbol();
if (symbol == null)
return;
yield* getDeclarationsForSymbol(symbol.getAliasedSymbol() || symbol);
}
else
yield declaration;

function* getDeclarationsForSymbol(symbol: Symbol | undefined): IterableIterator<Node> {
if (symbol == null)
return;
for (const d of symbol.getDeclarations())
yield* getDeclarationHandlingImportsAndExports(d);
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/ast/module/ExportSpecifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as errors from "../../../errors";
import { insertIntoParentTextRange, removeCommaSeparatedChild, removeChildren } from "../../../manipulation";
import { SyntaxKind, ts } from "../../../typescript";
import { StringUtils } from "../../../utils";
import { LocalTargetDeclarations } from "../aliases";
import { Node } from "../common";
import { Symbol } from "../../symbols";
import { callBaseGetStructure } from "../callBaseGetStructure";
Expand Down Expand Up @@ -148,9 +149,9 @@ export class ExportSpecifier extends ExportSpecifierBase<ts.ExportSpecifier> {
/**
* Gets all the declarations referenced by the export specifier.
*/
getLocalTargetDeclarations(): Node[] {
getLocalTargetDeclarations(): LocalTargetDeclarations[] {
const symbol = this.getLocalTargetSymbol();
return symbol == null ? [] : symbol.getDeclarations();
return symbol == null ? [] : symbol.getDeclarations() as LocalTargetDeclarations[];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export { createWrappedNode, CreateWrappedNodeOptions } from "./utils/compiler/cr
export { printNode, PrintNodeOptions } from "./utils/compiler/printNode";
export { SourceFileReferencingNodes } from "./utils/references/SourceFileReferenceContainer";
export { CompilerOptionsFromTsConfigOptions, CompilerOptionsFromTsConfigResult, getCompilerOptionsFromTsConfig } from "./utils/tsconfig/getCompilerOptionsFromTsConfig";
export { TypeGuards } from "./utils/TypeGuards";
export { ReadonlyMap, TypeGuards } from "./utils";
export { WriterFunctions, WriterFunctionOrValue } from "./structurePrinters/WriterFunctions";
export * from "./typescript/public";

0 comments on commit 752aaf1

Please sign in to comment.