1import type { ReportableEvent } from 'metro'; 2import type { TerminalReportableEvent } from 'metro/src/lib/TerminalReporter'; 3import type { Terminal } from 'metro-core'; 4 5export type GlobalCacheDisabledReason = 'too_many_errors' | 'too_many_misses'; 6 7export type BundleDetails = { 8 buildID?: string; 9 bundleType: string; 10 dev: boolean; 11 entryFile: string; 12 minify: boolean; 13 platform: string | null | undefined; 14 customTransformOptions?: { environment?: 'node' }; 15 runtimeBytecodeVersion: number | null | undefined; 16}; 17 18export type BundleProgress = { 19 bundleDetails: BundleDetails; 20 transformedFileCount: number; 21 totalFileCount: number; 22 ratio: number; 23}; 24 25export { TerminalReportableEvent }; 26 27export type BuildPhase = 'in_progress' | 'done' | 'failed'; 28 29/** 30 * Code across the application takes a reporter as an option and calls the 31 * update whenever one of the ReportableEvent happens. Code does not directly 32 * write to the standard output, because a build would be: 33 * 34 * 1. ad-hoc, embedded into another tool, in which case we do not want to 35 * pollute that tool's own output. The tool is free to present the 36 * warnings/progress we generate any way they want, by specifying a custom 37 * reporter. 38 * 2. run as a background process from another tool, in which case we want 39 * to expose updates in a way that is easily machine-readable, for example 40 * a JSON-stream. We don't want to pollute it with textual messages. 41 * 42 * We centralize terminal reporting into a single place because we want the 43 * output to be robust and consistent. The most common reporter is 44 * TerminalReporter, that should be the only place in the application should 45 * access the `terminal` module (nor the `console`). 46 */ 47export type Reporter = { update(event: ReportableEvent): void }; 48 49export interface SnippetError extends Error { 50 code?: string; 51 filename?: string; 52 snippet?: string; 53 54 /** Module that failed to load ex 'fs' */ 55 targetModuleName?: string; 56 originModulePath?: string; 57 58 errors?: any[]; 59} 60 61export interface TerminalReporterInterface { 62 new (terminal: Terminal): TerminalReporterInterface; 63 64 /** 65 * The bundle builds for which we are actively maintaining the status on the 66 * terminal, ie. showing a progress bar. There can be several bundles being 67 * built at the same time. 68 */ 69 _activeBundles: Map<string, BundleProgress>; 70 71 _scheduleUpdateBundleProgress: { 72 (data: { buildID: string; transformedFileCount: number; totalFileCount: number }): void; 73 cancel(): void; 74 }; 75 76 /** Set in super type */ 77 terminal: Terminal; 78 79 /** 80 * Construct a message that represents the progress of a 81 * single bundle build, for example: 82 * 83 * BUNDLE path/to/bundle.js ▓▓▓▓▓░░░░░░░░░░░ 36.6% (4790/7922) 84 */ 85 _getBundleStatusMessage( 86 { 87 bundleDetails: { entryFile, bundleType, runtimeBytecodeVersion }, 88 transformedFileCount, 89 totalFileCount, 90 ratio, 91 }: BundleProgress, 92 phase: BuildPhase 93 ): string; 94 95 /** 96 * This function is only concerned with logging and should not do state 97 * or terminal status updates. 98 */ 99 _log(event: TerminalReportableEvent): void; 100 101 _logCacheDisabled(reason: GlobalCacheDisabledReason): void; 102 103 _logBundleBuildDone(buildID: string): void; 104 105 _logBundleBuildFailed(buildID: string): void; 106 107 _logInitializing(port: number, hasReducedPerformance: boolean): void; 108 109 _logInitializingFailed(port: number, error: SnippetError): void; 110 111 /** 112 * We do not want to log the whole stacktrace for bundling error, because 113 * these are operational errors, not programming errors, and the stacktrace 114 * is not actionable to end users. 115 */ 116 _logBundlingError(error: SnippetError): void; 117 118 /** 119 * We use Math.pow(ratio, 2) to as a conservative measure of progress because 120 * we know the `totalCount` is going to progressively increase as well. We 121 * also prevent the ratio from going backwards. 122 */ 123 _updateBundleProgress({ 124 buildID, 125 transformedFileCount, 126 totalFileCount, 127 }: { 128 buildID: string; 129 transformedFileCount: number; 130 totalFileCount: number; 131 }): void; 132 133 /** 134 * This function is exclusively concerned with updating the internal state. 135 * No logging or status updates should be done at this point. 136 */ 137 _updateState(event: TerminalReportableEvent): void; 138 /** 139 * Return a status message that is always consistent with the current state 140 * of the application. Having this single function ensures we don't have 141 * different call sites overriding each other status messages. 142 */ 143 _getStatusMessage(): string; 144 145 _logHmrClientError(e: Error): void; 146 147 /** 148 * Single entry point for reporting events. That allows us to implement the 149 * corresponding JSON reporter easily and have a consistent reporting. 150 */ 151 update(event: TerminalReportableEvent): void; 152} 153