67
REACT_ELEMENT_TYPE,
68
REACT_POSTPONE_TYPE,
69
ASYNC_ITERATOR,
70
+ REACT_FRAGMENT_TYPE,
71
} from 'shared/ReactSymbols';
72
73
+import getComponentNameFromType from 'shared/getComponentNameFromType';
74
+
75
export type {CallServerCallback, EncodeFormActionCallback};
76
77
interface FlightStreamController {
576
}
577
}
578
579
+function getServerComponentTaskName(componentInfo: ReactComponentInfo): string {
580
+ return '<' + (componentInfo.name || '...') + '>';
581
+}
582
+
583
+function getTaskName(type: mixed): string {
584
+ if (type === REACT_FRAGMENT_TYPE) {
585
+ return '<>';
586
+ }
587
+ if (typeof type === 'function') {
588
+ // This is a function so it must have been a Client Reference that resolved to
589
+ // a function. We use "use client" to indicate that this is the boundary into
590
+ // the client. There should only be one for any given owner chain.
591
+ return '"use client"';
592
+ }
593
+ if (
594
+ typeof type === 'object' &&
595
+ type !== null &&
596
+ type.$$typeof === REACT_LAZY_TYPE
597
+ ) {
598
+ if (type._init === readChunk) {
599
+ // This is a lazy node created by Flight. It is probably a client reference.
600
+ // We use the "use client" string to indicate that this is the boundary into
601
+ // the client. There will only be one for any given owner chain.
602
+ return '"use client"';
603
+ }
604
+ // We don't want to eagerly initialize the initializer in DEV mode so we can't
605
+ // call it to extract the type so we don't know the type of this component.
606
+ return '<...>';
607
+ }
608
+ try {
609
+ const name = getComponentNameFromType(type);
610
+ return name ? '<' + name + '>' : '<...>';
611
+ } catch (x) {
612
+ return '<...>';
613
+ }
614
+}
615
+
616
function createElement(
617
type: mixed,
618
key: mixed,
687
writable: true,
688
value: stack,
689
});
690
+
691
+ let task: null | ConsoleTask = null;
692
+ if (supportsCreateTask && stack !== null) {
693
+ const createTaskFn = (console: any).createTask.bind(
694
+ console,
695
+ getTaskName(type),
696
+ );
697
+ const callStack = buildFakeCallStack(stack, createTaskFn);
698
+ // This owner should ideally have already been initialized to avoid getting
699
+ // user stack frames on the stack.
700
+ const ownerTask = owner === null ? null : initializeFakeTask(owner);
701
+ if (ownerTask === null) {
702
+ task = callStack();
703
+ } else {
704
+ task = ownerTask.run(callStack);
705
+ }
706
+ }
707
Object.defineProperty(element, '_debugTask', {
708
configurable: false,
709
enumerable: false,
710
writable: true,
654
- value: null,
711
+ value: task,
712
});
713
}
714
// TODO: We should be freezing the element but currently, we might write into
1639
dispatchHint(code, hintModel);
1640
}
1641
1642
+// eslint-disable-next-line react-internal/no-production-logging
1643
+const supportsCreateTask =
1644
+ __DEV__ && enableOwnerStacks && !!(console: any).createTask;
1645
+
1646
+const taskCache: null | WeakMap<
1647
+ ReactComponentInfo | ReactAsyncInfo,
1648
+ ConsoleTask,
1649
+> = supportsCreateTask ? new WeakMap() : null;
1650
+
1651
+type FakeFunction<T> = (FakeFunction<T>) => T;
1652
+const fakeFunctionCache: Map<string, FakeFunction<any>> = __DEV__
1653
+ ? new Map()
1654
+ : (null: any);
1655
+
1656
+function createFakeFunction<T>(
1657
+ name: string,
1658
+ filename: string,
1659
+ line: number,
1660
+ col: number,
1661
+): FakeFunction<T> {
1662
+ // This creates a fake copy of a Server Module. It represents a module that has already
1663
+ // executed on the server but we re-execute a blank copy for its stack frames on the client.
1664
+
1665
+ const comment =
1666
+ '/* This module was rendered by a Server Component. Turn on Source Maps to see the server source. */';
1667
+
1668
+ // We generate code where the call is at the line and column of the server executed code.
1669
+ // This allows us to use the original source map as the source map of this fake file to
1670
+ // point to the original source.
1671
+ let code;
1672
+ if (line <= 1) {
1673
+ code = '_=>' + ' '.repeat(col < 4 ? 0 : col - 4) + '_()\n' + comment + '\n';
1674
+ } else {
1675
+ code =
1676
+ comment +
1677
+ '\n'.repeat(line - 2) +
1678
+ '_=>\n' +
1679
+ ' '.repeat(col < 1 ? 0 : col - 1) +
1680
+ '_()\n';
1681
+ }
1682
+
1683
+ if (filename) {
1684
+ code += '//# sourceURL=' + filename;
1685
+ }
1686
+
1687
+ // eslint-disable-next-line no-eval
1688
+ const fn: FakeFunction<T> = (0, eval)(code);
1689
+ // $FlowFixMe[cannot-write]
1690
+ Object.defineProperty(fn, 'name', {value: name || '(anonymous)'});
1691
+ // $FlowFixMe[prop-missing]
1692
+ fn.displayName = name;
1693
+ return fn;
1694
+}
1695
+
1696
+const frameRegExp =
1697
+ /^ {3} at (?:(.+) \(([^\)]+):(\d+):(\d+)\)|([^\)]+):(\d+):(\d+))$/;
1698
+
1699
+function buildFakeCallStack<T>(stack: string, innerCall: () => T): () => T {
1700
+ const frames = stack.split('\n');
1701
+ let callStack = innerCall;
1702
+ for (let i = 0; i < frames.length; i++) {
1703
+ const frame = frames[i];
1704
+ let fn = fakeFunctionCache.get(frame);
1705
+ if (fn === undefined) {
1706
+ const parsed = frameRegExp.exec(frame);
1707
+ if (!parsed) {
1708
+ // We assume the server returns a V8 compatible stack trace.
1709
+ continue;
1710
+ }
1711
+ const name = parsed[1] || '';
1712
+ const filename = parsed[2] || parsed[5] || '';
1713
+ const line = +(parsed[3] || parsed[6]);
1714
+ const col = +(parsed[4] || parsed[7]);
1715
+ fn = createFakeFunction(name, filename, line, col);
1716
+ }
1717
+ callStack = fn.bind(null, callStack);
1718
+ }
1719
+ return callStack;
1720
+}
1721
+
1722
+function initializeFakeTask(
1723
+ debugInfo: ReactComponentInfo | ReactAsyncInfo,
1724
+): null | ConsoleTask {
1725
+ if (taskCache === null || typeof debugInfo.stack !== 'string') {
1726
+ return null;
1727
+ }
1728
+ const componentInfo: ReactComponentInfo = (debugInfo: any); // Refined
1729
+ const stack: string = debugInfo.stack;
1730
+ const cachedEntry = taskCache.get((componentInfo: any));
1731
+ if (cachedEntry !== undefined) {
1732
+ return cachedEntry;
1733
+ }
1734
+
1735
+ const ownerTask =
1736
+ componentInfo.owner == null
1737
+ ? null
1738
+ : initializeFakeTask(componentInfo.owner);
1739
+
1740
+ // eslint-disable-next-line react-internal/no-production-logging
1741
+ const createTaskFn = (console: any).createTask.bind(
1742
+ console,
1743
+ getServerComponentTaskName(componentInfo),
1744
+ );
1745
+ const callStack = buildFakeCallStack(stack, createTaskFn);
1746
+
1747
+ if (ownerTask === null) {
1748
+ return callStack();
1749
+ } else {
1750
+ return ownerTask.run(callStack);
1751
+ }
1752
+}
1753
+
1754
function resolveDebugInfo(
1755
response: Response,
1756
id: number,
1763
'resolveDebugInfo should never be called in production mode. This is a bug in React.',
1764
);
1765
}
1766
+ // We eagerly initialize the fake task because this resolving happens outside any
1767
+ // render phase so we're not inside a user space stack at this point. If we waited
1768
+ // to initialize it when we need it, we might be inside user code.
1769
+ initializeFakeTask(debugInfo);
1770
const chunk = getChunk(response, id);
1771
const chunkDebugInfo: ReactDebugInfo =
1772
chunk._debugInfo || (chunk._debugInfo = []);
1788
const payload: [string, string, null | ReactComponentInfo, string, mixed] =
1789
parseModel(response, value);
1790
const methodName = payload[0];
1618
- // TODO: Restore the fake stack before logging.
1619
- // const stackTrace = payload[1];
1620
- // const owner = payload[2];
1791
+ const stackTrace = payload[1];
1792
+ const owner = payload[2];
1793
const env = payload[3];
1794
const args = payload.slice(4);
1623
- printToConsole(methodName, args, env);
1795
+ if (!enableOwnerStacks) {
1796
+ // Printing with stack isn't really limited to owner stacks but
1797
+ // we gate it behind the same flag for now while iterating.
1798
+ printToConsole(methodName, args, env);
1799
+ return;
1800
+ }
1801
+ const callStack = buildFakeCallStack(
1802
+ stackTrace,
1803
+ printToConsole.bind(null, methodName, args, env),
1804
+ );
1805
+ if (owner != null) {
1806
+ const task = initializeFakeTask(owner);
1807
+ if (task !== null) {
1808
+ task.run(callStack);
1809
+ return;
1810
+ }
1811
+ }
1812
+ callStack();
1813
}
1814
1815
function mergeBuffer(