Skip to content

Commit

Permalink
feat: #249 - Remove TypeScript compiler peer dependency.
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Access the TypeScript compiler via `import {ts} from "ts-simple-ast";`
* NewLineKind is now the TypeScript compiler's enum.
  • Loading branch information
dsherret committed Feb 14, 2018
1 parent 0cecefe commit db9f0fc
Show file tree
Hide file tree
Showing 408 changed files with 6,475 additions and 2,280 deletions.
99 changes: 99 additions & 0 deletions code-generation/common/cloning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {StatementedNode, EnumDeclaration, EnumMemberStructure, InterfaceDeclaration, TypeAliasDeclaration, ClassDeclaration,
PropertyDeclaration} from "./../../src/main";

// todo: in the future this should be done in the library (ex. node.addInterface(cloningInterface.getStructure()))

export function cloneInterfaces(node: StatementedNode, cloningInterfaces: InterfaceDeclaration[]) {
node.addInterfaces(cloningInterfaces.map(cloningInterface => ({
isExported: true,
name: cloningInterface.getName(),
typeParameters: cloningInterface.getTypeParameters().map(p => ({
name: p.getName(),
constraint: p.getConstraintNode() == null ? undefined : p.getConstraintNode()!.getText()
})),
extends: cloningInterface.getExtends().map(e => e.getText()),
docs: cloningInterface.getJsDocs().map(d => ({ description: d.getInnerText() })),
properties: cloningInterface.getProperties().map(nodeProp => ({
name: nodeProp.getName(),
type: nodeProp.getTypeNodeOrThrow().getText(),
hasQuestionToken: nodeProp.hasQuestionToken(),
docs: nodeProp.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") }))
})),
methods: cloningInterface.getMethods().map(method => ({
name: method.getName(),
hasQuestionToken: method.hasQuestionToken(),
returnType: method.getReturnTypeNodeOrThrow().getText(),
typeParameters: method.getTypeParameters().map(p => ({
name: p.getName(),
constraint: p.getConstraintNode() == null ? undefined : p.getConstraintNode()!.getText()
})),
parameters: method.getParameters().map(p => ({
name: p.getNameOrThrow(),
hasQuestionToken: p.hasQuestionToken(),
type: p.getTypeNodeOrThrow().getText()
}))
})),
indexSignatures: cloningInterface.getIndexSignatures().map(s => ({
keyName: s.getKeyName(),
keyType: s.getKeyTypeNode().getText(),
returnType: s.getReturnTypeNode().getText(),
docs: s.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") }))
}))
})));
}

export function cloneEnums(node: StatementedNode, cloningEnums: EnumDeclaration[]) {
node.addEnums(cloningEnums.map(cloningEnum => ({
name: cloningEnum.getName(),
isExported: true,
members: cloningEnum.getMembers().map(m => ({
name: m.getName(),
docs: m.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") })),
value: m.getValue()
}) as EnumMemberStructure)
})));
}

export function cloneTypeAliases(node: StatementedNode, typeAliases: TypeAliasDeclaration[]) {
node.addTypeAliases(typeAliases.map(typeAlias => ({
name: typeAlias.getName(),
isExported: true,
typeParameters: typeAlias.getTypeParameters().map(p => ({
name: p.getName(),
constraint: p.getConstraintNode() == null ? undefined : p.getConstraintNode()!.getText()
})),
docs: typeAlias.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") })),
type: typeAlias.getTypeNodeOrThrow().getText()
})));
}

export function cloneClasses(node: StatementedNode, classes: ClassDeclaration[]) {
node.addClasses(classes.map(c => ({
name: c.getName(),
isExported: true,
typeParameters: c.getTypeParameters().map(p => ({
name: p.getName(),
constraint: p.getConstraintNode() == null ? undefined : p.getConstraintNode()!.getText()
})),
docs: c.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") })),
properties: (c.getInstanceProperties() as PropertyDeclaration[]).map(nodeProp => ({
name: nodeProp.getName(),
type: nodeProp.getType().getText(),
hasQuestionToken: nodeProp.hasQuestionToken(),
docs: nodeProp.getJsDocs().map(d => ({ description: d.getInnerText().replace(/\r?\n/g, "\r\n") }))
})),
methods: c.getInstanceMethods().map(method => ({
name: method.getName(),
returnType: method.getReturnTypeNodeOrThrow().getText(),
typeParameters: method.getTypeParameters().map(p => ({
name: p.getName(),
constraint: p.getConstraintNode() == null ? undefined : p.getConstraintNode()!.getText()
})),
parameters: method.getParameters().map(p => ({
name: p.getNameOrThrow(),
hasQuestionToken: p.hasQuestionToken(),
type: p.getTypeNodeOrThrow().getText()
}))
}))
})));
}
1 change: 1 addition & 0 deletions code-generation/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./cloning";
export * from "./getAst";
export * from "./getDefinitionAst";
export * from "./isNodeClass";
Expand Down
98 changes: 98 additions & 0 deletions code-generation/createCompilerApiLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* Code generation: Create Compiler API Layer
* ------------------------------------------
* This creates a file that contains the typings from the TypeScript compiler API.
* ------------------------------------------
*/
import * as path from "path";
import {rootFolder} from "./config";
import {InspectorFactory} from "./inspectors";
import {EnumDeclaration, InterfaceDeclaration, TypeAliasDeclaration, ClassDeclaration, TypeGuards, ts, SyntaxKind,
UnionTypeNode} from "./../src/main";
import {cloneEnums, cloneInterfaces, cloneTypeAliases, cloneClasses} from "./common/cloning";

const enumsToSeparate = ["SyntaxKind", "ScriptTarget", "ScriptKind", "LanguageVariant", "EmitHint", "JsxEmit", "ModuleKind", "ModuleResolutionKind",
"NewLineKind", "TypeFlags", "ObjectFlags", "SymbolFlags", "TypeFormatFlags", "DiagnosticCategory"];
const interfacesToSeparate = ["CompilerOptions", "MapLike"];
const typeAliasesToSeparate: string[] = [];

export function createCompilerApiLayer(factory: InspectorFactory) {
const tsInspector = factory.getTsInspector();
const ast = factory.getAst();
const declarationFile = tsInspector.getDeclarationFile();

const allEnums = declarationFile.getDescendantsOfKind(SyntaxKind.EnumDeclaration) as EnumDeclaration[];
const allInterfaces = declarationFile.getDescendantsOfKind(SyntaxKind.InterfaceDeclaration) as InterfaceDeclaration[];
const allTypeAliases = declarationFile.getDescendantsOfKind(SyntaxKind.TypeAliasDeclaration) as TypeAliasDeclaration[];

createTsSourceFile();

function createTsSourceFile() {
const sourceFile = getOrCreateSourceFile("ts.ts");

sourceFile.addImportDeclarations([{
namespaceImport: "tsCompiler",
moduleSpecifier: "typescript"
}, {
namedImports: [{ name: "ObjectUtils" }],
moduleSpecifier: sourceFile.getRelativePathToSourceFileAsModuleSpecifier(ast.getSourceFileOrThrow("src/utils/ObjectUtils.ts"))
}]);

addSeparatedDeclarations();

const tsNamespace = sourceFile.addNamespace({
name: "ts",
isExported: true
});

cloneInterfaces(tsNamespace, allInterfaces.filter(i => interfacesToSeparate.indexOf(i.getName()) === -1));
cloneEnums(tsNamespace, allEnums.filter(e => enumsToSeparate.indexOf(e.getName()) === -1));
cloneTypeAliases(tsNamespace, allTypeAliases.filter(t => typeAliasesToSeparate.indexOf(t.getName()) === -1));
cloneClasses(tsNamespace, declarationFile.getDescendantsOfKind(SyntaxKind.ClassDeclaration) as ClassDeclaration[]);
tsNamespace.getStatements().forEach(s => {
if (TypeGuards.isAmbientableNode(s))
s.setHasDeclareKeyword(true);
});

sourceFile.insertStatements(0, writer => {
writer.writeLine("/* tslint:disable */");
writer.writeLine("// DO NOT EDIT - This file is automatically generated by createCompilerApiLayer.ts");
});

tsNamespace.addStatements(writer => {
writer.newLine();
writer.writeLine("// overwrite this namespace with the TypeScript compiler");
writer.write("ObjectUtils.assign((ts as any), tsCompiler);");
});

sourceFile.replaceWithText(sourceFile.getFullText().replace(/\r?\n/g, "\r\n"));

sourceFile.save();

function addSeparatedDeclarations() {
for (const enumDec of allEnums.filter(e => enumsToSeparate.indexOf(e.getName()) >= 0))
cloneEnums(sourceFile, [enumDec]);

for (const interfaceDec of allInterfaces.filter(i => interfacesToSeparate.indexOf(i.getName()) >= 0))
cloneInterfaces(sourceFile, [interfaceDec]);

for (const typeAliasDec of allTypeAliases.filter(t => typeAliasesToSeparate.indexOf(t.getName()) >= 0))
cloneTypeAliases(sourceFile, [typeAliasDec]);

// todo: need a better way of doing this in the future...
const returnTypeNode = sourceFile.getInterfaceOrThrow("CompilerOptions").getIndexSignatures()[0].getReturnTypeNode() as UnionTypeNode;
returnTypeNode.getTypeNodes().map(n => {
if (n.getText() === "CompilerOptionsValue" || n.getText() === "JsonSourceFile")
n.replaceWithText(`ts.${n.getText()}`);
});
}
}

function getOrCreateSourceFile(fileName: string) {
const filePath = path.join(rootFolder, "src/typescript", fileName);
const existingSourceFile = ast.getSourceFile(filePath);
if (existingSourceFile != null)
existingSourceFile.replaceWithText("");
return existingSourceFile || ast.createSourceFile(filePath);
}
}
95 changes: 0 additions & 95 deletions code-generation/createNodePolyfills.ts

This file was deleted.

2 changes: 1 addition & 1 deletion code-generation/createTypeGuardsUtility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function createTypeGuardsUtility(inspector: TsSimpleAstInspector) {
returnType: `node is compiler.${method.wrapperName}` + (method.isMixin ? " & compiler.Node" : ""),
bodyText: (writer: CodeBlockWriter) => writer.write("switch (node.getKind())").block(() => {
for (const syntaxKindName of method.syntaxKinds) {
writer.writeLine(`case ts.SyntaxKind.${syntaxKindName}:`);
writer.writeLine(`case SyntaxKind.${syntaxKindName}:`);
}
writer.indent().write("return true;").newLine();
writer.writeLine("default:")
Expand Down
3 changes: 1 addition & 2 deletions code-generation/ensureNoDefinitionFileErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
* ---------------------------------------------------
*/
import * as path from "path";
import * as ts from "typescript";
import TsSimpleAst from "./../src/main";
import TsSimpleAst, {ts} from "./../src/main";
import {getDefinitionAst} from "./common";

const ast = getDefinitionAst();
Expand Down
20 changes: 15 additions & 5 deletions code-generation/flattenDeclarationFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,34 @@
* ----------------------------------------------
*/
import * as path from "path";
import * as ts from "typescript";
import TsSimpleAst, {SourceFile, ClassDeclaration, TypeGuards} from "./../src/main";
import TsSimpleAst, {SourceFile, ClassDeclaration, TypeGuards, ts, SyntaxKind} from "./../src/main";
import {getDefinitionAst} from "./common";

const ast = getDefinitionAst();

const definitionFiles = ast.getSourceFiles("**/dist/**/*.d.ts");
const definitionFiles = ast.getSourceFiles(["**/dist/**/*.d.ts", "!**/dist/typescript/ts.d.ts"]);
const mainFile = ast.getSourceFileOrThrow("main.d.ts");
const compilerApiFile = ast.getSourceFileOrThrow("dist/typescript/ts.d.ts");
const exportedDeclarations = mainFile.getExportedDeclarations();
mainFile.replaceWithText(`import * as ts from "typescript";\nimport CodeBlockWriter from "code-block-writer";\n`); // clear the source file
mainFile.replaceWithText(`import CodeBlockWriter from "code-block-writer";\n`); // clear the source file

for (let declaration of exportedDeclarations) {
if (declaration.getSourceFile() === compilerApiFile)
continue;
if (TypeGuards.isVariableDeclaration(declaration))
declaration = declaration.getFirstAncestorByKindOrThrow(ts.SyntaxKind.VariableStatement);
declaration = declaration.getFirstAncestorByKindOrThrow(SyntaxKind.VariableStatement);

mainFile.insertText(mainFile.getFullWidth(), declaration.getFullText() + "\n");
}

// add an import to the typescript compiler api file
mainFile.addImportDeclaration({
namedImports: ["ts", "SyntaxKind", "CompilerOptions", "EmitHint", "ScriptKind", "NewLineKind", "LanguageVariant", "ScriptTarget",
"TypeFlags", "ObjectFlags", "SymbolFlags", "TypeFormatFlags", "DiagnosticCategory"].map(name => ({ name })),
moduleSpecifier: mainFile.getRelativePathToSourceFileAsModuleSpecifier(compilerApiFile)
});

// update the main.d.ts file
mainFile.getClassOrThrow("TsSimpleAst").setIsDefaultExport(true);
mainFile.replaceWithText(mainFile.getFullText().replace(/compiler\.([A-Za-z]+)/g, "$1"));
mainFile.save();
Expand Down
2 changes: 1 addition & 1 deletion code-generation/inspectors/TsInspector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class TsInspector {

@Memoize
getTsNodes() {
const compilerApiFile = this.getDeclarationFile();
const compilerApiFile = this.ast.getSourceFileOrThrow("compiler/ts.ts");
const interfaces: InterfaceDeclaration[] = [];
for (const interfaceDec of ArrayUtils.flatten(compilerApiFile.getNamespaces().map(n => n.getInterfaces()))) {
if (interfaceDec.getBaseTypes().some(t => hasDescendantBaseType(t, checkingType => checkingType.getText() === "ts.Node")))
Expand Down
7 changes: 3 additions & 4 deletions code-generation/inspectors/ts/TsNode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as ts from "typescript";
import {InterfaceDeclaration, ClassDeclaration} from "./../../../src/main";
import {InterfaceDeclaration, ClassDeclaration, ts, SyntaxKind} from "./../../../src/main";
import {Memoize, ArrayUtils} from "./../../../src/utils";
import {WrapperFactory} from "./../WrapperFactory";
import {WrappedNode} from "./../tsSimpleAst";
Expand All @@ -26,9 +25,9 @@ export class TsNode {
continue;
const node = reference.getNode();
// ignore nodes in blocks
if (node.getFirstAncestorByKind(ts.SyntaxKind.Block) != null)
if (node.getFirstAncestorByKind(SyntaxKind.Block) != null)
continue;
const classDec = node.getFirstAncestorByKind(ts.SyntaxKind.ClassDeclaration);
const classDec = node.getFirstAncestorByKind(SyntaxKind.ClassDeclaration);
if (classDec != null)
return this.wrapperFactory.getWrapperNode(classDec as ClassDeclaration);
}
Expand Down

0 comments on commit db9f0fc

Please sign in to comment.