expo-type-information 0.0.0 → 0.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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/bin/cli.js +2 -0
  4. package/build/cli.d.ts +1 -0
  5. package/build/cli.js +35 -0
  6. package/build/cli.js.map +1 -0
  7. package/build/commands/commandUtils.d.ts +46 -0
  8. package/build/commands/commandUtils.js +274 -0
  9. package/build/commands/commandUtils.js.map +1 -0
  10. package/build/commands/generateJSXIntrinsicsCommand.d.ts +2 -0
  11. package/build/commands/generateJSXIntrinsicsCommand.js +28 -0
  12. package/build/commands/generateJSXIntrinsicsCommand.js.map +1 -0
  13. package/build/commands/generateMocksForFileCommand.d.ts +2 -0
  14. package/build/commands/generateMocksForFileCommand.js +22 -0
  15. package/build/commands/generateMocksForFileCommand.js.map +1 -0
  16. package/build/commands/generateModuleTypesCommand.d.ts +2 -0
  17. package/build/commands/generateModuleTypesCommand.js +32 -0
  18. package/build/commands/generateModuleTypesCommand.js.map +1 -0
  19. package/build/commands/generateViewTypesCommand.d.ts +2 -0
  20. package/build/commands/generateViewTypesCommand.js +28 -0
  21. package/build/commands/generateViewTypesCommand.js.map +1 -0
  22. package/build/commands/inlineModulesInterfaceCommand.d.ts +2 -0
  23. package/build/commands/inlineModulesInterfaceCommand.js +129 -0
  24. package/build/commands/inlineModulesInterfaceCommand.js.map +1 -0
  25. package/build/commands/moduleInterfaceCommand.d.ts +2 -0
  26. package/build/commands/moduleInterfaceCommand.js +46 -0
  27. package/build/commands/moduleInterfaceCommand.js.map +1 -0
  28. package/build/commands/shortModuleInterfaceCommand.d.ts +2 -0
  29. package/build/commands/shortModuleInterfaceCommand.js +17 -0
  30. package/build/commands/shortModuleInterfaceCommand.js.map +1 -0
  31. package/build/commands/typeInformationCommand.d.ts +2 -0
  32. package/build/commands/typeInformationCommand.js +24 -0
  33. package/build/commands/typeInformationCommand.js.map +1 -0
  34. package/build/index.d.ts +3 -0
  35. package/build/index.js +28 -0
  36. package/build/index.js.map +1 -0
  37. package/build/mockgen.d.ts +4 -0
  38. package/build/mockgen.js +204 -0
  39. package/build/mockgen.js.map +1 -0
  40. package/build/swift/sourcekittenTypeInformation.d.ts +6 -0
  41. package/build/swift/sourcekittenTypeInformation.js +875 -0
  42. package/build/swift/sourcekittenTypeInformation.js.map +1 -0
  43. package/build/typeInformation.d.ts +209 -0
  44. package/build/typeInformation.js +157 -0
  45. package/build/typeInformation.js.map +1 -0
  46. package/build/types.d.ts +59 -0
  47. package/build/types.js +3 -0
  48. package/build/types.js.map +1 -0
  49. package/build/typescriptGeneration.d.ts +61 -0
  50. package/build/typescriptGeneration.js +696 -0
  51. package/build/typescriptGeneration.js.map +1 -0
  52. package/build/utils.d.ts +6 -0
  53. package/build/utils.js +44 -0
  54. package/build/utils.js.map +1 -0
  55. package/jest.config.js +6 -0
  56. package/package.json +46 -5
  57. package/src/cli.ts +38 -0
  58. package/src/commands/commandUtils.ts +352 -0
  59. package/src/commands/generateJSXIntrinsicsCommand.ts +38 -0
  60. package/src/commands/generateMocksForFileCommand.ts +30 -0
  61. package/src/commands/generateModuleTypesCommand.ts +39 -0
  62. package/src/commands/generateViewTypesCommand.ts +39 -0
  63. package/src/commands/inlineModulesInterfaceCommand.ts +175 -0
  64. package/src/commands/moduleInterfaceCommand.ts +56 -0
  65. package/src/commands/shortModuleInterfaceCommand.ts +26 -0
  66. package/src/commands/typeInformationCommand.ts +35 -0
  67. package/src/index.ts +9 -0
  68. package/src/mockgen.ts +338 -0
  69. package/src/swift/sourcekittenTypeInformation.ts +1173 -0
  70. package/src/typeInformation.ts +326 -0
  71. package/src/types.ts +68 -0
  72. package/src/typescriptGeneration.ts +1179 -0
  73. package/src/utils.ts +44 -0
  74. package/tests/TestModule.swift +175 -0
  75. package/tests/__snapshots__/typeInformation.test.ts.snap +1578 -0
  76. package/tests/typeInformation.test.ts +134 -0
  77. package/tsconfig.json +11 -0
@@ -0,0 +1,1179 @@
1
+ 'use strict';
2
+ import prettier from 'prettier';
3
+ import ts from 'typescript';
4
+
5
+ import {
6
+ Argument,
7
+ ArrayType,
8
+ BasicType,
9
+ ClassDeclaration,
10
+ ConstantDeclaration,
11
+ ConstructorDeclaration,
12
+ DictionaryType,
13
+ EnumType,
14
+ EventDeclaration,
15
+ FileTypeInformation,
16
+ FunctionDeclaration,
17
+ ModuleClassDeclaration,
18
+ ParametrizedType,
19
+ PropDeclaration,
20
+ PropertyDeclaration,
21
+ RecordType,
22
+ SumType,
23
+ Type,
24
+ TypeKind,
25
+ ViewDeclaration,
26
+ } from './typeInformation';
27
+
28
+ const prefix = `Automatically generated by expo-type-information.`;
29
+
30
+ const exportModifier = () => ts.factory.createModifier(ts.SyntaxKind.ExportKeyword);
31
+ const declareModifier = () => ts.factory.createModifier(ts.SyntaxKind.DeclareKeyword);
32
+ const asyncModifier = () => ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword);
33
+ const readonlyModifier = () => ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword);
34
+ const constModifier = () => ts.factory.createModifier(ts.SyntaxKind.ConstKeyword);
35
+ const defaultModifier = () => ts.factory.createModifier(ts.SyntaxKind.DefaultKeyword);
36
+
37
+ const unknownKeywordType = () => ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
38
+ const anyKeywordType = () => ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
39
+ const voidKeywordType = () => ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
40
+
41
+ const newlineIdentifier = () => ts.factory.createIdentifier('\n\n');
42
+
43
+ export type OutputFile = {
44
+ content: string;
45
+ name: string;
46
+ };
47
+
48
+ export interface GenerationContext {
49
+ fileInfo: FileTypeInformation;
50
+ module: ModuleClassDeclaration;
51
+ view: ViewDeclaration | null;
52
+ missingTypes: Set<string>;
53
+ }
54
+
55
+ let freeId = 0;
56
+ function getNextFreeId() {
57
+ freeId += 1;
58
+ return freeId;
59
+ }
60
+
61
+ export function createDefaultGenerationContext(
62
+ fileInfo: FileTypeInformation
63
+ ): GenerationContext | null {
64
+ const module = fileInfo.moduleClasses[0];
65
+ if (!module) {
66
+ return null;
67
+ }
68
+ const view = module.views[0] ?? null;
69
+ return {
70
+ fileInfo,
71
+ module,
72
+ view,
73
+ missingTypes: getMissingTypeIdentifiers(fileInfo),
74
+ };
75
+ }
76
+
77
+ export function getBasicTypesIdentifiers(): Set<string> {
78
+ return new Set<string>(['any', 'number', 'string', 'undefined', 'null', 'Map', 'Set', 'Promise']);
79
+ }
80
+
81
+ function getAllNonBasicTypes(fileInfo: FileTypeInformation): Set<string> {
82
+ return fileInfo.declaredTypeIdentifiers
83
+ .union(fileInfo.usedTypeIdentifiers)
84
+ .difference(getBasicTypesIdentifiers());
85
+ }
86
+
87
+ function createGeneratedPrefix(): ts.Node[] {
88
+ return [
89
+ ts.addSyntheticLeadingComment(
90
+ ts.factory.createIdentifier(''),
91
+ ts.SyntaxKind.MultiLineCommentTrivia,
92
+ prefix
93
+ ),
94
+ ];
95
+ }
96
+
97
+ function constructModifiersArray(modifiers: {
98
+ exported?: boolean;
99
+ declare?: boolean;
100
+ async?: boolean;
101
+ readonly?: boolean;
102
+ }): ts.Modifier[] {
103
+ const modifiersArray: ts.Modifier[] = [];
104
+ if (modifiers.exported) modifiersArray.push(exportModifier());
105
+ if (modifiers.declare) modifiersArray.push(declareModifier());
106
+ if (modifiers.async) modifiersArray.push(asyncModifier());
107
+ if (modifiers.readonly) modifiersArray.push(readonlyModifier());
108
+ return modifiersArray;
109
+ }
110
+
111
+ export function joinTSNodesWithNewlines(nodes: ts.Node[][]): ts.Node[] {
112
+ const new_nodes: ts.Node[] = [];
113
+ for (const node of nodes) {
114
+ if (node.length > 0) {
115
+ new_nodes.push(...node);
116
+ new_nodes.push(newlineIdentifier());
117
+ }
118
+ }
119
+ return new_nodes;
120
+ }
121
+
122
+ function mapBasicTypeToTypeNode(basicType: BasicType): ts.TypeNode {
123
+ if (basicType === BasicType.UNRESOLVED) {
124
+ return ts.addSyntheticTrailingComment(
125
+ unknownKeywordType(),
126
+ ts.SyntaxKind.MultiLineCommentTrivia,
127
+ "The type couldn't be resolved automatically."
128
+ );
129
+ }
130
+
131
+ const BASIC_TYPE_MAP: Record<BasicType, ts.KeywordTypeSyntaxKind> = {
132
+ [BasicType.ANY]: ts.SyntaxKind.AnyKeyword,
133
+ [BasicType.BOOLEAN]: ts.SyntaxKind.BooleanKeyword,
134
+ [BasicType.NUMBER]: ts.SyntaxKind.NumberKeyword,
135
+ [BasicType.STRING]: ts.SyntaxKind.StringKeyword,
136
+ [BasicType.VOID]: ts.SyntaxKind.VoidKeyword,
137
+ [BasicType.UNDEFINED]: ts.SyntaxKind.UndefinedKeyword,
138
+ [BasicType.UNRESOLVED]: ts.SyntaxKind.UndefinedKeyword, // This is handled earlier
139
+ };
140
+
141
+ return ts.factory.createKeywordTypeNode(BASIC_TYPE_MAP[basicType]);
142
+ }
143
+
144
+ export function mapTypeToTsTypeNode(type: Type): ts.TypeNode {
145
+ switch (type.kind) {
146
+ case TypeKind.BASIC:
147
+ return mapBasicTypeToTypeNode(type.type as BasicType);
148
+ case TypeKind.IDENTIFIER:
149
+ return ts.factory.createTypeReferenceNode(type.type as string);
150
+ case TypeKind.SUM:
151
+ return ts.factory.createUnionTypeNode((type.type as SumType).types.map(mapTypeToTsTypeNode));
152
+ case TypeKind.ARRAY:
153
+ return ts.factory.createArrayTypeNode(mapTypeToTsTypeNode(type.type as ArrayType));
154
+ case TypeKind.DICTIONARY: {
155
+ const dictionaryType = type.type as DictionaryType;
156
+ const name = 'key';
157
+ const typeNode = mapTypeToTsTypeNode(dictionaryType.key);
158
+ const valueType = mapTypeToTsTypeNode(dictionaryType.value);
159
+ return ts.factory.createTypeLiteralNode([
160
+ ts.factory.createIndexSignature(
161
+ undefined,
162
+ [createParameter({ name, type: typeNode })],
163
+ valueType
164
+ ),
165
+ ]);
166
+ }
167
+ // Technically this one should only be the top one and it should be handled somewhere else
168
+ // for example when creating arguemnt adding the '?' token.
169
+ //
170
+ // However we can just make it (type | undefined) in here.
171
+ // TODO(@HubertBer): Maybe also need null?
172
+ case TypeKind.OPTIONAL:
173
+ return ts.factory.createUnionTypeNode([
174
+ mapTypeToTsTypeNode(type.type as Type),
175
+ mapBasicTypeToTypeNode(BasicType.UNDEFINED),
176
+ ]);
177
+ case TypeKind.PARAMETRIZED:
178
+ return ts.factory.createTypeReferenceNode(
179
+ (type.type as ParametrizedType).name,
180
+ (type.type as ParametrizedType).types.map(mapTypeToTsTypeNode)
181
+ );
182
+ }
183
+ return mapBasicTypeToTypeNode(BasicType.UNRESOLVED);
184
+ }
185
+
186
+ //
187
+ // ts.factory wrapper functions
188
+ //
189
+
190
+ function createImportDeclaration({
191
+ defaultImportName,
192
+ namedImportsNames,
193
+ importFromName,
194
+ }: {
195
+ defaultImportName?: string;
196
+ namedImportsNames?: string[];
197
+ importFromName: string;
198
+ }): ts.Node[] {
199
+ const hasDefault = !!defaultImportName;
200
+ const hasNamed = namedImportsNames && namedImportsNames.length > 0;
201
+
202
+ if (!hasDefault && !hasNamed) {
203
+ return [];
204
+ }
205
+
206
+ const defaultImport = hasDefault ? ts.factory.createIdentifier(defaultImportName) : undefined;
207
+
208
+ const namedImports = hasNamed
209
+ ? ts.factory.createNamedImports(
210
+ namedImportsNames.map((name) =>
211
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))
212
+ )
213
+ )
214
+ : undefined;
215
+
216
+ return [
217
+ ts.factory.createImportDeclaration(
218
+ undefined,
219
+ ts.factory.createImportClause(undefined, defaultImport, namedImports),
220
+ ts.factory.createStringLiteral(importFromName)
221
+ ),
222
+ ];
223
+ }
224
+
225
+ function createParameter({
226
+ modifiers,
227
+ name,
228
+ type,
229
+ questionToken,
230
+ dotDotDotToken,
231
+ initializer,
232
+ }: {
233
+ modifiers?: ts.Modifier[];
234
+ name: string | ts.BindingName;
235
+ type?: ts.TypeNode;
236
+ questionToken?: ts.QuestionToken;
237
+ dotDotDotToken?: ts.DotDotDotToken;
238
+ initializer?: ts.Expression;
239
+ }): ts.ParameterDeclaration {
240
+ return ts.factory.createParameterDeclaration(
241
+ modifiers,
242
+ dotDotDotToken,
243
+ name,
244
+ questionToken,
245
+ type,
246
+ initializer
247
+ );
248
+ }
249
+
250
+ function createProperty({
251
+ modifiers,
252
+ name,
253
+ typeNode,
254
+ initializer,
255
+ optional,
256
+ }: {
257
+ modifiers?: ts.Modifier[];
258
+ name: string | ts.PropertyName;
259
+ typeNode?: ts.TypeNode;
260
+ initializer?: ts.Expression;
261
+ optional?: boolean;
262
+ }): ts.PropertyDeclaration {
263
+ return ts.factory.createPropertyDeclaration(
264
+ modifiers,
265
+ name,
266
+ optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
267
+ typeNode,
268
+ initializer
269
+ );
270
+ }
271
+
272
+ function createPropertySignature({
273
+ name,
274
+ typeNode,
275
+ optional,
276
+ modifiers,
277
+ }: {
278
+ name: string | ts.PropertyName;
279
+ typeNode?: ts.TypeNode;
280
+ optional?: boolean;
281
+ modifiers?: ts.Modifier[];
282
+ }): ts.PropertySignature {
283
+ return ts.factory.createPropertySignature(
284
+ modifiers,
285
+ name,
286
+ optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
287
+ typeNode
288
+ );
289
+ }
290
+
291
+ function createCall({
292
+ expression,
293
+ args,
294
+ typeArgs,
295
+ }: {
296
+ expression: string | ts.Expression;
297
+ args?: ts.Expression[];
298
+ typeArgs?: ts.TypeNode[];
299
+ }): ts.CallExpression {
300
+ return ts.factory.createCallExpression(
301
+ typeof expression === 'string' ? ts.factory.createIdentifier(expression) : expression,
302
+ typeArgs,
303
+ args
304
+ );
305
+ }
306
+
307
+ function createRequireNativeModuleExpression({
308
+ moduleType,
309
+ moduleName,
310
+ }: {
311
+ moduleType?: string;
312
+ moduleName: string;
313
+ }) {
314
+ return ts.factory.createCallExpression(
315
+ ts.factory.createIdentifier('requireNativeModule'),
316
+ moduleType ? [ts.factory.createTypeReferenceNode(moduleType)] : undefined,
317
+ [ts.factory.createStringLiteral(moduleName)]
318
+ );
319
+ }
320
+
321
+ function createExportDefaultAsDeclaration({
322
+ exportAsName,
323
+ importFromName,
324
+ }: {
325
+ exportAsName: string;
326
+ importFromName: string;
327
+ }): ts.Node[] {
328
+ return [
329
+ ts.factory.createExportDeclaration(
330
+ undefined,
331
+ false,
332
+ ts.factory.createNamedExports([
333
+ ts.factory.createExportSpecifier(
334
+ false,
335
+ ts.factory.createIdentifier('default'),
336
+ ts.factory.createIdentifier(exportAsName)
337
+ ),
338
+ ]),
339
+ ts.factory.createStringLiteral(importFromName)
340
+ ),
341
+ ];
342
+ }
343
+
344
+ function createTypeAlias({
345
+ exported,
346
+ alias,
347
+ typeParams,
348
+ type,
349
+ }: {
350
+ exported?: boolean;
351
+ alias: string;
352
+ typeParams?: ts.TypeParameterDeclaration[];
353
+ type: ts.TypeNode;
354
+ }) {
355
+ return ts.factory.createTypeAliasDeclaration(
356
+ constructModifiersArray({ exported }),
357
+ alias,
358
+ typeParams,
359
+ type
360
+ );
361
+ }
362
+
363
+ function createRequireNativeViewDeclaration(module: ModuleClassDeclaration, view: ViewDeclaration) {
364
+ return [
365
+ createParameter({
366
+ modifiers: [constModifier()],
367
+ name: view.name,
368
+ initializer: createCall({
369
+ expression: 'requireNativeView',
370
+ typeArgs: [ts.factory.createTypeReferenceNode(getViewPropsTypeName(view))],
371
+ args: [
372
+ ts.factory.createStringLiteral(module.name),
373
+ ts.factory.createStringLiteral(view.name),
374
+ ],
375
+ }),
376
+ }),
377
+ ];
378
+ }
379
+
380
+ function createExportAllDeclaration({
381
+ importFromName,
382
+ justTypes,
383
+ }: {
384
+ importFromName: string;
385
+ justTypes?: boolean;
386
+ }): ts.Node[] {
387
+ return [
388
+ ts.factory.createExportDeclaration(
389
+ undefined,
390
+ justTypes ?? false,
391
+ undefined,
392
+ ts.factory.createStringLiteral(importFromName)
393
+ ),
394
+ ];
395
+ }
396
+
397
+ function createExportDefault(name: string = '_default'): ts.Node[] {
398
+ return [ts.factory.createExportDefault(ts.factory.createIdentifier(name))];
399
+ }
400
+
401
+ function createComponentType(propsTypeName: string) {
402
+ return ts.factory.createTypeReferenceNode('React.JSXElementConstructor', [
403
+ ts.factory.createTypeReferenceNode(propsTypeName),
404
+ ]);
405
+ }
406
+
407
+ function getMissingTypeIdentifiers(fileTypeInformation: FileTypeInformation): Set<string> {
408
+ return fileTypeInformation.usedTypeIdentifiers
409
+ .difference(fileTypeInformation.declaredTypeIdentifiers)
410
+ .difference(getBasicTypesIdentifiers());
411
+ }
412
+
413
+ function buildPropsMembers({ props, events }: ViewDeclaration): ts.TypeElement[] {
414
+ const buildEventPropertySignature = (eventDeclaration: EventDeclaration) => {
415
+ const name = eventDeclaration;
416
+ const typeNode = ts.factory.createFunctionTypeNode(
417
+ undefined,
418
+ [createParameter({ name: 'event', type: anyKeywordType() })],
419
+ voidKeywordType()
420
+ );
421
+ // TODO(@HubertBer) check whether we have ways of making events not optional
422
+ return createPropertySignature({ name, typeNode, optional: true });
423
+ };
424
+
425
+ const buildPropPropertySignature = (propDeclaration: PropDeclaration) => {
426
+ const propTypeArgument = propDeclaration.arguments[1]?.type;
427
+ if (!propDeclaration || !propDeclaration.arguments || !propTypeArgument) {
428
+ return undefined;
429
+ }
430
+ const name = propDeclaration.name;
431
+ const typeNode = mapTypeToTsTypeNode(propTypeArgument);
432
+ return createPropertySignature({ name, typeNode });
433
+ };
434
+
435
+ return [
436
+ ...(props.map(buildPropPropertySignature).filter((p) => p) as ts.PropertySignature[]),
437
+ ...events.map(buildEventPropertySignature),
438
+ ];
439
+ }
440
+
441
+ export function buildViewPropsInterface(
442
+ view: ViewDeclaration | null,
443
+ options: { exported?: boolean }
444
+ ): ts.Node[] {
445
+ if (!view) {
446
+ return [];
447
+ }
448
+ return [
449
+ ts.factory.createInterfaceDeclaration(
450
+ constructModifiersArray(options),
451
+ getViewPropsTypeName(view),
452
+ undefined,
453
+ [
454
+ ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
455
+ ts.factory.createExpressionWithTypeArguments(
456
+ ts.factory.createIdentifier('ViewProps'),
457
+ undefined
458
+ ),
459
+ ]),
460
+ ],
461
+ buildPropsMembers(view)
462
+ ),
463
+ ];
464
+ }
465
+
466
+ function buildClassProperty(declaration: PropertyDeclaration): ts.PropertyDeclaration {
467
+ return createProperty({
468
+ modifiers: [readonlyModifier()],
469
+ name: declaration.name,
470
+ typeNode: mapTypeToTsTypeNode(declaration.type),
471
+ });
472
+ }
473
+
474
+ function buildNativeModuleClassDeclaration({
475
+ moduleClassDeclaration,
476
+ exportedModuleName,
477
+ }: {
478
+ moduleClassDeclaration: ModuleClassDeclaration;
479
+ exportedModuleName?: string;
480
+ }): ts.Node[] {
481
+ const buildClassTypeProperty = (classDeclaration: ClassDeclaration) =>
482
+ createProperty({
483
+ // TODO(@HubertBer): that's a hack, but I couldn't find a proper way to do this
484
+ // The problem is that declare class semantics seem somewhat different than class semantics.
485
+ name: classDeclaration.name,
486
+ typeNode: ts.factory.createTypeQueryNode(ts.factory.createIdentifier(classDeclaration.name)),
487
+ });
488
+
489
+ const buildSyncMethod = (functionDeclaration: FunctionDeclaration): ts.MethodDeclaration =>
490
+ buildFunction({
491
+ functionDeclaration,
492
+ method: true,
493
+ declaration: true,
494
+ }) as ts.MethodDeclaration;
495
+
496
+ const buildAsyncMethod = (functionDeclaration: FunctionDeclaration): ts.MethodDeclaration =>
497
+ buildFunction({
498
+ functionDeclaration,
499
+ async: true,
500
+ method: true,
501
+ declaration: true,
502
+ }) as ts.MethodDeclaration;
503
+
504
+ return [
505
+ ts.factory.createClassDeclaration(
506
+ [exportModifier(), declareModifier()],
507
+ exportedModuleName ?? `${moduleClassDeclaration.name}NativeModuleType`,
508
+ undefined,
509
+ [
510
+ ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
511
+ ts.factory.createExpressionWithTypeArguments(
512
+ ts.factory.createIdentifier('NativeModule'),
513
+ undefined
514
+ ),
515
+ ]),
516
+ ],
517
+ [
518
+ ...moduleClassDeclaration.constants.map(buildClassProperty),
519
+ ...moduleClassDeclaration.properties.map(buildClassProperty),
520
+ ...moduleClassDeclaration.functions.map(buildSyncMethod),
521
+ ...moduleClassDeclaration.asyncFunctions.map(buildAsyncMethod),
522
+ ...moduleClassDeclaration.classes.map(buildClassTypeProperty),
523
+ ]
524
+ ),
525
+ ];
526
+ }
527
+
528
+ function buildArgumentDeclarationAndName(arg: Argument): {
529
+ argDeclaration: ts.ParameterDeclaration;
530
+ argName: string;
531
+ } {
532
+ const argName = arg.name ?? '_' + getNextFreeId();
533
+ const argDeclaration = createParameter({
534
+ name: argName,
535
+ type: mapTypeToTsTypeNode(arg.type),
536
+ });
537
+ return { argDeclaration, argName };
538
+ }
539
+
540
+ function buildArgumentDeclaration(arg: Argument): ts.ParameterDeclaration {
541
+ return buildArgumentDeclarationAndName(arg).argDeclaration;
542
+ }
543
+
544
+ export type buildFunctionOptions = {
545
+ functionDeclaration: FunctionDeclaration;
546
+ async?: boolean;
547
+ method?: boolean;
548
+ exported?: boolean;
549
+ declaration?: boolean;
550
+ returnStatement?: null | ts.ReturnStatement[];
551
+ overrideArgumentDeclarations?: ts.ParameterDeclaration[];
552
+ omitReturnType?: boolean;
553
+ };
554
+
555
+ export function buildFunction({
556
+ functionDeclaration,
557
+ async,
558
+ method,
559
+ exported,
560
+ declaration,
561
+ returnStatement,
562
+ overrideArgumentDeclarations,
563
+ omitReturnType,
564
+ }: buildFunctionOptions): ts.FunctionDeclaration | ts.MethodDeclaration {
565
+ const functionModifiers = constructModifiersArray({ exported, async: async && !declaration });
566
+ const customReturn = !!returnStatement;
567
+ const bareReturnTypeNode = mapTypeToTsTypeNode(functionDeclaration.returnType);
568
+
569
+ const wrapWithPromiseType = (typeNode: ts.TypeNode): ts.TypeNode =>
570
+ ts.factory.createTypeReferenceNode('Promise', [typeNode]);
571
+
572
+ let returnTypeNode: ts.TypeNode | undefined = async
573
+ ? wrapWithPromiseType(bareReturnTypeNode)
574
+ : bareReturnTypeNode;
575
+ if (omitReturnType) {
576
+ returnTypeNode = undefined;
577
+ }
578
+ const argumentDeclarations =
579
+ overrideArgumentDeclarations ?? functionDeclaration.arguments.map(buildArgumentDeclaration);
580
+
581
+ if (method) {
582
+ return ts.factory.createMethodDeclaration(
583
+ functionModifiers,
584
+ undefined,
585
+ functionDeclaration.name,
586
+ undefined,
587
+ undefined,
588
+ argumentDeclarations,
589
+ returnTypeNode,
590
+ declaration ? undefined : ts.factory.createBlock(customReturn ? returnStatement : [])
591
+ );
592
+ }
593
+ return ts.factory.createFunctionDeclaration(
594
+ functionModifiers,
595
+ undefined,
596
+ functionDeclaration.name,
597
+ undefined,
598
+ argumentDeclarations,
599
+ returnTypeNode,
600
+ declaration ? undefined : ts.factory.createBlock(customReturn ? returnStatement : [])
601
+ );
602
+ }
603
+
604
+ export function buildConstructor(
605
+ constructor: ConstructorDeclaration,
606
+ declaration: boolean
607
+ ): ts.ClassElement {
608
+ return ts.factory.createConstructorDeclaration(
609
+ undefined,
610
+ constructor.arguments.map(buildArgumentDeclaration),
611
+ declaration ? undefined : ts.factory.createBlock([])
612
+ );
613
+ }
614
+
615
+ type BuildClassOptions = {
616
+ classDeclaration: ClassDeclaration;
617
+ exported?: boolean;
618
+ declaration?: boolean;
619
+ getFunctionReturnBlock?: (functionDeclaration: FunctionDeclaration) => ts.ReturnStatement[];
620
+ };
621
+
622
+ // TODO(@HubertBer): figure out what about inheritance, should or should not inherit SharedObject
623
+ export function buildClass({
624
+ classDeclaration,
625
+ exported,
626
+ declaration,
627
+ getFunctionReturnBlock,
628
+ }: BuildClassOptions): ts.ClassDeclaration {
629
+ const getReturnStatement = (method: FunctionDeclaration) =>
630
+ !declaration && getFunctionReturnBlock ? getFunctionReturnBlock(method) : null;
631
+
632
+ const buildMethod = (method: FunctionDeclaration, async?: boolean) =>
633
+ buildFunction({
634
+ functionDeclaration: method,
635
+ method: true,
636
+ async,
637
+ declaration,
638
+ returnStatement: getReturnStatement(method),
639
+ }) as ts.MethodDeclaration;
640
+
641
+ const classMembers = [
642
+ ...classDeclaration.methods.map((m) => buildMethod(m)),
643
+ ...classDeclaration.asyncMethods.map((m) => buildMethod(m, true)),
644
+ ...(declaration ? classDeclaration.properties.map(buildClassProperty) : []),
645
+ classDeclaration.constructor
646
+ ? buildConstructor(classDeclaration.constructor, declaration ?? false)
647
+ : undefined,
648
+ ].filter((x) => x !== undefined);
649
+
650
+ return ts.factory.createClassDeclaration(
651
+ constructModifiersArray({ exported, declare: declaration }),
652
+ ts.factory.createIdentifier(classDeclaration.name),
653
+ undefined,
654
+ [],
655
+ classMembers
656
+ );
657
+ }
658
+
659
+ function buildModuleDefaultExport({
660
+ moduleName,
661
+ moduleType,
662
+ declaration,
663
+ }: {
664
+ moduleName: string;
665
+ moduleType?: string;
666
+ declaration?: boolean;
667
+ }): ts.Node[] {
668
+ const name = '_default';
669
+ const type = moduleType ? ts.factory.createTypeReferenceNode(moduleType) : undefined;
670
+ return [
671
+ createParameter({
672
+ modifiers: [constModifier()],
673
+ name,
674
+ type,
675
+ initializer: declaration
676
+ ? undefined
677
+ : createRequireNativeModuleExpression({ moduleName, moduleType }),
678
+ }),
679
+ ts.factory.createExportDefault(ts.factory.createIdentifier('_default')),
680
+ ];
681
+ }
682
+
683
+ export function buildUnknownTypeAlias(
684
+ identifier: string,
685
+ exported: boolean,
686
+ inferredTypeParametersCount: Map<string, number>
687
+ ): ts.Statement {
688
+ const paramCount = inferredTypeParametersCount.get(identifier);
689
+ const typeParamsList = [];
690
+ for (let i = 0; i < (paramCount ?? 0); i += 1) {
691
+ typeParamsList.push(ts.factory.createTypeParameterDeclaration(undefined, 'T' + i));
692
+ }
693
+ const typeParams = (paramCount ?? 0) === 0 ? undefined : typeParamsList;
694
+ return createTypeAlias({ exported, alias: identifier, type: unknownKeywordType(), typeParams });
695
+ }
696
+
697
+ export function buildRecordTypeAlias(recordType: RecordType, exported: boolean): ts.Node {
698
+ return createTypeAlias({
699
+ exported,
700
+ alias: recordType.name,
701
+ type: ts.factory.createTypeLiteralNode(
702
+ recordType.fields.map((field) => {
703
+ const optional = field.type.kind === TypeKind.OPTIONAL;
704
+ const typeNode = mapTypeToTsTypeNode(optional ? (field.type.type as Type) : field.type);
705
+ const name = field.name ?? '_' + getNextFreeId();
706
+ return createPropertySignature({ name, optional, typeNode });
707
+ })
708
+ ),
709
+ });
710
+ }
711
+
712
+ export function buildEnumTypeDeclaration(
713
+ enumType: EnumType,
714
+ exported: boolean,
715
+ declared: boolean
716
+ ): ts.Node {
717
+ return ts.factory.createEnumDeclaration(
718
+ constructModifiersArray({ exported, declare: declared }),
719
+ enumType.name,
720
+ enumType.cases.map((enumcase) => ts.factory.createEnumMember(enumcase))
721
+ );
722
+ }
723
+
724
+ function buildMissingTypesDeclarations(ctx: GenerationContext): ts.Node[] {
725
+ if (ctx.missingTypes.size === 0) {
726
+ return [];
727
+ }
728
+
729
+ const header = ts.addSyntheticLeadingComment(
730
+ ts.factory.createIdentifier(''),
731
+ ts.SyntaxKind.SingleLineCommentTrivia,
732
+ ` These types haven't been defined in provided file(s).`,
733
+ true
734
+ );
735
+
736
+ const aliases = [...ctx.missingTypes].map((identifier) =>
737
+ buildUnknownTypeAlias(identifier, true, ctx.fileInfo.inferredTypeParametersCount)
738
+ );
739
+
740
+ return [header, ...aliases];
741
+ }
742
+
743
+ function buildDefaultViewComponent({
744
+ componentName,
745
+ propsTypeAlias,
746
+ }: {
747
+ componentName: string;
748
+ propsTypeAlias: string;
749
+ }) {
750
+ const jsxElement = ts.factory.createJsxSelfClosingElement(
751
+ ts.factory.createIdentifier(componentName),
752
+ undefined,
753
+ ts.factory.createJsxAttributes([
754
+ ts.factory.createJsxSpreadAttribute(ts.factory.createIdentifier('props')),
755
+ ])
756
+ );
757
+
758
+ const functionBody = ts.factory.createBlock([ts.factory.createReturnStatement(jsxElement)]);
759
+
760
+ return [
761
+ ts.factory.createFunctionExpression(
762
+ [exportModifier(), defaultModifier()],
763
+ undefined,
764
+ componentName + 'Component',
765
+ undefined,
766
+ [
767
+ createParameter({
768
+ name: 'props',
769
+ type: ts.factory.createTypeReferenceNode(propsTypeAlias),
770
+ }),
771
+ ],
772
+ undefined,
773
+ functionBody
774
+ ),
775
+ ];
776
+ }
777
+
778
+ export function buildExposedTypesDeclarations(
779
+ ctx: GenerationContext,
780
+ options: { exported?: boolean; declare?: boolean }
781
+ ): ts.Node[] {
782
+ const recordDeclarationMap = (recordType: RecordType) =>
783
+ buildRecordTypeAlias(recordType, options.exported ?? false);
784
+ const enumDeclarationMap = (enumType: EnumType) =>
785
+ buildEnumTypeDeclaration(enumType, options.exported ?? false, options.declare ?? false);
786
+ const classDeclarationMap = (classDeclaration: ClassDeclaration) =>
787
+ buildClass({ classDeclaration, exported: true, declaration: true });
788
+
789
+ return joinTSNodesWithNewlines([
790
+ createImportDeclaration({ namedImportsNames: ['NativeModule'], importFromName: 'expo' }),
791
+ buildMissingTypesDeclarations(ctx),
792
+ ctx.fileInfo.records.flatMap(recordDeclarationMap),
793
+ ctx.fileInfo.enums.flatMap(enumDeclarationMap),
794
+ ctx.module.classes.map(classDeclarationMap),
795
+ ]);
796
+ }
797
+
798
+ function buildModuleDeclarationNodes(ctx: GenerationContext): ts.Node[] {
799
+ return joinTSNodesWithNewlines([
800
+ createGeneratedPrefix(),
801
+ buildExposedTypesDeclarations(ctx, { exported: true }),
802
+ buildNativeModuleClassDeclaration({ moduleClassDeclaration: ctx.module }),
803
+ buildModuleDefaultExport({
804
+ moduleName: ctx.module.name,
805
+ moduleType: ctx.module.name,
806
+ declaration: true,
807
+ }),
808
+ ] as ts.Node[][]);
809
+ }
810
+
811
+ export function getViewPropsTypeName(view: ViewDeclaration): string {
812
+ return view.name + (view.name.endsWith('View') ? 'Props' : 'ViewProps');
813
+ }
814
+
815
+ function buildViewDeclarationNodes(ctx: GenerationContext): ts.Node[] {
816
+ if (!ctx.view) {
817
+ return [];
818
+ }
819
+ const viewComponentType = createComponentType(getViewPropsTypeName(ctx.view));
820
+ const modifiers = [declareModifier(), constModifier()];
821
+ return joinTSNodesWithNewlines([
822
+ createGeneratedPrefix(),
823
+ createImportDeclaration({ namedImportsNames: ['ViewProps'], importFromName: 'react-native' }),
824
+ buildMissingTypesDeclarations(ctx),
825
+ buildViewPropsInterface(ctx.view, {}),
826
+ [createParameter({ modifiers, name: '_default', type: viewComponentType })],
827
+ createExportDefault(),
828
+ ]);
829
+ }
830
+
831
+ function buildJSXIntrinsicsViewNodes(ctx: GenerationContext): ts.Node[] {
832
+ const name = ctx.module.name;
833
+ const propsTypeNode = ctx.view
834
+ ? ts.factory.createTypeLiteralNode(buildPropsMembers(ctx.view))
835
+ : undefined;
836
+
837
+ const jsxIntrinsicElementsNodes = [];
838
+ if (ctx.view) {
839
+ const globalIdentifier = ts.factory.createIdentifier('global');
840
+ const jsxIdentifier = ts.factory.createIdentifier('JSX');
841
+ const intrinsicElementsIdentifier = ts.factory.createIdentifier('IntrinsicElements');
842
+ jsxIntrinsicElementsNodes.push(
843
+ ts.factory.createModuleDeclaration(
844
+ [declareModifier()],
845
+ globalIdentifier,
846
+ ts.factory.createModuleBlock([
847
+ ts.factory.createModuleDeclaration(
848
+ undefined,
849
+ jsxIdentifier,
850
+ ts.factory.createModuleBlock([
851
+ ts.factory.createInterfaceDeclaration(
852
+ undefined,
853
+ intrinsicElementsIdentifier,
854
+ undefined,
855
+ undefined,
856
+ [createPropertySignature({ name, typeNode: propsTypeNode })]
857
+ ),
858
+ ]),
859
+ ts.NodeFlags.Namespace
860
+ ),
861
+ ]),
862
+ ts.NodeFlags.GlobalAugmentation
863
+ )
864
+ );
865
+ }
866
+
867
+ return joinTSNodesWithNewlines([
868
+ buildExposedTypesDeclarations(ctx, { declare: true }),
869
+ jsxIntrinsicElementsNodes,
870
+ ]);
871
+ }
872
+
873
+ function buildNativeModuleGeneratedNodes(ctx: GenerationContext): ts.Node[] {
874
+ return joinTSNodesWithNewlines([
875
+ createGeneratedPrefix(),
876
+ createImportDeclaration({ namedImportsNames: ['ViewProps'], importFromName: 'react-native' }),
877
+ buildExposedTypesDeclarations(ctx, { exported: true }),
878
+ buildViewPropsInterface(ctx.view, { exported: true }),
879
+ buildNativeModuleClassDeclaration({ moduleClassDeclaration: ctx.module }),
880
+ ]);
881
+ }
882
+
883
+ function buildStableNativeModuleInterface(ctx: GenerationContext): ts.Node[] {
884
+ const generatedModuleAlias = ctx.module.name;
885
+ const generatedModuleTypeAlias = `${ctx.module.name}NativeModuleType`;
886
+ const generatedFilePath = `./${ctx.module.name}.generated`;
887
+
888
+ const exportedFunctionReturnStatement = (
889
+ functionDeclaration: FunctionDeclaration,
890
+ overrideArguments?: ts.Identifier[]
891
+ ) => {
892
+ const expression = `${generatedModuleAlias}.${functionDeclaration.name}`;
893
+ const args =
894
+ overrideArguments ??
895
+ functionDeclaration.arguments.map((arg) =>
896
+ ts.factory.createIdentifier(arg.name ?? 'unnamedArgument')
897
+ );
898
+
899
+ return ts.factory.createReturnStatement(
900
+ createCall({
901
+ expression,
902
+ args,
903
+ })
904
+ );
905
+ };
906
+
907
+ const mapFunctionDeclarationTemplate =
908
+ (isAsync: boolean) => (functionDeclaration: FunctionDeclaration) => {
909
+ const argumentDeclarations = [];
910
+ const argumentNames = [];
911
+ for (const arg of functionDeclaration.arguments) {
912
+ const { argDeclaration, argName } = buildArgumentDeclarationAndName(arg);
913
+ argumentDeclarations.push(argDeclaration);
914
+ argumentNames.push(argName);
915
+ }
916
+ return [
917
+ buildFunction({
918
+ functionDeclaration,
919
+ async: isAsync,
920
+ exported: true,
921
+ returnStatement: [
922
+ exportedFunctionReturnStatement(
923
+ functionDeclaration,
924
+ argumentNames.map(ts.factory.createIdentifier)
925
+ ),
926
+ ],
927
+ overrideArgumentDeclarations: argumentDeclarations,
928
+ omitReturnType: true,
929
+ }),
930
+ ];
931
+ };
932
+
933
+ const mapSyncFunctionDeclaration = mapFunctionDeclarationTemplate(false);
934
+ const mapAsyncFunctionDeclaration = mapFunctionDeclarationTemplate(true);
935
+ const buildConstantExportProperty = (constant: ConstantDeclaration): ts.Node => {
936
+ const typeNode = mapTypeToTsTypeNode(constant.type);
937
+ const modifiers = [exportModifier(), constModifier()];
938
+ const initializer = ts.factory.createIdentifier(`${generatedModuleAlias}.${constant.name}`);
939
+ return createProperty({ modifiers, name: constant.name, typeNode, initializer });
940
+ };
941
+
942
+ return joinTSNodesWithNewlines([
943
+ ctx.view
944
+ ? createImportDeclaration({ importFromName: 'react', defaultImportName: 'React' })
945
+ : [],
946
+
947
+ createImportDeclaration({
948
+ namedImportsNames: [
949
+ ...ctx.fileInfo.usedTypeIdentifiers.difference(getBasicTypesIdentifiers()),
950
+ ...[generatedModuleTypeAlias, ctx.view ? getViewPropsTypeName(ctx.view) : null].filter(
951
+ (v) => v !== null
952
+ ),
953
+ ],
954
+ importFromName: generatedFilePath,
955
+ }),
956
+
957
+ createImportDeclaration({
958
+ namedImportsNames: ['requireNativeModule', 'requireNativeView'],
959
+ importFromName: 'expo',
960
+ }),
961
+
962
+ [
963
+ createParameter({
964
+ modifiers: [constModifier()],
965
+ name: ctx.module.name,
966
+ type: ts.factory.createTypeReferenceNode(generatedModuleTypeAlias),
967
+ initializer: createRequireNativeModuleExpression({
968
+ moduleName: ctx.module.name,
969
+ moduleType: generatedModuleTypeAlias,
970
+ }),
971
+ }),
972
+ ],
973
+
974
+ ctx.view ? createRequireNativeViewDeclaration(ctx.module, ctx.view) : [],
975
+
976
+ ctx.module.constants.map(buildConstantExportProperty),
977
+
978
+ ctx.module.functions.flatMap(mapSyncFunctionDeclaration),
979
+ ctx.module.asyncFunctions.flatMap(mapAsyncFunctionDeclaration),
980
+
981
+ ctx.view
982
+ ? buildDefaultViewComponent({
983
+ componentName: ctx.view.name,
984
+ propsTypeAlias: getViewPropsTypeName(ctx.view),
985
+ })
986
+ : [],
987
+ ]);
988
+ }
989
+
990
+ async function tsNodesToString(elements: ts.Node[]): Promise<string> {
991
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
992
+ const resultFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
993
+ const viewTypes = ts.factory.createNodeArray(elements);
994
+ const printedTs = printer.printList(
995
+ ts.ListFormat.MultiLine | ts.ListFormat.PreserveLines,
996
+ viewTypes,
997
+ resultFile
998
+ );
999
+ return await prettifyCode(printedTs, 'typescript');
1000
+ }
1001
+
1002
+ export async function prettifyCode(text: string, parser: 'babel' | 'typescript' = 'babel') {
1003
+ return await prettier.format(text, {
1004
+ parser,
1005
+ tabWidth: 2,
1006
+ printWidth: 100,
1007
+ trailingComma: 'none',
1008
+ singleQuote: true,
1009
+ });
1010
+ }
1011
+
1012
+ export async function generateViewTypesFileContent(
1013
+ fileTypeInformation: FileTypeInformation
1014
+ ): Promise<string | null> {
1015
+ const ctx = createDefaultGenerationContext(fileTypeInformation);
1016
+ if (!ctx) {
1017
+ return null;
1018
+ }
1019
+ return tsNodesToString(buildViewDeclarationNodes(ctx));
1020
+ }
1021
+
1022
+ export async function generateJSXIntrinsicsFileContent(
1023
+ fileTypeInformation: FileTypeInformation
1024
+ ): Promise<string | null> {
1025
+ const ctx = createDefaultGenerationContext(fileTypeInformation);
1026
+ if (!ctx) {
1027
+ return null;
1028
+ }
1029
+ return tsNodesToString(buildJSXIntrinsicsViewNodes(ctx));
1030
+ }
1031
+
1032
+ export async function generateModuleTypesFileContent(
1033
+ fileTypeInformation: FileTypeInformation
1034
+ ): Promise<string | null> {
1035
+ const ctx = createDefaultGenerationContext(fileTypeInformation);
1036
+ if (!ctx) {
1037
+ return null;
1038
+ }
1039
+ return tsNodesToString(buildModuleDeclarationNodes(ctx));
1040
+ }
1041
+
1042
+ export async function generateConciseTsInterface(
1043
+ fileTypeInformation: FileTypeInformation
1044
+ ): Promise<{
1045
+ volatileGeneratedFileContent: string;
1046
+ moduleTypescriptInterfaceFileContent: string;
1047
+ }> {
1048
+ const ctx = createDefaultGenerationContext(fileTypeInformation);
1049
+ if (!ctx) {
1050
+ return { volatileGeneratedFileContent: '', moduleTypescriptInterfaceFileContent: '' };
1051
+ }
1052
+
1053
+ const volatileGeneratedFileContent = await tsNodesToString(buildNativeModuleGeneratedNodes(ctx));
1054
+
1055
+ const moduleTypescriptInterfaceFileContent = await tsNodesToString(
1056
+ buildStableNativeModuleInterface(ctx)
1057
+ );
1058
+
1059
+ return {
1060
+ volatileGeneratedFileContent,
1061
+ moduleTypescriptInterfaceFileContent,
1062
+ };
1063
+ }
1064
+
1065
+ export async function generateFullTsInterface(fileTypeInformation: FileTypeInformation): Promise<{
1066
+ moduleTypesFile: OutputFile;
1067
+ moduleViewsFiles: OutputFile[];
1068
+ moduleNativeFile: OutputFile;
1069
+ indexFile: OutputFile;
1070
+ } | null> {
1071
+ const ctx = createDefaultGenerationContext(fileTypeInformation);
1072
+ if (!ctx) {
1073
+ return null;
1074
+ }
1075
+
1076
+ const moduleNativeFileImportName = `${ctx?.module.name}Module`;
1077
+ const moduleTypesFileImportName = `${ctx?.module.name}.types`;
1078
+ const moduleViewsFilesImportNames: string[] = [];
1079
+ const moduleTypesFileNodes = joinTSNodesWithNewlines([
1080
+ createGeneratedPrefix(),
1081
+ createImportDeclaration({ namedImportsNames: ['ViewProps'], importFromName: 'react-native' }),
1082
+ buildExposedTypesDeclarations(ctx, { exported: true }),
1083
+ ...ctx.module.views.map((view) => buildViewPropsInterface(view, { exported: true })),
1084
+ ]);
1085
+
1086
+ const moduleViewFilesNodes = [];
1087
+ for (const view of ctx.module.views) {
1088
+ const moduleViewFileNodes = joinTSNodesWithNewlines([
1089
+ createGeneratedPrefix(),
1090
+ createImportDeclaration({
1091
+ namedImportsNames: ['requireNativeView'],
1092
+ importFromName: 'expo',
1093
+ }),
1094
+ createImportDeclaration({
1095
+ namedImportsNames: [getViewPropsTypeName(view)],
1096
+ importFromName: `./${moduleTypesFileImportName}`,
1097
+ }),
1098
+ createRequireNativeViewDeclaration(ctx.module, view),
1099
+
1100
+ buildDefaultViewComponent({
1101
+ componentName: view.name,
1102
+ propsTypeAlias: getViewPropsTypeName(view),
1103
+ }),
1104
+ ]);
1105
+ moduleViewFilesNodes.push(moduleViewFileNodes);
1106
+ moduleViewsFilesImportNames.push(`${view.name}View`);
1107
+ }
1108
+
1109
+ const moduleNativeModuleNodes = joinTSNodesWithNewlines([
1110
+ createGeneratedPrefix(),
1111
+ createImportDeclaration({
1112
+ namedImportsNames: ['requireNativeModule', 'NativeModule'],
1113
+ importFromName: 'expo',
1114
+ }),
1115
+ createImportDeclaration({
1116
+ namedImportsNames: [...getAllNonBasicTypes(ctx.fileInfo)],
1117
+ importFromName: `./${moduleTypesFileImportName}`,
1118
+ }),
1119
+ buildNativeModuleClassDeclaration({
1120
+ moduleClassDeclaration: ctx.module,
1121
+ exportedModuleName: ctx.module.name,
1122
+ }),
1123
+ buildModuleDefaultExport({ moduleName: ctx.module.name, moduleType: ctx.module.name }),
1124
+ ]);
1125
+
1126
+ const indexFileNodes = joinTSNodesWithNewlines([
1127
+ createGeneratedPrefix(),
1128
+ createExportAllDeclaration({
1129
+ importFromName: `./${moduleTypesFileImportName}`,
1130
+ justTypes: true,
1131
+ }),
1132
+ createExportDefaultAsDeclaration({
1133
+ exportAsName: ctx.module.name,
1134
+ importFromName: `./${moduleNativeFileImportName}`,
1135
+ }),
1136
+
1137
+ ...ctx.module.views.map((view, idx) =>
1138
+ createExportDefaultAsDeclaration({
1139
+ exportAsName: view.name,
1140
+ importFromName: `./${moduleViewsFilesImportNames[idx]}`,
1141
+ })
1142
+ ),
1143
+ ]);
1144
+
1145
+ const [
1146
+ moduleTypesFileContent,
1147
+ moduleViewFilesContents,
1148
+ moduleNativeFileContent,
1149
+ indexFileContent,
1150
+ ] = await Promise.all([
1151
+ tsNodesToString(moduleTypesFileNodes),
1152
+ Promise.all(moduleViewFilesNodes.map(tsNodesToString)),
1153
+ tsNodesToString(moduleNativeModuleNodes),
1154
+ tsNodesToString(indexFileNodes),
1155
+ ]);
1156
+
1157
+ const moduleTypesFile = {
1158
+ content: moduleTypesFileContent,
1159
+ name: `${moduleTypesFileImportName}.ts`,
1160
+ };
1161
+ const moduleViewsFiles = moduleViewFilesContents.map((moduleViewFileContent, idx) => {
1162
+ return {
1163
+ content: moduleViewFileContent,
1164
+ name: `${moduleViewsFilesImportNames[idx]}.tsx`,
1165
+ };
1166
+ });
1167
+ const moduleNativeFile = {
1168
+ content: moduleNativeFileContent,
1169
+ name: `${moduleNativeFileImportName}.ts`,
1170
+ };
1171
+ const indexFile = { content: indexFileContent, name: `index.ts` };
1172
+
1173
+ return {
1174
+ moduleTypesFile,
1175
+ moduleViewsFiles,
1176
+ moduleNativeFile,
1177
+ indexFile,
1178
+ };
1179
+ }