Skip to content

Commit

Permalink
#13 TypeMembersMap, conversions between accessors and property signat…
Browse files Browse the repository at this point in the history
…ures.
  • Loading branch information
ajvincent committed Jan 14, 2024
1 parent 56715e1 commit 7b7e667
Show file tree
Hide file tree
Showing 6 changed files with 424 additions and 28 deletions.
5 changes: 3 additions & 2 deletions stage_1_integration/toolbox/ClassMembersMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,9 @@ extends Map<string, ClassMemberImpl>

if (getter?.returnTypeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(getter.returnTypeStructure);
} else {
const setterParam = setter!.parameters[0] as ParameterDeclarationImpl;
}
else if (setter) {
const setterParam = setter.parameters[0] as ParameterDeclarationImpl;
if (setterParam.typeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(setterParam.typeStructure);
}
Expand Down
107 changes: 99 additions & 8 deletions stage_1_integration/toolbox/TypeMembersMap.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import {
KindedStructure,
JSDocStructure,
StructureKind
} from "ts-morph";

import {
CallSignatureDeclarationImpl,
ConstructSignatureDeclarationImpl,
GetAccessorDeclarationImpl,
JSDocImpl,
InterfaceDeclarationImpl,
IndexSignatureDeclarationImpl,
MemberedObjectTypeStructureImpl,
MethodSignatureImpl,
ParameterDeclarationImpl,
PropertySignatureImpl,
SetAccessorDeclarationImpl,
} from "../snapshot/source/exports.js";

import {
TypeStructureClassesMap,
cloneStructureOrStringArray,
} from "../snapshot/source/internal-exports.js";

export type TypeMemberImpl = (
CallSignatureDeclarationImpl |
ConstructSignatureDeclarationImpl |
Expand Down Expand Up @@ -162,37 +170,120 @@ extends Map<string, TypeMemberImpl>
* @see `TypeMembersMap::keyFromName`
*/
getAsKind<
Kind extends TypeMemberImpl["kind"]
Kind extends NamedTypeMemberImpl["kind"]
>
(
kind: Kind,
name: string,
): Extract<TypeMemberImpl, KindedStructure<Kind>> | undefined
{
const rv = this.get(name);
const key = TypeMembersMap.keyFromName(kind, name);
const rv = this.get(key);
if (rv?.kind === kind)
return rv as Extract<TypeMemberImpl, KindedStructure<Kind>>;
return undefined;
}

/**
* Convert get and/or set accessors to a property. This may be lossy, but we try to be faithful.
* @param name - the property name
*/
convertAccessorsToProperty(
name: string
): void
{
void(name);
throw new Error("not yet implemented");
const getter = this.getAsKind<StructureKind.GetAccessor>(StructureKind.GetAccessor, name);
const setter = this.getAsKind<StructureKind.SetAccessor>(StructureKind.SetAccessor, name);
if (!getter && !setter) {
throw new Error(name + " accessors not found!");
}

const prop = new PropertySignatureImpl(getter?.name ?? setter!.name);
// This is a merge operation: prefer getter fields over setter fields

const docs = getter?.docs ?? setter!.docs;
if (docs) {
prop.docs.push(...cloneStructureOrStringArray<JSDocStructure, StructureKind.JSDoc, JSDocImpl>(
docs as (string | JSDocImpl)[], StructureKind.JSDoc
));
}

prop.leadingTrivia.push(...(getter?.leadingTrivia ?? setter!.leadingTrivia));
prop.trailingTrivia.push(...(getter?.leadingTrivia ?? setter!.leadingTrivia));

if (getter?.returnTypeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(getter.returnTypeStructure);
}
else if (setter) {
const setterParam = setter.parameters[0] as ParameterDeclarationImpl;
if (setterParam.typeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(setterParam.typeStructure);
}
}

this.addMembers([prop]);
if (getter) {
this.delete(TypeMembersMap.keyFromMember(getter));
}
if (setter) {
this.delete(TypeMembersMap.keyFromMember(setter));
}
}

/**
* Convert a property signature to get and/or set accessors. This may be lossy, but we try to be faithful.
* @param name - the property name
* @param toGetter - true if the caller wants a getter
* @param toSetter - true if the caller wants a setter
*/
convertPropertyToAccessors(
name: string,
toGetter: boolean,
toSetter: boolean
): void
{
void(name);
void(toGetter);
void(toSetter);
throw new Error("not yet implemented");
if (!toGetter && !toSetter)
throw new Error("You must request either a get accessor or a set accessor!");

const prop = this.getAsKind<StructureKind.PropertySignature>(StructureKind.PropertySignature, name);
if (!prop) {
throw new Error(name + " property not found!");
}

if (toGetter) {
const getter = new GetAccessorDeclarationImpl(false, prop.name, prop.typeStructure);
if (prop.docs) {
getter.docs.push(...cloneStructureOrStringArray<JSDocStructure, StructureKind.JSDoc, JSDocImpl>(
prop.docs as (string | JSDocImpl)[]
, StructureKind.JSDoc))
}

getter.leadingTrivia.push(...prop.leadingTrivia);
getter.trailingTrivia.push(...prop.trailingTrivia);

this.addMembers([getter]);
}

if (toSetter) {
const param = new ParameterDeclarationImpl("value");
if (prop.typeStructure)
param.typeStructure = TypeStructureClassesMap.clone(prop.typeStructure);

const setter = new SetAccessorDeclarationImpl(false, prop.name, param);

if (prop.docs) {
setter.docs.push(...cloneStructureOrStringArray<JSDocStructure, StructureKind.JSDoc, JSDocImpl>(
prop.docs as (string | JSDocImpl)[]
, StructureKind.JSDoc))
}

setter.leadingTrivia.push(...prop.leadingTrivia);
setter.trailingTrivia.push(...prop.trailingTrivia);

this.addMembers([setter]);
}

this.delete(TypeMembersMap.keyFromMember(prop));
}

resolveIndexSignature(
Expand Down
4 changes: 2 additions & 2 deletions stage_2_fullset/snapshot/source/toolbox/ClassMembersMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ export default class ClassMembersMap extends Map<string, ClassMemberImpl> {
prop.typeStructure = TypeStructureClassesMap.clone(
getter.returnTypeStructure,
);
} else {
const setterParam = setter!.parameters[0] as ParameterDeclarationImpl;
} else if (setter) {
const setterParam = setter.parameters[0] as ParameterDeclarationImpl;
if (setterParam.typeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(
setterParam.typeStructure,
Expand Down
142 changes: 133 additions & 9 deletions stage_2_fullset/snapshot/source/toolbox/TypeMembersMap.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import { KindedStructure, StructureKind } from "ts-morph";
import { KindedStructure, JSDocStructure, StructureKind } from "ts-morph";

import {
CallSignatureDeclarationImpl,
ConstructSignatureDeclarationImpl,
GetAccessorDeclarationImpl,
JSDocImpl,
InterfaceDeclarationImpl,
IndexSignatureDeclarationImpl,
MemberedObjectTypeStructureImpl,
MethodSignatureImpl,
ParameterDeclarationImpl,
PropertySignatureImpl,
SetAccessorDeclarationImpl,
} from "../exports.js";

import {
TypeStructureClassesMap,
cloneStructureOrStringArray,
} from "../internal-exports.js";

export type TypeMemberImpl =
| CallSignatureDeclarationImpl
| ConstructSignatureDeclarationImpl
Expand Down Expand Up @@ -133,30 +140,147 @@ export default class TypeMembersMap extends Map<string, TypeMemberImpl> {
*
* @see `TypeMembersMap::keyFromName`
*/
getAsKind<Kind extends TypeMemberImpl["kind"]>(
getAsKind<Kind extends NamedTypeMemberImpl["kind"]>(
kind: Kind,
name: string,
): Extract<TypeMemberImpl, KindedStructure<Kind>> | undefined {
const rv = this.get(name);
const key = TypeMembersMap.keyFromName(kind, name);
const rv = this.get(key);
if (rv?.kind === kind)
return rv as Extract<TypeMemberImpl, KindedStructure<Kind>>;
return undefined;
}

/**
* Convert get and/or set accessors to a property. This may be lossy, but we try to be faithful.
* @param name - the property name
*/
convertAccessorsToProperty(name: string): void {
void name;
throw new Error("not yet implemented");
const getter = this.getAsKind<StructureKind.GetAccessor>(
StructureKind.GetAccessor,
name,
);
const setter = this.getAsKind<StructureKind.SetAccessor>(
StructureKind.SetAccessor,
name,
);
if (!getter && !setter) {
throw new Error(name + " accessors not found!");
}

const prop = new PropertySignatureImpl(getter?.name ?? setter!.name);
// This is a merge operation: prefer getter fields over setter fields

const docs = getter?.docs ?? setter!.docs;
if (docs) {
prop.docs.push(
...cloneStructureOrStringArray<
JSDocStructure,
StructureKind.JSDoc,
JSDocImpl
>(docs as (string | JSDocImpl)[], StructureKind.JSDoc),
);
}

prop.leadingTrivia.push(
...(getter?.leadingTrivia ?? setter!.leadingTrivia),
);
prop.trailingTrivia.push(
...(getter?.leadingTrivia ?? setter!.leadingTrivia),
);

if (getter?.returnTypeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(
getter.returnTypeStructure,
);
} else if (setter) {
const setterParam = setter.parameters[0] as ParameterDeclarationImpl;
if (setterParam.typeStructure) {
prop.typeStructure = TypeStructureClassesMap.clone(
setterParam.typeStructure,
);
}
}

this.addMembers([prop]);
if (getter) {
this.delete(TypeMembersMap.keyFromMember(getter));
}
if (setter) {
this.delete(TypeMembersMap.keyFromMember(setter));
}
}

/**
* Convert a property signature to get and/or set accessors. This may be lossy, but we try to be faithful.
* @param name - the property name
* @param toGetter - true if the caller wants a getter
* @param toSetter - true if the caller wants a setter
*/
convertPropertyToAccessors(
name: string,
toGetter: boolean,
toSetter: boolean,
): void {
void name;
void toGetter;
void toSetter;
throw new Error("not yet implemented");
if (!toGetter && !toSetter)
throw new Error(
"You must request either a get accessor or a set accessor!",
);

const prop = this.getAsKind<StructureKind.PropertySignature>(
StructureKind.PropertySignature,
name,
);
if (!prop) {
throw new Error(name + " property not found!");
}

if (toGetter) {
const getter = new GetAccessorDeclarationImpl(
false,
prop.name,
prop.typeStructure,
);
if (prop.docs) {
getter.docs.push(
...cloneStructureOrStringArray<
JSDocStructure,
StructureKind.JSDoc,
JSDocImpl
>(prop.docs as (string | JSDocImpl)[], StructureKind.JSDoc),
);
}

getter.leadingTrivia.push(...prop.leadingTrivia);
getter.trailingTrivia.push(...prop.trailingTrivia);

this.addMembers([getter]);
}

if (toSetter) {
const param = new ParameterDeclarationImpl("value");
if (prop.typeStructure)
param.typeStructure = TypeStructureClassesMap.clone(prop.typeStructure);

const setter = new SetAccessorDeclarationImpl(false, prop.name, param);

if (prop.docs) {
setter.docs.push(
...cloneStructureOrStringArray<
JSDocStructure,
StructureKind.JSDoc,
JSDocImpl
>(prop.docs as (string | JSDocImpl)[], StructureKind.JSDoc),
);
}

setter.leadingTrivia.push(...prop.leadingTrivia);
setter.trailingTrivia.push(...prop.trailingTrivia);

this.addMembers([setter]);
}

this.delete(TypeMembersMap.keyFromMember(prop));
}

resolveIndexSignature(): never {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ describe("ClassMembersMap", () => {
expect(prop5).not.toBeUndefined();
if (prop5) {
expect(prop5.name).toBe(getter5.name);
expect(prop5.typeStructure).toBe(getter5.returnTypeStructure);
}

expect(membersMap.getAsKind<StructureKind.GetAccessor>(
Expand All @@ -285,6 +286,8 @@ describe("ClassMembersMap", () => {
expect(prop5).not.toBeUndefined();
if (prop5) {
expect(prop5.name).toBe(setter5.name);
const param = setter5.parameters[0] as ParameterDeclarationImpl;
expect(prop5.typeStructure).toBe(param.typeStructure);
}

expect(membersMap.getAsKind<StructureKind.SetAccessor>(
Expand All @@ -300,6 +303,7 @@ describe("ClassMembersMap", () => {
expect(prop5).not.toBeUndefined();
if (prop5) {
expect(prop5.name).toBe(getter5.name);
expect(prop5.typeStructure).toBe(getter5.returnTypeStructure);
}

expect(membersMap.getAsKind<StructureKind.GetAccessor>(
Expand Down
Loading

0 comments on commit 7b7e667

Please sign in to comment.