UNPKG

ts-simple-ast

Version:

TypeScript compiler wrapper for AST navigation and code generation.

300 lines (298 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ts = require("typescript"); const errors = require("./../../errors"); const manipulation_1 = require("./../../manipulation"); const utils_1 = require("./../../utils"); const callBaseFill_1 = require("./../callBaseFill"); function StatementedNode(Base) { return class extends Base { /* General */ getStatements() { let statements; if (utils_1.TypeGuards.isSourceFile(this)) statements = this.compilerNode.statements; else if (utils_1.TypeGuards.isNamespaceDeclaration(this)) { // need to get the inner-most body for namespaces let node = this; while (utils_1.TypeGuards.isBodiedNode(node) && node.compilerNode.statements == null) { node = node.getBody(); } statements = node.compilerNode.statements; } else if (utils_1.TypeGuards.isBodyableNode(this)) statements = this.getBodyOrThrow().compilerNode.statements; else if (utils_1.TypeGuards.isBodiedNode(this)) statements = this.getBody().compilerNode.statements; else throw new errors.NotImplementedError(`Could not find the statements for the node: ${this.getText()}`); return statements.map(s => this.global.compilerFactory.getNodeFromCompilerNode(s, this.sourceFile)); } /* Classes */ addClass(structure) { return this.addClasses([structure])[0]; } addClasses(structures) { return this.insertClasses(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertClass(index, structure) { return this.insertClasses(index, [structure])[0]; } insertClasses(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => `${indentationText}class ${structure.name} {${newLineChar}${indentationText}}`); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.ClassDeclaration, (child, i) => { child.fill(structures[i]); }); return newChildren; } getClasses() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.ClassDeclaration); } getClass(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getClasses(), nameOrFindFunction); } getClassOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getClass(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class", nameOrFindFunction)); } /* Enums */ addEnum(structure) { return this.addEnums([structure])[0]; } addEnums(structures) { return this.insertEnums(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertEnum(index, structure) { return this.insertEnums(index, [structure])[0]; } insertEnums(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => `${indentationText}${structure.isConst ? "const " : ""}enum ${structure.name} {${newLineChar}${indentationText}}`); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.EnumDeclaration, (child, i) => { child.fill(structures[i]); }); return newChildren; } getEnums() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.EnumDeclaration); } getEnum(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getEnums(), nameOrFindFunction); } getEnumOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getEnum(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("enum", nameOrFindFunction)); } /* Functions */ addFunction(structure) { return this.addFunctions([structure])[0]; } addFunctions(structures) { return this.insertFunctions(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertFunction(index, structure) { return this.insertFunctions(index, [structure])[0]; } insertFunctions(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => `${indentationText}function ${structure.name}() {${newLineChar}${indentationText}}`); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.FunctionDeclaration, (child, i) => { child.fill(structures[i]); }); return newChildren; } getFunctions() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.FunctionDeclaration) .filter(f => f.isAmbient() || f.isImplementation()); } getFunction(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getFunctions(), nameOrFindFunction); } getFunctionOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getFunction(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("function", nameOrFindFunction)); } /* Interfaces */ addInterface(structure) { return this.addInterfaces([structure])[0]; } addInterfaces(structures) { return this.insertInterfaces(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertInterface(index, structure) { return this.insertInterfaces(index, [structure])[0]; } insertInterfaces(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => `${indentationText}interface ${structure.name} {${newLineChar}${indentationText}}`); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.InterfaceDeclaration, (child, i) => { child.fill(structures[i]); }); return newChildren; } getInterfaces() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.InterfaceDeclaration); } getInterface(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getInterfaces(), nameOrFindFunction); } getInterfaceOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getInterface(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("interface", nameOrFindFunction)); } /* Namespaces */ addNamespace(structure) { return this.addNamespaces([structure])[0]; } addNamespaces(structures) { return this.insertNamespaces(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertNamespace(index, structure) { return this.insertNamespaces(index, [structure])[0]; } insertNamespaces(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => { return `${indentationText}${structure.hasModuleKeyword ? "module" : "namespace"} ${structure.name} {${newLineChar}${indentationText}}`; }); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.ModuleDeclaration, (child, i) => { child.fill(structures[i]); }); return newChildren; } getNamespaces() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.ModuleDeclaration); } getNamespace(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getNamespaces(), nameOrFindFunction); } getNamespaceOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getNamespace(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("namespace", nameOrFindFunction)); } /* Type aliases */ addTypeAlias(structure) { return this.addTypeAliases([structure])[0]; } addTypeAliases(structures) { return this.insertTypeAliases(this.getChildSyntaxListOrThrow().getChildCount(), structures); } insertTypeAlias(index, structure) { return this.insertTypeAliases(index, [structure])[0]; } insertTypeAliases(index, structures) { const newLineChar = this.global.manipulationSettings.getNewLineKind(); const indentationText = this.getChildIndentationText(); const texts = structures.map(structure => { return `${indentationText}type ${structure.name} = ${structure.type};`; }); const newChildren = this._insertMainChildren(index, texts, structures, ts.SyntaxKind.TypeAliasDeclaration, (child, i) => { child.fill(structures[i]); }, { previousBlanklineWhen: previousMember => !utils_1.TypeGuards.isTypeAliasDeclaration(previousMember), separatorNewlineWhen: () => false, nextBlanklineWhen: nextMember => !utils_1.TypeGuards.isTypeAliasDeclaration(nextMember) }); return newChildren; } getTypeAliases() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.TypeAliasDeclaration); } getTypeAlias(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getTypeAliases(), nameOrFindFunction); } getTypeAliasOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getTypeAlias(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("type alias", nameOrFindFunction)); } getVariableStatements() { // todo: remove type assertion return this.getChildSyntaxListOrThrow().getChildrenOfKind(ts.SyntaxKind.VariableStatement); } getVariableStatement(findFunction) { return this.getVariableStatements().find(findFunction); } getVariableStatementOrThrow(findFunction) { return errors.throwIfNullOrUndefined(this.getVariableStatement(findFunction), "Expected to find a variable statement that matched the provided condition."); } getVariableDeclarationLists() { return this.getVariableStatements().map(s => s.getDeclarationList()); } getVariableDeclarationList(findFunction) { return this.getVariableDeclarationLists().find(findFunction); } getVariableDeclarationListOrThrow(findFunction) { return errors.throwIfNullOrUndefined(this.getVariableDeclarationList(findFunction), "Could not find a variable declaration that matched the provided condition."); } getVariableDeclarations() { const variables = []; for (const list of this.getVariableDeclarationLists()) { variables.push(...list.getDeclarations()); } return variables; } getVariableDeclaration(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getVariableDeclarations(), nameOrFindFunction); } getVariableDeclarationOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getVariableDeclaration(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("variable declaration", nameOrFindFunction)); } fill(structure) { callBaseFill_1.callBaseFill(Base.prototype, this, structure); if (structure.classes != null && structure.classes.length > 0) this.addClasses(structure.classes); if (structure.enums != null && structure.enums.length > 0) this.addEnums(structure.enums); if (structure.functions != null && structure.functions.length > 0) this.addFunctions(structure.functions); if (structure.interfaces != null && structure.interfaces.length > 0) this.addInterfaces(structure.interfaces); if (structure.namespaces != null && structure.namespaces.length > 0) this.addNamespaces(structure.namespaces); if (structure.typeAliases != null && structure.typeAliases.length > 0) this.addTypeAliases(structure.typeAliases); return this; } // todo: make this passed an object _insertMainChildren(index, childCodes, structures, expectedSyntaxKind, withEachChild, opts = {}) { const syntaxList = this.getChildSyntaxListOrThrow(); const mainChildren = syntaxList.getChildren(); const newLineChar = this.global.manipulationSettings.getNewLineKind(); index = manipulation_1.verifyAndGetIndex(index, mainChildren.length); // insert into a temp file const finalChildCodes = []; for (let i = 0; i < childCodes.length; i++) { utils_1.using(this.global.compilerFactory.createTempSourceFileFromText(childCodes[i]), tempSourceFile => { if (withEachChild != null) { const tempSyntaxList = tempSourceFile.getChildSyntaxListOrThrow(); withEachChild(tempSyntaxList.getChildren()[0], i); } finalChildCodes.push(tempSourceFile.getFullText()); }); } // insert const doBlankLine = () => true; manipulation_1.insertIntoBracesOrSourceFile({ parent: this, children: mainChildren, index, childCodes: finalChildCodes, structures, separator: newLineChar, previousBlanklineWhen: opts.previousBlanklineWhen || doBlankLine, separatorNewlineWhen: opts.separatorNewlineWhen || doBlankLine, nextBlanklineWhen: opts.nextBlanklineWhen || doBlankLine }); // get children return manipulation_1.getRangeFromArray(syntaxList.getChildren(), index, childCodes.length, expectedSyntaxKind); } }; } exports.StatementedNode = StatementedNode; //# sourceMappingURL=StatementedNode.js.map