diff --git a/stage_1_bootstrap/build/hooks/structure/addStaticClone.ts b/stage_1_bootstrap/build/hooks/structure/addStaticClone.ts index 79ec72ef..b87743c2 100644 --- a/stage_1_bootstrap/build/hooks/structure/addStaticClone.ts +++ b/stage_1_bootstrap/build/hooks/structure/addStaticClone.ts @@ -93,7 +93,7 @@ export default function addStaticClone( } classFieldsStatements.set( - ClassFieldStatementsMap.FIELD_HEAD_SUPER_CALL, + "(body)", ClassMembersMap.keyFromMember(cloneMethod), [ `const target = new ${classDecl.name!}(${constructorArgs.join(", ")});`, diff --git a/stage_1_bootstrap/build/hooks/structure/specialCases.ts b/stage_1_bootstrap/build/hooks/structure/specialCases.ts index 26dde74b..ff100f18 100644 --- a/stage_1_bootstrap/build/hooks/structure/specialCases.ts +++ b/stage_1_bootstrap/build/hooks/structure/specialCases.ts @@ -1,5 +1,6 @@ import { StructureKind, + VariableDeclarationKind, } from "ts-morph"; import StructureDictionaries, { @@ -10,6 +11,13 @@ import { StructureImplMeta } from "#stage_one/build/structureMeta/DataClasses.js"; +import { + LiteralTypedStructureImpl, + ParameterDeclarationImpl, + VariableDeclarationImpl, + VariableStatementImpl, +} from "#stage_one/prototype-snapshot/exports.js"; + import ClassFieldStatementsMap from "#stage_one/build/utilities/public/ClassFieldStatementsMap.js"; import ClassMembersMap from "#stage_one/build/utilities/public/ClassMembersMap.js"; @@ -24,6 +32,10 @@ export default function structureSpecialCases( case "IndexSignatureDeclarationImpl": fixKeyTypeField(parts, dictionaries); break; + + case "SetAccessorDeclarationImpl": + addParameterToSetAccessorCtor(parts, dictionaries); + break; } return Promise.resolve(); } @@ -77,3 +89,57 @@ function fixKeyTypeField( ] ); } + +function addParameterToSetAccessorCtor( + parts: StructureParts, + dictionaries: StructureDictionaries +): void +{ + parts.importsManager.addImports({ + pathToImportedModule: dictionaries.publicExports.absolutePathToExportFile, + isPackageImport: false, + importNames: ["ParameterDeclarationImpl"], + isDefaultImport: false, + isTypeOnly: false + }); + + const setterParam = new ParameterDeclarationImpl("setterParameter"); + setterParam.typeStructure = new LiteralTypedStructureImpl("ParameterDeclarationImpl"); + + const ctor = parts.classMembersMap.getAsKind( + "constructor", StructureKind.Constructor + )!; + ctor.parameters.push(setterParam); + + parts.classFieldsStatements.set("setterParameter", "constructor", [ + "this.parameters.push(setterParameter);" + ]); + + const cloneStatements = parts.classFieldsStatements.get( + "(body)", "static clone" + )!; + + const valueParamStatement = new VariableStatementImpl(); + valueParamStatement.declarationKind = VariableDeclarationKind.Const; + const valueParamDeclaration = new VariableDeclarationImpl("valueParam"); + valueParamDeclaration.type = "ParameterDeclarationImpl"; + valueParamDeclaration.initializer = `new ParameterDeclarationImpl("value");`; + valueParamStatement.declarations.push(valueParamDeclaration); + + cloneStatements.splice( + 0, 1, + // harder to read, I know, but also a valuable unit test + valueParamStatement, + `const hasSourceParameter = source.parameters && source.parameters.length > 0;`, + + // `const target = new ${classDecl.name!}(${constructorArgs.join(", ")});`, + (cloneStatements[0] as string).replace("source.name", "source.name, valueParam") + ); + + parts.classFieldsStatements.set("valueParam", "static clone", [ + `if (hasSourceParameter) { + // copy-fields included copying the existing parameter, so we have to drop our artificial one + target.parameters.shift(); + }` + ]); +} diff --git a/stage_2_fullset/snapshot/source/structures/SetAccessorDeclarationImpl.ts b/stage_2_fullset/snapshot/source/structures/SetAccessorDeclarationImpl.ts index 555fbb6c..6fec4f79 100644 --- a/stage_2_fullset/snapshot/source/structures/SetAccessorDeclarationImpl.ts +++ b/stage_2_fullset/snapshot/source/structures/SetAccessorDeclarationImpl.ts @@ -1,4 +1,5 @@ //#region preamble +import { ParameterDeclarationImpl } from "../exports.js"; import { type AbstractableNodeStructureFields, AbstractableNodeStructureMixin, @@ -78,20 +79,36 @@ export default class SetAccessorDeclarationImpl readonly kind: StructureKind.SetAccessor = StructureKind.SetAccessor; readonly isStatic: boolean; - constructor(isStatic: boolean, name: string) { + constructor( + isStatic: boolean, + name: string, + setterParameter: ParameterDeclarationImpl, + ) { super(); this.isStatic = isStatic; this.name = name; + this.parameters.push(setterParameter); } public static clone( source: OptionalKind, ): SetAccessorDeclarationImpl { + const valueParam: ParameterDeclarationImpl = new ParameterDeclarationImpl( + "value", + ); + const hasSourceParameter = + source.parameters && source.parameters.length > 0; const target = new SetAccessorDeclarationImpl( source.isStatic ?? false, source.name, + valueParam, ); this[COPY_FIELDS](source, target); + if (hasSourceParameter) { + // copy-fields included copying the existing parameter, so we have to drop our artificial one + target.parameters.shift(); + } + return target; } diff --git a/stage_2_fullset/spec-snapshot/source/toolbox/ClassMembersMap.ts b/stage_2_fullset/spec-snapshot/source/toolbox/ClassMembersMap.ts index 272203a8..f2a0e663 100644 --- a/stage_2_fullset/spec-snapshot/source/toolbox/ClassMembersMap.ts +++ b/stage_2_fullset/spec-snapshot/source/toolbox/ClassMembersMap.ts @@ -33,13 +33,11 @@ describe("ClassMembersMap", () => { method3 = new MethodDeclarationImpl(true, "three"); method4 = new MethodDeclarationImpl(false, "four"); + const value_five = new ParameterDeclarationImpl("five"); + value_five.typeStructure = "string"; + getter5 = new GetAccessorDeclarationImpl(false, "five"); - setter5 = new SetAccessorDeclarationImpl(false, "five"); - { - const param = new ParameterDeclarationImpl("value"); - param.typeStructure = "string"; - setter5.parameters.push(param); - } + setter5 = new SetAccessorDeclarationImpl(false, "five", value_five); }); it("ClassMembersMap allows us to organize class members by kind", () => { @@ -133,7 +131,7 @@ describe("ClassMembersMap", () => { expect(method4.statements).toEqual([`console.log(this.one);`]); expect(getter5.statements).toEqual([`return this.#five;`]); - expect(setter5.statements).toEqual([`this.#five = value;`]); + expect(setter5.statements).toEqual([`this.#five = five;`]); }); it("ClassMembersMap static methods give us keys we can use in the map", () => { diff --git a/stage_2_fullset/spec-snapshot/source/toolbox/TypeMembersMap.ts b/stage_2_fullset/spec-snapshot/source/toolbox/TypeMembersMap.ts index 7c7833ca..51cbf221 100644 --- a/stage_2_fullset/spec-snapshot/source/toolbox/TypeMembersMap.ts +++ b/stage_2_fullset/spec-snapshot/source/toolbox/TypeMembersMap.ts @@ -5,6 +5,7 @@ import { import { GetAccessorDeclarationImpl, MethodSignatureImpl, + ParameterDeclarationImpl, PropertySignatureImpl, SetAccessorDeclarationImpl, TypeMembersMap, @@ -17,7 +18,7 @@ it("TypeMembersMap allows us to organize type members by kind", () => { const method3 = new MethodSignatureImpl("three"); const getter4 = new GetAccessorDeclarationImpl(false, "four"); - const setter4 = new SetAccessorDeclarationImpl(false, "four"); + const setter4 = new SetAccessorDeclarationImpl(false, "four", new ParameterDeclarationImpl("value")); memberMap.addMembers([ prop1, prop2, method3, getter4, setter4