expo-modules-test-core 56.0.0 → 56.0.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 +21 -0
- package/android/build.gradle +2 -2
- package/build/index.js +18 -16
- package/build/index.js.map +1 -1
- package/package.json +11 -7
- package/src/index.ts +18 -16
- package/build/getStructure.d.ts +0 -3
- package/build/getStructure.d.ts.map +0 -1
- package/build/getStructure.js +0 -244
- package/build/getStructure.js.map +0 -1
- package/build/mockgen.d.ts +0 -4
- package/build/mockgen.d.ts.map +0 -1
- package/build/mockgen.js +0 -391
- package/build/mockgen.js.map +0 -1
- package/build/types.d.ts +0 -55
- package/build/types.d.ts.map +0 -1
- package/build/types.js +0 -3
- package/build/types.js.map +0 -1
- package/src/getStructure.ts +0 -299
- package/src/mockgen.ts +0 -525
- package/src/types.ts +0 -65
package/src/mockgen.ts
DELETED
|
@@ -1,525 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import * as prettier from 'prettier';
|
|
7
|
-
import ts from 'typescript';
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
Closure,
|
|
11
|
-
ClosureTypes,
|
|
12
|
-
Constant,
|
|
13
|
-
OutputModuleDefinition,
|
|
14
|
-
OutputNestedClassDefinition,
|
|
15
|
-
} from './types';
|
|
16
|
-
|
|
17
|
-
const directoryPath = process.cwd();
|
|
18
|
-
|
|
19
|
-
/*
|
|
20
|
-
We receive types from SourceKitten and `getStructure` like so (examples):
|
|
21
|
-
[AcceptedTypes]?, UIColor?, [String: Any]
|
|
22
|
-
|
|
23
|
-
We need to parse them first to TS nodes in `mapSwiftTypeToTsType` with the following helper functions.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
function isSwiftArray(type: string) {
|
|
27
|
-
// This can also be an object, but we check that first, so if it's not an object and is wrapped with [] it's an array.
|
|
28
|
-
return type.startsWith('[') && type.endsWith(']');
|
|
29
|
-
}
|
|
30
|
-
function maybeUnwrapSwiftArray(type: string) {
|
|
31
|
-
const isArray = isSwiftArray(type);
|
|
32
|
-
if (!isArray) {
|
|
33
|
-
return type;
|
|
34
|
-
}
|
|
35
|
-
const innerType = type.substring(1, type.length - 1);
|
|
36
|
-
return innerType;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isSwiftOptional(type: string) {
|
|
40
|
-
return type.endsWith('?');
|
|
41
|
-
}
|
|
42
|
-
function maybeUnwrapSwiftOptional(type: string) {
|
|
43
|
-
const isOptional = isSwiftOptional(type);
|
|
44
|
-
if (!isOptional) {
|
|
45
|
-
return type;
|
|
46
|
-
}
|
|
47
|
-
const innerType = type.substring(0, type.length - 1);
|
|
48
|
-
return innerType;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function isSwiftDictionary(type: string) {
|
|
52
|
-
return (
|
|
53
|
-
type.startsWith('[') &&
|
|
54
|
-
type.endsWith(']') &&
|
|
55
|
-
findRootColonInDictionary(type.substring(1, type.length - 1)) >= 0
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function isEither(type: string) {
|
|
60
|
-
return type.startsWith('Either<');
|
|
61
|
-
}
|
|
62
|
-
// "Either<TypeOne, TypeTwo>" -> ["TypeOne", "TypeTwo"]
|
|
63
|
-
function maybeUnwrapEither(type: string): string[] {
|
|
64
|
-
if (!isEither(type)) {
|
|
65
|
-
return [type];
|
|
66
|
-
}
|
|
67
|
-
const innerType = type.substring(7, type.length - 1);
|
|
68
|
-
return innerType.split(',').map((t) => t.trim());
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/*
|
|
72
|
-
The Swift object type can have nested objects as the type of it's values (or maybe even keys).
|
|
73
|
-
[String: [String: Any]]
|
|
74
|
-
|
|
75
|
-
We can't use regex to find the root colon, so this is the safest way – by counting brackets.
|
|
76
|
-
*/
|
|
77
|
-
function findRootColonInDictionary(type: string) {
|
|
78
|
-
let colonIndex = -1;
|
|
79
|
-
let openBracketsCount = 0;
|
|
80
|
-
for (let i = 0; i < type.length; i++) {
|
|
81
|
-
if (type[i] === '[') {
|
|
82
|
-
openBracketsCount++;
|
|
83
|
-
} else if (type[i] === ']') {
|
|
84
|
-
openBracketsCount--;
|
|
85
|
-
} else if (type[i] === ':' && openBracketsCount === 0) {
|
|
86
|
-
colonIndex = i;
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return colonIndex;
|
|
91
|
-
}
|
|
92
|
-
function unwrapSwiftDictionary(type: string) {
|
|
93
|
-
const innerType = type.substring(1, type.length - 1);
|
|
94
|
-
const colonPosition = findRootColonInDictionary(innerType);
|
|
95
|
-
return {
|
|
96
|
-
key: innerType.slice(0, colonPosition).trim(),
|
|
97
|
-
value: innerType.slice(colonPosition + 1).trim(),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
type TSNode =
|
|
102
|
-
| ts.UnionTypeNode
|
|
103
|
-
| ts.KeywordTypeNode
|
|
104
|
-
| ts.TypeReferenceNode
|
|
105
|
-
| ts.ArrayTypeNode
|
|
106
|
-
| ts.OptionalTypeNode
|
|
107
|
-
| ts.TypeLiteralNode;
|
|
108
|
-
|
|
109
|
-
/*
|
|
110
|
-
Main function that converts a string representation of a Swift type to a TypeScript compiler API node AST.
|
|
111
|
-
We can pass those types straight to a TypeScript printer (a function that converts AST to text).
|
|
112
|
-
*/
|
|
113
|
-
function mapSwiftTypeToTsType(type: string): TSNode {
|
|
114
|
-
if (!type) {
|
|
115
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
116
|
-
}
|
|
117
|
-
if (isSwiftOptional(type)) {
|
|
118
|
-
return ts.factory.createUnionTypeNode([
|
|
119
|
-
mapSwiftTypeToTsType(maybeUnwrapSwiftOptional(type)),
|
|
120
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
|
|
121
|
-
]);
|
|
122
|
-
}
|
|
123
|
-
if (isSwiftDictionary(type)) {
|
|
124
|
-
const { key, value } = unwrapSwiftDictionary(type);
|
|
125
|
-
const keyType = mapSwiftTypeToTsType(key);
|
|
126
|
-
const valueType = mapSwiftTypeToTsType(value);
|
|
127
|
-
|
|
128
|
-
const indexSignature = ts.factory.createIndexSignature(
|
|
129
|
-
undefined,
|
|
130
|
-
[ts.factory.createParameterDeclaration(undefined, undefined, 'key', undefined, keyType)],
|
|
131
|
-
valueType
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const typeLiteralNode = ts.factory.createTypeLiteralNode([indexSignature]);
|
|
135
|
-
return typeLiteralNode;
|
|
136
|
-
}
|
|
137
|
-
if (isSwiftArray(type)) {
|
|
138
|
-
return ts.factory.createArrayTypeNode(mapSwiftTypeToTsType(maybeUnwrapSwiftArray(type)));
|
|
139
|
-
}
|
|
140
|
-
// Custom handling for the Either convertible
|
|
141
|
-
if (isEither(type)) {
|
|
142
|
-
return ts.factory.createUnionTypeNode(
|
|
143
|
-
maybeUnwrapEither(type).map((t) => mapSwiftTypeToTsType(t))
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
switch (type) {
|
|
148
|
-
// Our custom representation for types that we have no type hints for. Not necessairly Swift any.
|
|
149
|
-
case 'unknown':
|
|
150
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
|
|
151
|
-
case 'String':
|
|
152
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
153
|
-
case 'Bool':
|
|
154
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
155
|
-
case 'Int':
|
|
156
|
-
case 'Float':
|
|
157
|
-
case 'Double':
|
|
158
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
159
|
-
case 'Any': // Swift Any type
|
|
160
|
-
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
|
|
161
|
-
default: // Custom Swift type (record) – for now mapped to a custom TS type exported at the top of the file by `getMockedTypes`.
|
|
162
|
-
return ts.factory.createTypeReferenceNode(type);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Mocks require sample return values, so we generate them based on TS AST.
|
|
167
|
-
function getMockLiterals(tsReturnType: TSNode) {
|
|
168
|
-
if (!tsReturnType) {
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
switch (tsReturnType.kind) {
|
|
172
|
-
case ts.SyntaxKind.AnyKeyword:
|
|
173
|
-
case ts.SyntaxKind.VoidKeyword:
|
|
174
|
-
return undefined;
|
|
175
|
-
case ts.SyntaxKind.UnionType:
|
|
176
|
-
// we take the first element of our union for the mock – we know the cast is correct since we create the type ourselves
|
|
177
|
-
// the second is `undefined` for optionals.
|
|
178
|
-
return getMockLiterals(tsReturnType.types[0] as TSNode);
|
|
179
|
-
case ts.SyntaxKind.StringKeyword:
|
|
180
|
-
return ts.factory.createStringLiteral('');
|
|
181
|
-
case ts.SyntaxKind.BooleanKeyword:
|
|
182
|
-
return ts.factory.createFalse();
|
|
183
|
-
case ts.SyntaxKind.NumberKeyword:
|
|
184
|
-
return ts.factory.createNumericLiteral('0');
|
|
185
|
-
case ts.SyntaxKind.ArrayType:
|
|
186
|
-
return ts.factory.createArrayLiteralExpression();
|
|
187
|
-
case ts.SyntaxKind.TypeLiteral:
|
|
188
|
-
// handles a dictionary, could be improved by creating an object fitting the schema instead of an empty one
|
|
189
|
-
return ts.factory.createObjectLiteralExpression([], false);
|
|
190
|
-
}
|
|
191
|
-
return undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function wrapWithAsync(tsType: ts.TypeNode) {
|
|
195
|
-
return ts.factory.createTypeReferenceNode('Promise', [tsType]);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function maybeWrapWithReturnStatement(tsType: TSNode) {
|
|
199
|
-
if (tsType.kind === ts.SyntaxKind.AnyKeyword || tsType.kind === ts.SyntaxKind.VoidKeyword) {
|
|
200
|
-
return [];
|
|
201
|
-
}
|
|
202
|
-
if (tsType.kind === ts.SyntaxKind.TypeReference) {
|
|
203
|
-
// A fallback – we print a comment that these mocks are not fitting the custom type. Could be improved by expanding a set of default mocks.
|
|
204
|
-
return [
|
|
205
|
-
ts.addSyntheticTrailingComment(
|
|
206
|
-
ts.factory.createReturnStatement(ts.factory.createNull()),
|
|
207
|
-
ts.SyntaxKind.SingleLineCommentTrivia,
|
|
208
|
-
` TODO: Replace with mock for value of type ${
|
|
209
|
-
((tsType as any)?.typeName as any)?.escapedText ?? ''
|
|
210
|
-
}.`
|
|
211
|
-
),
|
|
212
|
-
];
|
|
213
|
-
}
|
|
214
|
-
return [ts.factory.createReturnStatement(getMockLiterals(tsType))];
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/*
|
|
218
|
-
We iterate over a list of functions and we create TS AST for each of them.
|
|
219
|
-
*/
|
|
220
|
-
function getMockedFunctions(functions: Closure[], { async = false, classMethod = false } = {}) {
|
|
221
|
-
return functions.map((fnStructure) => {
|
|
222
|
-
const name = ts.factory.createIdentifier(fnStructure.name);
|
|
223
|
-
const returnType = mapSwiftTypeToTsType(fnStructure.types?.returnType);
|
|
224
|
-
const parameters =
|
|
225
|
-
fnStructure?.types?.parameters.map((p) =>
|
|
226
|
-
ts.factory.createParameterDeclaration(
|
|
227
|
-
undefined,
|
|
228
|
-
undefined,
|
|
229
|
-
p.name ?? '_',
|
|
230
|
-
undefined,
|
|
231
|
-
mapSwiftTypeToTsType(p.typename),
|
|
232
|
-
undefined
|
|
233
|
-
)
|
|
234
|
-
) ?? [];
|
|
235
|
-
const returnBlock = ts.factory.createBlock(maybeWrapWithReturnStatement(returnType), true);
|
|
236
|
-
|
|
237
|
-
if (classMethod) {
|
|
238
|
-
return ts.factory.createMethodDeclaration(
|
|
239
|
-
[async ? ts.factory.createToken(ts.SyntaxKind.AsyncKeyword) : undefined].flatMap((f) =>
|
|
240
|
-
f ? [f] : []
|
|
241
|
-
),
|
|
242
|
-
undefined,
|
|
243
|
-
name,
|
|
244
|
-
undefined,
|
|
245
|
-
undefined,
|
|
246
|
-
parameters,
|
|
247
|
-
async ? wrapWithAsync(returnType) : returnType,
|
|
248
|
-
returnBlock
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
const func = ts.factory.createFunctionDeclaration(
|
|
252
|
-
[
|
|
253
|
-
ts.factory.createToken(ts.SyntaxKind.ExportKeyword),
|
|
254
|
-
async ? ts.factory.createToken(ts.SyntaxKind.AsyncKeyword) : undefined,
|
|
255
|
-
].flatMap((f) => (f ? [f] : [])),
|
|
256
|
-
undefined,
|
|
257
|
-
name,
|
|
258
|
-
undefined,
|
|
259
|
-
parameters,
|
|
260
|
-
async ? wrapWithAsync(returnType) : returnType,
|
|
261
|
-
returnBlock
|
|
262
|
-
);
|
|
263
|
-
return func;
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/*
|
|
268
|
-
We iterate over a list of constants and create TS AST for each of them.
|
|
269
|
-
Constants are exported as `export const NAME = value;`
|
|
270
|
-
*/
|
|
271
|
-
function getMockedConstants(constants: Constant[]) {
|
|
272
|
-
return constants.map((constant) => {
|
|
273
|
-
const name = ts.factory.createIdentifier(constant.name);
|
|
274
|
-
const returnType = mapSwiftTypeToTsType(constant.types?.returnType ?? 'unknown');
|
|
275
|
-
const initializer = getMockLiterals(returnType) ?? ts.factory.createNumericLiteral('0');
|
|
276
|
-
|
|
277
|
-
return ts.factory.createVariableStatement(
|
|
278
|
-
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
279
|
-
ts.factory.createVariableDeclarationList(
|
|
280
|
-
[ts.factory.createVariableDeclaration(name, undefined, undefined, initializer)],
|
|
281
|
-
ts.NodeFlags.Const
|
|
282
|
-
)
|
|
283
|
-
);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Collect all type references used in any of the AST types to generate type aliases
|
|
289
|
-
* e.g. type `[URL: string]?` will generate `type URL = any;`
|
|
290
|
-
*/
|
|
291
|
-
function getAllTypeReferences(node: ts.Node, accumulator: string[]) {
|
|
292
|
-
if (ts.isTypeReferenceNode(node)) {
|
|
293
|
-
accumulator.push((node.typeName as any)?.escapedText);
|
|
294
|
-
}
|
|
295
|
-
node.forEachChild((n) => getAllTypeReferences(n, accumulator));
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Iterates over types to collect the aliases.
|
|
300
|
-
*/
|
|
301
|
-
function getTypesToMock(module: OutputModuleDefinition | OutputNestedClassDefinition) {
|
|
302
|
-
const foundTypes: string[] = [];
|
|
303
|
-
|
|
304
|
-
Object.values(module)
|
|
305
|
-
.flatMap((t) => (Array.isArray(t) ? t.map((t2) => (t2 as Closure)?.types) : []))
|
|
306
|
-
.forEach((types: ClosureTypes | null) => {
|
|
307
|
-
types?.parameters.forEach(({ typename }) => {
|
|
308
|
-
getAllTypeReferences(mapSwiftTypeToTsType(typename), foundTypes);
|
|
309
|
-
});
|
|
310
|
-
types?.returnType &&
|
|
311
|
-
getAllTypeReferences(mapSwiftTypeToTsType(types?.returnType), foundTypes);
|
|
312
|
-
});
|
|
313
|
-
return new Set(foundTypes);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Gets a mock for a custom type.
|
|
318
|
-
*/
|
|
319
|
-
function getMockedTypes(types: Set<string>) {
|
|
320
|
-
return Array.from(types).map((type) => {
|
|
321
|
-
const name = ts.factory.createIdentifier(type);
|
|
322
|
-
const typeAlias = ts.factory.createTypeAliasDeclaration(
|
|
323
|
-
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
324
|
-
name,
|
|
325
|
-
undefined,
|
|
326
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
|
|
327
|
-
);
|
|
328
|
-
return typeAlias;
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const prefix = `Automatically generated by expo-modules-test-core.
|
|
333
|
-
|
|
334
|
-
This autogenerated file provides a mock for native Expo module,
|
|
335
|
-
and works out of the box with the expo jest preset.
|
|
336
|
-
`;
|
|
337
|
-
function getPrefix() {
|
|
338
|
-
return [ts.factory.createJSDocComment(prefix)];
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function generatePropTypesForDefinition(definition: OutputNestedClassDefinition) {
|
|
342
|
-
return ts.factory.createTypeAliasDeclaration(
|
|
343
|
-
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
344
|
-
'ViewProps',
|
|
345
|
-
undefined,
|
|
346
|
-
ts.factory.createTypeLiteralNode([
|
|
347
|
-
...definition.props.map((p) => {
|
|
348
|
-
const propType = mapSwiftTypeToTsType(p.types.parameters[0]?.typename ?? '');
|
|
349
|
-
return ts.factory.createPropertySignature(undefined, p.name, undefined, propType);
|
|
350
|
-
}),
|
|
351
|
-
...definition.events.map((e) => {
|
|
352
|
-
const eventType = ts.factory.createFunctionTypeNode(
|
|
353
|
-
undefined,
|
|
354
|
-
[
|
|
355
|
-
ts.factory.createParameterDeclaration(
|
|
356
|
-
undefined,
|
|
357
|
-
undefined,
|
|
358
|
-
'event',
|
|
359
|
-
undefined,
|
|
360
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
|
|
361
|
-
),
|
|
362
|
-
],
|
|
363
|
-
ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
|
|
364
|
-
);
|
|
365
|
-
return ts.factory.createPropertySignature(undefined, e.name, undefined, eventType);
|
|
366
|
-
}),
|
|
367
|
-
])
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
/*
|
|
371
|
-
Generate a mock for view props and functions.
|
|
372
|
-
*/
|
|
373
|
-
function getMockedViews(viewDefinitions: OutputNestedClassDefinition[]) {
|
|
374
|
-
return viewDefinitions.flatMap((definition) => {
|
|
375
|
-
if (!definition) {
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
const propsType = generatePropTypesForDefinition(definition);
|
|
379
|
-
const props = ts.factory.createParameterDeclaration(
|
|
380
|
-
undefined,
|
|
381
|
-
undefined,
|
|
382
|
-
'props',
|
|
383
|
-
undefined,
|
|
384
|
-
ts.factory.createTypeReferenceNode('ViewProps', undefined),
|
|
385
|
-
undefined
|
|
386
|
-
);
|
|
387
|
-
const viewFunction = ts.factory.createFunctionDeclaration(
|
|
388
|
-
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
389
|
-
undefined,
|
|
390
|
-
// TODO: Handle this better once requireNativeViewManager accepts view name or a different solution for multiple views is built.
|
|
391
|
-
viewDefinitions.length === 1 ? 'View' : definition.name,
|
|
392
|
-
undefined,
|
|
393
|
-
[props],
|
|
394
|
-
undefined,
|
|
395
|
-
ts.factory.createBlock([])
|
|
396
|
-
);
|
|
397
|
-
return [propsType, viewFunction];
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function getMockedClass(def: OutputNestedClassDefinition) {
|
|
402
|
-
const classDecl = ts.factory.createClassDeclaration(
|
|
403
|
-
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
404
|
-
ts.factory.createIdentifier(def.name),
|
|
405
|
-
undefined,
|
|
406
|
-
undefined,
|
|
407
|
-
[
|
|
408
|
-
...getMockedFunctions(def.functions, { classMethod: true }),
|
|
409
|
-
...getMockedFunctions(def.asyncFunctions, { async: true, classMethod: true }),
|
|
410
|
-
] as ts.MethodDeclaration[]
|
|
411
|
-
);
|
|
412
|
-
return classDecl;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
function getMockedClasses(def: OutputNestedClassDefinition[]) {
|
|
416
|
-
return def.map((d) => getMockedClass(d));
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const newlineIdentifier = ts.factory.createIdentifier('\n\n') as any;
|
|
420
|
-
function separateWithNewlines<T>(arr: T) {
|
|
421
|
-
return [arr, newlineIdentifier];
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function omitFromSet(set: Set<string>, toOmit: (string | undefined)[]) {
|
|
425
|
-
const newSet = new Set(set);
|
|
426
|
-
toOmit.forEach((item) => {
|
|
427
|
-
if (item) {
|
|
428
|
-
newSet.delete(item);
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
return newSet;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function getMockForModule(module: OutputModuleDefinition, includeTypes: boolean) {
|
|
435
|
-
return (
|
|
436
|
-
[] as (
|
|
437
|
-
| ts.TypeAliasDeclaration
|
|
438
|
-
| ts.FunctionDeclaration
|
|
439
|
-
| ts.JSDoc
|
|
440
|
-
| ts.ClassDeclaration
|
|
441
|
-
| ts.VariableStatement
|
|
442
|
-
)[]
|
|
443
|
-
)
|
|
444
|
-
.concat(
|
|
445
|
-
getPrefix(),
|
|
446
|
-
newlineIdentifier,
|
|
447
|
-
includeTypes
|
|
448
|
-
? getMockedTypes(
|
|
449
|
-
omitFromSet(
|
|
450
|
-
new Set([
|
|
451
|
-
...getTypesToMock(module),
|
|
452
|
-
...new Set(...module.views.map((v) => getTypesToMock(v))),
|
|
453
|
-
...new Set(...module.classes.map((c) => getTypesToMock(c))),
|
|
454
|
-
]),
|
|
455
|
-
// Ignore all types that are actually native classes
|
|
456
|
-
[
|
|
457
|
-
module.name,
|
|
458
|
-
...module.views.map((c) => c.name),
|
|
459
|
-
...module.classes.map((c) => c.name),
|
|
460
|
-
]
|
|
461
|
-
)
|
|
462
|
-
)
|
|
463
|
-
: [],
|
|
464
|
-
newlineIdentifier,
|
|
465
|
-
getMockedConstants(module.constants),
|
|
466
|
-
newlineIdentifier,
|
|
467
|
-
getMockedFunctions(module.functions) as ts.FunctionDeclaration[],
|
|
468
|
-
getMockedFunctions(module.asyncFunctions, { async: true }) as ts.FunctionDeclaration[],
|
|
469
|
-
newlineIdentifier,
|
|
470
|
-
getMockedViews(module.views),
|
|
471
|
-
getMockedClasses(module.classes)
|
|
472
|
-
)
|
|
473
|
-
.flatMap(separateWithNewlines);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
async function prettifyCode(text: string, parser: 'babel' | 'typescript' = 'babel') {
|
|
477
|
-
return await prettier.format(text, {
|
|
478
|
-
parser,
|
|
479
|
-
tabWidth: 2,
|
|
480
|
-
printWidth: 100,
|
|
481
|
-
trailingComma: 'none',
|
|
482
|
-
singleQuote: true,
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
export async function generateMocks(
|
|
487
|
-
modules: OutputModuleDefinition[],
|
|
488
|
-
outputLanguage: 'javascript' | 'typescript' = 'javascript'
|
|
489
|
-
) {
|
|
490
|
-
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
491
|
-
|
|
492
|
-
for (const m of modules) {
|
|
493
|
-
const filename = m.name + (outputLanguage === 'javascript' ? '.js' : '.ts');
|
|
494
|
-
const resultFile = ts.createSourceFile(
|
|
495
|
-
filename,
|
|
496
|
-
'',
|
|
497
|
-
ts.ScriptTarget.Latest,
|
|
498
|
-
false,
|
|
499
|
-
ts.ScriptKind.TSX
|
|
500
|
-
);
|
|
501
|
-
fs.mkdirSync(path.join(directoryPath, 'mocks'), { recursive: true });
|
|
502
|
-
const filePath = path.join(directoryPath, 'mocks', filename);
|
|
503
|
-
// get ts nodearray from getMockForModule(m) array
|
|
504
|
-
const mock = ts.factory.createNodeArray(getMockForModule(m, outputLanguage === 'typescript'));
|
|
505
|
-
const printedTs = printer.printList(
|
|
506
|
-
ts.ListFormat.MultiLine + ts.ListFormat.PreserveLines,
|
|
507
|
-
mock,
|
|
508
|
-
resultFile
|
|
509
|
-
);
|
|
510
|
-
|
|
511
|
-
if (outputLanguage === 'javascript') {
|
|
512
|
-
const compiledJs = ts.transpileModule(printedTs, {
|
|
513
|
-
compilerOptions: {
|
|
514
|
-
module: ts.ModuleKind.ESNext,
|
|
515
|
-
target: ts.ScriptTarget.ESNext,
|
|
516
|
-
},
|
|
517
|
-
}).outputText;
|
|
518
|
-
const prettifiedJs = await prettifyCode(compiledJs);
|
|
519
|
-
fs.writeFileSync(filePath, prettifiedJs);
|
|
520
|
-
} else {
|
|
521
|
-
const prettifiedTs = await prettifyCode(printedTs, 'typescript');
|
|
522
|
-
fs.writeFileSync(filePath, prettifiedTs);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
export type FileType = {
|
|
2
|
-
path: string;
|
|
3
|
-
content: string;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
export type Structure = {
|
|
7
|
-
'key.substructure': Structure[];
|
|
8
|
-
'key.typename': string;
|
|
9
|
-
'key.name': string;
|
|
10
|
-
'key.kind': string;
|
|
11
|
-
'key.offset': number;
|
|
12
|
-
'key.length': number;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type CursorInfoOutput = {
|
|
16
|
-
'key.fully_annotated_decl': string;
|
|
17
|
-
'key.annotated_decl': string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type FullyAnnotatedDecl = {
|
|
21
|
-
'decl.function.free': {
|
|
22
|
-
'decl.var.parameter': {
|
|
23
|
-
'decl.var.parameter.argument_label': string;
|
|
24
|
-
'decl.var.parameter.type': string;
|
|
25
|
-
}[];
|
|
26
|
-
'decl.function.returntype': string;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type ClosureTypes = {
|
|
31
|
-
parameters: {
|
|
32
|
-
name: any;
|
|
33
|
-
typename: any;
|
|
34
|
-
}[];
|
|
35
|
-
returnType: any;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export type Closure = {
|
|
39
|
-
name: string;
|
|
40
|
-
types: ClosureTypes | null;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type Prop = {
|
|
44
|
-
name: string;
|
|
45
|
-
types: Omit<ClosureTypes, 'returnType'>;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export type Constant = {
|
|
49
|
-
name: string;
|
|
50
|
-
types: ClosureTypes | null;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type OutputModuleDefinition = {
|
|
54
|
-
name: string;
|
|
55
|
-
views: OutputNestedClassDefinition[];
|
|
56
|
-
classes: OutputNestedClassDefinition[];
|
|
57
|
-
events: {
|
|
58
|
-
name: string;
|
|
59
|
-
}[];
|
|
60
|
-
constants: Constant[];
|
|
61
|
-
} & Record<'asyncFunctions' | 'functions' | 'properties', Closure[]> &
|
|
62
|
-
Record<'props', Prop[]>;
|
|
63
|
-
|
|
64
|
-
// views and classes are a very similar structure, same as module but without more nesting levels
|
|
65
|
-
export type OutputNestedClassDefinition = Omit<OutputModuleDefinition, 'views' | 'classes'>;
|