@runtyped/type-compiler 1.0.20-alpha.1
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/LICENSE +22 -0
- package/README.md +4 -0
- package/compiler-debug.ts +38 -0
- package/deepkit-compiler-debug.js +5 -0
- package/deepkit-type-install.js +5 -0
- package/dist/cjs/compiler-debug.d.ts +1 -0
- package/dist/cjs/compiler-debug.js +28 -0
- package/dist/cjs/compiler-debug.js.map +1 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/install-transformer.d.ts +7 -0
- package/dist/cjs/install-transformer.js +77 -0
- package/dist/cjs/install-transformer.js.map +1 -0
- package/dist/cjs/src/compiler.d.ts +313 -0
- package/dist/cjs/src/compiler.js +2506 -0
- package/dist/cjs/src/compiler.js.map +1 -0
- package/dist/cjs/src/config.d.ts +83 -0
- package/dist/cjs/src/config.js +248 -0
- package/dist/cjs/src/config.js.map +1 -0
- package/dist/cjs/src/debug.d.ts +9 -0
- package/dist/cjs/src/debug.js +26 -0
- package/dist/cjs/src/debug.js.map +1 -0
- package/dist/cjs/src/loader.d.ts +18 -0
- package/dist/cjs/src/loader.js +55 -0
- package/dist/cjs/src/loader.js.map +1 -0
- package/dist/cjs/src/plugin.d.ts +14 -0
- package/dist/cjs/src/plugin.js +46 -0
- package/dist/cjs/src/plugin.js.map +1 -0
- package/dist/cjs/src/reflection-ast.d.ts +46 -0
- package/dist/cjs/src/reflection-ast.js +335 -0
- package/dist/cjs/src/reflection-ast.js.map +1 -0
- package/dist/cjs/src/resolver.d.ts +27 -0
- package/dist/cjs/src/resolver.js +88 -0
- package/dist/cjs/src/resolver.js.map +1 -0
- package/dist/cjs/src/ts-types.d.ts +17 -0
- package/dist/cjs/src/ts-types.js +6 -0
- package/dist/cjs/src/ts-types.js.map +1 -0
- package/dist/esm/compiler-debug.d.ts +1 -0
- package/dist/esm/compiler-debug.js +26 -0
- package/dist/esm/compiler-debug.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/install-transformer.d.ts +7 -0
- package/dist/esm/install-transformer.js +75 -0
- package/dist/esm/install-transformer.js.map +1 -0
- package/dist/esm/src/compiler.d.ts +313 -0
- package/dist/esm/src/compiler.js +2463 -0
- package/dist/esm/src/compiler.js.map +1 -0
- package/dist/esm/src/config.d.ts +83 -0
- package/dist/esm/src/config.js +238 -0
- package/dist/esm/src/config.js.map +1 -0
- package/dist/esm/src/debug.d.ts +9 -0
- package/dist/esm/src/debug.js +21 -0
- package/dist/esm/src/debug.js.map +1 -0
- package/dist/esm/src/loader.d.ts +18 -0
- package/dist/esm/src/loader.js +48 -0
- package/dist/esm/src/loader.js.map +1 -0
- package/dist/esm/src/plugin.d.ts +14 -0
- package/dist/esm/src/plugin.js +40 -0
- package/dist/esm/src/plugin.js.map +1 -0
- package/dist/esm/src/reflection-ast.d.ts +46 -0
- package/dist/esm/src/reflection-ast.js +284 -0
- package/dist/esm/src/reflection-ast.js.map +1 -0
- package/dist/esm/src/resolver.d.ts +27 -0
- package/dist/esm/src/resolver.js +80 -0
- package/dist/esm/src/resolver.js.map +1 -0
- package/dist/esm/src/ts-types.d.ts +17 -0
- package/dist/esm/src/ts-types.js +5 -0
- package/dist/esm/src/ts-types.js.map +1 -0
- package/index.ts +22 -0
- package/install-transformer.ts +84 -0
- package/package.json +59 -0
- package/src/compiler.ts +3003 -0
- package/src/config.ts +331 -0
- package/src/debug.ts +22 -0
- package/src/loader.ts +58 -0
- package/src/plugin.ts +58 -0
- package/src/reflection-ast.ts +335 -0
- package/src/resolver.ts +113 -0
- package/src/ts-types.ts +28 -0
- package/tsconfig.esm.json +16 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,2463 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Runtyped Framework
|
|
3
|
+
* Copyright (c) Deepkit UG, Marc J. Schmidt
|
|
4
|
+
* Copyright (c) Jacopo Scazzosi
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the MIT License.
|
|
8
|
+
*
|
|
9
|
+
* You should have received a copy of the MIT License along with this program.
|
|
10
|
+
*/
|
|
11
|
+
import ts, { isJSDocImportTag, } from 'typescript';
|
|
12
|
+
import { ensureImportIsEmitted, extractJSDocAttribute, findSourceFile, getEscapedText, getGlobalsOfSourceFile, getIdentifierName, getNameAsString, getPropertyName, hasModifier, isNodeWithLocals, NodeConverter, serializeEntityNameAsExpression, } from './reflection-ast.js';
|
|
13
|
+
import { MappedModifier, ReflectionOp, TypeIntrinsic, TypeNumberBrand } from '@runtyped/type-spec';
|
|
14
|
+
import { Resolver } from './resolver.js';
|
|
15
|
+
import { knownLibFilesForCompilerOptions } from '@typescript/vfs';
|
|
16
|
+
import { debug, debug2 } from './debug.js';
|
|
17
|
+
import { getConfigResolver, reflectionModeMatcher } from './config.js';
|
|
18
|
+
const { visitEachChild, visitNode, isPropertyAssignment, isArrayTypeNode, isArrowFunction, isBlock, isCallExpression, isCallSignatureDeclaration, isClassDeclaration, isClassExpression, isConstructorDeclaration, isConstructorTypeNode, isConstructSignatureDeclaration, isEnumDeclaration, isExportDeclaration, isExpression, isExpressionWithTypeArguments, isFunctionDeclaration, isFunctionExpression, isFunctionLike, isIdentifier, isImportClause, isImportDeclaration, isImportSpecifier, isInferTypeNode, isInterfaceDeclaration, isMethodDeclaration, isMethodSignature, isModuleDeclaration, isNamedExports, isNamedTupleMember, isNewExpression, isObjectLiteralExpression, isOptionalTypeNode, isParameter, isParenthesizedExpression, isParenthesizedTypeNode, isPropertyAccessExpression, isQualifiedName, isSourceFile, isStringLiteral, isTypeAliasDeclaration, isTypeLiteralNode, isTypeParameterDeclaration, isTypeQueryNode, isTypeReferenceNode, isUnionTypeNode, isExpressionStatement, isVariableDeclaration, getEffectiveConstraintOfTypeParameter, addSyntheticLeadingComment, createCompilerHost, createPrinter, escapeLeadingUnderscores, EmitHint, NodeFlags, SyntaxKind, ScriptTarget, ModifierFlags, ScriptKind, } = ts;
|
|
19
|
+
export function encodeOps(ops) {
|
|
20
|
+
return ops.map(v => String.fromCharCode(v + 33)).join('');
|
|
21
|
+
}
|
|
22
|
+
function filterUndefined(object) {
|
|
23
|
+
return Object.fromEntries(Object.entries(object).filter(([, v]) => v !== undefined));
|
|
24
|
+
}
|
|
25
|
+
export const packSizeByte = 6;
|
|
26
|
+
const serverEnv = 'undefined' !== typeof process;
|
|
27
|
+
/**
|
|
28
|
+
* It can't be more ops than this given number
|
|
29
|
+
*/
|
|
30
|
+
export const packSize = 2 ** packSizeByte; //64
|
|
31
|
+
const OPs = {
|
|
32
|
+
[ReflectionOp.literal]: { params: 1 },
|
|
33
|
+
// [ReflectionOp.pointer]: { params: 1 },
|
|
34
|
+
// [ReflectionOp.arg]: { params: 1 },
|
|
35
|
+
[ReflectionOp.classReference]: { params: 1 },
|
|
36
|
+
[ReflectionOp.propertySignature]: { params: 1 },
|
|
37
|
+
[ReflectionOp.property]: { params: 1 },
|
|
38
|
+
[ReflectionOp.jump]: { params: 1 },
|
|
39
|
+
[ReflectionOp.enum]: { params: 0 },
|
|
40
|
+
[ReflectionOp.enumMember]: { params: 1 },
|
|
41
|
+
[ReflectionOp.typeParameter]: { params: 1 },
|
|
42
|
+
[ReflectionOp.typeParameterDefault]: { params: 1 },
|
|
43
|
+
[ReflectionOp.mappedType]: { params: 2 },
|
|
44
|
+
[ReflectionOp.call]: { params: 1 },
|
|
45
|
+
[ReflectionOp.inline]: { params: 1 },
|
|
46
|
+
[ReflectionOp.inlineCall]: { params: 2 },
|
|
47
|
+
[ReflectionOp.loads]: { params: 2 },
|
|
48
|
+
[ReflectionOp.extends]: { params: 0 },
|
|
49
|
+
[ReflectionOp.infer]: { params: 2 },
|
|
50
|
+
[ReflectionOp.defaultValue]: { params: 1 },
|
|
51
|
+
[ReflectionOp.parameter]: { params: 1 },
|
|
52
|
+
[ReflectionOp.method]: { params: 1 },
|
|
53
|
+
[ReflectionOp.function]: { params: 1 },
|
|
54
|
+
[ReflectionOp.description]: { params: 1 },
|
|
55
|
+
[ReflectionOp.numberBrand]: { params: 1 },
|
|
56
|
+
[ReflectionOp.typeof]: { params: 1 },
|
|
57
|
+
[ReflectionOp.classExtends]: { params: 1 },
|
|
58
|
+
[ReflectionOp.distribute]: { params: 1 },
|
|
59
|
+
[ReflectionOp.jumpCondition]: { params: 2 },
|
|
60
|
+
[ReflectionOp.typeName]: { params: 1 },
|
|
61
|
+
[ReflectionOp.implements]: { params: 1 },
|
|
62
|
+
};
|
|
63
|
+
export function debugPackStruct(sourceFile, forType, pack) {
|
|
64
|
+
const items = [];
|
|
65
|
+
for (let i = 0; i < pack.ops.length; i++) {
|
|
66
|
+
const op = pack.ops[i];
|
|
67
|
+
const opInfo = OPs[op];
|
|
68
|
+
items.push(ReflectionOp[op]);
|
|
69
|
+
if (opInfo && opInfo.params > 0) {
|
|
70
|
+
for (let j = 0; j < opInfo.params; j++) {
|
|
71
|
+
const address = pack.ops[++i];
|
|
72
|
+
items.push(address);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const printer = createPrinter();
|
|
77
|
+
const stack = [];
|
|
78
|
+
for (const s of pack.stack) {
|
|
79
|
+
if ('object' === typeof s && 'getText' in s) {
|
|
80
|
+
stack.push(printer.printNode(EmitHint.Unspecified, s, sourceFile));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
stack.push(JSON.stringify(s));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
console.log(stack.join(','), '|', ...items);
|
|
87
|
+
}
|
|
88
|
+
function findVariable(frame, name, frameOffset = 0) {
|
|
89
|
+
const variable = frame.variables.find(v => v.name === name);
|
|
90
|
+
if (variable) {
|
|
91
|
+
return { frameOffset, stackIndex: variable.index };
|
|
92
|
+
}
|
|
93
|
+
if (frame.previous)
|
|
94
|
+
return findVariable(frame.previous, name, frameOffset + 1);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
function findConditionalFrame(frame) {
|
|
98
|
+
if (frame.conditional)
|
|
99
|
+
return frame;
|
|
100
|
+
if (frame.previous)
|
|
101
|
+
return findConditionalFrame(frame.previous);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
class CompilerProgram {
|
|
105
|
+
constructor(forNode, sourceFile) {
|
|
106
|
+
this.forNode = forNode;
|
|
107
|
+
this.sourceFile = sourceFile;
|
|
108
|
+
this.ops = [];
|
|
109
|
+
this.stack = [];
|
|
110
|
+
this.mainOffset = 0;
|
|
111
|
+
this.stackPosition = 0;
|
|
112
|
+
this.frame = { variables: [], opIndex: 0 };
|
|
113
|
+
this.activeCoRoutines = [];
|
|
114
|
+
this.coRoutines = [];
|
|
115
|
+
this.resolveFunctionParameters = new Map();
|
|
116
|
+
}
|
|
117
|
+
buildPackStruct() {
|
|
118
|
+
const ops = [...this.ops];
|
|
119
|
+
if (this.coRoutines.length) {
|
|
120
|
+
for (let i = this.coRoutines.length - 1; i >= 0; i--) {
|
|
121
|
+
ops.unshift(...this.coRoutines[i].ops);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (this.mainOffset) {
|
|
125
|
+
ops.unshift(ReflectionOp.jump, this.mainOffset);
|
|
126
|
+
}
|
|
127
|
+
return { ops, stack: this.stack };
|
|
128
|
+
}
|
|
129
|
+
isEmpty() {
|
|
130
|
+
return this.ops.length === 0;
|
|
131
|
+
}
|
|
132
|
+
pushConditionalFrame() {
|
|
133
|
+
const frame = this.pushFrame();
|
|
134
|
+
frame.conditional = true;
|
|
135
|
+
}
|
|
136
|
+
pushStack(item) {
|
|
137
|
+
this.stack.push(item);
|
|
138
|
+
return this.stackPosition++;
|
|
139
|
+
}
|
|
140
|
+
pushCoRoutine() {
|
|
141
|
+
this.pushFrame(true); //co-routines have implicit stack frames due to call convention
|
|
142
|
+
this.activeCoRoutines.push({ ops: [] });
|
|
143
|
+
}
|
|
144
|
+
popCoRoutine() {
|
|
145
|
+
const coRoutine = this.activeCoRoutines.pop();
|
|
146
|
+
if (!coRoutine)
|
|
147
|
+
throw new Error('No active co routine found');
|
|
148
|
+
this.popFrameImplicit();
|
|
149
|
+
if (this.mainOffset === 0) {
|
|
150
|
+
this.mainOffset = 2; //we add JUMP + index when building the program
|
|
151
|
+
}
|
|
152
|
+
const startIndex = this.mainOffset;
|
|
153
|
+
coRoutine.ops.push(ReflectionOp.return);
|
|
154
|
+
this.coRoutines.push(coRoutine);
|
|
155
|
+
this.mainOffset += coRoutine.ops.length;
|
|
156
|
+
return startIndex;
|
|
157
|
+
}
|
|
158
|
+
pushOp(...ops) {
|
|
159
|
+
for (const op of ops) {
|
|
160
|
+
if ('number' !== typeof op) {
|
|
161
|
+
throw new Error('No valid OP added');
|
|
162
|
+
}
|
|
163
|
+
// if (op + 33 > 126) {
|
|
164
|
+
//todo: encode as var int
|
|
165
|
+
// throw new Error('stack pointer too big ' + op);
|
|
166
|
+
// }
|
|
167
|
+
}
|
|
168
|
+
if (this.activeCoRoutines.length) {
|
|
169
|
+
this.activeCoRoutines[this.activeCoRoutines.length - 1].ops.push(...ops);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.ops.push(...ops);
|
|
173
|
+
}
|
|
174
|
+
pushOpAtFrame(frame, ...ops) {
|
|
175
|
+
if (this.activeCoRoutines.length) {
|
|
176
|
+
this.activeCoRoutines[this.activeCoRoutines.length - 1].ops.splice(frame.opIndex, 0, ...ops);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
this.ops.splice(frame.opIndex, 0, ...ops);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns the index of the `entry` in the stack, if already exists. If not, add it, and return that new index.
|
|
183
|
+
*/
|
|
184
|
+
findOrAddStackEntry(entry) {
|
|
185
|
+
const index = this.stack.indexOf(entry);
|
|
186
|
+
if (index !== -1)
|
|
187
|
+
return index;
|
|
188
|
+
return this.pushStack(entry);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* To make room for a stack entry expected on the stack as input for example.
|
|
192
|
+
*/
|
|
193
|
+
increaseStackPosition() {
|
|
194
|
+
return this.stackPosition++;
|
|
195
|
+
}
|
|
196
|
+
resolveFunctionParametersIncrease(fn) {
|
|
197
|
+
this.resolveFunctionParameters.set(fn, (this.resolveFunctionParameters.get(fn) || 0) + 1);
|
|
198
|
+
}
|
|
199
|
+
resolveFunctionParametersDecrease(fn) {
|
|
200
|
+
this.resolveFunctionParameters.set(fn, (this.resolveFunctionParameters.get(fn) || 1) - 1);
|
|
201
|
+
}
|
|
202
|
+
isResolveFunctionParameters(fn) {
|
|
203
|
+
return (this.resolveFunctionParameters.get(fn) || 0) > 0;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
*
|
|
207
|
+
* Each pushFrame() call needs a popFrame() call.
|
|
208
|
+
*/
|
|
209
|
+
pushFrame(implicit = false) {
|
|
210
|
+
if (!implicit)
|
|
211
|
+
this.pushOp(ReflectionOp.frame);
|
|
212
|
+
const opIndex = this.activeCoRoutines.length ? this.activeCoRoutines[this.activeCoRoutines.length - 1].ops.length : this.ops.length;
|
|
213
|
+
this.frame = { previous: this.frame, variables: [], opIndex };
|
|
214
|
+
return this.frame;
|
|
215
|
+
}
|
|
216
|
+
findConditionalFrame() {
|
|
217
|
+
return findConditionalFrame(this.frame);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Remove stack without doing it as OP in the processor. Some other command calls popFrame() already, which makes popFrameImplicit() an implicit popFrame.
|
|
221
|
+
* e.g. union, class, etc. all call popFrame(). the current CompilerProgram needs to be aware of that, which this function is for.
|
|
222
|
+
*/
|
|
223
|
+
popFrameImplicit() {
|
|
224
|
+
if (this.frame.previous)
|
|
225
|
+
this.frame = this.frame.previous;
|
|
226
|
+
}
|
|
227
|
+
moveFrame() {
|
|
228
|
+
this.pushOp(ReflectionOp.moveFrame);
|
|
229
|
+
if (this.frame.previous)
|
|
230
|
+
this.frame = this.frame.previous;
|
|
231
|
+
}
|
|
232
|
+
pushVariable(name, frame = this.frame) {
|
|
233
|
+
this.pushOpAtFrame(frame, ReflectionOp.var);
|
|
234
|
+
frame.variables.push({
|
|
235
|
+
index: frame.variables.length,
|
|
236
|
+
name,
|
|
237
|
+
});
|
|
238
|
+
return frame.variables.length - 1;
|
|
239
|
+
}
|
|
240
|
+
pushTemplateParameter(name, withDefault = false) {
|
|
241
|
+
this.pushOp(withDefault ? ReflectionOp.typeParameterDefault : ReflectionOp.typeParameter, this.findOrAddStackEntry(name));
|
|
242
|
+
this.frame.variables.push({
|
|
243
|
+
index: this.frame.variables.length,
|
|
244
|
+
name,
|
|
245
|
+
});
|
|
246
|
+
return this.frame.variables.length - 1;
|
|
247
|
+
}
|
|
248
|
+
findVariable(name, frame = this.frame) {
|
|
249
|
+
return findVariable(frame, name);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function getAssignTypeExpression(call) {
|
|
253
|
+
if (isParenthesizedExpression(call) && isCallExpression(call.expression)) {
|
|
254
|
+
call = call.expression;
|
|
255
|
+
}
|
|
256
|
+
if (isCallExpression(call) && isIdentifier(call.expression) && getIdentifierName(call.expression) === '__assignType' && call.arguments.length > 0) {
|
|
257
|
+
return call.arguments[0];
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
function getReceiveTypeParameter(type) {
|
|
262
|
+
if (isUnionTypeNode(type)) {
|
|
263
|
+
for (const t of type.types) {
|
|
264
|
+
const rfn = getReceiveTypeParameter(t);
|
|
265
|
+
if (rfn)
|
|
266
|
+
return rfn;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (isTypeReferenceNode(type) && isIdentifier(type.typeName)
|
|
270
|
+
&& getIdentifierName(type.typeName) === 'ReceiveType' && !!type.typeArguments
|
|
271
|
+
&& type.typeArguments.length === 1)
|
|
272
|
+
return type;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
export class Cache {
|
|
276
|
+
constructor() {
|
|
277
|
+
this.resolver = {};
|
|
278
|
+
this.sourceFiles = {};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Signals the cache to check if it needs to be cleared.
|
|
282
|
+
*/
|
|
283
|
+
tick() {
|
|
284
|
+
if (Object.keys(this.sourceFiles).length > 300) {
|
|
285
|
+
this.sourceFiles = {};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Read the TypeScript AST and generate pack struct (instructions + pre-defined stack).
|
|
291
|
+
*
|
|
292
|
+
* This transformer extracts type and add the encoded (so its small and low overhead) at classes and functions as property.
|
|
293
|
+
*
|
|
294
|
+
* runtyped/type can then extract and decode them on-demand.
|
|
295
|
+
*/
|
|
296
|
+
export class ReflectionTransformer {
|
|
297
|
+
constructor(context, cache = new Cache) {
|
|
298
|
+
this.context = context;
|
|
299
|
+
this.cache = cache;
|
|
300
|
+
this.embedAssignType = false;
|
|
301
|
+
/**
|
|
302
|
+
* Types added to this map will get a type program directly under it.
|
|
303
|
+
* This is for types used in the very same file.
|
|
304
|
+
*/
|
|
305
|
+
this.compileDeclarations = new Map();
|
|
306
|
+
/**
|
|
307
|
+
* Types added to this map will get a type program at the top root level of the program.
|
|
308
|
+
* This is for imported types, which need to be inlined into the current file, as we do not emit type imports (TS will omit them).
|
|
309
|
+
*/
|
|
310
|
+
this.embedDeclarations = new Map();
|
|
311
|
+
/**
|
|
312
|
+
* When a node was embedded or compiled (from the maps above), we store it here to know to not add it again.
|
|
313
|
+
*/
|
|
314
|
+
this.compiledDeclarations = new Set();
|
|
315
|
+
this.addImports = [];
|
|
316
|
+
this.additionalImports = new Map();
|
|
317
|
+
this.overriddenHost = false;
|
|
318
|
+
this.knownClasses = {
|
|
319
|
+
'Int8Array': ReflectionOp.int8Array,
|
|
320
|
+
'Uint8Array': ReflectionOp.uint8Array,
|
|
321
|
+
'Uint8ClampedArray': ReflectionOp.uint8ClampedArray,
|
|
322
|
+
'Int16Array': ReflectionOp.int16Array,
|
|
323
|
+
'Uint16Array': ReflectionOp.uint16Array,
|
|
324
|
+
'Int32Array': ReflectionOp.int32Array,
|
|
325
|
+
'Uint32Array': ReflectionOp.uint32Array,
|
|
326
|
+
'Float32Array': ReflectionOp.float32Array,
|
|
327
|
+
'Float64Array': ReflectionOp.float64Array,
|
|
328
|
+
'ArrayBuffer': ReflectionOp.arrayBuffer,
|
|
329
|
+
'BigInt64Array': ReflectionOp.bigInt64Array,
|
|
330
|
+
'Date': ReflectionOp.date,
|
|
331
|
+
'RegExp': ReflectionOp.regexp,
|
|
332
|
+
'String': ReflectionOp.string,
|
|
333
|
+
'Number': ReflectionOp.number,
|
|
334
|
+
'BigInt': ReflectionOp.bigint,
|
|
335
|
+
'Boolean': ReflectionOp.boolean,
|
|
336
|
+
};
|
|
337
|
+
this.f = context.factory;
|
|
338
|
+
this.nodeConverter = new NodeConverter(this.f);
|
|
339
|
+
// It is important to not have undefined values like {paths: undefined} because it would override the read tsconfig.json.
|
|
340
|
+
// Important to create a copy since we will modify it.
|
|
341
|
+
this.compilerOptions = { ...filterUndefined(context.getCompilerOptions()) };
|
|
342
|
+
// compilerHost has no internal cache and is cheap to build, so no cache needed.
|
|
343
|
+
// Resolver loads SourceFile which has cache implemented.
|
|
344
|
+
this.host = createCompilerHost(this.compilerOptions);
|
|
345
|
+
this.resolver = new Resolver(this.compilerOptions, this.host, this.cache.sourceFiles);
|
|
346
|
+
this.parseConfigHost = {
|
|
347
|
+
useCaseSensitiveFileNames: true,
|
|
348
|
+
fileExists: (path) => this.host.fileExists(path),
|
|
349
|
+
readFile: (path) => this.host.readFile(path),
|
|
350
|
+
readDirectory: (path, extensions, exclude, include, depth) => {
|
|
351
|
+
if (!this.host.readDirectory)
|
|
352
|
+
return [];
|
|
353
|
+
return this.host.readDirectory(path, extensions || [], exclude, include || [], depth);
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
{
|
|
357
|
+
// TypeAnnotation<T, Options> = { __meta?: never & [T, Options] }
|
|
358
|
+
const T = this.f.createIdentifier('T');
|
|
359
|
+
const Options = this.f.createIdentifier('Options');
|
|
360
|
+
this.intrinsicMetaDeclaration = this.f.createTypeAliasDeclaration([], 'TypeAnnotation', [
|
|
361
|
+
this.f.createTypeParameterDeclaration([], T),
|
|
362
|
+
this.f.createTypeParameterDeclaration([], Options, undefined, this.f.createTypeReferenceNode('never')),
|
|
363
|
+
], this.f.createTypeLiteralNode([
|
|
364
|
+
this.f.createPropertySignature(undefined, '__meta', this.f.createToken(SyntaxKind.QuestionToken), this.f.createIntersectionTypeNode([
|
|
365
|
+
this.f.createTypeReferenceNode('never'),
|
|
366
|
+
this.f.createTupleTypeNode([
|
|
367
|
+
this.f.createTypeReferenceNode(T),
|
|
368
|
+
this.f.createTypeReferenceNode(Options),
|
|
369
|
+
]),
|
|
370
|
+
])),
|
|
371
|
+
]));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
forHost(host) {
|
|
375
|
+
this.host = host;
|
|
376
|
+
this.resolver.host = host;
|
|
377
|
+
this.overriddenHost = true;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
withReflection(config) {
|
|
381
|
+
const match = (path) => {
|
|
382
|
+
const mode = reflectionModeMatcher(config, path);
|
|
383
|
+
return { mode, tsConfigPath: '' };
|
|
384
|
+
};
|
|
385
|
+
const configResolver = { ...config, path: '', mergeStrategy: 'replace', compilerOptions: this.compilerOptions };
|
|
386
|
+
this.overriddenConfigResolver = { config: configResolver, match };
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
transformBundle(node) {
|
|
390
|
+
return node;
|
|
391
|
+
}
|
|
392
|
+
getTempResultIdentifier() {
|
|
393
|
+
if (this.tempResultIdentifier)
|
|
394
|
+
return this.tempResultIdentifier;
|
|
395
|
+
const locals = isNodeWithLocals(this.sourceFile) ? this.sourceFile.locals : undefined;
|
|
396
|
+
if (locals) {
|
|
397
|
+
let found = 'Ωr';
|
|
398
|
+
for (let i = 0;; i++) {
|
|
399
|
+
found = 'Ωr' + (i ? i : '');
|
|
400
|
+
if (!locals.has(escapeLeadingUnderscores(found)))
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
this.tempResultIdentifier = this.f.createIdentifier(found);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
this.tempResultIdentifier = this.f.createIdentifier('Ωr');
|
|
407
|
+
}
|
|
408
|
+
return this.tempResultIdentifier;
|
|
409
|
+
}
|
|
410
|
+
getConfigResolver(sourceFile) {
|
|
411
|
+
if (this.overriddenConfigResolver)
|
|
412
|
+
return this.overriddenConfigResolver;
|
|
413
|
+
return getConfigResolver(this.cache.resolver, this.parseConfigHost, this.compilerOptions, sourceFile);
|
|
414
|
+
}
|
|
415
|
+
getReflectionConfig(sourceFile) {
|
|
416
|
+
const configResolver = this.getConfigResolver(sourceFile);
|
|
417
|
+
return configResolver.match(sourceFile.fileName);
|
|
418
|
+
}
|
|
419
|
+
isWithReflection(sourceFile, node) {
|
|
420
|
+
const mode = this.getExplicitReflectionMode(sourceFile, node);
|
|
421
|
+
if (mode === false)
|
|
422
|
+
return false;
|
|
423
|
+
if (!sourceFile)
|
|
424
|
+
return true; // intrinsic types are always with reflection
|
|
425
|
+
const reflection = this.getReflectionConfig(sourceFile);
|
|
426
|
+
// explicit means reflection needs to be enabled per Node/File via @reflection
|
|
427
|
+
if (reflection.mode === 'explicit')
|
|
428
|
+
return mode === true;
|
|
429
|
+
return reflection.mode === 'default';
|
|
430
|
+
}
|
|
431
|
+
transformSourceFile(sourceFile) {
|
|
432
|
+
this.sourceFile = sourceFile;
|
|
433
|
+
//if it's not a TS/TSX file, we do not transform it
|
|
434
|
+
if (sourceFile.scriptKind !== ScriptKind.TS && sourceFile.scriptKind !== ScriptKind.TSX)
|
|
435
|
+
return sourceFile;
|
|
436
|
+
if (sourceFile.deepkitTransformed)
|
|
437
|
+
return sourceFile;
|
|
438
|
+
this.embedAssignType = false;
|
|
439
|
+
this.addImports = [];
|
|
440
|
+
this.additionalImports.clear();
|
|
441
|
+
const start = Date.now();
|
|
442
|
+
const configResolver = this.getConfigResolver(sourceFile);
|
|
443
|
+
const reflection = configResolver.match(sourceFile.fileName);
|
|
444
|
+
// important to override the compilerOptions with the one from the configResolver
|
|
445
|
+
// since the one provided by TSC/plugins are not necessarily the full picture.
|
|
446
|
+
// ConfigResolver resolves the whole config.
|
|
447
|
+
// Since this.compilerOptions was already passed to Resolver, we update its values by reference.
|
|
448
|
+
Object.assign(this.compilerOptions, configResolver.config.compilerOptions);
|
|
449
|
+
if (reflection.mode === 'never') {
|
|
450
|
+
debug(`Transform file with reflection=${reflection.mode} took ${Date.now() - start}ms (${this.getModuleType()}) ${sourceFile.fileName} via config ${reflection.tsConfigPath || 'none'}.`);
|
|
451
|
+
return sourceFile;
|
|
452
|
+
}
|
|
453
|
+
if (!sourceFile.locals) {
|
|
454
|
+
//@ts-ignore
|
|
455
|
+
ts.bindSourceFile(sourceFile, this.compilerOptions);
|
|
456
|
+
}
|
|
457
|
+
if (sourceFile.kind !== SyntaxKind.SourceFile) {
|
|
458
|
+
if ('undefined' === typeof require) {
|
|
459
|
+
throw new Error(`Invalid TypeScript library imported. SyntaxKind different ${sourceFile.kind} !== ${SyntaxKind.SourceFile}.`);
|
|
460
|
+
}
|
|
461
|
+
const path = require.resolve('typescript');
|
|
462
|
+
throw new Error(`Invalid TypeScript library imported. SyntaxKind different ${sourceFile.kind} !== ${SyntaxKind.SourceFile}. typescript package path: ${path}`);
|
|
463
|
+
}
|
|
464
|
+
const visitor = (node) => {
|
|
465
|
+
node = visitEachChild(node, visitor, this.context);
|
|
466
|
+
if ((isInterfaceDeclaration(node) || isTypeAliasDeclaration(node) || isEnumDeclaration(node))) {
|
|
467
|
+
if (this.isWithReflection(sourceFile, node)) {
|
|
468
|
+
this.compileDeclarations.set(node, {
|
|
469
|
+
name: node.name,
|
|
470
|
+
sourceFile: this.sourceFile,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (isMethodDeclaration(node) && node.parent && node.body && isObjectLiteralExpression(node.parent)) {
|
|
475
|
+
//replace MethodDeclaration with MethodExpression
|
|
476
|
+
// {add(v: number) {}} => {add: function (v: number) {}}
|
|
477
|
+
//so that __type can be added.
|
|
478
|
+
//{default(){}} can not be converted without losing the function name, so we skip that for the moment.
|
|
479
|
+
let valid = true;
|
|
480
|
+
if (node.name.kind === SyntaxKind.Identifier && getIdentifierName(node.name) === 'default')
|
|
481
|
+
valid = false;
|
|
482
|
+
if (valid) {
|
|
483
|
+
const method = this.decorateFunctionExpression(this.f.createFunctionExpression(node.modifiers, node.asteriskToken, isIdentifier(node.name) ? node.name : undefined, node.typeParameters, node.parameters, node.type, node.body));
|
|
484
|
+
node = this.f.createPropertyAssignment(node.name, method);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (isClassDeclaration(node)) {
|
|
488
|
+
return this.decorateClass(sourceFile, node);
|
|
489
|
+
}
|
|
490
|
+
else if (isParameter(node) && node.parent && node.type) {
|
|
491
|
+
// ReceiveType
|
|
492
|
+
const typeParameters = isConstructorDeclaration(node.parent) ? node.parent.parent.typeParameters : node.parent.typeParameters;
|
|
493
|
+
if (!typeParameters)
|
|
494
|
+
return node;
|
|
495
|
+
const receiveType = getReceiveTypeParameter(node.type);
|
|
496
|
+
if (receiveType && receiveType.typeArguments) {
|
|
497
|
+
const first = receiveType.typeArguments[0];
|
|
498
|
+
if (first && isTypeReferenceNode(first) && isIdentifier(first.typeName)) {
|
|
499
|
+
const name = getIdentifierName(first.typeName);
|
|
500
|
+
//find type parameter position
|
|
501
|
+
const index = typeParameters.findIndex(v => getIdentifierName(v.name) === name);
|
|
502
|
+
let container = this.f.createIdentifier('globalThis');
|
|
503
|
+
if (isArrowFunction(node.parent)) {
|
|
504
|
+
const next = this.getArrowFunctionΩPropertyAccessIdentifier(node.parent);
|
|
505
|
+
if (!next)
|
|
506
|
+
return node;
|
|
507
|
+
container = next;
|
|
508
|
+
}
|
|
509
|
+
else if ((isFunctionDeclaration(node.parent) || isFunctionExpression(node.parent)) && node.parent.name) {
|
|
510
|
+
container = node.parent.name;
|
|
511
|
+
}
|
|
512
|
+
else if (isMethodDeclaration(node.parent) && isIdentifier(node.parent.name)) {
|
|
513
|
+
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), node.parent.name);
|
|
514
|
+
}
|
|
515
|
+
else if (isConstructorDeclaration(node.parent)) {
|
|
516
|
+
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), 'constructor');
|
|
517
|
+
}
|
|
518
|
+
return this.f.updateParameterDeclaration(node, node.modifiers, node.dotDotDotToken, node.name, node.questionToken, receiveType, this.f.createElementAccessChain(this.f.createPropertyAccessExpression(container, this.f.createIdentifier('Ω')), this.f.createToken(SyntaxKind.QuestionDotToken), this.f.createNumericLiteral(index)));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else if (isClassExpression(node)) {
|
|
523
|
+
return this.decorateClass(sourceFile, node);
|
|
524
|
+
}
|
|
525
|
+
else if (isFunctionExpression(node)) {
|
|
526
|
+
return this.decorateFunctionExpression(this.injectResetΩ(node));
|
|
527
|
+
}
|
|
528
|
+
else if (isFunctionDeclaration(node)) {
|
|
529
|
+
return this.decorateFunctionDeclaration(this.injectResetΩ(node));
|
|
530
|
+
}
|
|
531
|
+
else if (isMethodDeclaration(node) || isConstructorDeclaration(node)) {
|
|
532
|
+
return this.injectResetΩ(node);
|
|
533
|
+
}
|
|
534
|
+
else if (isArrowFunction(node)) {
|
|
535
|
+
return this.decorateArrowFunction(this.injectResetΩ(node));
|
|
536
|
+
}
|
|
537
|
+
else if ((isNewExpression(node) || isCallExpression(node)) && node.typeArguments && node.typeArguments.length > 0) {
|
|
538
|
+
if (isCallExpression(node)) {
|
|
539
|
+
const autoTypeFunctions = ['valuesOf', 'propertiesOf', 'typeOf'];
|
|
540
|
+
if (isIdentifier(node.expression) && autoTypeFunctions.includes(getIdentifierName(node.expression))) {
|
|
541
|
+
const args = [...node.arguments];
|
|
542
|
+
if (!args.length) {
|
|
543
|
+
args.push(this.f.createArrayLiteralExpression());
|
|
544
|
+
}
|
|
545
|
+
// const resolvedType = this.resolveType(node.typeArguments[0]);
|
|
546
|
+
const type = this.getTypeOfType(node.typeArguments[0]);
|
|
547
|
+
if (!type)
|
|
548
|
+
return node;
|
|
549
|
+
args.push(type);
|
|
550
|
+
return this.f.updateCallExpression(node, node.expression, node.typeArguments, this.f.createNodeArray(args));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
//put the type argument in FN.Ω
|
|
554
|
+
const expressionToCheck = getAssignTypeExpression(node.expression) || node.expression;
|
|
555
|
+
if (isArrowFunction(expressionToCheck)) {
|
|
556
|
+
//inline arrow functions are excluded from type passing
|
|
557
|
+
return node;
|
|
558
|
+
}
|
|
559
|
+
const typeExpressions = [];
|
|
560
|
+
for (const a of node.typeArguments) {
|
|
561
|
+
const type = this.getTypeOfType(a);
|
|
562
|
+
typeExpressions.push(type || this.f.createIdentifier('undefined'));
|
|
563
|
+
}
|
|
564
|
+
let container = this.f.createIdentifier('globalThis');
|
|
565
|
+
if (isIdentifier(node.expression)) {
|
|
566
|
+
container = node.expression;
|
|
567
|
+
}
|
|
568
|
+
else if (isPropertyAccessExpression(node.expression)) {
|
|
569
|
+
container = node.expression;
|
|
570
|
+
}
|
|
571
|
+
const assignQ = this.f.createBinaryExpression(this.f.createPropertyAccessExpression(container, 'Ω'), this.f.createToken(SyntaxKind.EqualsToken), this.f.createArrayLiteralExpression(typeExpressions));
|
|
572
|
+
const update = isNewExpression(node) ? this.f.updateNewExpression : this.f.updateCallExpression;
|
|
573
|
+
if (isPropertyAccessExpression(node.expression)) {
|
|
574
|
+
//e.g. http.deep.response();
|
|
575
|
+
if (isCallExpression(node.expression.expression)) {
|
|
576
|
+
//e.g. http.deep().response();
|
|
577
|
+
//change to (Ωr = http.deep(), Ωr.response.Ω = [], Ωr).response()
|
|
578
|
+
const r = this.getTempResultIdentifier();
|
|
579
|
+
const assignQ = this.f.createBinaryExpression(this.f.createPropertyAccessExpression(this.f.createPropertyAccessExpression(r, node.expression.name), 'Ω'), this.f.createToken(SyntaxKind.EqualsToken), this.f.createArrayLiteralExpression(typeExpressions));
|
|
580
|
+
return update(node, this.f.createPropertyAccessExpression(this.f.createParenthesizedExpression(this.f.createBinaryExpression(this.f.createBinaryExpression(this.f.createBinaryExpression(r, this.f.createToken(ts.SyntaxKind.EqualsToken), node.expression.expression), this.f.createToken(ts.SyntaxKind.CommaToken), assignQ), this.f.createToken(ts.SyntaxKind.CommaToken), r)), node.expression.name), node.typeArguments, node.arguments);
|
|
581
|
+
}
|
|
582
|
+
else if (isParenthesizedExpression(node.expression.expression)) {
|
|
583
|
+
//e.g. (http.deep()).response();
|
|
584
|
+
//only work necessary when `http.deep()` is using type args and was converted to:
|
|
585
|
+
// (Ω = [], http.deep()).response()
|
|
586
|
+
//it's a call like (obj.method.Ω = ['a'], obj.method()).method()
|
|
587
|
+
//which needs to be converted so that Ω is correctly read by the last call
|
|
588
|
+
//(r = (obj.method.Ω = [['a']], obj.method()), obj.method.Ω = [['b']], r).method());
|
|
589
|
+
const r = this.getTempResultIdentifier();
|
|
590
|
+
const assignQ = this.f.createBinaryExpression(this.f.createPropertyAccessExpression(this.f.createPropertyAccessExpression(r, node.expression.name), 'Ω'), this.f.createToken(SyntaxKind.EqualsToken), this.f.createArrayLiteralExpression(typeExpressions));
|
|
591
|
+
const updatedNode = update(node, this.f.updatePropertyAccessExpression(node.expression, this.f.updateParenthesizedExpression(node.expression.expression, this.f.createBinaryExpression(this.f.createBinaryExpression(this.f.createBinaryExpression(r, this.f.createToken(SyntaxKind.EqualsToken), node.expression.expression.expression), this.f.createToken(SyntaxKind.CommaToken), assignQ), this.f.createToken(SyntaxKind.CommaToken), r)), node.expression.name), node.typeArguments, node.arguments);
|
|
592
|
+
return this.f.createParenthesizedExpression(updatedNode);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
//e.g. http.deep.response();
|
|
596
|
+
//nothing to do
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
//(fn.Ω = [], call())
|
|
600
|
+
return this.f.createParenthesizedExpression(this.f.createBinaryExpression(assignQ, this.f.createToken(SyntaxKind.CommaToken), node));
|
|
601
|
+
}
|
|
602
|
+
return node;
|
|
603
|
+
};
|
|
604
|
+
this.sourceFile = visitNode(this.sourceFile, visitor);
|
|
605
|
+
const newTopStatements = [];
|
|
606
|
+
while (true) {
|
|
607
|
+
let allCompiled = true;
|
|
608
|
+
for (const d of this.compileDeclarations.values()) {
|
|
609
|
+
if (d.compiled)
|
|
610
|
+
continue;
|
|
611
|
+
allCompiled = false;
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
if (this.embedDeclarations.size === 0 && allCompiled)
|
|
615
|
+
break;
|
|
616
|
+
for (const [node, d] of [...this.compileDeclarations.entries()]) {
|
|
617
|
+
if (d.compiled)
|
|
618
|
+
continue;
|
|
619
|
+
d.compiled = this.createProgramVarFromNode(node, d.name, this.sourceFile);
|
|
620
|
+
}
|
|
621
|
+
if (this.embedDeclarations.size) {
|
|
622
|
+
for (const node of this.embedDeclarations.keys()) {
|
|
623
|
+
this.compiledDeclarations.add(node);
|
|
624
|
+
}
|
|
625
|
+
const entries = Array.from(this.embedDeclarations.entries());
|
|
626
|
+
this.embedDeclarations.clear();
|
|
627
|
+
for (const [node, d] of entries) {
|
|
628
|
+
newTopStatements.push(...this.createProgramVarFromNode(node, d.name, d.sourceFile));
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
//externalize type aliases
|
|
633
|
+
const compileDeclarations = (node) => {
|
|
634
|
+
node = visitEachChild(node, compileDeclarations, this.context);
|
|
635
|
+
if ((isTypeAliasDeclaration(node) || isInterfaceDeclaration(node) || isEnumDeclaration(node))) {
|
|
636
|
+
const d = this.compileDeclarations.get(node);
|
|
637
|
+
if (!d) {
|
|
638
|
+
return node;
|
|
639
|
+
}
|
|
640
|
+
this.compileDeclarations.delete(node);
|
|
641
|
+
this.compiledDeclarations.add(node);
|
|
642
|
+
if (d.compiled) {
|
|
643
|
+
return [...d.compiled, node];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return node;
|
|
647
|
+
};
|
|
648
|
+
this.sourceFile = visitNode(this.sourceFile, compileDeclarations);
|
|
649
|
+
if (this.addImports.length) {
|
|
650
|
+
const handledIdentifier = [];
|
|
651
|
+
// group by importDeclaration so that we have one `{...} per importDeclaration`
|
|
652
|
+
const importMap = new Map();
|
|
653
|
+
for (const imp of this.addImports) {
|
|
654
|
+
if (handledIdentifier.includes(getIdentifierName(imp.identifier)))
|
|
655
|
+
continue;
|
|
656
|
+
handledIdentifier.push(getIdentifierName(imp.identifier));
|
|
657
|
+
let arr = importMap.get(imp.importDeclaration);
|
|
658
|
+
if (!arr) {
|
|
659
|
+
arr = [];
|
|
660
|
+
importMap.set(imp.importDeclaration, arr);
|
|
661
|
+
}
|
|
662
|
+
arr.push(imp.identifier);
|
|
663
|
+
}
|
|
664
|
+
for (const [importDeclaration, identifiers] of importMap.entries()) {
|
|
665
|
+
if (this.additionalImports.has(importDeclaration)) {
|
|
666
|
+
throw new Error('Internal error: additional import already exists');
|
|
667
|
+
}
|
|
668
|
+
if (this.getModuleType() === 'cjs') {
|
|
669
|
+
// var {a, b, c} = require('./bar')
|
|
670
|
+
const varDeclaration = this.f.createVariableStatement(undefined, this.f.createVariableDeclarationList([this.f.createVariableDeclaration(this.f.createObjectBindingPattern(identifiers.map(identifier => this.f.createBindingElement(undefined, undefined, identifier, undefined))), undefined, undefined, this.f.createCallExpression(this.f.createIdentifier("require"), undefined, [importDeclaration.moduleSpecifier]))], ts.NodeFlags.None));
|
|
671
|
+
const typeDeclWithComment = addSyntheticLeadingComment(varDeclaration, SyntaxKind.MultiLineCommentTrivia, '@ts-ignore', true);
|
|
672
|
+
this.additionalImports.set(importDeclaration, typeDeclWithComment);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
// import {a, b, c} from './bar.js'
|
|
676
|
+
const namedImports = this.f.createNamedImports(identifiers.map(identifier => this.f.createImportSpecifier(false, undefined, identifier)));
|
|
677
|
+
const importStatement = this.f.createImportDeclaration(undefined, this.f.createImportClause(false, undefined, namedImports), importDeclaration.moduleSpecifier);
|
|
678
|
+
const typeDeclWithComment = addSyntheticLeadingComment(importStatement, SyntaxKind.MultiLineCommentTrivia, '@ts-ignore', true);
|
|
679
|
+
this.additionalImports.set(importDeclaration, typeDeclWithComment);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (this.embedAssignType) {
|
|
684
|
+
const assignType = this.f.createFunctionDeclaration(undefined, undefined, this.f.createIdentifier('__assignType'), undefined, [
|
|
685
|
+
this.f.createParameterDeclaration(undefined, undefined, this.f.createIdentifier('fn'), undefined, undefined, //this.f.createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
|
686
|
+
undefined),
|
|
687
|
+
this.f.createParameterDeclaration(undefined, undefined, this.f.createIdentifier('args'), undefined, undefined, //this.f.createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
|
688
|
+
undefined),
|
|
689
|
+
], undefined, //this.f.createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
|
690
|
+
this.f.createBlock([
|
|
691
|
+
this.f.createExpressionStatement(this.f.createBinaryExpression(this.f.createPropertyAccessExpression(this.f.createIdentifier('fn'), this.f.createIdentifier('__type')), this.f.createToken(SyntaxKind.EqualsToken), this.f.createIdentifier('args'))),
|
|
692
|
+
this.f.createReturnStatement(this.f.createIdentifier('fn')),
|
|
693
|
+
], true));
|
|
694
|
+
newTopStatements.push(assignType);
|
|
695
|
+
}
|
|
696
|
+
if (this.tempResultIdentifier) {
|
|
697
|
+
newTopStatements.push(this.f.createVariableStatement(undefined, this.f.createVariableDeclarationList([this.f.createVariableDeclaration(this.tempResultIdentifier, undefined, undefined, undefined)], ts.NodeFlags.None)));
|
|
698
|
+
}
|
|
699
|
+
// we want to keep "use strict", or "use client", etc at the very top
|
|
700
|
+
const indexOfFirstLiteralExpression = this.sourceFile.statements.findIndex(v => isExpressionStatement(v) && isStringLiteral(v.expression));
|
|
701
|
+
const newStatements = indexOfFirstLiteralExpression === -1
|
|
702
|
+
? [...newTopStatements, ...this.attachAdditionalStatements(this.sourceFile.statements)]
|
|
703
|
+
: [
|
|
704
|
+
...this.sourceFile.statements.slice(0, indexOfFirstLiteralExpression + 1),
|
|
705
|
+
...newTopStatements,
|
|
706
|
+
...this.attachAdditionalStatements(this.sourceFile.statements.slice(indexOfFirstLiteralExpression + 1)),
|
|
707
|
+
];
|
|
708
|
+
this.sourceFile = this.f.updateSourceFile(this.sourceFile, newStatements);
|
|
709
|
+
// console.log(createPrinter().printNode(EmitHint.SourceFile, this.sourceFile, this.sourceFile));
|
|
710
|
+
const took = Date.now() - start;
|
|
711
|
+
debug(`Transform file with reflection=${reflection.mode} took ${took}ms (${this.getModuleType()}) ${sourceFile.fileName} via config ${reflection.tsConfigPath || 'none'}.`);
|
|
712
|
+
this.sourceFile.deepkitTransformed = true;
|
|
713
|
+
return this.sourceFile;
|
|
714
|
+
}
|
|
715
|
+
attachAdditionalStatements(statements) {
|
|
716
|
+
const result = [];
|
|
717
|
+
for (const statement of statements) {
|
|
718
|
+
if (isImportDeclaration(statement) || isJSDocImportTag(statement)) {
|
|
719
|
+
const additional = this.additionalImports.get(statement);
|
|
720
|
+
if (additional) {
|
|
721
|
+
result.push(additional);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
result.push(statement);
|
|
725
|
+
}
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
getModuleType() {
|
|
729
|
+
if (this.compilerOptions.module === ts.ModuleKind.Node16 || this.compilerOptions.module === ts.ModuleKind.NodeNext) {
|
|
730
|
+
if (this.sourceFile.impliedNodeFormat === ts.ModuleKind.ESNext) {
|
|
731
|
+
return 'esm';
|
|
732
|
+
}
|
|
733
|
+
return 'cjs';
|
|
734
|
+
}
|
|
735
|
+
return this.compilerOptions.module === ts.ModuleKind.CommonJS ? 'cjs' : 'esm';
|
|
736
|
+
}
|
|
737
|
+
getArrowFunctionΩPropertyAccessIdentifier(node) {
|
|
738
|
+
let { parent } = node.original || node;
|
|
739
|
+
if (isVariableDeclaration(parent) && isIdentifier(parent.name)) {
|
|
740
|
+
return parent.name;
|
|
741
|
+
}
|
|
742
|
+
else if (isPropertyAssignment(parent) && isIdentifier(parent.name)) {
|
|
743
|
+
const names = [];
|
|
744
|
+
while (parent) {
|
|
745
|
+
if (isObjectLiteralExpression(parent)) {
|
|
746
|
+
parent = parent.parent;
|
|
747
|
+
}
|
|
748
|
+
else if (isVariableDeclaration(parent)) {
|
|
749
|
+
names.unshift(getIdentifierName(parent.name));
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
else if (isIdentifier(parent.name)) {
|
|
753
|
+
names.unshift(getIdentifierName(parent.name));
|
|
754
|
+
parent = parent.parent;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return this.f.createIdentifier(names.join('.'));
|
|
761
|
+
}
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
injectResetΩ(node) {
|
|
765
|
+
let hasReceiveType = false;
|
|
766
|
+
for (const param of node.parameters) {
|
|
767
|
+
if (param.type && getReceiveTypeParameter(param.type))
|
|
768
|
+
hasReceiveType = true;
|
|
769
|
+
}
|
|
770
|
+
if (!hasReceiveType)
|
|
771
|
+
return node;
|
|
772
|
+
let container = this.f.createIdentifier('globalThis');
|
|
773
|
+
if (isArrowFunction(node)) {
|
|
774
|
+
const next = this.getArrowFunctionΩPropertyAccessIdentifier(node);
|
|
775
|
+
if (!next)
|
|
776
|
+
return node;
|
|
777
|
+
container = next;
|
|
778
|
+
}
|
|
779
|
+
else if ((isFunctionDeclaration(node) || isFunctionExpression(node)) && node.name) {
|
|
780
|
+
container = node.name;
|
|
781
|
+
}
|
|
782
|
+
else if (isMethodDeclaration(node) && isIdentifier(node.name)) {
|
|
783
|
+
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), node.name);
|
|
784
|
+
}
|
|
785
|
+
else if (isConstructorDeclaration(node)) {
|
|
786
|
+
container = this.f.createPropertyAccessExpression(this.f.createIdentifier('this'), 'constructor');
|
|
787
|
+
}
|
|
788
|
+
const reset = this.f.createExpressionStatement(this.f.createBinaryExpression(this.f.createPropertyAccessExpression(container, this.f.createIdentifier('Ω')), this.f.createToken(ts.SyntaxKind.EqualsToken), this.f.createIdentifier('undefined')));
|
|
789
|
+
// convert expression into statements array
|
|
790
|
+
let body = node.body && isBlock(node.body) ? node.body : undefined;
|
|
791
|
+
let bodyStatements = node.body && isBlock(node.body) ? [...node.body.statements] : [];
|
|
792
|
+
if (node.body) {
|
|
793
|
+
if (isExpression(node.body)) {
|
|
794
|
+
bodyStatements = [this.f.createReturnStatement(node.body)];
|
|
795
|
+
}
|
|
796
|
+
body = this.f.updateBlock(node.body, [reset, ...bodyStatements]);
|
|
797
|
+
}
|
|
798
|
+
if (isArrowFunction(node)) {
|
|
799
|
+
return this.f.updateArrowFunction(node, node.modifiers, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, body);
|
|
800
|
+
}
|
|
801
|
+
else if (isFunctionDeclaration(node)) {
|
|
802
|
+
return this.f.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, body);
|
|
803
|
+
}
|
|
804
|
+
else if (isFunctionExpression(node)) {
|
|
805
|
+
return this.f.updateFunctionExpression(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, body || node.body);
|
|
806
|
+
}
|
|
807
|
+
else if (isMethodDeclaration(node)) {
|
|
808
|
+
return this.f.updateMethodDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, body);
|
|
809
|
+
}
|
|
810
|
+
else if (isConstructorDeclaration(node)) {
|
|
811
|
+
return this.f.updateConstructorDeclaration(node, node.modifiers, node.parameters, body);
|
|
812
|
+
}
|
|
813
|
+
return node;
|
|
814
|
+
}
|
|
815
|
+
createProgramVarFromNode(node, name, sourceFile) {
|
|
816
|
+
const typeProgram = new CompilerProgram(node, sourceFile);
|
|
817
|
+
if ((isTypeAliasDeclaration(node) || isInterfaceDeclaration(node)) && node.typeParameters) {
|
|
818
|
+
for (const param of node.typeParameters) {
|
|
819
|
+
if (param.default) {
|
|
820
|
+
//push default on the stack
|
|
821
|
+
this.extractPackStructOfType(param.default, typeProgram);
|
|
822
|
+
}
|
|
823
|
+
typeProgram.pushTemplateParameter(getIdentifierName(param.name), !!param.default);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
this.extractPackStructOfType(node, typeProgram);
|
|
827
|
+
if (isTypeAliasDeclaration(node) || isInterfaceDeclaration(node) || isClassDeclaration(node) || isClassExpression(node)) {
|
|
828
|
+
typeProgram.pushOp(ReflectionOp.nominal);
|
|
829
|
+
}
|
|
830
|
+
const typeProgramExpression = this.packOpsAndStack(typeProgram);
|
|
831
|
+
const variable = this.f.createVariableStatement([], this.f.createVariableDeclarationList([
|
|
832
|
+
this.f.createVariableDeclaration(this.getDeclarationVariableName(name), undefined, undefined, typeProgramExpression),
|
|
833
|
+
], NodeFlags.Const));
|
|
834
|
+
//when its commonJS, the `variable` would be exported as `exports.$name = $value`, but all references point just to $name.
|
|
835
|
+
//so the idea is, that we create a normal variable and export it via `export {$name}`.
|
|
836
|
+
if (hasModifier(node, SyntaxKind.ExportKeyword)) {
|
|
837
|
+
//propertyName in ExportSpecifier is set to avoid a TS compile error:
|
|
838
|
+
// TypeError: Cannot read properties of undefined (reading 'escapedText')
|
|
839
|
+
// at Object.idText (/Users/marc/bude/deepkit-framework/packages/benchmark/node_modules/typescript/lib/typescript.js:11875:67)
|
|
840
|
+
const exportNode = this.f.createExportDeclaration(undefined, false, this.f.createNamedExports([
|
|
841
|
+
this.f.createExportSpecifier(false, this.getDeclarationVariableName(name), this.getDeclarationVariableName(name)),
|
|
842
|
+
]));
|
|
843
|
+
return [variable, exportNode];
|
|
844
|
+
}
|
|
845
|
+
return [variable];
|
|
846
|
+
}
|
|
847
|
+
extractPackStructOfExpression(node, program) {
|
|
848
|
+
switch (node.kind) {
|
|
849
|
+
case SyntaxKind.StringLiteral: {
|
|
850
|
+
program.pushOp(ReflectionOp.string);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
case SyntaxKind.NumericLiteral: {
|
|
854
|
+
program.pushOp(ReflectionOp.number);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
case SyntaxKind.FalseKeyword:
|
|
858
|
+
case SyntaxKind.TrueKeyword: {
|
|
859
|
+
program.pushOp(ReflectionOp.boolean);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
case SyntaxKind.BigIntLiteral: {
|
|
863
|
+
program.pushOp(ReflectionOp.bigint);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
//Symbol() is a function call, so we need to check for that
|
|
867
|
+
case SyntaxKind.CallExpression: {
|
|
868
|
+
const call = node;
|
|
869
|
+
if (isIdentifier(call.expression) && getIdentifierName(call.expression) === 'Symbol') {
|
|
870
|
+
program.pushOp(ReflectionOp.symbol);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
//new Date()
|
|
876
|
+
case SyntaxKind.NewExpression: {
|
|
877
|
+
const call = node;
|
|
878
|
+
if (isIdentifier(call.expression)) {
|
|
879
|
+
const map = {
|
|
880
|
+
'Date': ReflectionOp.date,
|
|
881
|
+
'RegExp': ReflectionOp.regexp,
|
|
882
|
+
'Uint8Array': ReflectionOp.uint8Array,
|
|
883
|
+
'Uint8ClampedArray': ReflectionOp.uint8ClampedArray,
|
|
884
|
+
'Uint16Array': ReflectionOp.uint16Array,
|
|
885
|
+
'Uint32Array': ReflectionOp.uint32Array,
|
|
886
|
+
'Int8Array': ReflectionOp.int8Array,
|
|
887
|
+
'Int16Array': ReflectionOp.int16Array,
|
|
888
|
+
'Int32Array': ReflectionOp.int32Array,
|
|
889
|
+
'Float32Array': ReflectionOp.float32Array,
|
|
890
|
+
'Float64Array': ReflectionOp.float64Array,
|
|
891
|
+
'ArrayBuffer': ReflectionOp.arrayBuffer,
|
|
892
|
+
};
|
|
893
|
+
const op = map[getIdentifierName(call.expression)];
|
|
894
|
+
if (op) {
|
|
895
|
+
program.pushOp(op);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
program.pushOp(ReflectionOp.never);
|
|
903
|
+
}
|
|
904
|
+
extractPackStructOfType(node, program) {
|
|
905
|
+
if (isParenthesizedTypeNode(node))
|
|
906
|
+
return this.extractPackStructOfType(node.type, program);
|
|
907
|
+
switch (node.kind) {
|
|
908
|
+
case SyntaxKind.StringKeyword: {
|
|
909
|
+
program.pushOp(ReflectionOp.string);
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
case SyntaxKind.NumberKeyword: {
|
|
913
|
+
program.pushOp(ReflectionOp.number);
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
case SyntaxKind.BooleanKeyword: {
|
|
917
|
+
program.pushOp(ReflectionOp.boolean);
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
case SyntaxKind.BigIntKeyword: {
|
|
921
|
+
program.pushOp(ReflectionOp.bigint);
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
case SyntaxKind.VoidKeyword: {
|
|
925
|
+
program.pushOp(ReflectionOp.void);
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
case SyntaxKind.UnknownKeyword: {
|
|
929
|
+
program.pushOp(ReflectionOp.unknown);
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
case SyntaxKind.ObjectKeyword: {
|
|
933
|
+
program.pushOp(ReflectionOp.object);
|
|
934
|
+
break;
|
|
935
|
+
}
|
|
936
|
+
case SyntaxKind.SymbolKeyword: {
|
|
937
|
+
program.pushOp(ReflectionOp.symbol);
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
case SyntaxKind.NullKeyword: {
|
|
941
|
+
program.pushOp(ReflectionOp.null);
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
case SyntaxKind.NeverKeyword: {
|
|
945
|
+
program.pushOp(ReflectionOp.never);
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
case SyntaxKind.AnyKeyword: {
|
|
949
|
+
program.pushOp(ReflectionOp.any);
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
case SyntaxKind.UndefinedKeyword: {
|
|
953
|
+
program.pushOp(ReflectionOp.undefined);
|
|
954
|
+
break;
|
|
955
|
+
}
|
|
956
|
+
case SyntaxKind.TrueKeyword: {
|
|
957
|
+
program.pushOp(ReflectionOp.literal, program.pushStack(this.f.createTrue()));
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
case SyntaxKind.FalseKeyword: {
|
|
961
|
+
program.pushOp(ReflectionOp.literal, program.pushStack(this.f.createFalse()));
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
case SyntaxKind.ClassDeclaration:
|
|
965
|
+
case SyntaxKind.ClassExpression: {
|
|
966
|
+
//TypeScript does not narrow types down
|
|
967
|
+
const narrowed = node;
|
|
968
|
+
//class nodes have always their own program, so the start is always fresh, means we don't need a frame
|
|
969
|
+
if (node) {
|
|
970
|
+
const members = [];
|
|
971
|
+
if (narrowed.typeParameters) {
|
|
972
|
+
for (const typeParameter of narrowed.typeParameters) {
|
|
973
|
+
const name = getNameAsString(typeParameter.name);
|
|
974
|
+
if (typeParameter.default) {
|
|
975
|
+
//push default on the stack
|
|
976
|
+
this.extractPackStructOfType(typeParameter.default, program);
|
|
977
|
+
}
|
|
978
|
+
program.pushTemplateParameter(name, !!typeParameter.default);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (narrowed.heritageClauses) {
|
|
982
|
+
for (const heritage of narrowed.heritageClauses) {
|
|
983
|
+
if (heritage.token === SyntaxKind.ExtendsKeyword) {
|
|
984
|
+
for (const extendType of heritage.types) {
|
|
985
|
+
program.pushFrame();
|
|
986
|
+
if (extendType.typeArguments) {
|
|
987
|
+
for (const typeArgument of extendType.typeArguments) {
|
|
988
|
+
this.extractPackStructOfType(typeArgument, program);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const index = program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, this.nodeConverter.toExpression(extendType.expression)));
|
|
992
|
+
program.pushOp(ReflectionOp.classReference, index);
|
|
993
|
+
program.popFrameImplicit();
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
for (const member of narrowed.members) {
|
|
999
|
+
const name = getNameAsString(member.name);
|
|
1000
|
+
if (name) {
|
|
1001
|
+
const has = members.some(v => getNameAsString(v.name) === name);
|
|
1002
|
+
if (has)
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
members.push(member);
|
|
1006
|
+
this.extractPackStructOfType(member, program);
|
|
1007
|
+
}
|
|
1008
|
+
program.pushOp(ReflectionOp.class);
|
|
1009
|
+
if (narrowed.heritageClauses) {
|
|
1010
|
+
for (const heritageClause of narrowed.heritageClauses) {
|
|
1011
|
+
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
|
|
1012
|
+
//extends only supports extending one class
|
|
1013
|
+
const first = heritageClause.types[0];
|
|
1014
|
+
if (isExpressionWithTypeArguments(first) && first.typeArguments) {
|
|
1015
|
+
for (const typeArgument of first.typeArguments) {
|
|
1016
|
+
this.extractPackStructOfType(typeArgument, program);
|
|
1017
|
+
}
|
|
1018
|
+
program.pushOp(ReflectionOp.classExtends, first.typeArguments.length);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
else if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
|
|
1022
|
+
for (const type of heritageClause.types) {
|
|
1023
|
+
this.extractPackStructOfTypeReference(type, program);
|
|
1024
|
+
}
|
|
1025
|
+
program.pushOp(ReflectionOp.implements, heritageClause.types.length);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (narrowed.name)
|
|
1030
|
+
this.resolveTypeName(getIdentifierName(narrowed.name), program);
|
|
1031
|
+
// for whatever reason: narrowed.name.parent !== narrowed. narrowed.name.parent has jsDoc, narrowed.name not.
|
|
1032
|
+
const description = extractJSDocAttribute(this.sourceFile, narrowed.name?.parent, 'description');
|
|
1033
|
+
if (description)
|
|
1034
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1035
|
+
}
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
case SyntaxKind.IntersectionType: {
|
|
1039
|
+
//TypeScript does not narrow types down
|
|
1040
|
+
const narrowed = node;
|
|
1041
|
+
program.pushFrame();
|
|
1042
|
+
for (const type of narrowed.types) {
|
|
1043
|
+
this.extractPackStructOfType(type, program);
|
|
1044
|
+
}
|
|
1045
|
+
program.pushOp(ReflectionOp.intersection);
|
|
1046
|
+
program.popFrameImplicit();
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
case SyntaxKind.MappedType: {
|
|
1050
|
+
//TypeScript does not narrow types down
|
|
1051
|
+
const narrowed = node;
|
|
1052
|
+
//<Type>{[Property in keyof Type]: boolean;};
|
|
1053
|
+
program.pushFrame();
|
|
1054
|
+
program.pushVariable(getIdentifierName(narrowed.typeParameter.name));
|
|
1055
|
+
const constraint = getEffectiveConstraintOfTypeParameter(narrowed.typeParameter);
|
|
1056
|
+
if (constraint) {
|
|
1057
|
+
this.extractPackStructOfType(constraint, program);
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
program.pushOp(ReflectionOp.never);
|
|
1061
|
+
}
|
|
1062
|
+
let modifier = 0;
|
|
1063
|
+
if (narrowed.questionToken) {
|
|
1064
|
+
if (narrowed.questionToken.kind === SyntaxKind.QuestionToken) {
|
|
1065
|
+
modifier |= MappedModifier.optional;
|
|
1066
|
+
}
|
|
1067
|
+
if (narrowed.questionToken.kind === SyntaxKind.MinusToken) {
|
|
1068
|
+
modifier |= MappedModifier.removeOptional;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (narrowed.readonlyToken) {
|
|
1072
|
+
if (narrowed.readonlyToken.kind === SyntaxKind.ReadonlyKeyword) {
|
|
1073
|
+
modifier |= MappedModifier.readonly;
|
|
1074
|
+
}
|
|
1075
|
+
if (narrowed.readonlyToken.kind === SyntaxKind.MinusToken) {
|
|
1076
|
+
modifier |= MappedModifier.removeReadonly;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
program.pushCoRoutine();
|
|
1080
|
+
if (narrowed.nameType)
|
|
1081
|
+
program.pushFrame();
|
|
1082
|
+
if (narrowed.type) {
|
|
1083
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
program.pushOp(ReflectionOp.never);
|
|
1087
|
+
}
|
|
1088
|
+
if (narrowed.nameType) {
|
|
1089
|
+
this.extractPackStructOfType(narrowed.nameType, program);
|
|
1090
|
+
program.pushOp(ReflectionOp.tuple);
|
|
1091
|
+
program.popFrameImplicit();
|
|
1092
|
+
}
|
|
1093
|
+
const coRoutineIndex = program.popCoRoutine();
|
|
1094
|
+
if (narrowed.nameType) {
|
|
1095
|
+
program.pushOp(ReflectionOp.mappedType2, coRoutineIndex, modifier);
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
program.pushOp(ReflectionOp.mappedType, coRoutineIndex, modifier);
|
|
1099
|
+
}
|
|
1100
|
+
program.popFrameImplicit();
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
case SyntaxKind.TypeAliasDeclaration: {
|
|
1104
|
+
let narrowed = node;
|
|
1105
|
+
if (program.sourceFile && getNameAsString(narrowed.name) === 'TypeAnnotation') {
|
|
1106
|
+
const attribute = extractJSDocAttribute(program.sourceFile, narrowed, 'intrinsic');
|
|
1107
|
+
if (attribute !== undefined) {
|
|
1108
|
+
// TypeAnnotation<T> is like an intrinsic type, so we don't need to resolve it
|
|
1109
|
+
narrowed = this.intrinsicMetaDeclaration;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1113
|
+
if (narrowed.name)
|
|
1114
|
+
this.resolveTypeName(getIdentifierName(narrowed.name), program);
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
case SyntaxKind.TypeLiteral:
|
|
1118
|
+
case SyntaxKind.InterfaceDeclaration: {
|
|
1119
|
+
//TypeScript does not narrow types down
|
|
1120
|
+
const narrowed = node;
|
|
1121
|
+
let descriptionNode = narrowed;
|
|
1122
|
+
program.pushFrame();
|
|
1123
|
+
//first all extend expressions
|
|
1124
|
+
if (isInterfaceDeclaration(narrowed) && narrowed.heritageClauses) {
|
|
1125
|
+
for (const heritage of narrowed.heritageClauses) {
|
|
1126
|
+
if (heritage.token === SyntaxKind.ExtendsKeyword) {
|
|
1127
|
+
for (const extendType of heritage.types) {
|
|
1128
|
+
this.extractPackStructOfTypeReference(extendType, program);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
for (const member of narrowed.members) {
|
|
1134
|
+
this.extractPackStructOfType(member, program);
|
|
1135
|
+
}
|
|
1136
|
+
program.pushOp(ReflectionOp.objectLiteral);
|
|
1137
|
+
if (isTypeLiteralNode(narrowed)) {
|
|
1138
|
+
descriptionNode = narrowed.parent;
|
|
1139
|
+
}
|
|
1140
|
+
const description = descriptionNode && extractJSDocAttribute(this.sourceFile, descriptionNode, 'description');
|
|
1141
|
+
if (description)
|
|
1142
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1143
|
+
if (isInterfaceDeclaration(narrowed)) {
|
|
1144
|
+
if (narrowed.name)
|
|
1145
|
+
this.resolveTypeName(getIdentifierName(narrowed.name), program);
|
|
1146
|
+
}
|
|
1147
|
+
program.popFrameImplicit();
|
|
1148
|
+
break;
|
|
1149
|
+
}
|
|
1150
|
+
case SyntaxKind.TypeReference: {
|
|
1151
|
+
this.extractPackStructOfTypeReference(node, program);
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
case SyntaxKind.ArrayType: {
|
|
1155
|
+
this.extractPackStructOfType(node.elementType, program);
|
|
1156
|
+
program.pushOp(ReflectionOp.array);
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
case SyntaxKind.RestType: {
|
|
1160
|
+
let type = node.type;
|
|
1161
|
+
if (isArrayTypeNode(type)) {
|
|
1162
|
+
type = type.elementType;
|
|
1163
|
+
}
|
|
1164
|
+
this.extractPackStructOfType(type, program);
|
|
1165
|
+
program.pushOp(ReflectionOp.rest);
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
case SyntaxKind.TupleType: {
|
|
1169
|
+
program.pushFrame();
|
|
1170
|
+
for (const element of node.elements) {
|
|
1171
|
+
if (isOptionalTypeNode(element)) {
|
|
1172
|
+
this.extractPackStructOfType(element.type, program);
|
|
1173
|
+
program.pushOp(ReflectionOp.tupleMember);
|
|
1174
|
+
program.pushOp(ReflectionOp.optional);
|
|
1175
|
+
}
|
|
1176
|
+
else if (isNamedTupleMember(element)) {
|
|
1177
|
+
if (element.dotDotDotToken) {
|
|
1178
|
+
let type = element.type;
|
|
1179
|
+
if (isArrayTypeNode(type)) {
|
|
1180
|
+
type = type.elementType;
|
|
1181
|
+
}
|
|
1182
|
+
this.extractPackStructOfType(type, program);
|
|
1183
|
+
program.pushOp(ReflectionOp.rest);
|
|
1184
|
+
}
|
|
1185
|
+
else {
|
|
1186
|
+
this.extractPackStructOfType(element.type, program);
|
|
1187
|
+
}
|
|
1188
|
+
const index = program.findOrAddStackEntry(getIdentifierName(element.name));
|
|
1189
|
+
program.pushOp(ReflectionOp.namedTupleMember, index);
|
|
1190
|
+
if (element.questionToken) {
|
|
1191
|
+
program.pushOp(ReflectionOp.optional);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
this.extractPackStructOfType(element, program);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
program.pushOp(ReflectionOp.tuple);
|
|
1199
|
+
program.popFrameImplicit();
|
|
1200
|
+
break;
|
|
1201
|
+
}
|
|
1202
|
+
case SyntaxKind.PropertySignature: {
|
|
1203
|
+
//TypeScript does not narrow types down
|
|
1204
|
+
const narrowed = node;
|
|
1205
|
+
if (narrowed.type) {
|
|
1206
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1207
|
+
const name = getPropertyName(this.f, narrowed.name);
|
|
1208
|
+
program.pushOp(ReflectionOp.propertySignature, program.findOrAddStackEntry(name));
|
|
1209
|
+
if (narrowed.questionToken)
|
|
1210
|
+
program.pushOp(ReflectionOp.optional);
|
|
1211
|
+
if (hasModifier(narrowed, SyntaxKind.ReadonlyKeyword))
|
|
1212
|
+
program.pushOp(ReflectionOp.readonly);
|
|
1213
|
+
const description = extractJSDocAttribute(this.sourceFile, narrowed, 'description');
|
|
1214
|
+
if (description)
|
|
1215
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1216
|
+
}
|
|
1217
|
+
else {
|
|
1218
|
+
program.pushOp(ReflectionOp.unknown);
|
|
1219
|
+
}
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
case SyntaxKind.PropertyDeclaration: {
|
|
1223
|
+
//TypeScript does not narrow types down
|
|
1224
|
+
const narrowed = node;
|
|
1225
|
+
// if the property was explicitly marked as `@reflection no`, we ignore it
|
|
1226
|
+
if (false === this.getExplicitReflectionMode(program.sourceFile, narrowed))
|
|
1227
|
+
return;
|
|
1228
|
+
if (narrowed.type) {
|
|
1229
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1230
|
+
}
|
|
1231
|
+
else if (narrowed.initializer) {
|
|
1232
|
+
this.extractPackStructOfExpression(narrowed.initializer, program);
|
|
1233
|
+
}
|
|
1234
|
+
else {
|
|
1235
|
+
program.pushOp(ReflectionOp.unknown);
|
|
1236
|
+
}
|
|
1237
|
+
const name = getPropertyName(this.f, narrowed.name);
|
|
1238
|
+
program.pushOp(ReflectionOp.property, program.findOrAddStackEntry(name));
|
|
1239
|
+
if (narrowed.questionToken)
|
|
1240
|
+
program.pushOp(ReflectionOp.optional);
|
|
1241
|
+
if (hasModifier(narrowed, SyntaxKind.ReadonlyKeyword))
|
|
1242
|
+
program.pushOp(ReflectionOp.readonly);
|
|
1243
|
+
if (hasModifier(narrowed, SyntaxKind.PrivateKeyword))
|
|
1244
|
+
program.pushOp(ReflectionOp.private);
|
|
1245
|
+
if (hasModifier(narrowed, SyntaxKind.ProtectedKeyword))
|
|
1246
|
+
program.pushOp(ReflectionOp.protected);
|
|
1247
|
+
if (hasModifier(narrowed, SyntaxKind.AbstractKeyword))
|
|
1248
|
+
program.pushOp(ReflectionOp.abstract);
|
|
1249
|
+
if (hasModifier(narrowed, SyntaxKind.StaticKeyword))
|
|
1250
|
+
program.pushOp(ReflectionOp.static);
|
|
1251
|
+
if (narrowed.initializer) {
|
|
1252
|
+
//important to use Function, since it will be called using a different `this`
|
|
1253
|
+
program.pushOp(ReflectionOp.defaultValue, program.findOrAddStackEntry(this.f.createFunctionExpression(undefined, undefined, undefined, undefined, undefined, undefined, this.f.createBlock([this.f.createReturnStatement(narrowed.initializer)]))));
|
|
1254
|
+
}
|
|
1255
|
+
const description = extractJSDocAttribute(this.sourceFile, narrowed, 'description');
|
|
1256
|
+
if (description)
|
|
1257
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
case SyntaxKind.ConditionalType: {
|
|
1261
|
+
//TypeScript does not narrow types down
|
|
1262
|
+
const narrowed = node;
|
|
1263
|
+
// Depending on whether this a distributive conditional type or not, it has to be moved to its own function
|
|
1264
|
+
// my understanding of when a distributive conditional type is used is:
|
|
1265
|
+
// 1. the `checkType` is a simple identifier (just `T`, no `[T]`, no `T | x`, no `{a: T}`, etc)
|
|
1266
|
+
const distributiveOverIdentifier = isTypeReferenceNode(narrowed.checkType) && isIdentifier(narrowed.checkType.typeName)
|
|
1267
|
+
? narrowed.checkType.typeName : undefined;
|
|
1268
|
+
if (distributiveOverIdentifier) {
|
|
1269
|
+
program.pushFrame();
|
|
1270
|
+
//first we add to the stack the origin type we distribute over.
|
|
1271
|
+
this.extractPackStructOfType(narrowed.checkType, program);
|
|
1272
|
+
//since the distributive conditional type is a loop that changes only the found `T`, it is necessary to add that as variable,
|
|
1273
|
+
//so call convention can take over.
|
|
1274
|
+
program.pushVariable(getIdentifierName(distributiveOverIdentifier));
|
|
1275
|
+
program.pushCoRoutine();
|
|
1276
|
+
}
|
|
1277
|
+
program.pushConditionalFrame(); //gets its own frame for `infer T` ops. all infer variables will be registered in this frame
|
|
1278
|
+
this.extractPackStructOfType(narrowed.checkType, program);
|
|
1279
|
+
this.extractPackStructOfType(narrowed.extendsType, program);
|
|
1280
|
+
program.pushOp(ReflectionOp.extends);
|
|
1281
|
+
program.pushCoRoutine();
|
|
1282
|
+
this.extractPackStructOfType(narrowed.trueType, program);
|
|
1283
|
+
const trueProgram = program.popCoRoutine();
|
|
1284
|
+
program.pushCoRoutine();
|
|
1285
|
+
this.extractPackStructOfType(narrowed.falseType, program);
|
|
1286
|
+
const falseProgram = program.popCoRoutine();
|
|
1287
|
+
program.pushOp(ReflectionOp.jumpCondition, trueProgram, falseProgram);
|
|
1288
|
+
program.moveFrame(); //pops frame
|
|
1289
|
+
if (distributiveOverIdentifier) {
|
|
1290
|
+
const coRoutineIndex = program.popCoRoutine();
|
|
1291
|
+
program.pushOp(ReflectionOp.distribute, coRoutineIndex);
|
|
1292
|
+
program.popFrameImplicit();
|
|
1293
|
+
}
|
|
1294
|
+
break;
|
|
1295
|
+
}
|
|
1296
|
+
case SyntaxKind.InferType: {
|
|
1297
|
+
//TypeScript does not narrow types down
|
|
1298
|
+
const narrowed = node;
|
|
1299
|
+
const frame = program.findConditionalFrame();
|
|
1300
|
+
if (frame) {
|
|
1301
|
+
const typeParameterName = getIdentifierName(narrowed.typeParameter.name);
|
|
1302
|
+
let variable = program.findVariable(typeParameterName);
|
|
1303
|
+
if (!variable) {
|
|
1304
|
+
program.pushVariable(typeParameterName, frame);
|
|
1305
|
+
variable = program.findVariable(typeParameterName);
|
|
1306
|
+
if (!variable)
|
|
1307
|
+
throw new Error('Could not find inserted infer variable');
|
|
1308
|
+
}
|
|
1309
|
+
program.pushOp(ReflectionOp.infer, variable.frameOffset, variable.stackIndex);
|
|
1310
|
+
}
|
|
1311
|
+
else {
|
|
1312
|
+
program.pushOp(ReflectionOp.never);
|
|
1313
|
+
}
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
case SyntaxKind.MethodSignature:
|
|
1317
|
+
case SyntaxKind.MethodDeclaration:
|
|
1318
|
+
case SyntaxKind.Constructor:
|
|
1319
|
+
case SyntaxKind.ArrowFunction:
|
|
1320
|
+
case SyntaxKind.FunctionExpression:
|
|
1321
|
+
case SyntaxKind.ConstructSignature:
|
|
1322
|
+
case SyntaxKind.ConstructorType:
|
|
1323
|
+
case SyntaxKind.FunctionType:
|
|
1324
|
+
case SyntaxKind.CallSignature:
|
|
1325
|
+
case SyntaxKind.FunctionDeclaration: {
|
|
1326
|
+
//TypeScript does not narrow types down
|
|
1327
|
+
const narrowed = node;
|
|
1328
|
+
// if the function was explicitly marked as `@reflection no`, we ignore it
|
|
1329
|
+
if (false === this.getExplicitReflectionMode(program.sourceFile, narrowed)) {
|
|
1330
|
+
program.pushOp(ReflectionOp.any);
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
const name = isCallSignatureDeclaration(node)
|
|
1334
|
+
? '' : isConstructorTypeNode(narrowed) || isConstructSignatureDeclaration(node)
|
|
1335
|
+
? 'new' : isConstructorDeclaration(narrowed) ? 'constructor' : getPropertyName(this.f, narrowed.name);
|
|
1336
|
+
if (!narrowed.type && narrowed.parameters.length === 0 && !name)
|
|
1337
|
+
return;
|
|
1338
|
+
program.pushFrame();
|
|
1339
|
+
for (let i = 0; i < narrowed.parameters.length; i++) {
|
|
1340
|
+
const parameter = narrowed.parameters[i];
|
|
1341
|
+
const parameterName = isIdentifier(parameter.name) ? getNameAsString(parameter.name) : 'param' + i;
|
|
1342
|
+
const type = parameter.type
|
|
1343
|
+
? (parameter.dotDotDotToken && isArrayTypeNode(parameter.type) ? parameter.type.elementType : parameter.type) : undefined;
|
|
1344
|
+
if (type) {
|
|
1345
|
+
this.extractPackStructOfType(type, program);
|
|
1346
|
+
}
|
|
1347
|
+
else {
|
|
1348
|
+
program.pushOp(ReflectionOp.any);
|
|
1349
|
+
}
|
|
1350
|
+
if (parameter.dotDotDotToken) {
|
|
1351
|
+
program.pushOp(ReflectionOp.rest);
|
|
1352
|
+
}
|
|
1353
|
+
program.pushOp(ReflectionOp.parameter, program.findOrAddStackEntry(parameterName));
|
|
1354
|
+
if (parameter.questionToken)
|
|
1355
|
+
program.pushOp(ReflectionOp.optional);
|
|
1356
|
+
if (hasModifier(parameter, SyntaxKind.PublicKeyword))
|
|
1357
|
+
program.pushOp(ReflectionOp.public);
|
|
1358
|
+
if (hasModifier(parameter, SyntaxKind.PrivateKeyword))
|
|
1359
|
+
program.pushOp(ReflectionOp.private);
|
|
1360
|
+
if (hasModifier(parameter, SyntaxKind.ProtectedKeyword))
|
|
1361
|
+
program.pushOp(ReflectionOp.protected);
|
|
1362
|
+
if (hasModifier(parameter, SyntaxKind.ReadonlyKeyword))
|
|
1363
|
+
program.pushOp(ReflectionOp.readonly);
|
|
1364
|
+
const description = extractJSDocAttribute(this.sourceFile, parameter, 'description');
|
|
1365
|
+
if (description)
|
|
1366
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1367
|
+
if (parameter.initializer && parameter.type && !getReceiveTypeParameter(parameter.type)) {
|
|
1368
|
+
program.pushOp(ReflectionOp.defaultValue, program.findOrAddStackEntry(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, parameter.initializer)));
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
if (narrowed.type) {
|
|
1372
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
program.pushOp(ReflectionOp.any);
|
|
1376
|
+
}
|
|
1377
|
+
program.pushOp(isCallSignatureDeclaration(node) ? ReflectionOp.callSignature :
|
|
1378
|
+
isMethodSignature(narrowed) || isConstructSignatureDeclaration(narrowed)
|
|
1379
|
+
? ReflectionOp.methodSignature
|
|
1380
|
+
: isMethodDeclaration(narrowed) || isConstructorDeclaration(narrowed)
|
|
1381
|
+
? ReflectionOp.method : ReflectionOp.function, program.findOrAddStackEntry(name));
|
|
1382
|
+
if ((isMethodSignature(narrowed) || isMethodDeclaration(narrowed)) && narrowed.questionToken) {
|
|
1383
|
+
program.pushOp(ReflectionOp.optional);
|
|
1384
|
+
}
|
|
1385
|
+
if (isMethodDeclaration(narrowed)) {
|
|
1386
|
+
if (hasModifier(narrowed, SyntaxKind.PrivateKeyword))
|
|
1387
|
+
program.pushOp(ReflectionOp.private);
|
|
1388
|
+
if (hasModifier(narrowed, SyntaxKind.ProtectedKeyword))
|
|
1389
|
+
program.pushOp(ReflectionOp.protected);
|
|
1390
|
+
if (hasModifier(narrowed, SyntaxKind.AbstractKeyword))
|
|
1391
|
+
program.pushOp(ReflectionOp.abstract);
|
|
1392
|
+
if (hasModifier(narrowed, SyntaxKind.StaticKeyword))
|
|
1393
|
+
program.pushOp(ReflectionOp.static);
|
|
1394
|
+
}
|
|
1395
|
+
const description = extractJSDocAttribute(this.sourceFile, narrowed, 'description');
|
|
1396
|
+
if (description)
|
|
1397
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1398
|
+
program.popFrameImplicit();
|
|
1399
|
+
break;
|
|
1400
|
+
}
|
|
1401
|
+
case SyntaxKind.LiteralType: {
|
|
1402
|
+
//TypeScript does not narrow types down
|
|
1403
|
+
const narrowed = node;
|
|
1404
|
+
if (narrowed.literal.kind === SyntaxKind.NullKeyword) {
|
|
1405
|
+
program.pushOp(ReflectionOp.null);
|
|
1406
|
+
}
|
|
1407
|
+
else {
|
|
1408
|
+
program.pushOp(ReflectionOp.literal, program.findOrAddStackEntry(narrowed.literal));
|
|
1409
|
+
}
|
|
1410
|
+
break;
|
|
1411
|
+
}
|
|
1412
|
+
case SyntaxKind.TemplateLiteralType: {
|
|
1413
|
+
//TypeScript does not narrow types down
|
|
1414
|
+
const narrowed = node;
|
|
1415
|
+
program.pushFrame();
|
|
1416
|
+
if (narrowed.head.rawText) {
|
|
1417
|
+
program.pushOp(ReflectionOp.literal, program.findOrAddStackEntry(narrowed.head.rawText));
|
|
1418
|
+
}
|
|
1419
|
+
for (const span of narrowed.templateSpans) {
|
|
1420
|
+
this.extractPackStructOfType(span.type, program);
|
|
1421
|
+
if (span.literal.rawText) {
|
|
1422
|
+
program.pushOp(ReflectionOp.literal, program.findOrAddStackEntry(span.literal.rawText));
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
program.pushOp(ReflectionOp.templateLiteral);
|
|
1426
|
+
program.popFrameImplicit();
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
case SyntaxKind.UnionType: {
|
|
1430
|
+
//TypeScript does not narrow types down
|
|
1431
|
+
const narrowed = node;
|
|
1432
|
+
if (narrowed.types.length === 0) {
|
|
1433
|
+
//nothing to emit
|
|
1434
|
+
}
|
|
1435
|
+
else if (narrowed.types.length === 1) {
|
|
1436
|
+
//only emit the type
|
|
1437
|
+
this.extractPackStructOfType(narrowed.types[0], program);
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
program.pushFrame();
|
|
1441
|
+
for (const subType of narrowed.types) {
|
|
1442
|
+
this.extractPackStructOfType(subType, program);
|
|
1443
|
+
}
|
|
1444
|
+
program.pushOp(ReflectionOp.union);
|
|
1445
|
+
program.popFrameImplicit();
|
|
1446
|
+
}
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
case SyntaxKind.EnumDeclaration: {
|
|
1450
|
+
//TypeScript does not narrow types down
|
|
1451
|
+
const narrowed = node;
|
|
1452
|
+
program.pushFrame();
|
|
1453
|
+
for (const type of narrowed.members) {
|
|
1454
|
+
const name = getPropertyName(this.f, type.name);
|
|
1455
|
+
program.pushOp(ReflectionOp.enumMember, program.findOrAddStackEntry(name));
|
|
1456
|
+
if (type.initializer) {
|
|
1457
|
+
program.pushOp(ReflectionOp.defaultValue, program.findOrAddStackEntry(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, type.initializer)));
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
program.pushOp(ReflectionOp.enum);
|
|
1461
|
+
const description = extractJSDocAttribute(this.sourceFile, narrowed, 'description');
|
|
1462
|
+
if (description)
|
|
1463
|
+
program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
|
|
1464
|
+
if (narrowed.name)
|
|
1465
|
+
this.resolveTypeName(getIdentifierName(narrowed.name), program);
|
|
1466
|
+
program.popFrameImplicit();
|
|
1467
|
+
break;
|
|
1468
|
+
}
|
|
1469
|
+
case SyntaxKind.IndexSignature: {
|
|
1470
|
+
//TypeScript does not narrow types down
|
|
1471
|
+
const narrowed = node;
|
|
1472
|
+
//node.parameters = first item is {[name: string]: number} => 'name: string'
|
|
1473
|
+
if (narrowed.parameters.length && narrowed.parameters[0].type) {
|
|
1474
|
+
this.extractPackStructOfType(narrowed.parameters[0].type, program);
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
program.pushOp(ReflectionOp.any);
|
|
1478
|
+
}
|
|
1479
|
+
//node.type = first item is {[name: string]: number} => 'number'
|
|
1480
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1481
|
+
program.pushOp(ReflectionOp.indexSignature);
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
case SyntaxKind.TypeQuery: {
|
|
1485
|
+
//TypeScript does not narrow types down
|
|
1486
|
+
const narrowed = node;
|
|
1487
|
+
// if (program.importSpecifier) {
|
|
1488
|
+
// //if this is set, the current program is embedded into another file. All locally used symbols like a variable in `typeof` need to be imported
|
|
1489
|
+
// //in the other file as well.
|
|
1490
|
+
// if (isIdentifier(narrowed.exprName)) {
|
|
1491
|
+
// const originImportStatement = program.importSpecifier.parent.parent.parent;
|
|
1492
|
+
// this.addImports.push({ identifier: narrowed.exprName, from: originImportStatement.moduleSpecifier });
|
|
1493
|
+
// }
|
|
1494
|
+
// }
|
|
1495
|
+
if (isIdentifier(narrowed.exprName)) {
|
|
1496
|
+
const resolved = this.resolveDeclaration(narrowed.exprName);
|
|
1497
|
+
if (resolved && findSourceFile(resolved.declaration) !== this.sourceFile && resolved.importDeclaration) {
|
|
1498
|
+
ensureImportIsEmitted(resolved.importDeclaration, narrowed.exprName);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
const expression = serializeEntityNameAsExpression(this.f, narrowed.exprName);
|
|
1502
|
+
program.pushOp(ReflectionOp.typeof, program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, expression)));
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
case SyntaxKind.TypeOperator: {
|
|
1506
|
+
//TypeScript does not narrow types down
|
|
1507
|
+
const narrowed = node;
|
|
1508
|
+
if (narrowed.type.kind === SyntaxKind.ThisType) {
|
|
1509
|
+
//for the moment we treat `keyof this` as any, since `this` is not implemented at all.
|
|
1510
|
+
//this makes it possible that the code above works at least.
|
|
1511
|
+
program.pushOp(ReflectionOp.any);
|
|
1512
|
+
break;
|
|
1513
|
+
}
|
|
1514
|
+
switch (narrowed.operator) {
|
|
1515
|
+
case SyntaxKind.KeyOfKeyword: {
|
|
1516
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1517
|
+
program.pushOp(ReflectionOp.keyof);
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
case SyntaxKind.ReadonlyKeyword: {
|
|
1521
|
+
this.extractPackStructOfType(narrowed.type, program);
|
|
1522
|
+
program.pushOp(ReflectionOp.readonly);
|
|
1523
|
+
break;
|
|
1524
|
+
}
|
|
1525
|
+
default: {
|
|
1526
|
+
program.pushOp(ReflectionOp.never);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
break;
|
|
1530
|
+
}
|
|
1531
|
+
case SyntaxKind.IndexedAccessType: {
|
|
1532
|
+
//TypeScript does not narrow types down
|
|
1533
|
+
const narrowed = node;
|
|
1534
|
+
this.extractPackStructOfType(narrowed.objectType, program);
|
|
1535
|
+
this.extractPackStructOfType(narrowed.indexType, program);
|
|
1536
|
+
program.pushOp(ReflectionOp.indexAccess);
|
|
1537
|
+
break;
|
|
1538
|
+
}
|
|
1539
|
+
case SyntaxKind.Identifier: {
|
|
1540
|
+
//TypeScript does not narrow types down
|
|
1541
|
+
const narrowed = node;
|
|
1542
|
+
//check if it references a variable
|
|
1543
|
+
const variable = program.findVariable(getIdentifierName(narrowed));
|
|
1544
|
+
if (variable) {
|
|
1545
|
+
program.pushOp(ReflectionOp.loads, variable.frameOffset, variable.stackIndex);
|
|
1546
|
+
}
|
|
1547
|
+
else {
|
|
1548
|
+
program.pushOp(ReflectionOp.never);
|
|
1549
|
+
}
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
case SyntaxKind.IntrinsicKeyword: {
|
|
1553
|
+
if (node.parent?.kind !== SyntaxKind.TypeAliasDeclaration) {
|
|
1554
|
+
program.pushOp(ReflectionOp.never);
|
|
1555
|
+
break;
|
|
1556
|
+
}
|
|
1557
|
+
const parent = node.parent;
|
|
1558
|
+
const T = parent.typeParameters?.[0];
|
|
1559
|
+
// All intrinsics require one type parameter
|
|
1560
|
+
if (!T) {
|
|
1561
|
+
program.pushOp(ReflectionOp.never);
|
|
1562
|
+
break;
|
|
1563
|
+
}
|
|
1564
|
+
const name = getNameAsString(parent.name);
|
|
1565
|
+
const mapping = {
|
|
1566
|
+
'Capitalize': TypeIntrinsic.Capitalize,
|
|
1567
|
+
'Uppercase': TypeIntrinsic.Uppercase,
|
|
1568
|
+
'Lowercase': TypeIntrinsic.Lowercase,
|
|
1569
|
+
'Uncapitalize': TypeIntrinsic.Uncapitalize,
|
|
1570
|
+
};
|
|
1571
|
+
const intrinsic = mapping[name];
|
|
1572
|
+
if (intrinsic === undefined) {
|
|
1573
|
+
program.pushOp(ReflectionOp.never);
|
|
1574
|
+
break;
|
|
1575
|
+
}
|
|
1576
|
+
this.extractPackStructOfTypeReference(T.name, program);
|
|
1577
|
+
program.pushOp(ReflectionOp.intrinsic, Number(intrinsic));
|
|
1578
|
+
break;
|
|
1579
|
+
}
|
|
1580
|
+
default: {
|
|
1581
|
+
program.pushOp(ReflectionOp.never);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
getGlobalLibs() {
|
|
1586
|
+
if (this.cache.globalSourceFiles)
|
|
1587
|
+
return this.cache.globalSourceFiles;
|
|
1588
|
+
this.cache.globalSourceFiles = [];
|
|
1589
|
+
//todo also read compiler options "types" + typeRoot
|
|
1590
|
+
//currently knownLibFilesForCompilerOptions from @typescript/vfs doesn't return correct lib files for esnext,
|
|
1591
|
+
//so we switch here to es2022 if bigger than es2022.
|
|
1592
|
+
const options = { ...this.compilerOptions };
|
|
1593
|
+
if (options.target && (options.target === ScriptTarget.ESNext)) {
|
|
1594
|
+
options.target = ScriptTarget.ES2022;
|
|
1595
|
+
}
|
|
1596
|
+
const libs = knownLibFilesForCompilerOptions(options, ts);
|
|
1597
|
+
for (const lib of libs) {
|
|
1598
|
+
if (this.isExcluded(lib))
|
|
1599
|
+
continue;
|
|
1600
|
+
const sourceFile = this.resolver.resolveSourceFile(this.sourceFile, this.f.createStringLiteral('typescript/lib/' + lib.replace('.d.ts', '')));
|
|
1601
|
+
if (!sourceFile)
|
|
1602
|
+
continue;
|
|
1603
|
+
this.cache.globalSourceFiles.push(sourceFile);
|
|
1604
|
+
}
|
|
1605
|
+
return this.cache.globalSourceFiles;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* This is a custom resolver based on populated `locals` from the binder. It uses a custom resolution algorithm since
|
|
1609
|
+
* we have no access to the binder/TypeChecker directly and instantiating a TypeChecker per file/transformer is incredible slow.
|
|
1610
|
+
*/
|
|
1611
|
+
resolveDeclaration(typeName) {
|
|
1612
|
+
let current = typeName.parent;
|
|
1613
|
+
if (typeName.kind === SyntaxKind.QualifiedName)
|
|
1614
|
+
return; //namespace access not supported yet, e.g. type a = Namespace.X;
|
|
1615
|
+
let declaration = undefined;
|
|
1616
|
+
while (current) {
|
|
1617
|
+
if (isNodeWithLocals(current) && current.locals) {
|
|
1618
|
+
const found = current.locals.get(typeName.escapedText);
|
|
1619
|
+
if (found && found.declarations && found.declarations[0]) {
|
|
1620
|
+
/**
|
|
1621
|
+
* Discard parameters, since they can not be referenced from inside
|
|
1622
|
+
*
|
|
1623
|
+
* ```typescript
|
|
1624
|
+
* type B = string;
|
|
1625
|
+
* function a(B: B) {}
|
|
1626
|
+
*
|
|
1627
|
+
* class A {
|
|
1628
|
+
* constructor(B: B) {}
|
|
1629
|
+
* }
|
|
1630
|
+
* ```
|
|
1631
|
+
*
|
|
1632
|
+
*/
|
|
1633
|
+
if (!isParameter(found.declarations[0])) {
|
|
1634
|
+
declaration = found.declarations[0];
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (current.kind === SyntaxKind.SourceFile)
|
|
1640
|
+
break;
|
|
1641
|
+
current = current.parent;
|
|
1642
|
+
}
|
|
1643
|
+
if (!declaration) {
|
|
1644
|
+
// look in globals, read through all files, see checker.ts initializeTypeChecker
|
|
1645
|
+
for (const file of this.getGlobalLibs()) {
|
|
1646
|
+
const globals = getGlobalsOfSourceFile(file);
|
|
1647
|
+
if (!globals)
|
|
1648
|
+
continue;
|
|
1649
|
+
const symbol = globals.get(typeName.escapedText);
|
|
1650
|
+
if (symbol && symbol.declarations && symbol.declarations[0]) {
|
|
1651
|
+
declaration = symbol.declarations[0];
|
|
1652
|
+
// console.log('found global', typeName.escapedText, 'in', file.fileName);
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
let importDeclaration = undefined;
|
|
1658
|
+
let typeOnly = false;
|
|
1659
|
+
if (declaration && isImportSpecifier(declaration)) {
|
|
1660
|
+
if (declaration.isTypeOnly)
|
|
1661
|
+
typeOnly = true;
|
|
1662
|
+
importDeclaration = declaration.parent.parent.parent;
|
|
1663
|
+
}
|
|
1664
|
+
else if (declaration && isImportDeclaration(declaration)) {
|
|
1665
|
+
// declaration = this.resolveImportSpecifier(typeName.escapedText, declaration);
|
|
1666
|
+
importDeclaration = declaration;
|
|
1667
|
+
}
|
|
1668
|
+
else if (declaration && isImportClause(declaration)) {
|
|
1669
|
+
importDeclaration = declaration.parent;
|
|
1670
|
+
}
|
|
1671
|
+
if (importDeclaration) {
|
|
1672
|
+
if (importDeclaration.importClause && importDeclaration.importClause.isTypeOnly)
|
|
1673
|
+
typeOnly = true;
|
|
1674
|
+
declaration = this.resolveImportSpecifier(getEscapedText(typeName), importDeclaration, this.sourceFile);
|
|
1675
|
+
}
|
|
1676
|
+
if (declaration && declaration.kind === SyntaxKind.TypeParameter && declaration.parent.kind === SyntaxKind.TypeAliasDeclaration) {
|
|
1677
|
+
//for alias like `type MyAlias<T> = T`, `T` is returned from `typeChecker.getDeclaredTypeOfSymbol(symbol)`.
|
|
1678
|
+
declaration = declaration.parent;
|
|
1679
|
+
}
|
|
1680
|
+
if (!declaration)
|
|
1681
|
+
return;
|
|
1682
|
+
return { declaration, importDeclaration, typeOnly };
|
|
1683
|
+
}
|
|
1684
|
+
getDeclarationVariableName(typeName) {
|
|
1685
|
+
if (isIdentifier(typeName)) {
|
|
1686
|
+
return this.f.createIdentifier('__Ω' + getIdentifierName(typeName));
|
|
1687
|
+
}
|
|
1688
|
+
function joinQualifiedName(name) {
|
|
1689
|
+
if (isIdentifier(name))
|
|
1690
|
+
return getIdentifierName(name);
|
|
1691
|
+
return joinQualifiedName(name.left) + '_' + getIdentifierName(name.right);
|
|
1692
|
+
}
|
|
1693
|
+
return this.f.createIdentifier('__Ω' + joinQualifiedName(typeName));
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* The semantic of isExcluded is different from checking if the fileName is part
|
|
1697
|
+
* of reflection config option. isExcluded checks if the file should be excluded
|
|
1698
|
+
* via the exclude option. mainly used to exclude globals and external libraries.
|
|
1699
|
+
*/
|
|
1700
|
+
isExcluded(fileName) {
|
|
1701
|
+
// getConfigResolver depends on the current source file, so we know the "exclude" option from deepkit config
|
|
1702
|
+
const resolver = this.overriddenConfigResolver || getConfigResolver(this.cache.resolver, this.parseConfigHost, this.compilerOptions, this.sourceFile);
|
|
1703
|
+
const res = reflectionModeMatcher({ reflection: 'default', exclude: resolver.config.exclude }, fileName);
|
|
1704
|
+
return res === 'never';
|
|
1705
|
+
}
|
|
1706
|
+
extractPackStructOfTypeReference(type, program) {
|
|
1707
|
+
const typeName = isIdentifier(type)
|
|
1708
|
+
? type
|
|
1709
|
+
: isTypeReferenceNode(type)
|
|
1710
|
+
? type.typeName
|
|
1711
|
+
: (isIdentifier(type.expression) ? type.expression : undefined);
|
|
1712
|
+
const typeArguments = isTypeReferenceNode(type) || isExpressionWithTypeArguments(type) ? type.typeArguments : undefined;
|
|
1713
|
+
if (!typeName) {
|
|
1714
|
+
program.pushOp(ReflectionOp.any);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
if (isIdentifier(typeName) && getIdentifierName(typeName) === 'InlineRuntimeType' && typeArguments && typeArguments[0] && isTypeQueryNode(typeArguments[0])) {
|
|
1718
|
+
const expression = serializeEntityNameAsExpression(this.f, typeArguments[0].exprName);
|
|
1719
|
+
program.pushOp(ReflectionOp.arg, program.pushStack(expression));
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
if (isIdentifier(typeName) && getIdentifierName(typeName) !== 'constructor' && this.knownClasses[getIdentifierName(typeName)]) {
|
|
1723
|
+
const name = getIdentifierName(typeName);
|
|
1724
|
+
const op = this.knownClasses[name];
|
|
1725
|
+
program.pushOp(op);
|
|
1726
|
+
}
|
|
1727
|
+
else if (isIdentifier(typeName) && getIdentifierName(typeName) === 'Promise') {
|
|
1728
|
+
//promise has always one sub type
|
|
1729
|
+
if (typeArguments && typeArguments[0]) {
|
|
1730
|
+
this.extractPackStructOfType(typeArguments[0], program);
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
program.pushOp(ReflectionOp.any);
|
|
1734
|
+
}
|
|
1735
|
+
program.pushOp(ReflectionOp.promise);
|
|
1736
|
+
}
|
|
1737
|
+
else if (isIdentifier(typeName) && getIdentifierName(typeName) === 'integer') {
|
|
1738
|
+
program.pushOp(ReflectionOp.numberBrand, TypeNumberBrand.integer);
|
|
1739
|
+
}
|
|
1740
|
+
else if (isIdentifier(typeName) && getIdentifierName(typeName) !== 'constructor' && TypeNumberBrand[getIdentifierName(typeName)] !== undefined) {
|
|
1741
|
+
program.pushOp(ReflectionOp.numberBrand, TypeNumberBrand[getIdentifierName(typeName)]);
|
|
1742
|
+
}
|
|
1743
|
+
else {
|
|
1744
|
+
//check if it references a variable
|
|
1745
|
+
if (isIdentifier(typeName)) {
|
|
1746
|
+
const variable = program.findVariable(getIdentifierName(typeName));
|
|
1747
|
+
if (variable) {
|
|
1748
|
+
program.pushOp(ReflectionOp.loads, variable.frameOffset, variable.stackIndex);
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
else if (isInferTypeNode(typeName)) {
|
|
1753
|
+
this.extractPackStructOfType(typeName, program);
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
const resolved = this.resolveDeclaration(typeName);
|
|
1757
|
+
if (!resolved) {
|
|
1758
|
+
//maybe reference to enum
|
|
1759
|
+
if (isQualifiedName(typeName)) {
|
|
1760
|
+
if (isIdentifier(typeName.left)) {
|
|
1761
|
+
const resolved = this.resolveDeclaration(typeName.left);
|
|
1762
|
+
if (resolved && isEnumDeclaration(resolved.declaration)) {
|
|
1763
|
+
let lastExpression;
|
|
1764
|
+
let indexValue = 0;
|
|
1765
|
+
for (const member of resolved.declaration.members) {
|
|
1766
|
+
if (getNameAsString(member.name) === getNameAsString(typeName.right)) {
|
|
1767
|
+
if (member.initializer) {
|
|
1768
|
+
program.pushOp(ReflectionOp.arg, program.pushStack(this.nodeConverter.toExpression(member.initializer)));
|
|
1769
|
+
}
|
|
1770
|
+
else if (lastExpression) {
|
|
1771
|
+
const exp = this.nodeConverter.toExpression(lastExpression);
|
|
1772
|
+
program.pushOp(ReflectionOp.arg, program.pushStack(this.f.createBinaryExpression(exp, SyntaxKind.PlusToken, this.nodeConverter.toExpression(indexValue))));
|
|
1773
|
+
}
|
|
1774
|
+
else {
|
|
1775
|
+
program.pushOp(ReflectionOp.arg, program.pushStack(this.nodeConverter.toExpression(indexValue)));
|
|
1776
|
+
}
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
else {
|
|
1780
|
+
indexValue++;
|
|
1781
|
+
if (member.initializer) {
|
|
1782
|
+
lastExpression = member.initializer;
|
|
1783
|
+
//restart index
|
|
1784
|
+
indexValue = 0;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
//non-existing references are ignored.
|
|
1792
|
+
program.pushOp(ReflectionOp.never);
|
|
1793
|
+
debug2(`Could not resolve ${getNameAsString(typeName)} in ${program.sourceFile?.fileName || 'intrinsic'}`);
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
let declaration = resolved.declaration;
|
|
1797
|
+
const declarationSourceFile = findSourceFile(declaration);
|
|
1798
|
+
// if (!declarationSourceFile) {
|
|
1799
|
+
// program.pushOp(ReflectionOp.never);
|
|
1800
|
+
// debug2(`Could not find source file for ${getNameAsString(typeName)} in ${program.sourceFile.fileName}`);
|
|
1801
|
+
// return;
|
|
1802
|
+
// }
|
|
1803
|
+
const isGlobal = !declarationSourceFile || (resolved.importDeclaration === undefined && declarationSourceFile.fileName !== this.sourceFile.fileName);
|
|
1804
|
+
const isFromImport = resolved.importDeclaration !== undefined;
|
|
1805
|
+
if (isVariableDeclaration(declaration)) {
|
|
1806
|
+
if (declaration.type) {
|
|
1807
|
+
declaration = declaration.type;
|
|
1808
|
+
}
|
|
1809
|
+
else if (declaration.initializer) {
|
|
1810
|
+
declaration = declaration.initializer;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (isModuleDeclaration(declaration) && resolved.importDeclaration) {
|
|
1814
|
+
if (isIdentifier(typeName))
|
|
1815
|
+
ensureImportIsEmitted(resolved.importDeclaration, typeName);
|
|
1816
|
+
//we can not infer from module declaration, so do `typeof T` in runtime
|
|
1817
|
+
program.pushOp(ReflectionOp.typeof, program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, serializeEntityNameAsExpression(this.f, typeName))));
|
|
1818
|
+
}
|
|
1819
|
+
else if (isTypeAliasDeclaration(declaration) || isInterfaceDeclaration(declaration) || isEnumDeclaration(declaration)) {
|
|
1820
|
+
//Set/Map are interface declarations
|
|
1821
|
+
const name = getNameAsString(typeName);
|
|
1822
|
+
if (name === 'Array') {
|
|
1823
|
+
if (typeArguments && typeArguments[0]) {
|
|
1824
|
+
this.extractPackStructOfType(typeArguments[0], program);
|
|
1825
|
+
}
|
|
1826
|
+
else {
|
|
1827
|
+
program.pushOp(ReflectionOp.any);
|
|
1828
|
+
}
|
|
1829
|
+
program.pushOp(ReflectionOp.array);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
else if (name === 'Function') {
|
|
1833
|
+
program.pushFrame();
|
|
1834
|
+
const index = program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, this.f.createIdentifier('Function')));
|
|
1835
|
+
program.pushOp(ReflectionOp.functionReference, index);
|
|
1836
|
+
program.popFrameImplicit();
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
else if (name === 'Set') {
|
|
1840
|
+
if (typeArguments && typeArguments[0]) {
|
|
1841
|
+
this.extractPackStructOfType(typeArguments[0], program);
|
|
1842
|
+
}
|
|
1843
|
+
else {
|
|
1844
|
+
program.pushOp(ReflectionOp.any);
|
|
1845
|
+
}
|
|
1846
|
+
program.pushOp(ReflectionOp.set);
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
else if (name === 'Map') {
|
|
1850
|
+
if (typeArguments && typeArguments[0]) {
|
|
1851
|
+
this.extractPackStructOfType(typeArguments[0], program);
|
|
1852
|
+
}
|
|
1853
|
+
else {
|
|
1854
|
+
program.pushOp(ReflectionOp.any);
|
|
1855
|
+
}
|
|
1856
|
+
if (typeArguments && typeArguments[1]) {
|
|
1857
|
+
this.extractPackStructOfType(typeArguments[1], program);
|
|
1858
|
+
}
|
|
1859
|
+
else {
|
|
1860
|
+
program.pushOp(ReflectionOp.any);
|
|
1861
|
+
}
|
|
1862
|
+
program.pushOp(ReflectionOp.map);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const runtimeTypeName = this.getDeclarationVariableName(typeName);
|
|
1866
|
+
//to break recursion, we track which declaration has already been compiled
|
|
1867
|
+
if (!this.compiledDeclarations.has(declaration) && !this.compileDeclarations.has(declaration)) {
|
|
1868
|
+
if (declarationSourceFile && this.isExcluded(declarationSourceFile.fileName)) {
|
|
1869
|
+
program.pushOp(ReflectionOp.any);
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (isGlobal) {
|
|
1873
|
+
//we don't embed non-global imported declarations anymore, only globals
|
|
1874
|
+
this.embedDeclarations.set(declaration, {
|
|
1875
|
+
name: typeName,
|
|
1876
|
+
sourceFile: declarationSourceFile,
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
else if (isFromImport) {
|
|
1880
|
+
if (resolved.importDeclaration) {
|
|
1881
|
+
//if explicit `import {type T}`, we do not emit an import and instead push any
|
|
1882
|
+
if (resolved.typeOnly) {
|
|
1883
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
// debug('import', getNameAsString(typeName), 'from',
|
|
1887
|
+
// (resolved.importDeclaration.moduleSpecifier as StringLiteral).text, ' in', program.sourceFile.fileName);
|
|
1888
|
+
// Previously we checked for tsconfig.json/package.json with a "reflection" option.
|
|
1889
|
+
// This is now changed, and we look directly if there is a __Ω{name} exported.
|
|
1890
|
+
// If so, then we can be 100% sure that the referenced module is built with runtime types.
|
|
1891
|
+
// Note that if `found` is a TypeScript file (not d.ts), then we need to check using the fileName
|
|
1892
|
+
// since it is part of the current transpilation phase. Thus, it depends on the
|
|
1893
|
+
// current config + @reflection decorator instead.
|
|
1894
|
+
if (declarationSourceFile.fileName.endsWith('.d.ts')) {
|
|
1895
|
+
// Note that if import was something like `import { XY } from 'my-module'` then resolve()
|
|
1896
|
+
// returns the index.d.ts file of the module, not the actual file where XY is exported.
|
|
1897
|
+
// this is necessary since we emit an additional import `import { __ΩXY } from 'my-module'`,
|
|
1898
|
+
// so we check if whatever file we get from resolve() actually exports __ΩXY.
|
|
1899
|
+
const resolverDecVariable = this.resolveImportSpecifier(getEscapedText(runtimeTypeName), resolved.importDeclaration, this.sourceFile);
|
|
1900
|
+
if (!resolverDecVariable) {
|
|
1901
|
+
debug2(`Symbol ${runtimeTypeName.escapedText} not found in ${declarationSourceFile.fileName}`);
|
|
1902
|
+
//no __Ω{name} exported, so we can not be sure if the module is built with runtime types
|
|
1903
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
this.addImports.push({ identifier: runtimeTypeName, importDeclaration: resolved.importDeclaration });
|
|
1907
|
+
}
|
|
1908
|
+
else {
|
|
1909
|
+
const reflection = this.getReflectionConfig(declarationSourceFile);
|
|
1910
|
+
// if this is never, then its generally disabled for this file
|
|
1911
|
+
if (reflection.mode === 'never') {
|
|
1912
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
const declarationReflection = this.isWithReflection(declarationSourceFile, declaration);
|
|
1916
|
+
if (!declarationReflection) {
|
|
1917
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
this.addImports.push({ identifier: runtimeTypeName, importDeclaration: resolved.importDeclaration });
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
else {
|
|
1925
|
+
//it's a reference type inside the same file. Make sure its type is reflected
|
|
1926
|
+
const reflection = this.isWithReflection(program.sourceFile, declaration);
|
|
1927
|
+
if (!reflection) {
|
|
1928
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
this.compileDeclarations.set(declaration, {
|
|
1932
|
+
name: typeName,
|
|
1933
|
+
sourceFile: declarationSourceFile,
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
const index = program.pushStack(program.forNode === declaration ? 0 : this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, runtimeTypeName));
|
|
1938
|
+
if (typeArguments) {
|
|
1939
|
+
for (const argument of typeArguments) {
|
|
1940
|
+
this.extractPackStructOfType(argument, program);
|
|
1941
|
+
}
|
|
1942
|
+
program.pushOp(ReflectionOp.inlineCall, index, typeArguments.length);
|
|
1943
|
+
}
|
|
1944
|
+
else {
|
|
1945
|
+
program.pushOp(ReflectionOp.inline, index);
|
|
1946
|
+
}
|
|
1947
|
+
// if (typeArguments) {
|
|
1948
|
+
// for (const argument of typeArguments) {
|
|
1949
|
+
// this.extractPackStructOfType(argument, program);
|
|
1950
|
+
// }
|
|
1951
|
+
// program.pushOp(ReflectionOp.inlineCall, index, typeArguments.length);
|
|
1952
|
+
// } else {
|
|
1953
|
+
// program.pushOp(ReflectionOp.inline, index);
|
|
1954
|
+
// }
|
|
1955
|
+
// } else if (isTypeLiteralNode(declaration)) {
|
|
1956
|
+
// this.extractPackStructOfType(declaration, program);
|
|
1957
|
+
// return;
|
|
1958
|
+
// } else if (isMappedTypeNode(declaration)) {
|
|
1959
|
+
// //<Type>{[Property in keyof Type]: boolean;};
|
|
1960
|
+
// this.extractPackStructOfType(declaration, program);
|
|
1961
|
+
// return;
|
|
1962
|
+
}
|
|
1963
|
+
else if (isClassDeclaration(declaration) || isFunctionDeclaration(declaration) || isFunctionExpression(declaration) || isArrowFunction(declaration)) {
|
|
1964
|
+
// classes, functions and arrow functions are handled differently, since they exist in runtime.
|
|
1965
|
+
//if explicit `import {type T}`, we do not emit an import and instead push any
|
|
1966
|
+
if (resolved.typeOnly) {
|
|
1967
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
// If a function/class declarations comes from a built library (e.g. node_modules), then we
|
|
1971
|
+
// declarationSourceFile is a d.ts file. We do know if they are built in runtime by checking `xy.__type`.
|
|
1972
|
+
// Otherwise, check if the file will be built with runtime types.
|
|
1973
|
+
const reflection = declarationSourceFile?.fileName.endsWith('.d.ts') || this.isWithReflection(program.sourceFile, declaration);
|
|
1974
|
+
if (!reflection) {
|
|
1975
|
+
this.resolveTypeOnlyImport(typeName, program);
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
if (resolved.importDeclaration && isIdentifier(typeName))
|
|
1979
|
+
ensureImportIsEmitted(resolved.importDeclaration, typeName);
|
|
1980
|
+
program.pushFrame();
|
|
1981
|
+
if (typeArguments) {
|
|
1982
|
+
for (const typeArgument of typeArguments) {
|
|
1983
|
+
this.extractPackStructOfType(typeArgument, program);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
const body = isIdentifier(typeName) ? typeName : this.createAccessorForEntityName(typeName);
|
|
1987
|
+
const index = program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, body));
|
|
1988
|
+
program.pushOp(isClassDeclaration(declaration) ? ReflectionOp.classReference : ReflectionOp.functionReference, index);
|
|
1989
|
+
program.popFrameImplicit();
|
|
1990
|
+
}
|
|
1991
|
+
else if (isTypeParameterDeclaration(declaration)) {
|
|
1992
|
+
this.resolveTypeParameter(declaration, type, program);
|
|
1993
|
+
}
|
|
1994
|
+
else {
|
|
1995
|
+
this.extractPackStructOfType(declaration, program);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Returns the class declaration, function/arrow declaration, or block where type was used.
|
|
2001
|
+
*/
|
|
2002
|
+
getTypeUser(type) {
|
|
2003
|
+
let current = type;
|
|
2004
|
+
while (current) {
|
|
2005
|
+
if (current.kind === SyntaxKind.Block)
|
|
2006
|
+
return current; //return the block
|
|
2007
|
+
if (current.kind === SyntaxKind.ClassDeclaration)
|
|
2008
|
+
return current; //return the class
|
|
2009
|
+
if (current.kind === SyntaxKind.ClassExpression)
|
|
2010
|
+
return current; //return the class
|
|
2011
|
+
if (current.kind === SyntaxKind.Constructor)
|
|
2012
|
+
return current.parent; //return the class
|
|
2013
|
+
if (current.kind === SyntaxKind.MethodDeclaration)
|
|
2014
|
+
return current.parent; //return the class
|
|
2015
|
+
if (current.kind === SyntaxKind.ArrowFunction || current.kind === SyntaxKind.FunctionDeclaration || current.kind === SyntaxKind.FunctionExpression)
|
|
2016
|
+
return current;
|
|
2017
|
+
current = current.parent;
|
|
2018
|
+
}
|
|
2019
|
+
return current;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* With this function we want to check if `type` is used in the signature itself from the parent of `declaration`.
|
|
2023
|
+
* If so, we do not try to infer the type from runtime values.
|
|
2024
|
+
*
|
|
2025
|
+
* Examples where we do not infer from runtime, `type` being `T` and `declaration` being `<T>` (return false):
|
|
2026
|
+
*
|
|
2027
|
+
* ```typescript
|
|
2028
|
+
* class User<T> {
|
|
2029
|
+
* config: T;
|
|
2030
|
+
* }
|
|
2031
|
+
*
|
|
2032
|
+
* class User<T> {
|
|
2033
|
+
* constructor(public config: T) {}
|
|
2034
|
+
* }
|
|
2035
|
+
*
|
|
2036
|
+
* function do<T>(item: T): void {}
|
|
2037
|
+
* function do<T>(item: T): T {}
|
|
2038
|
+
* ```
|
|
2039
|
+
*
|
|
2040
|
+
* Examples where we infer from runtime (return true):
|
|
2041
|
+
*
|
|
2042
|
+
* ```typescript
|
|
2043
|
+
* function do<T>(item: T) {
|
|
2044
|
+
* return typeOf<T>; //<-- because of that
|
|
2045
|
+
* }
|
|
2046
|
+
*
|
|
2047
|
+
* function do<T>(item: T) {
|
|
2048
|
+
* class A {
|
|
2049
|
+
* config: T; //<-- because of that
|
|
2050
|
+
* }
|
|
2051
|
+
* return A;
|
|
2052
|
+
* }
|
|
2053
|
+
*
|
|
2054
|
+
* function do<T>(item: T) {
|
|
2055
|
+
* class A {
|
|
2056
|
+
* doIt() {
|
|
2057
|
+
* class B {
|
|
2058
|
+
* config: T; //<-- because of that
|
|
2059
|
+
* }
|
|
2060
|
+
* return B;
|
|
2061
|
+
* }
|
|
2062
|
+
* }
|
|
2063
|
+
* return A;
|
|
2064
|
+
* }
|
|
2065
|
+
*
|
|
2066
|
+
* function do<T>(item: T) {
|
|
2067
|
+
* class A {
|
|
2068
|
+
* doIt(): T { //<-- because of that
|
|
2069
|
+
* }
|
|
2070
|
+
* }
|
|
2071
|
+
* return A;
|
|
2072
|
+
* }
|
|
2073
|
+
* ```
|
|
2074
|
+
*/
|
|
2075
|
+
needsToBeInferred(declaration, type) {
|
|
2076
|
+
const declarationUser = this.getTypeUser(declaration);
|
|
2077
|
+
const typeUser = this.getTypeUser(type);
|
|
2078
|
+
return declarationUser !== typeUser;
|
|
2079
|
+
}
|
|
2080
|
+
resolveTypeOnlyImport(entityName, program) {
|
|
2081
|
+
program.pushOp(ReflectionOp.any);
|
|
2082
|
+
const typeName = ts.isIdentifier(entityName)
|
|
2083
|
+
? getIdentifierName(entityName)
|
|
2084
|
+
: getIdentifierName(entityName.right);
|
|
2085
|
+
this.resolveTypeName(typeName, program);
|
|
2086
|
+
}
|
|
2087
|
+
resolveTypeName(typeName, program) {
|
|
2088
|
+
if (!typeName)
|
|
2089
|
+
return;
|
|
2090
|
+
program.pushOp(ReflectionOp.typeName, program.findOrAddStackEntry(typeName));
|
|
2091
|
+
}
|
|
2092
|
+
resolveTypeParameter(declaration, type, program) {
|
|
2093
|
+
//check if `type` was used in an expression. if so, we need to resolve it from runtime, otherwise we mark it as T
|
|
2094
|
+
const isUsedInFunction = isFunctionLike(declaration.parent);
|
|
2095
|
+
const resolveRuntimeTypeParameter = (isUsedInFunction && program.isResolveFunctionParameters(declaration.parent)) || (this.needsToBeInferred(declaration, type));
|
|
2096
|
+
if (resolveRuntimeTypeParameter) {
|
|
2097
|
+
//go through all parameters and look where `type.name.escapedText` is used (recursively).
|
|
2098
|
+
//go through all found parameters and replace `T` with `infer T` and embed its type in `typeof parameter extends Type<infer T> ? T : never`, if T is not directly used
|
|
2099
|
+
const argumentName = declaration.name.escapedText; //T
|
|
2100
|
+
const foundUsers = [];
|
|
2101
|
+
if (isUsedInFunction) {
|
|
2102
|
+
for (const parameter of declaration.parent.parameters) {
|
|
2103
|
+
if (!parameter.type)
|
|
2104
|
+
continue;
|
|
2105
|
+
//if deeply available?
|
|
2106
|
+
let found = false;
|
|
2107
|
+
const searchArgument = (node) => {
|
|
2108
|
+
node = visitEachChild(node, searchArgument, this.context);
|
|
2109
|
+
if (isIdentifier(node) && node.escapedText === argumentName) {
|
|
2110
|
+
//transform to infer T
|
|
2111
|
+
found = true;
|
|
2112
|
+
node = this.f.createInferTypeNode(declaration);
|
|
2113
|
+
}
|
|
2114
|
+
return node;
|
|
2115
|
+
};
|
|
2116
|
+
if (isIdentifier(parameter.name)) {
|
|
2117
|
+
const updatedParameterType = visitEachChild(parameter.type, searchArgument, this.context);
|
|
2118
|
+
if (found) {
|
|
2119
|
+
foundUsers.push({ type: updatedParameterType, parameterName: parameter.name });
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
if (foundUsers.length) {
|
|
2125
|
+
//todo: if there are multiple infers, we need to create an intersection
|
|
2126
|
+
if (foundUsers.length > 1) {
|
|
2127
|
+
//todo: intersection start
|
|
2128
|
+
}
|
|
2129
|
+
const isReceiveType = foundUsers.find(v => isTypeReferenceNode(v.type) && isIdentifier(v.type.typeName) && getIdentifierName(v.type.typeName) === 'ReceiveType');
|
|
2130
|
+
if (isReceiveType) {
|
|
2131
|
+
// If it's used in ReceiveType<T>, then we can just use T directly without trying to infer it from ReceiveType<T> itself
|
|
2132
|
+
program.pushOp(ReflectionOp.inline, program.pushStack(isReceiveType.parameterName));
|
|
2133
|
+
}
|
|
2134
|
+
else {
|
|
2135
|
+
for (const foundUser of foundUsers) {
|
|
2136
|
+
program.pushConditionalFrame();
|
|
2137
|
+
program.pushOp(ReflectionOp.typeof, program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, foundUser.parameterName)));
|
|
2138
|
+
this.extractPackStructOfType(foundUser.type, program);
|
|
2139
|
+
program.pushOp(ReflectionOp.extends);
|
|
2140
|
+
const found = program.findVariable(getIdentifierName(declaration.name));
|
|
2141
|
+
if (found) {
|
|
2142
|
+
this.extractPackStructOfType(declaration.name, program);
|
|
2143
|
+
}
|
|
2144
|
+
else {
|
|
2145
|
+
//type parameter was never found in X of `Y extends X` (no `infer X` was created), probably due to a not supported parameter type expression.
|
|
2146
|
+
program.pushOp(ReflectionOp.any);
|
|
2147
|
+
}
|
|
2148
|
+
this.extractPackStructOfType({ kind: SyntaxKind.NeverKeyword }, program);
|
|
2149
|
+
program.pushOp(ReflectionOp.condition);
|
|
2150
|
+
program.popFrameImplicit();
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
if (foundUsers.length > 1) {
|
|
2154
|
+
//todo: intersection end
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
else if (declaration.constraint) {
|
|
2158
|
+
if (isUsedInFunction)
|
|
2159
|
+
program.resolveFunctionParametersIncrease(declaration.parent);
|
|
2160
|
+
const constraint = getEffectiveConstraintOfTypeParameter(declaration);
|
|
2161
|
+
if (constraint) {
|
|
2162
|
+
this.extractPackStructOfType(constraint, program);
|
|
2163
|
+
}
|
|
2164
|
+
else {
|
|
2165
|
+
program.pushOp(ReflectionOp.never);
|
|
2166
|
+
}
|
|
2167
|
+
if (isUsedInFunction)
|
|
2168
|
+
program.resolveFunctionParametersDecrease(declaration.parent);
|
|
2169
|
+
}
|
|
2170
|
+
else {
|
|
2171
|
+
program.pushOp(ReflectionOp.never);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
else {
|
|
2175
|
+
program.pushOp(ReflectionOp.any);
|
|
2176
|
+
// program.pushOp(ReflectionOp.typeParameter, program.findOrAddStackEntry(getNameAsString(typeName)));
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
createAccessorForEntityName(e) {
|
|
2180
|
+
return this.f.createPropertyAccessExpression(isIdentifier(e.left) ? e.left : this.createAccessorForEntityName(e.left), e.right);
|
|
2181
|
+
}
|
|
2182
|
+
findDeclarationInFile(sourceFile, declarationName) {
|
|
2183
|
+
if (isNodeWithLocals(sourceFile) && sourceFile.locals) {
|
|
2184
|
+
const declarationSymbol = sourceFile.locals.get(declarationName);
|
|
2185
|
+
if (declarationSymbol && declarationSymbol.declarations && declarationSymbol.declarations[0]) {
|
|
2186
|
+
return declarationSymbol.declarations[0];
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
resolveImportSpecifier(_declarationName, importOrExport, sourceFile) {
|
|
2192
|
+
const declarationName = 'string' === typeof _declarationName ? _declarationName : getIdentifierName(_declarationName);
|
|
2193
|
+
if (!importOrExport.moduleSpecifier || !isStringLiteral(importOrExport.moduleSpecifier)) {
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
const source = this.resolver.resolve(sourceFile, importOrExport);
|
|
2197
|
+
if (!source) {
|
|
2198
|
+
debug('module not found', importOrExport.moduleSpecifier.text, 'Is transpileOnly enabled? It needs to be disabled.');
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
const declaration = this.findDeclarationInFile(source, declarationName);
|
|
2202
|
+
sourceFile = source;
|
|
2203
|
+
/**
|
|
2204
|
+
* declaration could also be `import {PrimaryKey} from 'xy'`, which we want to skip
|
|
2205
|
+
*/
|
|
2206
|
+
if (declaration && !isImportSpecifier(declaration)) {
|
|
2207
|
+
//if `export {PrimaryKey} from 'xy'`, then follow xy
|
|
2208
|
+
if (isExportDeclaration(declaration)) {
|
|
2209
|
+
return this.followExport(declarationName, declaration, sourceFile);
|
|
2210
|
+
}
|
|
2211
|
+
return declaration;
|
|
2212
|
+
}
|
|
2213
|
+
//not found, look in exports
|
|
2214
|
+
if (isSourceFile(sourceFile)) {
|
|
2215
|
+
for (const statement of sourceFile.statements) {
|
|
2216
|
+
if (!isExportDeclaration(statement))
|
|
2217
|
+
continue;
|
|
2218
|
+
const found = this.followExport(declarationName, statement, sourceFile);
|
|
2219
|
+
if (found)
|
|
2220
|
+
return found;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
followExport(declarationName, statement, sourceFile) {
|
|
2226
|
+
if (statement.exportClause) {
|
|
2227
|
+
//export {y} from 'x'
|
|
2228
|
+
if (isNamedExports(statement.exportClause)) {
|
|
2229
|
+
for (const element of statement.exportClause.elements) {
|
|
2230
|
+
//see if declarationName is exported
|
|
2231
|
+
if (getEscapedText(element.name) === declarationName) {
|
|
2232
|
+
if (!statement.moduleSpecifier || !isStringLiteral(statement.moduleSpecifier)) {
|
|
2233
|
+
// it's `export {Class}` and Class is either a Declaration or ImportSpecifier
|
|
2234
|
+
if (!statement.moduleSpecifier || !isStringLiteral(statement.moduleSpecifier)) {
|
|
2235
|
+
// it's `export {Class};` and Class is either a Declaration or ImportSpecifier
|
|
2236
|
+
if (isNodeWithLocals(sourceFile) && sourceFile.locals) {
|
|
2237
|
+
const found = sourceFile.locals.get(declarationName);
|
|
2238
|
+
if (found && found.declarations && found.declarations[0]) {
|
|
2239
|
+
const declaration = found.declarations[0];
|
|
2240
|
+
if (declaration && isImportSpecifier(declaration)) {
|
|
2241
|
+
const importOrExport = declaration.parent.parent.parent;
|
|
2242
|
+
const found = this.resolveImportSpecifier(element.propertyName ? getEscapedText(element.propertyName) : declarationName, importOrExport, sourceFile);
|
|
2243
|
+
if (found)
|
|
2244
|
+
return found;
|
|
2245
|
+
}
|
|
2246
|
+
else if (declaration) {
|
|
2247
|
+
}
|
|
2248
|
+
return declaration;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
else {
|
|
2254
|
+
// it's `export {Class} from 'x'`
|
|
2255
|
+
const found = this.resolveImportSpecifier(element.propertyName ? getEscapedText(element.propertyName) : declarationName, statement, sourceFile);
|
|
2256
|
+
if (found)
|
|
2257
|
+
return found;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
else {
|
|
2264
|
+
//export * from 'x'
|
|
2265
|
+
//see if `x` exports declarationName (or one of its exports * from 'y')
|
|
2266
|
+
const found = this.resolveImportSpecifier(declarationName, statement, sourceFile);
|
|
2267
|
+
if (found) {
|
|
2268
|
+
return found;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
getTypeOfType(type) {
|
|
2274
|
+
const reflection = this.isWithReflection(this.sourceFile, type);
|
|
2275
|
+
if (!reflection)
|
|
2276
|
+
return;
|
|
2277
|
+
const program = new CompilerProgram(type, this.sourceFile);
|
|
2278
|
+
this.extractPackStructOfType(type, program);
|
|
2279
|
+
return this.packOpsAndStack(program);
|
|
2280
|
+
}
|
|
2281
|
+
packOpsAndStack(program) {
|
|
2282
|
+
const packStruct = program.buildPackStruct();
|
|
2283
|
+
if (packStruct.ops.length === 0)
|
|
2284
|
+
return;
|
|
2285
|
+
// debugPackStruct(this.sourceFile, program.forNode, packStruct);
|
|
2286
|
+
const packed = [...packStruct.stack, encodeOps(packStruct.ops)];
|
|
2287
|
+
return this.valueToExpression(packed);
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Note: We have to duplicate the expressions as it can be that incoming expression are from another file and contain wrong pos/end properties,
|
|
2291
|
+
* so the code generation is then broken when we simply reuse them. Wrong code like ``User.__type = [.toEqual({`` is then generated.
|
|
2292
|
+
* This function is probably not complete, but we add new copies when required.
|
|
2293
|
+
*/
|
|
2294
|
+
valueToExpression(value) {
|
|
2295
|
+
return this.nodeConverter.toExpression(value);
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* A class is decorated with type information by adding a static variable.
|
|
2299
|
+
*
|
|
2300
|
+
* class Model {
|
|
2301
|
+
* static __types = pack(ReflectionOp.string); //<-- encoded type information
|
|
2302
|
+
* title: string;
|
|
2303
|
+
* }
|
|
2304
|
+
*/
|
|
2305
|
+
decorateClass(sourceFile, node) {
|
|
2306
|
+
const reflection = this.isWithReflection(sourceFile, node);
|
|
2307
|
+
if (!reflection) {
|
|
2308
|
+
return node;
|
|
2309
|
+
}
|
|
2310
|
+
const type = this.getTypeOfType(node);
|
|
2311
|
+
const __type = this.f.createPropertyDeclaration(this.f.createModifiersFromModifierFlags(ModifierFlags.Static), '__type', undefined, undefined, type);
|
|
2312
|
+
if (isClassDeclaration(node)) {
|
|
2313
|
+
// return node;
|
|
2314
|
+
return this.f.updateClassDeclaration(node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, this.f.createNodeArray([...node.members, __type]));
|
|
2315
|
+
}
|
|
2316
|
+
return this.f.updateClassExpression(node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, this.f.createNodeArray([...node.members, __type]));
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* const fn = function() {}
|
|
2320
|
+
*
|
|
2321
|
+
* => const fn = __assignType(function() {}, [34])
|
|
2322
|
+
*/
|
|
2323
|
+
decorateFunctionExpression(expression) {
|
|
2324
|
+
const encodedType = this.getTypeOfType(expression);
|
|
2325
|
+
if (!encodedType)
|
|
2326
|
+
return expression;
|
|
2327
|
+
return this.wrapWithAssignType(expression, encodedType);
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* function name() {}
|
|
2331
|
+
*
|
|
2332
|
+
* => function name() {}; name.__type = 34;
|
|
2333
|
+
*/
|
|
2334
|
+
decorateFunctionDeclaration(declaration) {
|
|
2335
|
+
const encodedType = this.getTypeOfType(declaration);
|
|
2336
|
+
if (!encodedType)
|
|
2337
|
+
return declaration;
|
|
2338
|
+
if (!declaration.name) {
|
|
2339
|
+
//its likely `export default function() {}`
|
|
2340
|
+
if (!declaration.body)
|
|
2341
|
+
return;
|
|
2342
|
+
//since a new default export is created, we do not need ExportKey&DefaultKeyword on the function anymore,
|
|
2343
|
+
//but it should preserve all others like Async.
|
|
2344
|
+
const modifier = declaration.modifiers
|
|
2345
|
+
? declaration.modifiers.filter(v => v.kind !== SyntaxKind.ExportKeyword && v.kind !== SyntaxKind.DefaultKeyword && v.kind !== SyntaxKind.Decorator)
|
|
2346
|
+
: [];
|
|
2347
|
+
return this.f.createExportAssignment(undefined, undefined, this.wrapWithAssignType(this.f.createFunctionExpression(modifier, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, declaration.body), encodedType));
|
|
2348
|
+
}
|
|
2349
|
+
const statements = [declaration];
|
|
2350
|
+
statements.push(this.f.createExpressionStatement(this.f.createAssignment(this.f.createPropertyAccessExpression(serializeEntityNameAsExpression(this.f, declaration.name), '__type'), encodedType)));
|
|
2351
|
+
return statements;
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* const fn = () => {}
|
|
2355
|
+
* => const fn = __assignType(() => {}, [34])
|
|
2356
|
+
*/
|
|
2357
|
+
decorateArrowFunction(expression) {
|
|
2358
|
+
const encodedType = this.getTypeOfType(expression);
|
|
2359
|
+
if (!encodedType)
|
|
2360
|
+
return expression;
|
|
2361
|
+
return this.wrapWithAssignType(expression, encodedType);
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Object.assign(fn, {__type: []}) is much slower than a custom implementation like
|
|
2365
|
+
*
|
|
2366
|
+
* assignType(fn, [])
|
|
2367
|
+
*
|
|
2368
|
+
* where we embed assignType() at the beginning of the type.
|
|
2369
|
+
*/
|
|
2370
|
+
wrapWithAssignType(fn, type) {
|
|
2371
|
+
this.embedAssignType = true;
|
|
2372
|
+
return this.f.createCallExpression(this.f.createIdentifier('__assignType'), undefined, [
|
|
2373
|
+
fn,
|
|
2374
|
+
type,
|
|
2375
|
+
]);
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Checks if reflection was disabled/enabled in file via JSDoc attribute for a particular
|
|
2379
|
+
* Node, e.g `@reflection no`. If nothing is found, "reflection" config option needs to be used.
|
|
2380
|
+
*/
|
|
2381
|
+
getExplicitReflectionMode(sourceFile, node) {
|
|
2382
|
+
let current = node;
|
|
2383
|
+
let reflectionComment = undefined;
|
|
2384
|
+
while ('undefined' === typeof reflectionComment && current) {
|
|
2385
|
+
const next = sourceFile && extractJSDocAttribute(sourceFile, current, 'reflection');
|
|
2386
|
+
if ('undefined' !== typeof next)
|
|
2387
|
+
reflectionComment = next;
|
|
2388
|
+
current = current.parent;
|
|
2389
|
+
}
|
|
2390
|
+
if (reflectionComment === '' || reflectionComment === 'true' || reflectionComment === 'default'
|
|
2391
|
+
|| reflectionComment === 'enabled' || reflectionComment === '1') {
|
|
2392
|
+
return true;
|
|
2393
|
+
}
|
|
2394
|
+
if (reflectionComment === 'false' || reflectionComment === 'disabled' || reflectionComment === 'never'
|
|
2395
|
+
|| reflectionComment === 'no' || reflectionComment === '0') {
|
|
2396
|
+
return false;
|
|
2397
|
+
}
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
export class DeclarationTransformer extends ReflectionTransformer {
|
|
2402
|
+
constructor() {
|
|
2403
|
+
super(...arguments);
|
|
2404
|
+
this.addExports = [];
|
|
2405
|
+
}
|
|
2406
|
+
transformSourceFile(sourceFile) {
|
|
2407
|
+
if (sourceFile.runtypedDeclarationTransformed)
|
|
2408
|
+
return sourceFile;
|
|
2409
|
+
this.sourceFile = sourceFile;
|
|
2410
|
+
this.addExports = [];
|
|
2411
|
+
const configResolver = this.getConfigResolver(sourceFile);
|
|
2412
|
+
const reflection = configResolver.match(sourceFile.fileName);
|
|
2413
|
+
// important to override the compilerOptions with the one from the configResolver
|
|
2414
|
+
// since the one provided by TSC/plugins are not necessarily the full picture.
|
|
2415
|
+
// ConfigResolver resolves the whole config.
|
|
2416
|
+
// Since this.compilerOptions was already passed to Resolver, we update its values by reference.
|
|
2417
|
+
Object.assign(this.compilerOptions, configResolver.config.compilerOptions);
|
|
2418
|
+
if (reflection.mode === 'never')
|
|
2419
|
+
return sourceFile;
|
|
2420
|
+
const visitor = (node) => {
|
|
2421
|
+
node = visitEachChild(node, visitor, this.context);
|
|
2422
|
+
if ((isTypeAliasDeclaration(node) || isInterfaceDeclaration(node) || isEnumDeclaration(node)) && hasModifier(node, SyntaxKind.ExportKeyword)) {
|
|
2423
|
+
const reflection = this.isWithReflection(sourceFile, node);
|
|
2424
|
+
if (reflection) {
|
|
2425
|
+
this.addExports.push({ identifier: getIdentifierName(this.getDeclarationVariableName(node.name)) });
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
return node;
|
|
2429
|
+
};
|
|
2430
|
+
this.sourceFile = visitNode(this.sourceFile, visitor);
|
|
2431
|
+
if (this.addExports.length) {
|
|
2432
|
+
const exports = [];
|
|
2433
|
+
const handledIdentifier = [];
|
|
2434
|
+
for (const imp of this.addExports) {
|
|
2435
|
+
if (handledIdentifier.includes(imp.identifier))
|
|
2436
|
+
continue;
|
|
2437
|
+
handledIdentifier.push(imp.identifier);
|
|
2438
|
+
//export declare type __ΩXY = any[];
|
|
2439
|
+
exports.push(this.f.createTypeAliasDeclaration([
|
|
2440
|
+
this.f.createModifier(SyntaxKind.ExportKeyword),
|
|
2441
|
+
this.f.createModifier(SyntaxKind.DeclareKeyword),
|
|
2442
|
+
], this.f.createIdentifier(imp.identifier), undefined, this.f.createArrayTypeNode(this.f.createKeywordTypeNode(SyntaxKind.AnyKeyword))));
|
|
2443
|
+
}
|
|
2444
|
+
this.sourceFile = this.f.updateSourceFile(this.sourceFile, [...this.sourceFile.statements, ...exports]);
|
|
2445
|
+
}
|
|
2446
|
+
this.sourceFile.runtypedDeclarationTransformed = true;
|
|
2447
|
+
return this.sourceFile;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
let loaded = false;
|
|
2451
|
+
const cache = new Cache;
|
|
2452
|
+
export const transformer = function runtypedTransformer(context) {
|
|
2453
|
+
if (!loaded) {
|
|
2454
|
+
debug('@runtyped/type transformer loaded\n');
|
|
2455
|
+
loaded = true;
|
|
2456
|
+
}
|
|
2457
|
+
cache.tick();
|
|
2458
|
+
return new ReflectionTransformer(context, cache);
|
|
2459
|
+
};
|
|
2460
|
+
export const declarationTransformer = function runtypedDeclarationTransformer(context) {
|
|
2461
|
+
return new DeclarationTransformer(context, cache);
|
|
2462
|
+
};
|
|
2463
|
+
//# sourceMappingURL=compiler.js.map
|