ts-simple-ast
Version:
TypeScript compiler wrapper for AST navigation and code generation.
139 lines (137 loc) • 5.63 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ts = require("typescript");
const errors = require("./../../errors");
const manipulation_1 = require("./../../manipulation");
function ModifierableNode(Base) {
return class extends Base {
getModifiers() {
return this.compilerNode.modifiers == null ? [] : this.compilerNode.modifiers.map(m => this.global.compilerFactory.getNodeFromCompilerNode(m, this.sourceFile));
}
getFirstModifierByKindOrThrow(kind) {
return errors.throwIfNullOrUndefined(this.getFirstModifierByKind(kind), `Expected a modifier of syntax kind: ${ts.SyntaxKind[kind]}`);
}
getFirstModifierByKind(kind) {
for (const modifier of this.getModifiers()) {
if (modifier.getKind() === kind)
return modifier;
}
return undefined;
}
hasModifier(textOrKind) {
if (typeof textOrKind === "string")
return this.getModifiers().some(m => m.getText() === textOrKind);
else
return this.getModifiers().some(m => m.getKind() === textOrKind);
}
toggleModifier(text, value) {
if (value == null)
value = !this.hasModifier(text);
if (value)
this.addModifier(text);
else
this.removeModifier(text);
return this;
}
addModifier(text) {
const modifiers = this.getModifiers();
const hasModifier = modifiers.some(m => m.getText() === text);
if (hasModifier)
return this.getModifiers().find(m => m.getText() === text);
// get insert position & index
const { insertPos, insertIndex } = getInsertInfo(this);
// insert setup
let startPos;
let newText;
const isFirstModifier = modifiers.length === 0 || insertPos === modifiers[0].getStart();
if (isFirstModifier) {
newText = text + " ";
startPos = insertPos;
}
else {
newText = " " + text;
startPos = insertPos + 1;
}
// insert
manipulation_1.insertIntoCreatableSyntaxList({
parent: this,
insertPos,
newText,
syntaxList: modifiers.length === 0 ? undefined : modifiers[0].getParentSyntaxListOrThrow(),
childIndex: insertIndex,
insertItemsCount: 1
});
return this.getModifiers().find(m => m.getStart() === startPos);
function getInsertInfo(node) {
let pos = getInitialInsertPos();
let index = 0;
for (const addAfterText of getAddAfterModifierTexts(text)) {
for (let i = 0; i < modifiers.length; i++) {
const modifier = modifiers[i];
if (modifier.getText() === addAfterText) {
if (pos < modifier.getEnd()) {
pos = modifier.getEnd();
index = i + 1;
}
break;
}
}
}
return { insertPos: pos, insertIndex: index };
function getInitialInsertPos() {
if (modifiers.length > 0)
return modifiers[0].getStart();
for (const child of node.getChildrenIterator()) {
// skip over any initial syntax lists (ex. decorators) or js docs
if (child.getKind() === ts.SyntaxKind.SyntaxList || ts.isJSDocCommentContainingNode(child.compilerNode))
continue;
return child.getStart();
}
return node.getStart();
}
}
}
removeModifier(text) {
const modifier = this.getModifiers().find(m => m.getText() === text);
if (modifier == null)
return false;
manipulation_1.removeChildrenWithFormattingFromCollapsibleSyntaxList({
children: [modifier],
getSiblingFormatting: () => manipulation_1.FormattingKind.Space
});
return true;
}
};
}
exports.ModifierableNode = ModifierableNode;
/**
* @returns The texts the specified text should appear after.
*/
function getAddAfterModifierTexts(text) {
switch (text) {
case "export":
return [];
case "default":
return ["export"];
case "declare":
return ["export", "default"];
case "abstract":
return ["export", "default", "declare", "public", "private", "protected"];
case "readonly":
return ["export", "default", "declare", "public", "private", "protected", "abstract", "static"];
case "public":
case "protected":
case "private":
return [];
case "static":
return ["public", "protected", "private"];
case "async":
return ["export", "public", "protected", "private", "static", "abstract"];
case "const":
return [];
/* istanbul ignore next */
default:
throw new errors.NotImplementedError(`Not implemented modifier: ${text}`);
}
}
//# sourceMappingURL=ModifierableNode.js.map