restfuncs-transformer 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AddRemoteMethodsMeta.d.ts +24 -6
- package/AddRemoteMethodsMeta.d.ts.map +1 -1
- package/AddRemoteMethodsMeta.js +164 -37
- package/AddRemoteMethodsMeta.js.map +1 -1
- package/AddRemoteMethodsMeta.ts +244 -166
- package/devPlayground.d.ts +2 -1
- package/devPlayground.d.ts.map +1 -1
- package/devPlayground.js +1 -29
- package/devPlayground.js.map +1 -1
- package/devPlayground.ts +2 -12
- package/index.d.ts.map +1 -1
- package/index.js +43 -7
- package/index.js.map +1 -1
- package/index.ts +23 -7
- package/package.json +7 -3
- package/readme.md +37 -9
- package/transformerUtil.d.ts +10 -0
- package/transformerUtil.d.ts.map +1 -1
- package/transformerUtil.js +23 -1
- package/transformerUtil.js.map +1 -1
- package/transformerUtil.test.d.ts +2 -0
- package/transformerUtil.test.d.ts.map +1 -0
- package/transformerUtil.test.js +11 -0
- package/transformerUtil.test.js.map +1 -0
- package/transformerUtil.test.ts +12 -0
- package/transformerUtil.ts +23 -0
package/AddRemoteMethodsMeta.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import ts, {
|
|
2
|
-
|
|
2
|
+
ArrowFunction,
|
|
3
|
+
ClassDeclaration, Expression, FunctionTypeNode, Identifier,
|
|
3
4
|
MethodDeclaration,
|
|
4
|
-
Node,
|
|
5
|
-
ObjectLiteralExpression,
|
|
5
|
+
Node, NodeArray,
|
|
6
|
+
ObjectLiteralExpression, ParameterDeclaration,
|
|
6
7
|
PropertyAccessExpression,
|
|
7
|
-
SyntaxKind
|
|
8
|
+
SyntaxKind, TypeNode, TypeReference, TypeReferenceNode
|
|
8
9
|
} from 'typescript';
|
|
9
|
-
import {FileTransformRun} from "./transformerUtil";
|
|
10
|
+
import {FileTransformRun, TextPatch} from "./transformerUtil";
|
|
10
11
|
import {transformerVersion} from "./index";
|
|
12
|
+
import {visitReplace} from "restfuncs-common";
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -19,32 +21,36 @@ import {transformerVersion} from "./index";
|
|
|
19
21
|
* </pre></code>
|
|
20
22
|
*/
|
|
21
23
|
export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
22
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Should this transformer squeeze the added declarations (result) into one line to keep the line numbers in the source text intact ?
|
|
26
|
+
* Prevents the [broken source maps bug](https://github.com/bogeeee/restfuncs/issues/2)
|
|
27
|
+
*/
|
|
28
|
+
static squeezeDeclarationsIntoOneLine = true;
|
|
23
29
|
diagnosis_currentClass_instanceMethodsSeen?: Set<string>;
|
|
24
|
-
currentClass_instanceMethodsMeta?: Record<string,
|
|
25
|
-
|
|
30
|
+
currentClass_instanceMethodsMeta?: Record<string, string>; // The {...} that should be added below... see example in readme.md
|
|
31
|
+
result = new TextPatch();
|
|
26
32
|
|
|
27
33
|
/* Visitor Function */
|
|
28
34
|
visit(node: Node): Node {
|
|
29
35
|
if (node.kind === SyntaxKind.MethodDeclaration) { // @remote ?
|
|
30
36
|
const methodDeclaration = (node as MethodDeclaration)
|
|
31
37
|
if(this.getChilds(methodDeclaration).some(n => n.kind == SyntaxKind.StaticKeyword)) { // static ?
|
|
32
|
-
return node;
|
|
38
|
+
return node; // no special handling
|
|
33
39
|
}
|
|
34
40
|
if(this.getParent().kind !== SyntaxKind.ClassDeclaration) { // Method not under a class (i.e. an anonymous object ?}
|
|
35
|
-
return node;
|
|
41
|
+
return node; // no special handling
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
const methodName = (methodDeclaration.name as any).escapedText;
|
|
39
45
|
try {
|
|
40
46
|
let remoteDecorator = this.getChilds(methodDeclaration).find(d => d.kind === SyntaxKind.Decorator);
|
|
41
47
|
if (!remoteDecorator) { // no @remote found ?
|
|
42
|
-
return node;
|
|
48
|
+
return node; // no special handling
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
// Diagnosis: Check for overloads:
|
|
46
52
|
if(this.diagnosis_currentClass_instanceMethodsSeen?.has(methodName)) { // Seen twice ?
|
|
47
|
-
throw new Error(`@remote methods cannot have multiple/overloaded signatures: ${methodName}`) // TODO: add diagnosis
|
|
53
|
+
throw new Error(`@remote methods cannot have multiple/overloaded signatures: ${methodName}. Location: ${this.diag_sourceLocation(node)}`) // TODO: add diagnosis
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
this.currentClass_instanceMethodsMeta![methodName] = this.createMethodMetaExpression(methodDeclaration, methodName) // create the code:
|
|
@@ -62,10 +68,18 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
62
68
|
let result = this.visitChilds(node) as ClassDeclaration
|
|
63
69
|
|
|
64
70
|
if(Object.keys(this.currentClass_instanceMethodsMeta).length > 0) { // Current class has @remote methods ?
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
if(this.result.patches.length === 0) { // First patch
|
|
72
|
+
this.result.patches.push({position: 0, contentToInsert: '/* inserted by restfuncs-transformer:*/import _rf_typia from "typia";'}); // add the type import, which is needed by the following
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// *** Create the "getRemoteMethodsMeta()" function and add a patch for the source file to the result: ***
|
|
76
|
+
let methodDeclarationSourceText = this.create_static_getRemoteMethodsMeta_expression();
|
|
77
|
+
if(AddRemoteMethodsMeta.squeezeDeclarationsIntoOneLine) {
|
|
78
|
+
methodDeclarationSourceText = '/* code squeezed into one line to keep line numbers intact. You can output it prettier by setting "pretty":true in the plugin configuration in tsconfig.json */' + methodDeclarationSourceText.replaceAll("\n","");
|
|
79
|
+
}
|
|
80
|
+
methodDeclarationSourceText = ";" + methodDeclarationSourceText; // prepend with semicolon to prevent syntax error if there's stuff in the same line of the closing bracket
|
|
81
|
+
|
|
82
|
+
this.result.patches.push({position: node.end -1 /* insert before the closing bracket*/, contentToInsert: methodDeclarationSourceText}); // Add patch to result
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
|
|
@@ -89,6 +103,10 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
89
103
|
* arguments: {
|
|
90
104
|
* ...
|
|
91
105
|
* }
|
|
106
|
+
* result: {
|
|
107
|
+
* ...
|
|
108
|
+
* }
|
|
109
|
+
* callbacks:[...]
|
|
92
110
|
* jsDoc: {
|
|
93
111
|
* ...
|
|
94
112
|
* }
|
|
@@ -98,33 +116,52 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
98
116
|
* @param methodName
|
|
99
117
|
* @private
|
|
100
118
|
*/
|
|
101
|
-
createMethodMetaExpression(node: MethodDeclaration, methodName: string) {
|
|
119
|
+
createMethodMetaExpression(node: MethodDeclaration, methodName: string): string {
|
|
120
|
+
// Note: These `factory.create...` "pyramids of doom", which you see all along in this method's code, were mostly created by copying the example code from readme.md#how-it-works through the [AST viewer tool](https://ts-ast-viewer.com/)
|
|
121
|
+
// TODO: remove them und use plain strings
|
|
102
122
|
const factory = this.context.factory;
|
|
103
123
|
|
|
104
|
-
const arguments_typiaFuncs:Record<string,
|
|
124
|
+
const arguments_typiaFuncs:Record<string, string> = {
|
|
125
|
+
"validateEquals": "_rf_typia.validateEquals",
|
|
126
|
+
"validatePrune": "_rf_typia.misc.validatePrune"
|
|
127
|
+
}
|
|
128
|
+
const result_typiaFuncs = {...arguments_typiaFuncs};
|
|
129
|
+
|
|
130
|
+
const old_typiaFuncs:Record<string, PropertyAccessExpression> = {
|
|
105
131
|
/**
|
|
106
132
|
* typia.validateEquals
|
|
107
133
|
*/
|
|
108
134
|
validateEquals: factory.createPropertyAccessExpression(
|
|
109
|
-
factory.createIdentifier("
|
|
135
|
+
factory.createIdentifier("_rf_typia"),
|
|
110
136
|
factory.createIdentifier("validateEquals")
|
|
111
137
|
),
|
|
112
138
|
"validatePrune": factory.createPropertyAccessExpression(factory.createPropertyAccessExpression(
|
|
113
|
-
factory.createIdentifier("
|
|
139
|
+
factory.createIdentifier("_rf_typia"),
|
|
114
140
|
factory.createIdentifier("misc")
|
|
115
141
|
), factory.createIdentifier("validatePrune"))
|
|
116
142
|
}
|
|
117
|
-
const result_typiaFuncs = {...arguments_typiaFuncs};
|
|
118
143
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
// Clone parameters and convert all ParameterDeclarations to NamedTupleMembers (They look the same in the .ts code but are of a different SyntaxKind).
|
|
145
|
+
const methodParametersWithPlaceholders = structuredClone(node.parameters).map(paramDecl => {
|
|
146
|
+
return factory.createNamedTupleMember(
|
|
147
|
+
paramDecl.dotDotDotToken,
|
|
148
|
+
paramDecl.name as Identifier,
|
|
149
|
+
paramDecl.questionToken,
|
|
150
|
+
paramDecl.type || factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
|
|
151
|
+
)
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ** Callbacks: **
|
|
155
|
+
// Replace the (arrow-) function declarations inside methodParametersWithPlaceholders with "_callback" placeholders and create the callbackDeclarations array which contains the
|
|
156
|
+
// declarations like in readme.md saying "[{ // here for the `someCallbackFn: (p:number) => Promise<string>` declaration ...}]"
|
|
157
|
+
let callbackDeclarations: ObjectLiteralExpression[] = [];
|
|
158
|
+
visitReplace(methodParametersWithPlaceholders, (value, visitChilds, context) => {
|
|
159
|
+
if (value && (value as Node).kind === SyntaxKind.FunctionType) { // found an arrow-style function type declaration ?
|
|
160
|
+
const functionTypeNode = value as FunctionTypeNode;
|
|
161
|
+
// **** Create the callbackDeclaration like `{ // here for the `someCallbackFn: (p:number) => Promise<string>` declaration...}` in readme.md ****
|
|
162
|
+
// Create argumentsDeclaration. `{ validateEquals: ..., validatePrune: ...}` like in readme.md
|
|
163
|
+
const argumentsDeclaration = factory.createObjectLiteralExpression(
|
|
164
|
+
Object.keys(arguments_typiaFuncs).map((typiaFnName) => // for validateEquals + validatePrune
|
|
128
165
|
factory.createPropertyAssignment(
|
|
129
166
|
factory.createIdentifier(typiaFnName),
|
|
130
167
|
factory.createArrowFunction(
|
|
@@ -140,76 +177,125 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
140
177
|
)],
|
|
141
178
|
undefined,
|
|
142
179
|
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
143
|
-
factory.createCallExpression(
|
|
144
|
-
[factory.
|
|
145
|
-
factory.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
undefined
|
|
153
|
-
),
|
|
154
|
-
factory.createLiteralTypeNode(factory.createStringLiteral(methodName))
|
|
155
|
-
)]
|
|
156
|
-
)],
|
|
180
|
+
factory.createCallExpression(old_typiaFuncs[typiaFnName],
|
|
181
|
+
[factory.createTupleTypeNode(structuredClone(functionTypeNode.parameters).map(paramDecl => { // Must convert ParameterDeclarations to NamedTupleMembers (They look the same in the .ts code but are of a different SyntaxKind).
|
|
182
|
+
return factory.createNamedTupleMember(
|
|
183
|
+
paramDecl.dotDotDotToken,
|
|
184
|
+
paramDecl.name as Identifier,
|
|
185
|
+
paramDecl.questionToken,
|
|
186
|
+
paramDecl.type || factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
|
|
187
|
+
)
|
|
188
|
+
}))],
|
|
157
189
|
[factory.createIdentifier("args")]
|
|
158
190
|
)
|
|
159
191
|
)
|
|
160
192
|
)),
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
factory.createIdentifier("
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
true
|
|
194
|
+
);
|
|
195
|
+
// Create awaitedResultDeclaration. `awaitedResult = ...` like in readme.md:
|
|
196
|
+
let awaitedResultDeclaration: Expression;
|
|
197
|
+
if (functionTypeNode.type.kind == SyntaxKind.VoidKeyword) { // Return type is (sync) void ?
|
|
198
|
+
awaitedResultDeclaration = factory.createIdentifier("undefined");
|
|
199
|
+
} else {
|
|
200
|
+
if (functionTypeNode.type.kind == SyntaxKind.TypeReference && ((functionTypeNode.type as TypeReferenceNode).typeName as any).escapedText == "Promise") { // Returns via Promise
|
|
201
|
+
// Validity check:
|
|
202
|
+
if (!(functionTypeNode.type as TypeReferenceNode).typeArguments || (functionTypeNode.type as TypeReferenceNode).typeArguments!.length != 1) {
|
|
203
|
+
throw new Error(`A callback function, declared in ${methodName}'s parameters, returns a Promise with an invalid number of type arguments. Location: ${this.diag_sourceLocation(node)}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const awaitedType: TypeReferenceNode = (functionTypeNode.type as TypeReferenceNode).typeArguments![0] as TypeReferenceNode;
|
|
207
|
+
|
|
208
|
+
awaitedResultDeclaration = factory.createObjectLiteralExpression(
|
|
209
|
+
Object.keys(result_typiaFuncs).map((typiaFnName) => // for validateEquals + validatePrune
|
|
210
|
+
factory.createPropertyAssignment(
|
|
211
|
+
factory.createIdentifier(typiaFnName),
|
|
212
|
+
factory.createArrowFunction(
|
|
176
213
|
undefined,
|
|
177
|
-
factory.createIdentifier("value"),
|
|
178
214
|
undefined,
|
|
179
|
-
factory.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
factory.createIdentifier("Awaited"), [factory.createTypeReferenceNode(
|
|
187
|
-
factory.createIdentifier("ReturnType"),
|
|
188
|
-
[factory.createIndexedAccessTypeNode(
|
|
189
|
-
factory.createTypeQueryNode(
|
|
190
|
-
factory.createQualifiedName(
|
|
191
|
-
factory.createIdentifier("this"),
|
|
192
|
-
factory.createIdentifier("prototype")
|
|
193
|
-
),
|
|
194
|
-
undefined
|
|
195
|
-
),
|
|
196
|
-
factory.createLiteralTypeNode(factory.createStringLiteral(methodName))
|
|
197
|
-
)]
|
|
198
|
-
)]
|
|
215
|
+
[factory.createParameterDeclaration(
|
|
216
|
+
undefined,
|
|
217
|
+
undefined,
|
|
218
|
+
factory.createIdentifier("value"),
|
|
219
|
+
undefined,
|
|
220
|
+
factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
221
|
+
undefined
|
|
199
222
|
)],
|
|
200
|
-
|
|
223
|
+
undefined,
|
|
224
|
+
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
225
|
+
factory.createCallExpression(old_typiaFuncs[typiaFnName],
|
|
226
|
+
[awaitedType],
|
|
227
|
+
[factory.createIdentifier("value")]
|
|
228
|
+
)
|
|
201
229
|
)
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
230
|
+
)),
|
|
231
|
+
true
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
throw new Error(`A callback function, declared in ${methodName}'s parameters, declares that it returns a value directly (sync) and not via Promise (this is not possible over the wire). Please use Promise<${this.nodeToString(functionTypeNode.type, true, true)}> instead. Location: ${this.diag_sourceLocation(functionTypeNode.type)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Compose result:
|
|
239
|
+
const callbackDeclaration = factory.createObjectLiteralExpression(
|
|
240
|
+
[
|
|
241
|
+
// arguments: {...}
|
|
242
|
+
factory.createPropertyAssignment(
|
|
243
|
+
factory.createIdentifier("arguments"),
|
|
244
|
+
argumentsDeclaration
|
|
245
|
+
),
|
|
246
|
+
|
|
247
|
+
// awaitedResult: {...}
|
|
248
|
+
factory.createPropertyAssignment(
|
|
249
|
+
factory.createIdentifier("awaitedResult"),
|
|
250
|
+
awaitedResultDeclaration
|
|
251
|
+
),
|
|
252
|
+
// source:
|
|
253
|
+
factory.createPropertyAssignment(
|
|
254
|
+
factory.createIdentifier("diagnosis_source"), this.createSourceNodeDiagnosisExpression(functionTypeNode)
|
|
255
|
+
)
|
|
256
|
+
],
|
|
257
|
+
true
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
callbackDeclarations.push(callbackDeclaration);
|
|
261
|
+
const declarationIndex = callbackDeclarations.length -1;
|
|
262
|
+
return factory.createExpressionWithTypeArguments(factory.createIdentifier("CallbackPlaceholder"),[factory.createLiteralTypeNode(factory.createNumericLiteral(declarationIndex))]); // replace with CallbackPlaceholder<n>
|
|
263
|
+
}
|
|
264
|
+
return visitChilds(value, context)
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// ** Obtain, what goes into typia.validate...<...> ***
|
|
268
|
+
const hasPlaceholders = callbackDeclarations.length > 0;
|
|
269
|
+
let typeParamForTypiaValidate = `[${node.parameters.map(param => this.nodeToString(param, true, true)).join(", ")}]`; // [param1: type1, param2: type2, ...]
|
|
270
|
+
let typeParamForTypiaValidateWithPlaceholders: string = this.nodeToString(factory.createTupleTypeNode(methodParametersWithPlaceholders),true, true);
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
if (node.parameters.some(p => p.type === undefined)) { // Some parameters don't have an explicit type ? (i.e. the type is inherited from the base class)
|
|
274
|
+
if (hasPlaceholders) {
|
|
275
|
+
throw new Error(`Parameter '${(node.parameters.find(p => p.type === undefined) as any)!.name.escapedText}' does not have a (/ an explicit) type in remote method ${methodName}. In combination with declared callback functions, all parameters must have explicit types. Location: ${this.diag_sourceLocation(node)}`) // TODO: add diagnosis
|
|
276
|
+
}
|
|
277
|
+
// Use the old style: Parameters<typeof this.prototype["method name"]>. This worked very fine in old versions but we now want to battle-test the other style
|
|
278
|
+
typeParamForTypiaValidate = typeParamForTypiaValidateWithPlaceholders = `Parameters<typeof this.prototype["${methodName}"]>`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ** compose result **
|
|
282
|
+
return `{
|
|
283
|
+
arguments: {${/* for validateEquals + validatePrune */ Object.keys(arguments_typiaFuncs).map((typiaFnName) => `
|
|
284
|
+
${typiaFnName}: (args: unknown) => ${arguments_typiaFuncs[typiaFnName]}<${typeParamForTypiaValidate}>(args)`).join(",")}
|
|
285
|
+
,
|
|
286
|
+
withPlaceholders: ${hasPlaceholders?`{${/* for validateEquals + validatePrune */ Object.keys(arguments_typiaFuncs).map((typiaFnName) => `
|
|
287
|
+
${typiaFnName}: (args: unknown, onValidateCallbackArg: (placeholderId: string, declarationIndex: number) => boolean) => ${arguments_typiaFuncs[typiaFnName]}<${typeParamForTypiaValidateWithPlaceholders}>(args)`).join(",")}
|
|
288
|
+
}`:"undefined"}
|
|
289
|
+
},
|
|
290
|
+
result: {${/* for validateEquals + validatePrune */ Object.keys(result_typiaFuncs).map((typiaFnName) => `
|
|
291
|
+
${typiaFnName}: (value: unknown) => ${result_typiaFuncs[typiaFnName]}<Awaited<ReturnType<typeof this.prototype["${methodName}"]>>>(value)`).join(",")}
|
|
292
|
+
},
|
|
293
|
+
callbacks: [${callbackDeclarations.map(cbd => `
|
|
294
|
+
${this.nodeToString(cbd, true)}`).join(",")}
|
|
295
|
+
],
|
|
296
|
+
jsDoc: ${this.nodeToString(this.createJsDocExpression(node, methodName), true)},
|
|
297
|
+
diagnosis_source: ${this.nodeToString(this.createSourceNodeDiagnosisExpression(node), true)},
|
|
298
|
+
}`;
|
|
213
299
|
}
|
|
214
300
|
|
|
215
301
|
/**
|
|
@@ -243,7 +329,7 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
243
329
|
|
|
244
330
|
const comment = jsdoc.comment || "";
|
|
245
331
|
if(typeof comment !== "string") {
|
|
246
|
-
throw new Error(`Expected comment to be a string. Got: ${comment}
|
|
332
|
+
throw new Error(`Expected comment to be a string. Got: ${comment}. Location: ${this.diag_sourceLocation(node)}`); // TODO add to diagnostics instead
|
|
247
333
|
}
|
|
248
334
|
|
|
249
335
|
// Collect params and tags
|
|
@@ -253,7 +339,7 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
253
339
|
const name = (tag.tagName.escapedText as string).toLowerCase()
|
|
254
340
|
const comment = tag.comment;
|
|
255
341
|
if(comment && typeof comment !== "string") {
|
|
256
|
-
throw new Error(`Expected comment to be a string. Got: ${comment}` ); // TODO add to diagnostics instead
|
|
342
|
+
throw new Error(`Expected comment to be a string. Got: ${comment}. Location: ${this.diag_sourceLocation(node)}` ); // TODO add to diagnostics instead
|
|
257
343
|
}
|
|
258
344
|
|
|
259
345
|
if(name === "param") {
|
|
@@ -314,7 +400,7 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
314
400
|
/**
|
|
315
401
|
* Crates the following expression like in readme.md#how-it-works
|
|
316
402
|
* <code><pre>
|
|
317
|
-
* static getRemoteMethodsMeta() {
|
|
403
|
+
* static getRemoteMethodsMeta(...) :... {
|
|
318
404
|
* ....
|
|
319
405
|
* }
|
|
320
406
|
* </pre></code>
|
|
@@ -322,82 +408,74 @@ export class AddRemoteMethodsMeta extends FileTransformRun {
|
|
|
322
408
|
* @param methodName
|
|
323
409
|
* @private
|
|
324
410
|
*/
|
|
325
|
-
private create_static_getRemoteMethodsMeta_expression() :
|
|
411
|
+
private create_static_getRemoteMethodsMeta_expression() : string {
|
|
412
|
+
return `` +
|
|
413
|
+
`static getRemoteMethodsMeta(): (typeof this.type_remoteMethodsMeta) {
|
|
414
|
+
this.__hello_developer__make_sure_your_class_is_a_subclass_of_ServerSession;` /* Attempt to give a friendly error message when this is not the case. Otherwise the previous line would fail and leaves the user wondering. */ + `
|
|
415
|
+
type CallbackPlaceholder<DECL_INDEX extends number> = string & _rf_typia.tags.TagBase<{kind: "callbackFn", target: "string", value: undefined, validate: \`onValidateCallbackArg($input, \${DECL_INDEX})\`}>;
|
|
416
|
+
const result= {
|
|
417
|
+
transformerVersion: {major: ${transformerVersion.major}, feature: ${transformerVersion.feature} },
|
|
418
|
+
instanceMethods: {${Object.keys(this.currentClass_instanceMethodsMeta!).map(methodName => `
|
|
419
|
+
${methodName}: ${this.currentClass_instanceMethodsMeta![methodName]}`).join(",")}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
return result; /* Code style note for this line: Why not do \`return {...}\` directly ? This tiny difference allows for extra properties which ensure backward compatibility with older "restfuncs-server" packages. */
|
|
424
|
+
}`
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
nodeToString(node: Node, removeComments= false, removeLineBreaks = false): string {
|
|
428
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, removeComments});
|
|
429
|
+
let result = printer.printNode(ts.EmitHint.Unspecified, node, this.sourceFile) as string;
|
|
430
|
+
if(removeLineBreaks) {
|
|
431
|
+
result = result.replaceAll(/\n\s*/g," ")
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Creates a node, that declares a SourceNodeDiagnosis (defined in ServerSession.ts) object.
|
|
438
|
+
*
|
|
439
|
+
* To output the info at runtime: i.e. "MyServerSessionClass.ts:23:30, reading: async myRemoteMethod(param1:...): Promise<void> {}
|
|
440
|
+
* @param node
|
|
441
|
+
*/
|
|
442
|
+
createSourceNodeDiagnosisExpression(node: Node) {
|
|
326
443
|
const factory = this.context.factory;
|
|
444
|
+
if((node as any).body !== undefined) {
|
|
445
|
+
// Empty the body
|
|
446
|
+
node = structuredClone(node);
|
|
447
|
+
(node as any).body = factory.createBlock([],false);
|
|
448
|
+
}
|
|
327
449
|
|
|
450
|
+
const lineAndChar = this.sourceFile.getLineAndCharacterOfPosition((node.getStart?.(this.sourceFile, false) || node.pos));
|
|
328
451
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
// Example from readme.md#how-it-works Copy&pasted through the [AST viewer tool](https://ts-ast-viewer.com/)
|
|
337
|
-
return factory.createMethodDeclaration(
|
|
338
|
-
[factory.createToken(ts.SyntaxKind.StaticKeyword)],
|
|
339
|
-
undefined,
|
|
340
|
-
factory.createIdentifier("getRemoteMethodsMeta"),
|
|
341
|
-
undefined,
|
|
342
|
-
undefined,
|
|
343
|
-
[],
|
|
344
|
-
factory.createParenthesizedType(factory.createTypeQueryNode(
|
|
345
|
-
factory.createQualifiedName(
|
|
346
|
-
factory.createIdentifier("this"),
|
|
347
|
-
factory.createIdentifier("type_remoteMethodsMeta")
|
|
452
|
+
|
|
453
|
+
return factory.createObjectLiteralExpression(
|
|
454
|
+
[
|
|
455
|
+
factory.createPropertyAssignment(
|
|
456
|
+
factory.createIdentifier("file"),
|
|
457
|
+
factory.createStringLiteral(this.sourceFile.fileName)
|
|
348
458
|
),
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
undefined,
|
|
364
|
-
factory.createPropertyAccessExpression(
|
|
365
|
-
factory.createThis(),
|
|
366
|
-
factory.createIdentifier("typiaRuntime")
|
|
367
|
-
)
|
|
368
|
-
)],
|
|
369
|
-
ts.NodeFlags.Let
|
|
370
|
-
)
|
|
371
|
-
),
|
|
372
|
-
factory.createReturnStatement(factory.createObjectLiteralExpression(
|
|
373
|
-
[
|
|
374
|
-
factory.createPropertyAssignment(
|
|
375
|
-
factory.createIdentifier("transformerVersion"),
|
|
376
|
-
factory.createObjectLiteralExpression(
|
|
377
|
-
[
|
|
378
|
-
factory.createPropertyAssignment(
|
|
379
|
-
factory.createIdentifier("major"),
|
|
380
|
-
factory.createIdentifier(`${transformerVersion.major}`)
|
|
381
|
-
),
|
|
382
|
-
factory.createPropertyAssignment(
|
|
383
|
-
factory.createIdentifier("feature"),
|
|
384
|
-
factory.createIdentifier(`${transformerVersion.feature}`)
|
|
385
|
-
)
|
|
386
|
-
],
|
|
387
|
-
false
|
|
388
|
-
)
|
|
389
|
-
),
|
|
390
|
-
factory.createPropertyAssignment(
|
|
391
|
-
factory.createIdentifier("instanceMethods"),
|
|
392
|
-
instanceMethods
|
|
393
|
-
)
|
|
394
|
-
],
|
|
395
|
-
true
|
|
396
|
-
))
|
|
397
|
-
],
|
|
398
|
-
true
|
|
399
|
-
)
|
|
459
|
+
factory.createPropertyAssignment(
|
|
460
|
+
factory.createIdentifier("line"),
|
|
461
|
+
factory.createNumericLiteral(lineAndChar.line + 1)
|
|
462
|
+
),
|
|
463
|
+
factory.createPropertyAssignment(
|
|
464
|
+
factory.createIdentifier("character"),
|
|
465
|
+
factory.createNumericLiteral(lineAndChar.character + 1)
|
|
466
|
+
),
|
|
467
|
+
factory.createPropertyAssignment(
|
|
468
|
+
factory.createIdentifier("signatureText"),
|
|
469
|
+
factory.createStringLiteral(this.nodeToString(node, false))
|
|
470
|
+
)
|
|
471
|
+
],
|
|
472
|
+
false
|
|
400
473
|
)
|
|
401
474
|
|
|
402
475
|
}
|
|
476
|
+
|
|
477
|
+
diag_sourceLocation(node: Node) {
|
|
478
|
+
const lineAndChar = this.sourceFile.getLineAndCharacterOfPosition((node.getStart?.(this.sourceFile, false) || node.pos));
|
|
479
|
+
return `${this.sourceFile.fileName}:${lineAndChar.line+1}:${lineAndChar.character+1}`
|
|
480
|
+
}
|
|
403
481
|
}
|
package/devPlayground.d.ts
CHANGED
package/devPlayground.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devPlayground.d.ts","sourceRoot":"","sources":["devPlayground.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"devPlayground.d.ts","sourceRoot":"","sources":["devPlayground.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAGtE,QAAA,MAAM,CAAC,EAAE,cAAiC,CAAC"}
|
package/devPlayground.js
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
const ts = __importStar(require("typescript"));
|
|
27
|
-
const typescript_1 = require("typescript");
|
|
28
|
-
var ScriptKind = ts.ScriptKind;
|
|
29
|
-
let sourceFile = ts.createSourceFile("dev/some.ts", "const i:string = 'myStr';", typescript_1.ScriptTarget.ES2020, false, ScriptKind.JS);
|
|
30
|
-
let i = undefined;
|
|
2
|
+
const f = ["", 123, "", ""];
|
|
31
3
|
//# sourceMappingURL=devPlayground.js.map
|
package/devPlayground.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devPlayground.js","sourceRoot":"","sources":["devPlayground.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"devPlayground.js","sourceRoot":"","sources":["devPlayground.ts"],"names":[],"mappings":";AAGA,MAAM,CAAC,GAAmB,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAC,EAAE,CAAC,CAAC"}
|
package/devPlayground.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {ScriptTarget} from 'typescript';
|
|
3
|
-
import ScriptKind = ts.ScriptKind;
|
|
1
|
+
type paramsLikeInFn = [a: string, b: number, ...restOfArgs: string[]];
|
|
4
2
|
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
const f: paramsLikeInFn = ["", 123, "",""];
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type X = {
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let i: Awaited<X> | undefined = undefined
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAW,EAAC,YAAY,EAAmB,OAAO,EAAa,MAAM,YAAY,CAAC;AAClF,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAW,EAAC,YAAY,EAAmB,OAAO,EAAa,MAAM,YAAY,CAAC;AAClF,OAAO,EAAC,YAAY,EAAE,wBAAwB,EAAC,MAAM,UAAU,CAAC;AAOhE,eAAO,MAAM,kBAAkB;;;CAA2B,CAAA;AAwC1D,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAiDT"}
|