Skip to content

Commit a49fba1

Browse files
committed
feature(transformer): Add serialization helpers including one for method signature-based hash
1 parent 24fef12 commit a49fba1

File tree

1 file changed

+138
-1
lines changed

1 file changed

+138
-1
lines changed

src/transformer/helper/creator.ts

Lines changed: 138 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,131 @@ 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+
// FIXME: This is not really a hash right now. Keep this as is for debugging
127+
// though... maybe even be more verbose than this.
128+
export function createSignatureHash(signature: ts.SignatureDeclaration | ts.SignatureDeclaration[]): string {
129+
function serializeSignature(s: ts.SignatureDeclaration): string {
130+
const parameters: ts.NodeArray<ts.ParameterDeclaration> = s.parameters;
131+
132+
const signatureParts: string[] = [];
133+
134+
if (parameters.length) {
135+
signatureParts.push(
136+
...parameters.map(<T extends { type?: ts.TypeNode }>(p: T) => {
137+
const type: ts.TypeNode | undefined = p.type;
138+
139+
if (!type) {
140+
return '';
141+
}
142+
143+
if (ts.isFunctionLike(type)) {
144+
return `(${serializeSignature(type)})`;
145+
}
146+
147+
return serialize(type);
148+
})
149+
);
150+
}
151+
152+
const signatureType: ts.TypeNode | undefined = s.type;
153+
154+
if (signatureType) {
155+
signatureParts.push(serialize(signatureType));
156+
}
157+
158+
return signatureParts.join('|');
159+
}
160+
161+
const signatures: ts.SignatureDeclaration[] = Array.isArray(signature) ? signature : [signature];
162+
163+
// TODO: Check debug option and emit a verbose string representation
164+
165+
// TODO: Make sure this don't result in collisions
166+
return Buffer.from(
167+
[
168+
Array.from(signatures.map((s: ts.SignatureDeclaration) => serializeSignature(s)).join('|'))
169+
.reduce((s: number, c: string) => {
170+
// eslint-disable-next-line
171+
const charCode: number = c.charCodeAt(0) | 0;
172+
173+
return Math.imul(31, s) + charCode;
174+
}, 0),
175+
],
176+
).toString('base64');
177+
}
178+
179+
function isDefinitiveMethodSignature(signature: ts.MethodSignature): signature is MethodSignature {
180+
return !!signature.type;
181+
}
182+
183+
function isDefinitiveParameterDeclaration(parameter: ts.ParameterDeclaration): parameter is ParameterDeclaration {
184+
return !!parameter.type;
185+
}
186+
187+
export function createMethodSignature(parameterTypes: Array<ts.TypeNode | undefined> = [], returnType: ts.TypeNode | undefined): MethodSignature {
188+
const parameters: ParameterDeclaration[] = parameterTypes
189+
.filter((type: ts.TypeNode | undefined): type is ts.TypeNode => !!type)
190+
.map((parameterType: ts.TypeNode, i: number) => {
191+
// TODO: Merge/move this block with/to typescriptLibs.ts
192+
if (ts.isTypeReferenceNode(parameterType)) {
193+
const declaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(parameterType.typeName);
194+
if (IsTypescriptType(declaration)) {
195+
parameterType = ts.createFunctionTypeNode(undefined, [], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword));
196+
}
197+
}
198+
199+
const parameter: ts.ParameterDeclaration = ts.createParameter(
200+
undefined,
201+
undefined,
202+
undefined,
203+
`__${i++}`,
204+
undefined,
205+
parameterType,
206+
undefined,
207+
);
208+
209+
if (!isDefinitiveParameterDeclaration(parameter)) {
210+
throw new Error();
211+
}
212+
213+
return parameter;
214+
});
215+
216+
const signature: ts.MethodSignature = ts.createMethodSignature(
217+
undefined,
218+
parameters,
219+
returnType || ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword),
220+
'',
221+
undefined,
222+
);
223+
224+
if (!isDefinitiveMethodSignature(signature)) {
225+
throw new Error();
226+
}
227+
228+
return signature;
229+
}
230+
94231
export function createVariableDeclaration(variableIdentifier: ts.Identifier, initializer: ts.Expression): ts.VariableDeclaration {
95232
return ts.createVariableDeclaration(
96233
variableIdentifier,

0 commit comments

Comments
 (0)