Skip to content

Commit

Permalink
feat: Add ExternalModuleReference and ImportEqualsDeclaration.
Browse files Browse the repository at this point in the history
Closes #225.
  • Loading branch information
dsherret committed Jan 28, 2018
1 parent 4c07c13 commit c140982
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 13 deletions.
8 changes: 3 additions & 5 deletions code-generation/inspectors/TsInspector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import TsSimpleAst, {InterfaceDeclaration} from "./../../src/main";
import {Memoize, ArrayUtils} from "./../../src/utils";
import {hasDescendantNodeType} from "./../common";
import {hasDescendantBaseType} from "./../common";
import {TsNode} from "./ts";
import {WrapperFactory} from "./WrapperFactory";

Expand All @@ -13,11 +13,9 @@ export class TsInspector {
const compilerApiFile = this.ast.getSourceFileOrThrow("node_modules/typescript/lib/typescript.d.ts");
const interfaces: InterfaceDeclaration[] = [];
for (const interfaceDec of ArrayUtils.flatten(compilerApiFile.getNamespaces().map(n => n.getInterfaces()))) {
if (interfaceDec.getBaseTypes().some(t => hasDescendantNodeType(t)))
if (interfaceDec.getBaseTypes().some(t => hasDescendantBaseType(t, checkingType => checkingType.getText() === "ts.Node")))
interfaces.push(interfaceDec);
}
const tsNodes = interfaces.map(i => this.wrapperFactory.getTsNode(i));
tsNodes.sort((a, b) => a.getName() < b.getName() ? -1 : 1);
return tsNodes;
return ArrayUtils.sortByProperty(interfaces.map(i => this.wrapperFactory.getTsNode(i)), item => item.getName());
}
}
2 changes: 2 additions & 0 deletions code-generation/outputWrappedNodesInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ outputCoverage("Exist", wrappedTsNodes);
output += "\n";
outputCoverage("Not Exist", notWrappedTsNodes);
fs.writeFileSync(path.join(rootFolder, "wrapped-nodes.md"), output);

// play a tone to indicate it's done
console.log("\x07");

function outputCoverage(header: string, tsNodesForOutput: TsNode[], additionalText?: string) {
Expand Down
32 changes: 31 additions & 1 deletion src/compiler/aliases.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as ts from "typescript";
import {Identifier, ComputedPropertyName} from "./common";
import {Identifier, ComputedPropertyName, QualifiedName} from "./common";
import {PropertyAssignment, ShorthandPropertyAssignment, SpreadAssignment} from "./expression";
import {ExternalModuleReference} from "./file";
import {CaseClause, DefaultClause} from "./statement";
import {GetAccessorDeclaration, SetAccessorDeclaration, MethodDeclaration} from "./class";
import {StringLiteral, NumericLiteral} from "./literal";
Expand Down Expand Up @@ -35,6 +36,20 @@ function accessorDeclarationAliasValidation() {
}
}

export type EntityName = Identifier | QualifiedName;

/* istanbul ignore next */
function entityNameValidation() {
const value: ts.EntityName = null as any;
switch (value.kind) {
case ts.SyntaxKind.Identifier:
case ts.SyntaxKind.QualifiedName:
return;
default:
const ensureNever: never = value;
}
}

export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment | MethodDeclaration | AccessorDeclaration;

/* istanbul ignore next */
Expand Down Expand Up @@ -66,3 +81,18 @@ function caseOrDefaultClauseValidation() {
const ensureNever: never = value;
}
}

export type ModuleReference = EntityName | ExternalModuleReference;

/* istanbul ignore next */
function moduleReferenceValidation() {
const value: ts.ModuleReference = null as any;
switch (value.kind) {
case ts.SyntaxKind.Identifier:
case ts.SyntaxKind.QualifiedName:
case ts.SyntaxKind.ExternalModuleReference:
return;
default:
const ensureNever: never = value;
}
}
8 changes: 8 additions & 0 deletions src/compiler/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,14 @@ export class Node<NodeType extends ts.Node = ts.Node> {
protected getNodeFromCompilerNode<LocalNodeType extends ts.Node>(compilerNode: LocalNodeType): Node<LocalNodeType> {
return this.global.compilerFactory.getNodeFromCompilerNode(compilerNode, this.sourceFile);
}

/**
* Gets or creates a node from the internal cache, if it exists.
* @internal
*/
protected getNodeFromCompilerNodeIfExists<LocalNodeType extends ts.Node>(compilerNode: LocalNodeType | undefined): Node<LocalNodeType> | undefined {
return compilerNode == null ? undefined : this.getNodeFromCompilerNode(compilerNode);
}
}

function getWrappedNode(thisNode: Node, compilerNode: ts.Node): Node {
Expand Down
3 changes: 1 addition & 2 deletions src/compiler/common/QualifiedName.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as ts from "typescript";
import {Node} from "./Node";
import {Identifier} from "./Identifier";

export type EntityName = Identifier | QualifiedName;
import {EntityName} from "./../aliases";

export class QualifiedName extends Node<ts.QualifiedName> {
/**
Expand Down
20 changes: 20 additions & 0 deletions src/compiler/file/ExternalModuleReference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as ts from "typescript";
import * as errors from "./../../errors";
import {Node} from "./../common";
import {Expression} from "./../expression";

export class ExternalModuleReference extends Node<ts.ExternalModuleReference> {
/**
* Gets the expression or undefined of the yield expression.
*/
getExpression() {
return this.getNodeFromCompilerNodeIfExists(this.compilerNode.expression) as Expression | undefined;
}

/**
* Gets the expression of the yield expression or throws if it does not exist.
*/
getExpressionOrThrow() {
return errors.throwIfNullOrUndefined(this.getExpression(), "Expected to find an expression.");
}
}
15 changes: 15 additions & 0 deletions src/compiler/file/ImportEqualsDeclaration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as ts from "typescript";
import {ModuleReference} from "./../aliases";
import {JSDocableNode, NamedNode} from "./../base";
import {Node} from "./../common";
import {Statement} from "./../statement";

export const ImportEqualsDeclarationBase = JSDocableNode(NamedNode(Statement));
export class ImportEqualsDeclaration extends ImportEqualsDeclarationBase<ts.ImportEqualsDeclaration> {
/**
* Gets the module reference of the import equals declaration.
*/
getModuleReference(): ModuleReference {
return this.getNodeFromCompilerNode(this.compilerNode.moduleReference) as ModuleReference;
}
}
2 changes: 2 additions & 0 deletions src/compiler/file/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from "./ExportAssignment";
export * from "./ExportDeclaration";
export * from "./ExportSpecifier";
export * from "./ExternalModuleReference";
export * from "./FileSystemRefreshResult";
export * from "./ImportDeclaration";
export * from "./ImportEqualsDeclaration";
export * from "./ImportSpecifier";
export * from "./SourceFile";
2 changes: 1 addition & 1 deletion src/compiler/type/TypeReferenceNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as ts from "typescript";
import {TypeNode} from "./TypeNode";
import {EntityName} from "./../common";
import {EntityName} from "./../aliases";

export class TypeReferenceNode extends TypeNode<ts.TypeReferenceNode> {
/**
Expand Down
2 changes: 2 additions & 0 deletions src/factories/nodeToWrapperMappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const nodeToWrapperMappings: { [key: number]: any } = {
[ts.SyntaxKind.ExportSpecifier]: compiler.ExportSpecifier,
[ts.SyntaxKind.ExpressionWithTypeArguments]: compiler.ExpressionWithTypeArguments,
[ts.SyntaxKind.ExpressionStatement]: compiler.ExpressionStatement,
[ts.SyntaxKind.ExternalModuleReference]: compiler.ExternalModuleReference,
[ts.SyntaxKind.FirstLiteralToken]: compiler.NumericLiteral,
[ts.SyntaxKind.FirstNode]: compiler.QualifiedName,
[ts.SyntaxKind.ForInStatement]: compiler.ForInStatement,
Expand All @@ -52,6 +53,7 @@ export const nodeToWrapperMappings: { [key: number]: any } = {
[ts.SyntaxKind.Identifier]: compiler.Identifier,
[ts.SyntaxKind.IfStatement]: compiler.IfStatement,
[ts.SyntaxKind.ImportDeclaration]: compiler.ImportDeclaration,
[ts.SyntaxKind.ImportEqualsDeclaration]: compiler.ImportEqualsDeclaration,
[ts.SyntaxKind.ImportSpecifier]: compiler.ImportSpecifier,
[ts.SyntaxKind.InterfaceDeclaration]: compiler.InterfaceDeclaration,
[ts.SyntaxKind.LabeledStatement]: compiler.LabeledStatement,
Expand Down
27 changes: 27 additions & 0 deletions src/tests/compiler/file/externalModuleReferenceTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {expect} from "chai";
import * as ts from "typescript";
import {ExternalModuleReference} from "./../../../compiler";
import * as errors from "./../../../errors";
import {getInfoFromTextWithDescendant} from "./../testHelpers";

describe(nameof(ExternalModuleReference), () => {
function getNode(text: string) {
return getInfoFromTextWithDescendant<ExternalModuleReference>(text, ts.SyntaxKind.ExternalModuleReference);
}

// I'm not sure how to make expression null

describe(nameof<ExternalModuleReference>(n => n.getExpression), () => {
it("should get the expression", () => {
const {descendant} = getNode("import test = require('expression');");
expect(descendant.getExpression()!.getText()).to.equal("'expression'");
});
});

describe(nameof<ExternalModuleReference>(n => n.getExpressionOrThrow), () => {
it("should get the expression", () => {
const {descendant} = getNode("import test = require('expression');");
expect(descendant.getExpressionOrThrow().getText()).to.equal("'expression'");
});
});
});
43 changes: 43 additions & 0 deletions src/tests/compiler/file/importEqualsDeclarationTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {expect} from "chai";
import {ImportEqualsDeclaration} from "./../../../compiler";
import {getInfoFromText} from "./../testHelpers";

describe(nameof(ImportEqualsDeclaration), () => {
describe(nameof<ImportEqualsDeclaration>(n => n.getName), () => {
function doTest(text: string, expected: string) {
const {firstChild} = getInfoFromText<ImportEqualsDeclaration>(text);
expect(firstChild.getName()).to.equal(expected);
}

it("should get the name", () => {
doTest("import test = Namespace.Test;", "test");
});
});

describe(nameof<ImportEqualsDeclaration>(n => n.getModuleReference), () => {
function doTest(text: string, expected: string) {
const {firstChild} = getInfoFromText<ImportEqualsDeclaration>(text);
expect(firstChild.getModuleReference().getText()).to.equal(expected);
}

it("should get the module reference when specifying an entity", () => {
doTest("import test = Namespace.Test;", "Namespace.Test");
});

it("should get the module specifier when importing a require", () => {
doTest(`import test = require("testing");`, `require("testing")`);
});
});

describe(nameof<ImportEqualsDeclaration>(d => d.remove), () => {
function doTest(text: string, index: number, expectedText: string) {
const {sourceFile} = getInfoFromText(text);
(sourceFile.getStatements()[index] as ImportEqualsDeclaration).remove();
expect(sourceFile.getFullText()).to.equal(expectedText);
}

it("should remove the import equals declaration", () => {
doTest("import test = Namespace.Test;", 0, "");
});
});
});
5 changes: 5 additions & 0 deletions src/utils/ArrayUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export class ArrayUtils {
}
}

static sortByProperty<T>(items: T[], getProp: (item: T) => string | number) {
items.sort((a, b) => getProp(a) < getProp(b) ? -1 : 1);
return items;
}

static binaryInsert<T>(items: T[], newItem: T, isGreaterThan: (item: T) => boolean) {
let top = items.length - 1;
let bottom = 0;
Expand Down
11 changes: 7 additions & 4 deletions wrapped-nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The disadvantage to a node not being wrapped is that it won't have helper method

## Exist

**Total:** 131
**Total:** 133

* [ArrayDestructuringAssignment](src/compiler/expression/array/ArrayDestructuringAssignment.ts)
* :heavy_check_mark: left
Expand Down Expand Up @@ -95,6 +95,8 @@ The disadvantage to a node not being wrapped is that it won't have helper method
* [ExpressionWithTypeArguments](src/compiler/type/ExpressionWithTypeArguments.ts)
* :heavy_check_mark: expression
* :heavy_check_mark: typeArguments
* [ExternalModuleReference](src/compiler/file/ExternalModuleReference.ts)
* :heavy_check_mark: expression
* [ForInStatement](src/compiler/statement/ForInStatement.ts)
* :heavy_check_mark: initializer
* :heavy_check_mark: expression
Expand Down Expand Up @@ -132,6 +134,9 @@ The disadvantage to a node not being wrapped is that it won't have helper method
* [ImportDeclaration](src/compiler/file/ImportDeclaration.ts)
* :heavy_check_mark: importClause
* :heavy_check_mark: moduleSpecifier
* [ImportEqualsDeclaration](src/compiler/file/ImportEqualsDeclaration.ts)
* :heavy_check_mark: name
* :heavy_check_mark: moduleReference
* [ImportExpression](src/compiler/expression/ImportExpression.ts)
* [ImportSpecifier](src/compiler/file/ImportSpecifier.ts)
* :x: propertyName
Expand Down Expand Up @@ -335,7 +340,7 @@ The disadvantage to a node not being wrapped is that it won't have helper method

## Not Exist

**Total:** 74
**Total:** 72

* ArrayBindingPattern
* ArrayTypeNode
Expand All @@ -347,11 +352,9 @@ The disadvantage to a node not being wrapped is that it won't have helper method
* ClassLikeDeclarationBase
* ConstructorTypeNode
* DeclarationStatement
* ExternalModuleReference
* FunctionTypeNode
* ImportCall
* ImportClause
* ImportEqualsDeclaration
* IndexSignatureDeclaration
* IndexedAccessTypeNode
* IntersectionTypeNode
Expand Down

0 comments on commit c140982

Please sign in to comment.