UNPKG

ts-simple-ast

Version:

TypeScript compiler wrapper for AST navigation and code generation.

390 lines (388 loc) 17 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 common_1 = require("./../common"); const base_1 = require("./../base"); const base_2 = require("./base"); const function_1 = require("./../function"); const namespace_1 = require("./../namespace"); const callBaseFill_1 = require("./../callBaseFill"); const ConstructorDeclaration_1 = require("./ConstructorDeclaration"); const MethodDeclaration_1 = require("./MethodDeclaration"); const PropertyDeclaration_1 = require("./PropertyDeclaration"); const GetAccessorDeclaration_1 = require("./GetAccessorDeclaration"); const SetAccessorDeclaration_1 = require("./SetAccessorDeclaration"); exports.ClassDeclarationBase = base_1.TextInsertableNode(base_1.ImplementsClauseableNode(base_1.HeritageClauseableNode(base_1.DecoratableNode(base_1.TypeParameteredNode(namespace_1.NamespaceChildableNode(base_1.DocumentationableNode(base_1.AmbientableNode(base_2.AbstractableNode(base_1.ExportableNode(base_1.ModifierableNode(base_1.NamedNode(common_1.Node)))))))))))); class ClassDeclaration extends exports.ClassDeclarationBase { /** * Fills the node from a structure. * @param structure - Structure to fill. */ fill(structure) { callBaseFill_1.callBaseFill(exports.ClassDeclarationBase.prototype, this, structure); if (structure.extends != null) this.setExtends(structure.extends); if (structure.ctor != null) this.addConstructor(structure.ctor); if (structure.properties != null) this.addProperties(structure.properties); if (structure.methods != null) this.addMethods(structure.methods); return this; } /** * Sets the extends expression. * @param text - Text to set as the extends expression. */ setExtends(text) { errors.throwIfNotStringOrWhitespace(text, "text"); const heritageClauses = this.getHeritageClauses(); const extendsClause = heritageClauses.find(c => c.compilerNode.token === ts.SyntaxKind.ExtendsKeyword); if (extendsClause != null) { const childSyntaxList = extendsClause.getFirstChildByKindOrThrow(ts.SyntaxKind.SyntaxList); const childSyntaxListStart = childSyntaxList.getStart(); manipulation_1.insertIntoParent({ parent: extendsClause, childIndex: childSyntaxList.getChildIndex(), insertItemsCount: 1, newText: text, insertPos: childSyntaxListStart, replacing: { length: childSyntaxList.getEnd() - childSyntaxListStart, nodes: [childSyntaxList] } }); return this; } const implementsClause = heritageClauses.find(c => c.compilerNode.token === ts.SyntaxKind.ImplementsKeyword); let insertPos; if (implementsClause != null) insertPos = implementsClause.getStart(); else insertPos = this.getFirstChildByKindOrThrow(ts.SyntaxKind.OpenBraceToken).getStart(); const isLastSpace = /\s/.test(this.getSourceFile().getFullText()[insertPos - 1]); let newText = `extends ${text} `; if (!isLastSpace) newText = " " + newText; manipulation_1.insertIntoCreatableSyntaxList({ parent: this, insertPos, newText, syntaxList: implementsClause == null ? undefined : implementsClause.getParentSyntaxListOrThrow(), childIndex: 0, insertItemsCount: 1 }); return this; } /** * Gets the extends expression. */ getExtends() { const heritageClauses = this.getHeritageClauses(); const extendsClause = heritageClauses.find(c => c.compilerNode.token === ts.SyntaxKind.ExtendsKeyword); if (extendsClause == null) return undefined; const types = extendsClause.getTypes(); return types.length === 0 ? undefined : types[0]; } /** * Adds a constructor. Will remove the previous constructor if it exists. * @param structure - Structure of the constructor. */ addConstructor(structure = {}) { return this.insertConstructor(manipulation_1.getEndIndexFromArray(this.getBodyMembers()), structure); } /** * Inserts a constructor. Will remove the previous constructor if it exists. * @param index - Index to insert at. * @param structure - Structure of the constructor. */ insertConstructor(index, structure = {}) { for (const c of this.getConstructors()) { c.remove(); } const indentationText = this.getChildIndentationText(); const newLineChar = this.global.manipulationSettings.getNewLineKind(); const code = `${indentationText}constructor() {${newLineChar}${indentationText}}`; return manipulation_1.insertIntoBracesOrSourceFileWithFillAndGetChildren({ getIndexedChildren: () => this.getBodyMembers(), sourceFile: this.getSourceFile(), parent: this, index, childCodes: [code], structures: [structure], previousBlanklineWhen: () => true, nextBlanklineWhen: () => true, expectedKind: ts.SyntaxKind.Constructor, fillFunction: (node, struct) => node.fill(struct) })[0]; } /** * Gets the constructor declarations. */ getConstructors() { return this.getAllMembers().filter(m => utils_1.TypeGuards.isConstructorDeclaration(m)); } /** * Add property. * @param structure - Structure representing the property. */ addProperty(structure) { return this.addProperties([structure])[0]; } /** * Add properties. * @param structures - Structures representing the properties. */ addProperties(structures) { return this.insertProperties(manipulation_1.getEndIndexFromArray(this.getBodyMembers()), structures); } /** * Insert property. * @param index - Index to insert at. * @param structure - Structure representing the property. */ insertProperty(index, structure) { return this.insertProperties(index, [structure])[0]; } /** * Insert properties. * @param index - Index to insert at. * @param structures - Structures representing the properties. */ insertProperties(index, structures) { const indentationText = this.getChildIndentationText(); // create code const codes = []; for (const structure of structures) { let code = `${indentationText}`; if (structure.isStatic) code += "static "; code += structure.name; if (structure.hasQuestionToken) code += "?"; if (structure.type != null && structure.type.length > 0) code += `: ${structure.type}`; code += ";"; codes.push(code); } return manipulation_1.insertIntoBracesOrSourceFileWithFillAndGetChildren({ getIndexedChildren: () => this.getBodyMembers(), sourceFile: this.getSourceFile(), parent: this, index, childCodes: codes, structures, previousBlanklineWhen: n => utils_1.TypeGuards.isBodyableNode(n) || utils_1.TypeGuards.isBodiedNode(n), nextBlanklineWhen: n => utils_1.TypeGuards.isBodyableNode(n) || utils_1.TypeGuards.isBodiedNode(n), expectedKind: ts.SyntaxKind.PropertyDeclaration, fillFunction: (node, structure) => node.fill(structure) }); } getInstanceProperty(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getInstanceProperties(), nameOrFindFunction); } getInstancePropertyOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getInstanceProperty(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class instance property", nameOrFindFunction)); } /** * Gets the class instance property declarations. */ getInstanceProperties() { return this.getInstanceMembers() .filter(m => isClassPropertyType(m)); } getStaticProperty(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getStaticProperties(), nameOrFindFunction); } getStaticPropertyOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getStaticProperty(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class static property", nameOrFindFunction)); } /** * Gets the class instance property declarations. */ getStaticProperties() { return this.getStaticMembers() .filter(m => isClassPropertyType(m)); } /** * Add method. * @param structure - Structure representing the method. */ addMethod(structure) { return this.addMethods([structure])[0]; } /** * Add methods. * @param structures - Structures representing the methods. */ addMethods(structures) { return this.insertMethods(manipulation_1.getEndIndexFromArray(this.getBodyMembers()), structures); } /** * Insert method. * @param index - Index to insert at. * @param structure - Structure representing the method. */ insertMethod(index, structure) { return this.insertMethods(index, [structure])[0]; } /** * Insert methods. * @param index - Index to insert at. * @param structures - Structures representing the methods. */ insertMethods(index, structures) { const indentationText = this.getChildIndentationText(); const newLineChar = this.global.manipulationSettings.getNewLineKind(); const isAmbient = this.isAmbient(); // create code const codes = []; for (const structure of structures) { let code = indentationText; if (structure.isStatic) code += "static "; code += `${structure.name}()`; if (structure.returnType != null && structure.returnType.length > 0) code += `: ${structure.returnType}`; if (isAmbient) code += ";"; else code += ` {` + newLineChar + indentationText + `}`; codes.push(code); } // insert, fill, and get created nodes return manipulation_1.insertIntoBracesOrSourceFileWithFillAndGetChildren({ getIndexedChildren: () => this.getBodyMembers(), sourceFile: this.getSourceFile(), parent: this, index, childCodes: codes, structures, previousBlanklineWhen: () => !isAmbient, nextBlanklineWhen: () => !isAmbient, separatorNewlineWhen: () => !isAmbient, expectedKind: ts.SyntaxKind.MethodDeclaration, fillFunction: (node, structure) => node.fill(structure) }); } getInstanceMethod(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getInstanceMethods(), nameOrFindFunction); } getInstanceMethodOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getInstanceMethod(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class instance method", nameOrFindFunction)); } /** * Gets the class instance method declarations. */ getInstanceMethods() { return this.getInstanceMembers().filter(m => m instanceof MethodDeclaration_1.MethodDeclaration); } getStaticMethod(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getStaticMethods(), nameOrFindFunction); } getStaticMethodOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getStaticMethod(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class static method", nameOrFindFunction)); } /** * Gets the class instance method declarations. */ getStaticMethods() { return this.getStaticMembers().filter(m => m instanceof MethodDeclaration_1.MethodDeclaration); } getInstanceMember(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getInstanceMembers(), nameOrFindFunction); } getInstanceMemberOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getInstanceMember(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class instance member", nameOrFindFunction)); } /** * Gets the instance members. */ getInstanceMembers() { return this.getAllMembers().filter(m => !utils_1.TypeGuards.isConstructorDeclaration(m) && (m instanceof function_1.ParameterDeclaration || !m.isStatic())); } getStaticMember(nameOrFindFunction) { return utils_1.getNamedNodeByNameOrFindFunction(this.getStaticMembers(), nameOrFindFunction); } getStaticMemberOrThrow(nameOrFindFunction) { return errors.throwIfNullOrUndefined(this.getStaticMember(nameOrFindFunction), () => utils_1.getNotFoundErrorMessageForNameOrFindFunction("class static member", nameOrFindFunction)); } /** * Gets the static members. */ getStaticMembers() { return this.getAllMembers().filter(m => !utils_1.TypeGuards.isConstructorDeclaration(m) && !(m instanceof function_1.ParameterDeclaration) && m.isStatic()); } /** * Gets the constructors, methods, properties, and class parameter properties. */ getAllMembers() { const members = this.getBodyMembers(); const implementationCtors = members.filter(c => utils_1.TypeGuards.isConstructorDeclaration(c) && c.isImplementation()); for (const ctor of implementationCtors) { // insert after the constructor let insertIndex = members.indexOf(ctor) + 1; for (const param of ctor.getParameters()) { if (param.isParameterProperty()) { members.splice(insertIndex, 0, param); insertIndex++; } } } return members; } /** * Gets all the derived classes. */ getDerivedClasses() { const classes = this.getImmediateDerivedClasses(); for (let i = 0; i < classes.length; i++) { const derivedClasses = classes[i].getImmediateDerivedClasses(); for (const derivedClass of derivedClasses) { // don't allow circular references if (derivedClass !== this && classes.indexOf(derivedClass) === -1) classes.push(derivedClass); } } return classes; } /** * Removes this class declaration. */ remove() { manipulation_1.removeStatementedNodeChild(this); } getImmediateDerivedClasses() { const references = this.getNameIdentifier().findReferences(); const classes = []; for (const reference of references) { for (const ref of reference.getReferences()) { if (ref.isDefinition()) continue; const node = ref.getNode(); const nodeParent = node.getParentIfKind(ts.SyntaxKind.ExpressionWithTypeArguments); if (nodeParent == null) continue; const heritageClause = nodeParent.getParentIfKind(ts.SyntaxKind.HeritageClause); if (heritageClause == null || heritageClause.getToken() !== ts.SyntaxKind.ExtendsKeyword) continue; classes.push(heritageClause.getFirstAncestorByKindOrThrow(ts.SyntaxKind.ClassDeclaration)); } } return classes; } getBodyMembers() { const members = this.compilerNode.members.map(m => this.global.compilerFactory.getNodeFromCompilerNode(m, this.sourceFile)); // filter out the method declarations or constructor declarations without a body if not ambient return this.isAmbient() ? members : members.filter(m => !(m instanceof ConstructorDeclaration_1.ConstructorDeclaration || m instanceof MethodDeclaration_1.MethodDeclaration) || m.isImplementation()); } } exports.ClassDeclaration = ClassDeclaration; function isClassPropertyType(m) { return m instanceof PropertyDeclaration_1.PropertyDeclaration || m instanceof SetAccessorDeclaration_1.SetAccessorDeclaration || m instanceof GetAccessorDeclaration_1.GetAccessorDeclaration || m instanceof function_1.ParameterDeclaration; } //# sourceMappingURL=ClassDeclaration.js.map