Skip to content

Commit 0ab0947

Browse files
committed
feature(transformer): Add serialization helpers including one for method signature-based hash
1 parent 92fe6c9 commit 0ab0947

File tree

1 file changed

+136
-1
lines changed

1 file changed

+136
-1
lines changed

src/transformer/helper/creator.ts

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
import { PropertyName, VariableDeclaration, VariableStatement} from 'typescript';
1+
import { PropertyName, VariableDeclaration, VariableStatement } from 'typescript';
22
import * as ts from 'typescript';
3+
import { IsTypescriptType } from '../descriptor/tsLibs/typecriptLibs';
4+
import { TypescriptHelper } from '../descriptor/helper/helper';
5+
import { NodeToString } from '../printNode';
6+
7+
export interface MethodSignature extends ts.MethodSignature {
8+
parameters: ts.NodeArray<ParameterDeclaration>;
9+
type: ts.TypeNode;
10+
}
11+
12+
export interface ParameterDeclaration extends ts.ParameterDeclaration {
13+
type: ts.TypeNode;
14+
}
315

416
export namespace TypescriptCreator {
517
export function createArrowFunction(block: ts.ConciseBody, parameter: ReadonlyArray<ts.ParameterDeclaration> = []): ts.ArrowFunction {
@@ -91,6 +103,129 @@ export namespace TypescriptCreator {
91103
);
92104
}
93105

106+
function serialize(node: ts.TypeNode): string {
107+
if (TypescriptHelper.IsLiteralOrPrimitive(node) || ts.isTypeReferenceNode(node)) {
108+
return NodeToString(node);
109+
}
110+
111+
if (ts.isTypeLiteralNode(node)) {
112+
const serialized: string = node.members
113+
.filter((member: ts.TypeElement): member is ts.PropertySignature => ts.isPropertySignature(member))
114+
.map((member: ts.PropertySignature) => member.type ? serialize(member.type) : '').join('|');
115+
return `{${serialized}}`;
116+
}
117+
118+
if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {
119+
const serialized: string = node.types.map((member: ts.TypeNode) => serialize(member)).join('|');
120+
return `[${serialized}]`;
121+
}
122+
123+
return '';
124+
}
125+
126+
export function createSignatureHash(signature: ts.SignatureDeclaration | ts.SignatureDeclaration[]): string {
127+
function serializeSignature(s: ts.SignatureDeclaration): string {
128+
const parameters: ts.NodeArray<ts.ParameterDeclaration> = s.parameters;
129+
130+
const signatureParts: string[] = [];
131+
132+
if (parameters.length) {
133+
signatureParts.push(
134+
...parameters.map(<T extends { type?: ts.TypeNode }>(p: T) => {
135+
const type: ts.TypeNode | undefined = p.type;
136+
137+
if (!type) {
138+
return '';
139+
}
140+
141+
if (ts.isFunctionLike(type)) {
142+
return `(${serializeSignature(type)})`;
143+
}
144+
145+
return serialize(type);
146+
})
147+
);
148+
}
149+
150+
const signatureType: ts.TypeNode | undefined = s.type;
151+
152+
if (signatureType) {
153+
signatureParts.push(serialize(signatureType));
154+
}
155+
156+
return signatureParts.join('|');
157+
}
158+
159+
const signatures: ts.SignatureDeclaration[] = Array.isArray(signature) ? signature : [signature];
160+
161+
// TODO: Check debug option and emit a verbose string representation
162+
163+
// TODO: Make sure this doesn't result in collisions
164+
return Buffer.from(
165+
[
166+
Array.from(signatures.map((s: ts.SignatureDeclaration) => serializeSignature(s)).join('|'))
167+
.reduce((s: number, c: string) => {
168+
// eslint-disable-next-line
169+
const charCode: number = c.charCodeAt(0) | 0;
170+
171+
return Math.imul(31, s) + charCode;
172+
}, 0),
173+
],
174+
).toString('base64');
175+
}
176+
177+
function isDefinitiveMethodSignature(signature: ts.MethodSignature): signature is MethodSignature {
178+
return !!signature.type;
179+
}
180+
181+
function isDefinitiveParameterDeclaration(parameter: ts.ParameterDeclaration): parameter is ParameterDeclaration {
182+
return !!parameter.type;
183+
}
184+
185+
export function createMethodSignature(parameterTypes: Array<ts.TypeNode | undefined> = [], returnType: ts.TypeNode | undefined): MethodSignature {
186+
const parameters: ParameterDeclaration[] = parameterTypes
187+
.filter((type: ts.TypeNode | undefined): type is ts.TypeNode => !!type)
188+
.map((parameterType: ts.TypeNode, i: number) => {
189+
// TODO: Merge/move this block with/to typescriptLibs.ts
190+
if (ts.isTypeReferenceNode(parameterType)) {
191+
const declaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(parameterType.typeName);
192+
if (IsTypescriptType(declaration)) {
193+
parameterType = ts.createFunctionTypeNode(undefined, [], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword));
194+
}
195+
}
196+
197+
const parameter: ts.ParameterDeclaration = ts.createParameter(
198+
undefined,
199+
undefined,
200+
undefined,
201+
`__${i++}`,
202+
undefined,
203+
parameterType,
204+
undefined,
205+
);
206+
207+
if (!isDefinitiveParameterDeclaration(parameter)) {
208+
throw new Error();
209+
}
210+
211+
return parameter;
212+
});
213+
214+
const signature: ts.MethodSignature = ts.createMethodSignature(
215+
undefined,
216+
parameters,
217+
returnType || ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword),
218+
'',
219+
undefined,
220+
);
221+
222+
if (!isDefinitiveMethodSignature(signature)) {
223+
throw new Error();
224+
}
225+
226+
return signature;
227+
}
228+
94229
export function createVariableDeclaration(variableIdentifier: ts.Identifier, initializer: ts.Expression): ts.VariableDeclaration {
95230
return ts.createVariableDeclaration(
96231
variableIdentifier,

0 commit comments

Comments
 (0)