diff --git a/packages/ts-morph/src/compiler/ast/jsx/JsxElement.ts b/packages/ts-morph/src/compiler/ast/jsx/JsxElement.ts index 6ffa8a3dc..977b81c00 100644 --- a/packages/ts-morph/src/compiler/ast/jsx/JsxElement.ts +++ b/packages/ts-morph/src/compiler/ast/jsx/JsxElement.ts @@ -1,5 +1,5 @@ -import { errors, nameof, ts } from "@ts-morph/common"; -import { insertIntoParentTextRange } from "../../../manipulation"; +import { errors, nameof, SyntaxKind, ts } from "@ts-morph/common"; +import { insertIntoParentTextRange, removeChildren } from "../../../manipulation"; import { JsxElementSpecificStructure, JsxElementStructure, StructureKind } from "../../../structures"; import { WriterFunction } from "../../../types"; import { printTextFromStringOrWriter } from "../../../utils"; @@ -104,6 +104,21 @@ export class JsxElement extends JsxElementBase { delete structure.children; return structure; } + + /** + * Removes the JSX element. + */ + remove() { + const parentKind = this.getParent()?.getKind() + + if (!(parentKind === SyntaxKind.JsxElement || parentKind === SyntaxKind.JsxOpeningElement || parentKind === SyntaxKind.JsxFragment)) { + throw new errors.InvalidOperationError(`Error removing JsxElement: parent is ${this.getParent()?.getKindName() ?? '(no parent)'} and therefore the node cannot be removed. Only JsxElements with JsxElement/JsxOpeningElement/JsxFragment parent can be removed`) + } + + return removeChildren({ + children: [this], + }) + } } function setText(element: JsxElement, newText: string) { diff --git a/packages/ts-morph/src/compiler/ast/jsx/JsxSelfClosingElement.ts b/packages/ts-morph/src/compiler/ast/jsx/JsxSelfClosingElement.ts index faf95b6ee..35677b5f2 100644 --- a/packages/ts-morph/src/compiler/ast/jsx/JsxSelfClosingElement.ts +++ b/packages/ts-morph/src/compiler/ast/jsx/JsxSelfClosingElement.ts @@ -1,5 +1,6 @@ -import { ts } from "@ts-morph/common"; +import { errors, ts, SyntaxKind } from "@ts-morph/common"; import { JsxSelfClosingElementSpecificStructure, JsxSelfClosingElementStructure, StructureKind } from "../../../structures"; +import { removeChildren } from "../../../manipulation"; import { callBaseGetStructure } from "../callBaseGetStructure"; import { callBaseSet } from "../callBaseSet"; import { PrimaryExpression } from "../expression"; @@ -25,4 +26,19 @@ export class JsxSelfClosingElement extends JsxSelfClosingElementBase }); }); }); + + describe(nameof("remove"), () => { + function doRemove(text: string) { + const { descendant, sourceFile } = getInfo(text); + descendant.remove(); + } + + function doTestWithJsxElementChild(text: string, expected: string) { + const { descendant, sourceFile } = getInfo(text); + (descendant.getFirstDescendantByKind(SyntaxKind.JsxElement) as JsxElement).remove(); + expect(sourceFile.getFullText()).to.equal(expected); + } + + it("should not remove the root JsxElement", () => { + let error = null; + + try { + doRemove(`var t = ();`); + } + catch (err) { + error = err; + } + + expect(error).to.be.instanceOf(errors.InvalidOperationError); + }); + + it("should remove the JsxElement child", () => { + doTestWithJsxElementChild(`var t = ();`, `var t = ();`); + }); + }); }); diff --git a/packages/ts-morph/src/tests/compiler/ast/jsx/jsxSelfClosingElementTests.ts b/packages/ts-morph/src/tests/compiler/ast/jsx/jsxSelfClosingElementTests.ts index 43130cca9..bc6ea15f9 100644 --- a/packages/ts-morph/src/tests/compiler/ast/jsx/jsxSelfClosingElementTests.ts +++ b/packages/ts-morph/src/tests/compiler/ast/jsx/jsxSelfClosingElementTests.ts @@ -1,4 +1,4 @@ -import { nameof, SyntaxKind } from "@ts-morph/common"; +import { errors, nameof, SyntaxKind } from "@ts-morph/common"; import { expect } from "chai"; import { JsxSelfClosingElement } from "../../../../compiler"; import { JsxAttributeStructure, JsxSelfClosingElementStructure, StructureKind } from "../../../../structures"; @@ -167,4 +167,34 @@ describe("JsxSelfClosingElement", () => { expect(descendant.getFullText()).to.equal(``); }); }); + + describe(nameof("remove"), () => { + function doRemove(text: string) { + const { descendant, sourceFile } = getInfo(text); + descendant.remove(); + } + + function doTestWithJsxSelfClosingElementChild(text: string, expected: string) { + const { descendant, sourceFile } = getInfo(text); + descendant.remove(); + expect(sourceFile.getFullText()).to.equal(expected); + } + + it("should not remove the root JsxSelfClosingElement", () => { + let error = null; + + try { + doRemove(`var t = ();`); + } + catch (err) { + error = err; + } + + expect(error).to.be.instanceOf(errors.InvalidOperationError); + }); + + it("should remove the JsxSelfClosingElement child", () => { + doTestWithJsxSelfClosingElementChild(`var t = ();`, `var t = ();`); + }); + }); });