mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 12:20:38 +01:00
Stacked on #30410. Use "owner stacks" as the appended component stack if it is available on the Fiber. This will only be available if the enableOwnerStacks flag is on. Otherwise it fallback to parent stacks. In prod, there's no owner so it's never added there. I was going back and forth on whether to inject essentially `captureOwnerStack` as part of the DevTools hooks or replicate the implementation but decided to replicate the implementation. The DevTools needs all the same information from internals to implement owner views elsewhere in the UI anyway so we're not saving anything in terms of the scope of internals. Additionally, we really need this information for non-current components as well like "rendered by" views of the currently selected component. It can also be useful if we need to change the format after the fact like we did for parent stacks in: https://github.com/facebook/react/pull/30289 Injecting the implementation would lock us into specifics both in terms of what the core needs to provide and what the DevTools can use. The implementation depends on the technique used in #30369 which tags frames to strip out with `react-stack-bottom-frame`. That's how the implementation knows how to materialize the error if it hasn't already. Firefox: <img width="487" alt="Screenshot 2024-07-21 at 11 33 37 PM" src="https://github.com/user-attachments/assets/d3539b53-4578-4fdd-af25-25698b2bcc7d"> Follow up: One thing about this view is that it doesn't include the current actual synchronous stack. When I used to append these I would include both the real current stack and the owner stack. That's because the owner stack doesn't include the name of the currently executing component. I'll probably inject the current stack too in addition to the owner stack. This is similar to how native Async Stacks are basically just appended onto the current stack rather than its own.
88 lines
3.0 KiB
JavaScript
88 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
const semver = require('semver');
|
|
const {ReactVersion} = require('../../../ReactVersions');
|
|
|
|
// DevTools stores preferences between sessions in localStorage
|
|
if (!global.hasOwnProperty('localStorage')) {
|
|
global.localStorage = require('local-storage-fallback').default;
|
|
}
|
|
|
|
// Mimic the global we set with Webpack's DefinePlugin
|
|
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
|
global.__TEST__ = true;
|
|
global.__IS_FIREFOX__ = false;
|
|
global.__IS_CHROME__ = false;
|
|
global.__IS_EDGE__ = false;
|
|
|
|
const ReactVersionTestingAgainst = process.env.REACT_VERSION || ReactVersion;
|
|
|
|
global._test_react_version = (range, testName, callback) => {
|
|
const shouldPass = semver.satisfies(ReactVersionTestingAgainst, range);
|
|
|
|
if (shouldPass) {
|
|
test(testName, callback);
|
|
} else {
|
|
test.skip(testName, callback);
|
|
}
|
|
};
|
|
|
|
global._test_react_version_focus = (range, testName, callback) => {
|
|
const shouldPass = semver.satisfies(ReactVersionTestingAgainst, range);
|
|
|
|
if (shouldPass) {
|
|
test.only(testName, callback);
|
|
} else {
|
|
test.skip(testName, callback);
|
|
}
|
|
};
|
|
|
|
global._test_ignore_for_react_version = (testName, callback) => {
|
|
test.skip(testName, callback);
|
|
};
|
|
|
|
// Most of our tests call jest.resetModules in a beforeEach and the
|
|
// re-require all the React modules. However, the JSX runtime is injected by
|
|
// the compiler, so those bindings don't get updated. This causes warnings
|
|
// logged by the JSX runtime to not have a component stack, because component
|
|
// stack relies on the the secret internals object that lives on the React
|
|
// module, which because of the resetModules call is longer the same one.
|
|
//
|
|
// To workaround this issue, we use a proxy that re-requires the latest
|
|
// JSX Runtime from the require cache on every function invocation.
|
|
//
|
|
// Longer term we should migrate all our tests away from using require() and
|
|
// resetModules, and use import syntax instead so this kind of thing doesn't
|
|
// happen.
|
|
if (semver.gte(ReactVersionTestingAgainst, '17.0.0')) {
|
|
lazyRequireFunctionExports('react/jsx-dev-runtime');
|
|
|
|
// TODO: We shouldn't need to do this in the production runtime, but until
|
|
// we remove string refs they also depend on the shared state object. Remove
|
|
// once we remove string refs.
|
|
lazyRequireFunctionExports('react/jsx-runtime');
|
|
}
|
|
|
|
function lazyRequireFunctionExports(moduleName) {
|
|
jest.mock(moduleName, () => {
|
|
return new Proxy(jest.requireActual(moduleName), {
|
|
get(originalModule, prop) {
|
|
// If this export is a function, return a wrapper function that lazily
|
|
// requires the implementation from the current module cache.
|
|
if (typeof originalModule[prop] === 'function') {
|
|
// eslint-disable-next-line no-eval
|
|
const wrapper = eval(`
|
|
(function () {
|
|
return jest.requireActual(moduleName)[prop].apply(this, arguments);
|
|
})
|
|
// We use this to trick the filtering of Flight to exclude this frame.
|
|
//# sourceURL=<anonymous>`);
|
|
return wrapper;
|
|
} else {
|
|
return originalModule[prop];
|
|
}
|
|
},
|
|
});
|
|
});
|
|
}
|