expo-type-information 0.1.1 → 0.1.3

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.
@@ -47,6 +47,8 @@ const swiftDeclarationKind = {
47
47
  varParameter: 'source.lang.swift.decl.var.parameter',
48
48
  closure: 'source.lang.swift.expr.closure',
49
49
  enumcase: 'source.lang.swift.decl.enumcase',
50
+ varStatic: 'source.lang.swift.decl.var.static',
51
+ varGlobal: 'source.lang.swift.decl.var.global',
50
52
  };
51
53
 
52
54
  function isSwiftDictionary(type: string): boolean {
@@ -82,6 +84,14 @@ function isEnumStructure(structure: Structure): boolean {
82
84
  return structure['key.kind'] === swiftDeclarationKind.enum;
83
85
  }
84
86
 
87
+ function isClassStructure(structure: Structure): boolean {
88
+ return structure['key.kind'] === swiftDeclarationKind.class;
89
+ }
90
+
91
+ function isStructStructure(structure: Structure): boolean {
92
+ return structure['key.kind'] === swiftDeclarationKind.struct;
93
+ }
94
+
85
95
  function isRecordStructure(structure: Structure): boolean {
86
96
  const isRecordOrClass =
87
97
  structure['key.kind'] === swiftDeclarationKind.struct ||
@@ -256,6 +266,66 @@ function getIdentifierFromOffsetObject(offsetObject: Structure, file: FileType)
256
266
  return file.content.substring(startIndex, endIndex).replaceAll('"', '');
257
267
  }
258
268
 
269
+ function getLiteralOrResolvedIdentifier(offsetObject: Structure, ctx: ParseContext): string {
270
+ const { namespaces, file } = ctx;
271
+ const startIndex = offsetObject['key.offset'];
272
+ const endIndex = offsetObject['key.offset'] + offsetObject['key.length'];
273
+ const literalOrIdentifier = file.content.substring(startIndex, endIndex);
274
+
275
+ if (literalOrIdentifier.startsWith('"')) {
276
+ return literalOrIdentifier.replaceAll('"', '');
277
+ }
278
+
279
+ const parts = literalOrIdentifier.split('.');
280
+
281
+ const traverse = (baseObj: namespace | string | null, keys: string[]) => {
282
+ let current = baseObj;
283
+ for (const key of keys) {
284
+ if (typeof current === 'string' || current === null) {
285
+ return undefined;
286
+ }
287
+ const subNamespace = current[key];
288
+ if (subNamespace === undefined) {
289
+ return undefined;
290
+ }
291
+ current = subNamespace;
292
+ }
293
+ return current;
294
+ };
295
+
296
+ const n0 = traverse(namespaces, parts);
297
+ if (typeof n0 === 'string') {
298
+ return n0.replaceAll('"', '');
299
+ }
300
+
301
+ const n1 = traverse(namespaces[''] || {}, parts);
302
+ if (typeof n1 === 'string') {
303
+ return n1.replaceAll('"', '');
304
+ }
305
+
306
+ return parts[parts.length - 1] ?? literalOrIdentifier;
307
+ }
308
+
309
+ function isStringLiteral(str: string): boolean {
310
+ return str.length > 1 && str[0] == '"' && str[str.length - 1] == '"';
311
+ }
312
+
313
+ function getInitializerValue(structure: Structure, file: FileType): string | null {
314
+ const nameEnd = structure['key.nameoffset'] + structure['key.namelength'];
315
+ const declarationEnd = structure['key.offset'] + structure['key.length'];
316
+ if (!nameEnd || !declarationEnd) {
317
+ return null;
318
+ }
319
+ const valueSubstring = file.content.substring(nameEnd, declarationEnd);
320
+ const indexOfInitializerStart = valueSubstring.indexOf('=');
321
+ const initializer = valueSubstring.slice(indexOfInitializerStart + 1).trim();
322
+
323
+ if (!isStringLiteral(initializer)) {
324
+ return null;
325
+ }
326
+ return initializer;
327
+ }
328
+
259
329
  function hasSubstructure(structure: Structure) {
260
330
  return structure?.['key.substructure'] && structure['key.substructure'].length > 0;
261
331
  }
@@ -518,9 +588,10 @@ function getClosureBodyStructure(structure: Structure): Structure | null {
518
588
 
519
589
  async function parseModuleClassStructure(
520
590
  structure: Structure,
521
- file: FileType,
522
- options: SwiftFileTypeInformationOptions
591
+ options: SwiftFileTypeInformationOptions,
592
+ ctx: ParseContext
523
593
  ): Promise<ClassDeclaration> {
594
+ const { file } = ctx;
524
595
  const nestedModuleSubstructure = getClosureBodyStructure(structure)?.['key.substructure'];
525
596
  const nameSubstrucutre = structure['key.substructure']?.[0];
526
597
  const name = nameSubstrucutre
@@ -542,10 +613,10 @@ async function parseModuleClassStructure(
542
613
  // `parseModuleStructure` returns `ModuleClassDeclaration` with a found name or with the provided 'UNUSED_NAME', we don't need it here.
543
614
  const classTypeInfo = await parseModuleStructure(
544
615
  nestedModuleSubstructure,
545
- file,
546
616
  'UNUSED_NAME',
547
617
  structure['key.offset'],
548
- options
618
+ options,
619
+ ctx
549
620
  );
550
621
  return {
551
622
  name,
@@ -615,9 +686,10 @@ async function parseModulePropDeclaration(
615
686
 
616
687
  async function parseModuleViewDeclaration(
617
688
  substructure: Structure,
618
- file: FileType,
619
- options: SwiftFileTypeInformationOptions
689
+ options: SwiftFileTypeInformationOptions,
690
+ ctx: ParseContext
620
691
  ): Promise<ViewDeclaration | null> {
692
+ const { file } = ctx;
621
693
  // The View arguments is a.self for some class a we want.
622
694
  const suffixLength = 5;
623
695
  const nameSubstrucutre = substructure['key.substructure']?.[0];
@@ -634,17 +706,20 @@ async function parseModuleViewDeclaration(
634
706
 
635
707
  return await parseModuleStructure(
636
708
  viewSubstructure,
637
- file,
638
709
  name,
639
710
  viewStructure['key.offset'],
640
- options
711
+ options,
712
+ ctx
641
713
  );
642
714
  }
643
715
 
644
- function parseModuleEventDeclaration(structure: Structure, file: FileType, events: string[]) {
645
- structure['key.substructure'].forEach((substructure) =>
646
- events.push(getIdentifierFromOffsetObject(substructure, file))
647
- );
716
+ function parseModuleEventDeclaration(structure: Structure, events: string[], ctx: ParseContext) {
717
+ structure['key.substructure'].forEach((substructure) => {
718
+ const eventName = getLiteralOrResolvedIdentifier(substructure, ctx);
719
+ if (eventName) {
720
+ events.push(eventName);
721
+ }
722
+ });
648
723
  }
649
724
 
650
725
  function hasFieldAttribute(attributes: Attribute[] | null, file: FileType): boolean {
@@ -740,13 +815,23 @@ function parsePropertyString(
740
815
  };
741
816
  }
742
817
 
818
+ type namespace = {
819
+ [key: string]: namespace | string | null;
820
+ };
821
+
822
+ type ParseContext = {
823
+ file: FileType;
824
+ namespaces: { [key: string]: namespace };
825
+ };
826
+
743
827
  async function parseModuleStructure(
744
828
  moduleStructure: Structure[],
745
- file: FileType,
746
829
  name: string,
747
830
  definitionOffset: number,
748
- options: SwiftFileTypeInformationOptions
831
+ options: SwiftFileTypeInformationOptions,
832
+ ctx: ParseContext
749
833
  ): Promise<ModuleClassDeclaration> {
834
+ const { file } = ctx;
750
835
  const moduleClassDeclaration: ModuleClassDeclaration = {
751
836
  name,
752
837
  constants: [],
@@ -797,7 +882,7 @@ async function parseModuleStructure(
797
882
  }
798
883
  case 'Class':
799
884
  moduleClassDeclaration.classes.push(
800
- await parseModuleClassStructure(structure, file, options)
885
+ await parseModuleClassStructure(structure, options, ctx)
801
886
  );
802
887
  break;
803
888
  case 'Property': {
@@ -835,14 +920,14 @@ async function parseModuleStructure(
835
920
  );
836
921
  break;
837
922
  case 'View': {
838
- const viewDeclaration = await parseModuleViewDeclaration(structure, file, options);
923
+ const viewDeclaration = await parseModuleViewDeclaration(structure, options, ctx);
839
924
  if (viewDeclaration) {
840
925
  moduleClassDeclaration.views.push(viewDeclaration);
841
926
  }
842
927
  break;
843
928
  }
844
929
  case 'Events':
845
- parseModuleEventDeclaration(structure, file, moduleClassDeclaration.events);
930
+ parseModuleEventDeclaration(structure, moduleClassDeclaration.events, ctx);
846
931
  break;
847
932
  default:
848
933
  console.warn(`Module substructure not supported. ${structure['key.name']}`);
@@ -993,6 +1078,41 @@ function collectModuleTypeIdentifiers(
993
1078
  });
994
1079
  }
995
1080
 
1081
+ function parseNamespaces(
1082
+ structure: Structure,
1083
+ namespaces: { [key: string]: namespace },
1084
+ file: FileType,
1085
+ currentNamespace: namespace
1086
+ ) {
1087
+ if (
1088
+ isModuleStructure(structure) ||
1089
+ isRecordStructure(structure) ||
1090
+ isStructStructure(structure) ||
1091
+ isEnumStructure(structure) ||
1092
+ isClassStructure(structure)
1093
+ ) {
1094
+ const moduleName = structure['key.name'];
1095
+ namespaces[moduleName] = namespaces[moduleName] || {};
1096
+ const ns: namespace = namespaces[moduleName];
1097
+ currentNamespace[moduleName] = ns;
1098
+ structure['key.substructure'].forEach((substructure) => {
1099
+ parseNamespaces(substructure, namespaces, file, ns);
1100
+ });
1101
+ return;
1102
+ }
1103
+ if (
1104
+ structure['key.kind'] === swiftDeclarationKind.varStatic ||
1105
+ structure['key.kind'] === swiftDeclarationKind.varGlobal
1106
+ ) {
1107
+ currentNamespace[structure['key.name']] = getInitializerValue(structure, file);
1108
+ return;
1109
+ }
1110
+ const substructures = structure['key.substructure'];
1111
+ substructures?.forEach((substructure) =>
1112
+ parseNamespaces(substructure, namespaces, file, currentNamespace)
1113
+ );
1114
+ }
1115
+
996
1116
  export type SwiftFileTypeInformationOptions = {
997
1117
  typeInference: boolean;
998
1118
  };
@@ -1006,13 +1126,12 @@ export async function getSwiftFileTypeInformation(
1006
1126
  const modulesStructures: { name: string; structure: Structure }[] = [];
1007
1127
  const recordsStructures: Structure[] = [];
1008
1128
  const enumsStructures: Structure[] = [];
1009
- parseStructure(
1010
- getStructureFromFile(file),
1011
- '',
1012
- modulesStructures,
1013
- recordsStructures,
1014
- enumsStructures
1015
- );
1129
+ const fileStructure = getStructureFromFile(file);
1130
+ parseStructure(fileStructure, '', modulesStructures, recordsStructures, enumsStructures);
1131
+
1132
+ const namespaces: { [key: string]: namespace } = {};
1133
+ namespaces[''] = {};
1134
+ parseNamespaces(fileStructure, namespaces, file, namespaces['']);
1016
1135
 
1017
1136
  const inferredTypeParametersCount = new Map<string, number>();
1018
1137
  const moduleClasses: ModuleClassDeclaration[] = [];
@@ -1034,14 +1153,16 @@ export async function getSwiftFileTypeInformation(
1034
1153
  const recordsPromise = taskAll(recordsStructures, recordMap);
1035
1154
  const moduleClassDeclarationsPromise = taskAll(
1036
1155
  modulesStructures.filter(({ structure }) => hasSubstructure(structure)),
1037
- ({ structure, name }) =>
1038
- parseModuleStructure(
1156
+ ({ structure, name }) => {
1157
+ namespaces[name] = namespaces[name] || {};
1158
+ return parseModuleStructure(
1039
1159
  structure['key.substructure'],
1040
- file,
1041
1160
  name,
1042
1161
  structure['key.offset'],
1043
- options
1044
- )
1162
+ options,
1163
+ { namespaces, file }
1164
+ );
1165
+ }
1045
1166
  );
1046
1167
 
1047
1168
  const [records, moduleClassDeclarations] = await Promise.all([
package/src/types.ts CHANGED
@@ -17,6 +17,7 @@ export type Structure = {
17
17
  'key.offset': number;
18
18
  'key.length': number;
19
19
  'key.nameoffset': number;
20
+ 'key.namelength': number;
20
21
  'key.inheritedtypes': { 'key.name': string }[];
21
22
  'key.attributes': Attribute[];
22
23
  };
@@ -3,7 +3,20 @@ import WebKit
3
3
 
4
4
  public class TestModule: Module {
5
5
  public func definition() -> ModuleDefinition {
6
- Events("event1", "event2", "event3")
6
+ Events(
7
+ "event1",
8
+ "event2",
9
+ "event3",
10
+ globalEventName,
11
+ privateGlobalEventName,
12
+ EventNames.staticLetEvent,
13
+ EventNames.staticVarEvent,
14
+ EventStructNamespace.structEvent,
15
+ EventClassNamespace.classEvent,
16
+ OuterNamespace.InnerNamespace.nestedEvent,
17
+ // TODO(@HubertBer) Maybe fix this if this is needed, for now just document we don't support it
18
+ globalAssignedToGlobal
19
+ )
7
20
 
8
21
  Constant("StringConstant") { () -> Int in
9
22
  return "Swift constant 1283"
@@ -196,3 +209,27 @@ enum IntBackedEnum1: Int {
196
209
  enum IntBackedEnum2: Something, Int, SomethingElse {
197
210
  case simpleCase
198
211
  }
212
+ // Global variable names
213
+ let globalEventName = "onGlobalEvent"
214
+
215
+ private let privateGlobalEventName = "onPrivateGlobalEvent"
216
+ private let globalAssignedToGlobal = globalEventName
217
+
218
+ enum EventNames {
219
+ static let staticLetEvent = "onStaticLetEvent"
220
+ static var staticVarEvent = "onStaticVarEvent"
221
+ }
222
+
223
+ struct EventStructNamespace {
224
+ static let structEvent = "onStructEvent"
225
+ }
226
+
227
+ class EventClassNamespace {
228
+ static let classEvent = "onClassEvent"
229
+ }
230
+
231
+ enum OuterNamespace {
232
+ enum InnerNamespace {
233
+ static let nestedEvent = "onNestedEvent"
234
+ }
235
+ }