expo-modules-test-core 56.0.0 → 56.0.2

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/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'>;