ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
300 lines (298 loc) • 15.7 kB
JavaScript
"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