main
js 110 lines 3.28 KB
Raw
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 {
11 ReactDebugInfo,
12 ReactIOInfo,
13 ReactAsyncInfo,
14 } from 'shared/ReactTypes';
15
16 import type {Chunk} from '../shared/ReactFlightImportMetadata';
17
18 export function loadChunk(filename: Chunk): Promise<mixed> {
19 return __turbopack_load_by_url__(filename);
20 }
21
22 // We cache ReactIOInfo across requests so that inner refreshes can dedupe with outer.
23 const chunkIOInfoCache: Map<string, ReactIOInfo> = __DEV__
24 ? new Map()
25 : (null as any);
26
27 export function addChunkDebugInfo(
28 target: ReactDebugInfo,
29 filename: string,
30 ): void {
31 if (!__DEV__) {
32 return;
33 }
34 let ioInfo = chunkIOInfoCache.get(filename);
35 if (ioInfo === undefined) {
36 let href;
37 try {
38 // $FlowFixMe[incompatible-type]
39 href = new URL(filename, document.baseURI).href;
40 } catch (_) {
41 href = filename;
42 }
43 let start = -1;
44 let end = -1;
45 let byteSize = 0;
46 // $FlowFixMe[method-unbinding]
47 if (typeof performance.getEntriesByType === 'function') {
48 // We may be able to collect the start and end time of this resource from Performance Observer.
49 const resourceEntries = performance.getEntriesByType('resource');
50 for (let i = 0; i < resourceEntries.length; i++) {
51 const resourceEntry = resourceEntries[i];
52 if (resourceEntry.name === href) {
53 start = resourceEntry.startTime;
54 end = start + resourceEntry.duration;
55 // $FlowFixMe[prop-missing]
56 byteSize = (resourceEntry.transferSize as any) || 0;
57 }
58 }
59 }
60 const value = Promise.resolve(href);
61 // $FlowFixMe[prop-missing]
62 value.status = 'fulfilled';
63 // Is there some more useful representation for the chunk?
64 // $FlowFixMe[prop-missing]
65 value.value = href;
66 // Create a fake stack frame that points to the beginning of the chunk. This is
67 // probably not source mapped so will link to the compiled source rather than
68 // any individual file that goes into the chunks.
69 const fakeStack = new Error('react-stack-top-frame');
70 if (fakeStack.stack.startsWith('Error: react-stack-top-frame')) {
71 // Looks like V8
72 fakeStack.stack =
73 'Error: react-stack-top-frame\n' +
74 // Add two frames since we always trim one off the top.
75 ' at Client Component Bundle (' +
76 href +
77 ':1:1)\n' +
78 ' at Client Component Bundle (' +
79 href +
80 ':1:1)';
81 } else {
82 // Looks like Firefox or Safari.
83 // Add two frames since we always trim one off the top.
84 fakeStack.stack =
85 'Client Component Bundle@' +
86 href +
87 ':1:1\n' +
88 'Client Component Bundle@' +
89 href +
90 ':1:1';
91 }
92 ioInfo = {
93 name: 'script',
94 start: start,
95 end: end,
96 value: value,
97 debugStack: fakeStack,
98 } as ReactIOInfo;
99 if (byteSize > 0) {
100 // $FlowFixMe[cannot-write]
101 ioInfo.byteSize = byteSize;
102 }
103 chunkIOInfoCache.set(filename, ioInfo);
104 }
105 // We could dedupe the async info too but conceptually each request is its own await.
106 const asyncInfo: ReactAsyncInfo = {
107 awaited: ioInfo,
108 };
109 target.push(asyncInfo);
110 }