diff --git a/deno/ts_morph.d.ts b/deno/ts_morph.d.ts index 02d4fe21e..0da779556 100644 --- a/deno/ts_morph.d.ts +++ b/deno/ts_morph.d.ts @@ -10309,6 +10309,7 @@ export interface ClassLikeDeclarationBaseStructure extends NameableNodeStructure interface ClassLikeDeclarationBaseSpecificStructure { extends?: string | WriterFunction; ctors?: OptionalKind[]; + staticBlocks?: OptionalKind[]; properties?: OptionalKind[]; getAccessors?: OptionalKind[]; setAccessors?: OptionalKind[]; diff --git a/deno/ts_morph.js b/deno/ts_morph.js index cc80e8d86..5c13653c1 100644 --- a/deno/ts_morph.js +++ b/deno/ts_morph.js @@ -6521,6 +6521,7 @@ class ClassDeclarationStructurePrinter extends NodePrinter { this.#printHeader(writer, structure); writer.inlineBlock(() => { this.factory.forPropertyDeclaration().printTexts(writer, structure.properties); + this.#printStaticBlocks(writer, structure); this.#printCtors(writer, structure, isAmbient); this.#printGetAndSet(writer, structure, isAmbient); if (!ArrayUtils.isNullOrEmpty(structure.methods)) { @@ -6559,6 +6560,14 @@ class ClassDeclarationStructurePrinter extends NodePrinter { this.factory.forConstructorDeclaration({ isAmbient }).printText(writer, ctor); } } + #printStaticBlocks(writer, structure) { + if (ArrayUtils.isNullOrEmpty(structure.staticBlocks)) + return; + for (const block of structure.staticBlocks) { + this.#conditionalSeparator(writer, false); + this.factory.forClassStaticBlockDeclaration().printText(writer, block); + } + } #printGetAndSet(writer, structure, isAmbient) { if (structure.getAccessors == null && structure.setAccessors == null) return; @@ -7235,6 +7244,7 @@ function forClassLikeDeclarationBase(structure, callback) { || forTypeParameteredNode(structure, callback) || forJSDocableNode(structure, callback) || forAll(structure.ctors, callback, StructureKind.Constructor) + || forAll(structure.staticBlocks, callback, StructureKind.ClassStaticBlock) || forAll(structure.properties, callback, StructureKind.Property) || forAll(structure.getAccessors, callback, StructureKind.GetAccessor) || forAll(structure.setAccessors, callback, StructureKind.SetAccessor) @@ -14655,6 +14665,10 @@ class ClassDeclaration extends ClassDeclarationBase { this.getConstructors().forEach(c => c.remove()); this.addConstructors(structure.ctors); } + if (structure.staticBlocks != null) { + this.getStaticBlocks().forEach(c => c.remove()); + this.addStaticBlocks(structure.staticBlocks); + } if (structure.properties != null) { this.getProperties().forEach(p => p.remove()); this.addProperties(structure.properties); @@ -14679,6 +14693,7 @@ class ClassDeclaration extends ClassDeclarationBase { return callBaseGetStructure(ClassDeclarationBase.prototype, this, { kind: StructureKind.Class, ctors: this.getConstructors().filter(ctor => isAmbient || !ctor.isOverload()).map(ctor => ctor.getStructure()), + staticBlocks: this.getStaticBlocks().map(ctor => ctor.getStructure()), methods: this.getMethods().filter(method => isAmbient || !method.isOverload()).map(method => method.getStructure()), properties: this.getProperties().map(property => property.getStructure()), extends: getExtends ? getExtends.getText() : undefined, diff --git a/packages/ts-morph/lib/ts-morph.d.ts b/packages/ts-morph/lib/ts-morph.d.ts index 3edcecb4e..276db1c0e 100644 --- a/packages/ts-morph/lib/ts-morph.d.ts +++ b/packages/ts-morph/lib/ts-morph.d.ts @@ -10309,6 +10309,7 @@ export interface ClassLikeDeclarationBaseStructure extends NameableNodeStructure interface ClassLikeDeclarationBaseSpecificStructure { extends?: string | WriterFunction; ctors?: OptionalKind[]; + staticBlocks?: OptionalKind[]; properties?: OptionalKind[]; getAccessors?: OptionalKind[]; setAccessors?: OptionalKind[]; diff --git a/packages/ts-morph/src/compiler/ast/class/ClassDeclaration.ts b/packages/ts-morph/src/compiler/ast/class/ClassDeclaration.ts index 160d595d4..eacd2e264 100644 --- a/packages/ts-morph/src/compiler/ast/class/ClassDeclaration.ts +++ b/packages/ts-morph/src/compiler/ast/class/ClassDeclaration.ts @@ -3,6 +3,7 @@ import { ClassDeclarationSpecificStructure, ClassDeclarationStructure, ClassLikeDeclarationBaseSpecificStructure, + ClassStaticBlockDeclarationStructure, ConstructorDeclarationStructure, InterfaceDeclarationStructure, JSDocStructure, @@ -48,6 +49,10 @@ export class ClassDeclaration extends ClassDeclarationBase this.getConstructors().forEach(c => c.remove()); this.addConstructors(structure.ctors); } + if (structure.staticBlocks != null) { + this.getStaticBlocks().forEach(c => c.remove()); + this.addStaticBlocks(structure.staticBlocks); + } if (structure.properties != null) { this.getProperties().forEach(p => p.remove()); this.addProperties(structure.properties); @@ -77,6 +82,7 @@ export class ClassDeclaration extends ClassDeclarationBase return callBaseGetStructure(ClassDeclarationBase.prototype, this, { kind: StructureKind.Class, ctors: this.getConstructors().filter(ctor => isAmbient || !ctor.isOverload()).map(ctor => ctor.getStructure() as ConstructorDeclarationStructure), + staticBlocks: this.getStaticBlocks().map(ctor => ctor.getStructure() as ClassStaticBlockDeclarationStructure), methods: this.getMethods().filter(method => isAmbient || !method.isOverload()).map(method => method.getStructure() as MethodDeclarationStructure), properties: this.getProperties().map(property => property.getStructure()), extends: getExtends ? getExtends.getText() : undefined, diff --git a/packages/ts-morph/src/structurePrinters/class/ClassDeclarationStructurePrinter.ts b/packages/ts-morph/src/structurePrinters/class/ClassDeclarationStructurePrinter.ts index 0f2919bcc..d80d4282e 100644 --- a/packages/ts-morph/src/structurePrinters/class/ClassDeclarationStructurePrinter.ts +++ b/packages/ts-morph/src/structurePrinters/class/ClassDeclarationStructurePrinter.ts @@ -27,6 +27,7 @@ export class ClassDeclarationStructurePrinter extends NodePrinter { this.factory.forPropertyDeclaration().printTexts(writer, structure.properties); + this.#printStaticBlocks(writer, structure); this.#printCtors(writer, structure, isAmbient); this.#printGetAndSet(writer, structure, isAmbient); @@ -74,6 +75,16 @@ export class ClassDeclarationStructurePrinter extends NodePrinter) { + if (ArrayUtils.isNullOrEmpty(structure.staticBlocks)) + return; + + for (const block of structure.staticBlocks) { + this.#conditionalSeparator(writer, /* is ambient */ false); + this.factory.forClassStaticBlockDeclaration().printText(writer, block); + } + } + #printGetAndSet(writer: CodeBlockWriter, structure: OptionalKind, isAmbient: boolean) { if (structure.getAccessors == null && structure.setAccessors == null) return; diff --git a/packages/ts-morph/src/structures/class/base/ClassLikeDeclarationBaseStructure.ts b/packages/ts-morph/src/structures/class/base/ClassLikeDeclarationBaseStructure.ts index 29a111fab..fbbd9d7ce 100644 --- a/packages/ts-morph/src/structures/class/base/ClassLikeDeclarationBaseStructure.ts +++ b/packages/ts-morph/src/structures/class/base/ClassLikeDeclarationBaseStructure.ts @@ -8,6 +8,7 @@ import { TypeParameteredNodeStructure, } from "../../base"; import { OptionalKind } from "../../types"; +import { ClassStaticBlockDeclarationStructure } from "../ClassStaticBlockDeclarationStructure"; import { ConstructorDeclarationStructure } from "../ConstructorDeclarationStructure"; import { GetAccessorDeclarationStructure } from "../GetAccessorDeclarationStructure"; import { MethodDeclarationStructure } from "../MethodDeclarationStructure"; @@ -29,6 +30,7 @@ export interface ClassLikeDeclarationBaseStructure export interface ClassLikeDeclarationBaseSpecificStructure { extends?: string | WriterFunction; ctors?: OptionalKind[]; + staticBlocks?: OptionalKind[]; properties?: OptionalKind[]; getAccessors?: OptionalKind[]; setAccessors?: OptionalKind[]; diff --git a/packages/ts-morph/src/structures/utils/forEachStructureChild.ts b/packages/ts-morph/src/structures/utils/forEachStructureChild.ts index 99f1902e5..c30d741ca 100644 --- a/packages/ts-morph/src/structures/utils/forEachStructureChild.ts +++ b/packages/ts-morph/src/structures/utils/forEachStructureChild.ts @@ -145,6 +145,7 @@ function forClassLikeDeclarationBase(structure: ClassLikeDeclaration || forTypeParameteredNode(structure, callback) || forJSDocableNode(structure, callback) || forAll(structure.ctors, callback, StructureKind.Constructor) + || forAll(structure.staticBlocks, callback, StructureKind.ClassStaticBlock) || forAll(structure.properties, callback, StructureKind.Property) || forAll(structure.getAccessors, callback, StructureKind.GetAccessor) || forAll(structure.setAccessors, callback, StructureKind.SetAccessor) diff --git a/packages/ts-morph/src/tests/compiler/ast/class/classDeclarationTests.ts b/packages/ts-morph/src/tests/compiler/ast/class/classDeclarationTests.ts index 0ebea1783..23cb5546b 100644 --- a/packages/ts-morph/src/tests/compiler/ast/class/classDeclarationTests.ts +++ b/packages/ts-morph/src/tests/compiler/ast/class/classDeclarationTests.ts @@ -70,6 +70,10 @@ class Identifier extends Other { constructor() { } + static { + test; + } + p; get g() { @@ -85,6 +89,7 @@ class Identifier extends Other { const structure: OptionalKindAndTrivia> = { extends: "Other", ctors: [{}], + staticBlocks: [{ statements: ["test;"]}], properties: [{ name: "p" }], getAccessors: [{ name: "g" }], setAccessors: [{ name: "s", parameters: [{ name: "value", type: "string" }] }], @@ -110,6 +115,7 @@ class Identifier { const structure: OptionalKindAndTrivia> = { extends: undefined, ctors: [], + staticBlocks: [], properties: [], getAccessors: [], setAccessors: [], @@ -130,6 +136,7 @@ class Identifier { doTest("class Identifier {}", { kind: StructureKind.Class, ctors: [], + staticBlocks: [], decorators: [], docs: [], extends: undefined, @@ -152,6 +159,7 @@ class Identifier { /** Test */ @dec export default abstract class Identifier extends Base implements IBase { constructor() {} + static {} method() {} prop: string; get getAccessor() {} @@ -161,6 +169,7 @@ class Identifier { doTest(code, { kind: StructureKind.Class, ctors: [{ statements: [], overloads: [] }], + staticBlocks: [{}], decorators: [{ name: "dec" }], docs: [{ description: "Test" }], extends: "Base", @@ -193,6 +202,7 @@ declare class Identifier { doTest(code, { kind: StructureKind.Class, ctors: [{ returnType: "string" }, { returnType: "number" }], + staticBlocks: [], decorators: [], docs: [], extends: undefined, diff --git a/packages/ts-morph/src/tests/compiler/ast/statement/statementedNode/classTests.ts b/packages/ts-morph/src/tests/compiler/ast/statement/statementedNode/classTests.ts index 256143a6e..9a7adbb2b 100644 --- a/packages/ts-morph/src/tests/compiler/ast/statement/statementedNode/classTests.ts +++ b/packages/ts-morph/src/tests/compiler/ast/statement/statementedNode/classTests.ts @@ -63,6 +63,7 @@ describe("StatementedNode", () => { const structure: OptionalKindAndTrivia> = { name: "C", ctors: [{}, {}], + staticBlocks: [{}], decorators: [{ name: "D" }], docs: [{ description: "Test" }], extends: "Base", @@ -79,6 +80,7 @@ describe("StatementedNode", () => { }; const expectedText = "/** Test */\n@D\nexport default abstract class C extends Base implements IBase, IBase2 {\n" + " p: number;\n\n" + + " static {\n }\n\n" + " constructor() {\n }\n\n" + " constructor() {\n }\n\n" + " get g() {\n }\n\n set g() {\n }\n\n get s() {\n }\n\n set s() {\n }\n\n" diff --git a/packages/ts-morph/src/tests/compiler/testHelpers/fillStructureWithDefaults.ts b/packages/ts-morph/src/tests/compiler/testHelpers/fillStructureWithDefaults.ts index 5db1efb33..b6d560f87 100644 --- a/packages/ts-morph/src/tests/compiler/testHelpers/fillStructureWithDefaults.ts +++ b/packages/ts-morph/src/tests/compiler/testHelpers/fillStructureWithDefaults.ts @@ -2,6 +2,7 @@ import { ModuleDeclarationKind, VariableDeclarationKind } from "../../../compile import { CallSignatureDeclarationStructure, ClassDeclarationStructure, + ClassStaticBlockDeclarationStructure, ConstructorDeclarationOverloadStructure, ConstructorDeclarationStructure, ConstructSignatureDeclarationStructure, @@ -96,6 +97,7 @@ export namespace fillStructures { setIfNull(structure, "isDefaultExport", false); setIfNull(structure, "isExported", false); setIfNull(structure, "ctors", []); + setIfNull(structure, "staticBlocks", []); setIfNull(structure, "decorators", []); setIfNull(structure, "docs", []); setIfNull(structure, "extends", undefined); @@ -110,6 +112,7 @@ export namespace fillStructures { fill(structure.decorators!, decorator); fill(structure.typeParameters!, typeParameter); fill(structure.ctors!, constructorDeclaration); + fill(structure.staticBlocks!, classStaticBlock); fill(structure.methods!, method); fill(structure.properties!, property); fill(structure.getAccessors!, getAccessor); @@ -119,6 +122,13 @@ export namespace fillStructures { return structure as ClassDeclarationStructure; } + export function classStaticBlock(structure: OptionalKind): ClassStaticBlockDeclarationStructure { + setIfNull(structure, "statements", []); + setIfNull(structure, "docs", []); + setIfNull(structure, "kind", StructureKind.ClassStaticBlock); + return structure as ClassStaticBlockDeclarationStructure; + } + export function constructorDeclaration(structure: OptionalKind): ConstructorDeclarationStructure { constructorBase(structure); setIfNull(structure, "statements", undefined); diff --git a/packages/ts-morph/src/tests/structurePrinters/class/classDeclarationStructurePrinterTests.ts b/packages/ts-morph/src/tests/structurePrinters/class/classDeclarationStructurePrinterTests.ts index fd74d23be..fd04c35c3 100644 --- a/packages/ts-morph/src/tests/structurePrinters/class/classDeclarationStructurePrinterTests.ts +++ b/packages/ts-morph/src/tests/structurePrinters/class/classDeclarationStructurePrinterTests.ts @@ -37,6 +37,9 @@ describe("ClassDeclarationStructurePrinter", () => { name: "pProtected", }], ctors: [{}], + staticBlocks: [{ + statements: ["test;"], + }], methods: [{ scope: Scope.Private, name: "m1", @@ -59,6 +62,10 @@ describe("ClassDeclarationStructurePrinter", () => { private pPrivate; protected pProtected; + static { + test; + } + constructor() { }