1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {Wakeable} from 'shared/ReactTypes';
    
  11. import type {TimelineData} from './types';
    
  12. 
    
  13. import {importFile as importFileWorker} from './import-worker';
    
  14. 
    
  15. const Pending = 0;
    
  16. const Resolved = 1;
    
  17. const Rejected = 2;
    
  18. 
    
  19. type PendingRecord = {
    
  20.   status: 0,
    
  21.   value: Wakeable,
    
  22. };
    
  23. 
    
  24. type ResolvedRecord<T> = {
    
  25.   status: 1,
    
  26.   value: T,
    
  27. };
    
  28. 
    
  29. type RejectedRecord = {
    
  30.   status: 2,
    
  31.   value: Error,
    
  32. };
    
  33. 
    
  34. type Record<T> = PendingRecord | ResolvedRecord<T> | RejectedRecord;
    
  35. 
    
  36. // This is intentionally a module-level Map, rather than a React-managed one.
    
  37. // Otherwise, refreshing the inspected element cache would also clear this cache.
    
  38. // Profiler file contents are static anyway.
    
  39. const fileNameToProfilerDataMap: Map<string, Record<TimelineData>> = new Map();
    
  40. 
    
  41. function readRecord<T>(record: Record<T>): ResolvedRecord<T> | RejectedRecord {
    
  42.   if (record.status === Resolved) {
    
  43.     // This is just a type refinement.
    
  44.     return record;
    
  45.   } else if (record.status === Rejected) {
    
  46.     // This is just a type refinement.
    
  47.     return record;
    
  48.   } else {
    
  49.     throw record.value;
    
  50.   }
    
  51. }
    
  52. 
    
  53. export function importFile(file: File): TimelineData | Error {
    
  54.   const fileName = file.name;
    
  55.   let record = fileNameToProfilerDataMap.get(fileName);
    
  56. 
    
  57.   if (!record) {
    
  58.     const callbacks = new Set<() => mixed>();
    
  59.     const wakeable: Wakeable = {
    
  60.       then(callback: () => mixed) {
    
  61.         callbacks.add(callback);
    
  62.       },
    
  63. 
    
  64.       // Optional property used by Timeline:
    
  65.       displayName: `Importing file "${fileName}"`,
    
  66.     };
    
  67. 
    
  68.     const wake = () => {
    
  69.       // This assumes they won't throw.
    
  70.       callbacks.forEach(callback => callback());
    
  71.       callbacks.clear();
    
  72.     };
    
  73. 
    
  74.     const newRecord: Record<TimelineData> = (record = {
    
  75.       status: Pending,
    
  76.       value: wakeable,
    
  77.     });
    
  78. 
    
  79.     importFileWorker(file).then(data => {
    
  80.       switch (data.status) {
    
  81.         case 'SUCCESS':
    
  82.           const resolvedRecord =
    
  83.             ((newRecord: any): ResolvedRecord<TimelineData>);
    
  84.           resolvedRecord.status = Resolved;
    
  85.           resolvedRecord.value = data.processedData;
    
  86.           break;
    
  87.         case 'INVALID_PROFILE_ERROR':
    
  88.         case 'UNEXPECTED_ERROR':
    
  89.           const thrownRecord = ((newRecord: any): RejectedRecord);
    
  90.           thrownRecord.status = Rejected;
    
  91.           thrownRecord.value = data.error;
    
  92.           break;
    
  93.       }
    
  94. 
    
  95.       wake();
    
  96.     });
    
  97. 
    
  98.     fileNameToProfilerDataMap.set(fileName, record);
    
  99.   }
    
  100. 
    
  101.   const response = readRecord(record).value;
    
  102.   return response;
    
  103. }