2017-05-24 04:22:21 +08:00
|
|
|
/**
|
2022-10-18 23:19:24 +08:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2017-05-24 04:22:21 +08:00
|
|
|
*
|
2017-09-25 04:48:13 +08:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2017-05-24 04:22:21 +08:00
|
|
|
*
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
|
Split cross-package types from implementation
Some of our internal reconciler types have leaked into other packages.
Usually, these types are treated as opaque; we don't read and write
to its fields. This is good.
However, the type is often passed back to a reconciler method. For
example, React DOM creates a FiberRoot with `createContainer`, then
passes that root to `updateContainer`. It doesn't do anything with the
root except pass it through, but because `updateContainer` expects a
full FiberRoot, React DOM is still coupled to all its fields.
I don't know if there's an idiomatic way to handle this in Flow. Opaque
types are simlar, but those only work within a single file. AFAIK,
there's no way to use a package as the boundary for opaqueness.
The immediate problem this presents is that the reconciler refactor will
involve changes to our internal data structures. I don't want to have to
fork every single package that happens to pass through a Fiber or
FiberRoot, or access any one of its fields. So my current plan is to
share the same Flow type across both forks. The shared type will be a
superset of each implementation's type, e.g. Fiber will have both an
`expirationTime` field and a `lanes` field. The implementations will
diverge, but not the types.
To do this, I lifted the type definitions into a separate module.
2020-04-09 14:48:24 +08:00
|
|
|
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
2020-03-31 03:42:41 +08:00
|
|
|
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';
|
2017-10-26 02:07:54 +08:00
|
|
|
|
2017-11-03 03:50:03 +08:00
|
|
|
import {
|
|
|
|
|
findCurrentHostFiber,
|
|
|
|
|
findCurrentFiberUsingSlowPath,
|
2020-03-07 08:20:42 +08:00
|
|
|
} from 'react-reconciler/src/ReactFiberTreeReflection';
|
2021-03-06 05:02:02 +08:00
|
|
|
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
2020-03-22 06:22:01 +08:00
|
|
|
import {HostComponent} from 'react-reconciler/src/ReactWorkTags';
|
2017-10-25 07:55:00 +08:00
|
|
|
// Module provided by RN:
|
2019-05-23 15:23:54 +08:00
|
|
|
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
2021-07-26 21:56:59 +08:00
|
|
|
import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags';
|
2017-11-05 19:58:36 +08:00
|
|
|
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
2017-05-24 04:22:21 +08:00
|
|
|
|
Inline fbjs/lib/emptyObject (#13055)
* Inline fbjs/lib/emptyObject
* Explicit naming
* Compare to undefined
* Another approach for detecting whether we can mutate
Each renderer would have its own local LegacyRefsObject function.
While in general we don't want `instanceof`, here it lets us do a simple check: did *we* create the refs object?
Then we can mutate it.
If the check didn't pass, either we're attaching ref for the first time (so we know to use the constructor),
or (unlikely) we're attaching a ref to a component owned by another renderer. In this case, to avoid "losing"
refs, we assign them onto the new object. Even in that case it shouldn't "hop" between renderers anymore.
* Clearer naming
* Add test case for strings refs across renderers
* Use a shared empty object for refs by reading it from React
* Remove string refs from ReactART test
It's not currently possible to resetModules() between several renderers
without also resetting the `React` module. However, that leads to losing
the referential identity of the empty ref object, and thus subsequent
checks in the renderers for whether it is pooled fail (and cause assignments
to a frozen object).
This has always been the case, but we used to work around it by shimming
fbjs/lib/emptyObject in tests and preserving its referential identity.
This won't work anymore because we've inlined it. And preserving referential
identity of React itself wouldn't be great because it could be confusing during
testing (although we might want to revisit this in the future by moving its
stateful parts into a separate package).
For now, I'm removing string ref usage from this test because only this is
the only place in our tests where we hit this problem, and it's only
related to string refs, and not just ref mechanism in general.
* Simplify the condition
2018-06-19 20:41:42 +08:00
|
|
|
const emptyObject = {};
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
Object.freeze(emptyObject);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-26 21:56:59 +08:00
|
|
|
let createHierarchy;
|
|
|
|
|
let getHostNode;
|
|
|
|
|
let getHostProps;
|
|
|
|
|
let lastNonHostInstance;
|
2022-10-04 05:03:33 +08:00
|
|
|
let getInspectorDataForInstance: (
|
|
|
|
|
closestInstance: Fiber | null,
|
|
|
|
|
) => InspectorData;
|
2021-07-26 21:56:59 +08:00
|
|
|
let getOwnerHierarchy;
|
|
|
|
|
let traverseOwnerTreeUp;
|
2017-05-24 04:22:21 +08:00
|
|
|
|
2021-07-26 21:56:59 +08:00
|
|
|
if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
|
|
|
|
|
createHierarchy = function(fiberHierarchy) {
|
2017-05-24 04:22:21 +08:00
|
|
|
return fiberHierarchy.map(fiber => ({
|
2021-03-06 05:02:02 +08:00
|
|
|
name: getComponentNameFromType(fiber.type),
|
2020-03-31 03:42:41 +08:00
|
|
|
getInspectorData: findNodeHandle => {
|
|
|
|
|
return {
|
|
|
|
|
props: getHostProps(fiber),
|
|
|
|
|
source: fiber._debugSource,
|
|
|
|
|
measure: callback => {
|
|
|
|
|
// If this is Fabric, we'll find a ShadowNode and use that to measure.
|
|
|
|
|
const hostFiber = findCurrentHostFiber(fiber);
|
|
|
|
|
const shadowNode =
|
|
|
|
|
hostFiber != null &&
|
|
|
|
|
hostFiber.stateNode !== null &&
|
|
|
|
|
hostFiber.stateNode.node;
|
|
|
|
|
|
|
|
|
|
if (shadowNode) {
|
|
|
|
|
nativeFabricUIManager.measure(shadowNode, callback);
|
|
|
|
|
} else {
|
|
|
|
|
return UIManager.measure(
|
|
|
|
|
getHostNode(fiber, findNodeHandle),
|
|
|
|
|
callback,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
},
|
2017-05-24 04:22:21 +08:00
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-26 21:56:59 +08:00
|
|
|
getHostNode = function(fiber: Fiber | null, findNodeHandle) {
|
|
|
|
|
let hostNode;
|
|
|
|
|
// look for children first for the hostNode
|
|
|
|
|
// as composite fibers do not have a hostNode
|
|
|
|
|
while (fiber) {
|
|
|
|
|
if (fiber.stateNode !== null && fiber.tag === HostComponent) {
|
|
|
|
|
hostNode = findNodeHandle(fiber.stateNode);
|
|
|
|
|
}
|
|
|
|
|
if (hostNode) {
|
|
|
|
|
return hostNode;
|
|
|
|
|
}
|
|
|
|
|
fiber = fiber.child;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
getHostProps = function(fiber) {
|
|
|
|
|
const host = findCurrentHostFiber(fiber);
|
|
|
|
|
if (host) {
|
|
|
|
|
return host.memoizedProps || emptyObject;
|
|
|
|
|
}
|
|
|
|
|
return emptyObject;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
getInspectorDataForInstance = function(
|
|
|
|
|
closestInstance: Fiber | null,
|
|
|
|
|
): InspectorData {
|
2020-03-31 03:42:41 +08:00
|
|
|
// Handle case where user clicks outside of ReactNative
|
|
|
|
|
if (!closestInstance) {
|
|
|
|
|
return {
|
|
|
|
|
hierarchy: [],
|
|
|
|
|
props: emptyObject,
|
|
|
|
|
selectedIndex: null,
|
|
|
|
|
source: null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fiber = findCurrentFiberUsingSlowPath(closestInstance);
|
|
|
|
|
const fiberHierarchy = getOwnerHierarchy(fiber);
|
|
|
|
|
const instance = lastNonHostInstance(fiberHierarchy);
|
|
|
|
|
const hierarchy = createHierarchy(fiberHierarchy);
|
|
|
|
|
const props = getHostProps(instance);
|
|
|
|
|
const source = instance._debugSource;
|
|
|
|
|
const selectedIndex = fiberHierarchy.indexOf(instance);
|
|
|
|
|
|
|
|
|
|
return {
|
2022-10-08 06:43:02 +08:00
|
|
|
closestInstance: instance,
|
2020-03-31 03:42:41 +08:00
|
|
|
hierarchy,
|
|
|
|
|
props,
|
|
|
|
|
selectedIndex,
|
|
|
|
|
source,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-26 21:56:59 +08:00
|
|
|
getOwnerHierarchy = function(instance: any) {
|
|
|
|
|
const hierarchy = [];
|
|
|
|
|
traverseOwnerTreeUp(hierarchy, instance);
|
|
|
|
|
return hierarchy;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
lastNonHostInstance = function(hierarchy) {
|
|
|
|
|
for (let i = hierarchy.length - 1; i > 1; i--) {
|
|
|
|
|
const instance = hierarchy[i];
|
|
|
|
|
|
|
|
|
|
if (instance.tag !== HostComponent) {
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return hierarchy[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
traverseOwnerTreeUp = function(hierarchy, instance: any) {
|
|
|
|
|
if (instance) {
|
|
|
|
|
hierarchy.unshift(instance);
|
|
|
|
|
traverseOwnerTreeUp(hierarchy, instance._debugOwner);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-04 05:03:33 +08:00
|
|
|
let getInspectorDataForViewTag: (viewTag: number) => Object;
|
|
|
|
|
let getInspectorDataForViewAtPoint: (
|
|
|
|
|
findNodeHandle: (componentOrHandle: any) => ?number,
|
|
|
|
|
inspectedView: Object,
|
|
|
|
|
locationX: number,
|
|
|
|
|
locationY: number,
|
|
|
|
|
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
|
|
|
|
) => void;
|
2021-07-26 21:56:59 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
2017-05-24 04:22:21 +08:00
|
|
|
getInspectorDataForViewTag = function(viewTag: number): Object {
|
2017-06-10 07:10:40 +08:00
|
|
|
const closestInstance = getClosestInstanceFromNode(viewTag);
|
|
|
|
|
|
|
|
|
|
// Handle case where user clicks outside of ReactNative
|
|
|
|
|
if (!closestInstance) {
|
|
|
|
|
return {
|
|
|
|
|
hierarchy: [],
|
|
|
|
|
props: emptyObject,
|
2020-03-31 03:42:41 +08:00
|
|
|
selectedIndex: null,
|
2017-06-10 07:10:40 +08:00
|
|
|
source: null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fiber = findCurrentFiberUsingSlowPath(closestInstance);
|
2017-05-24 04:22:21 +08:00
|
|
|
const fiberHierarchy = getOwnerHierarchy(fiber);
|
|
|
|
|
const instance = lastNonHostInstance(fiberHierarchy);
|
|
|
|
|
const hierarchy = createHierarchy(fiberHierarchy);
|
|
|
|
|
const props = getHostProps(instance);
|
|
|
|
|
const source = instance._debugSource;
|
2020-03-31 03:42:41 +08:00
|
|
|
const selectedIndex = fiberHierarchy.indexOf(instance);
|
2017-05-24 04:22:21 +08:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
hierarchy,
|
|
|
|
|
props,
|
2020-03-31 03:42:41 +08:00
|
|
|
selectedIndex,
|
2017-05-24 04:22:21 +08:00
|
|
|
source,
|
|
|
|
|
};
|
|
|
|
|
};
|
2020-03-31 03:42:41 +08:00
|
|
|
|
|
|
|
|
getInspectorDataForViewAtPoint = function(
|
|
|
|
|
findNodeHandle: (componentOrHandle: any) => ?number,
|
|
|
|
|
inspectedView: Object,
|
|
|
|
|
locationX: number,
|
|
|
|
|
locationY: number,
|
|
|
|
|
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
|
|
|
|
): void {
|
|
|
|
|
let closestInstance = null;
|
|
|
|
|
|
|
|
|
|
if (inspectedView._internalInstanceHandle != null) {
|
|
|
|
|
// For Fabric we can look up the instance handle directly and measure it.
|
|
|
|
|
nativeFabricUIManager.findNodeAtPoint(
|
|
|
|
|
inspectedView._internalInstanceHandle.stateNode.node,
|
|
|
|
|
locationX,
|
|
|
|
|
locationY,
|
|
|
|
|
internalInstanceHandle => {
|
|
|
|
|
if (internalInstanceHandle == null) {
|
|
|
|
|
callback({
|
|
|
|
|
pointerY: locationY,
|
|
|
|
|
frame: {left: 0, top: 0, width: 0, height: 0},
|
|
|
|
|
...getInspectorDataForInstance(closestInstance),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closestInstance =
|
|
|
|
|
internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
|
2021-03-26 07:23:39 +08:00
|
|
|
|
|
|
|
|
// Note: this is deprecated and we want to remove it ASAP. Keeping it here for React DevTools compatibility for now.
|
|
|
|
|
const nativeViewTag =
|
|
|
|
|
internalInstanceHandle.stateNode.canonical._nativeTag;
|
|
|
|
|
|
2020-03-31 03:42:41 +08:00
|
|
|
nativeFabricUIManager.measure(
|
|
|
|
|
internalInstanceHandle.stateNode.node,
|
|
|
|
|
(x, y, width, height, pageX, pageY) => {
|
2021-03-26 07:23:39 +08:00
|
|
|
const inspectorData = getInspectorDataForInstance(
|
|
|
|
|
closestInstance,
|
|
|
|
|
);
|
2020-03-31 03:42:41 +08:00
|
|
|
callback({
|
2021-03-26 07:23:39 +08:00
|
|
|
...inspectorData,
|
2020-03-31 03:42:41 +08:00
|
|
|
pointerY: locationY,
|
|
|
|
|
frame: {left: pageX, top: pageY, width, height},
|
2021-03-26 07:23:39 +08:00
|
|
|
touchedViewTag: nativeViewTag,
|
2020-03-31 03:42:41 +08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
} else if (inspectedView._internalFiberInstanceHandleDEV != null) {
|
|
|
|
|
// For Paper we fall back to the old strategy using the React tag.
|
|
|
|
|
UIManager.findSubviewIn(
|
|
|
|
|
findNodeHandle(inspectedView),
|
|
|
|
|
[locationX, locationY],
|
|
|
|
|
(nativeViewTag, left, top, width, height) => {
|
|
|
|
|
const inspectorData = getInspectorDataForInstance(
|
|
|
|
|
getClosestInstanceFromNode(nativeViewTag),
|
|
|
|
|
);
|
|
|
|
|
callback({
|
|
|
|
|
...inspectorData,
|
|
|
|
|
pointerY: locationY,
|
|
|
|
|
frame: {left, top, width, height},
|
|
|
|
|
touchedViewTag: nativeViewTag,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
console.error(
|
2020-03-31 22:55:30 +08:00
|
|
|
'getInspectorDataForViewAtPoint expects to receive a host component',
|
2020-03-31 03:42:41 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-05-24 04:22:21 +08:00
|
|
|
} else {
|
|
|
|
|
getInspectorDataForViewTag = () => {
|
[RFC] Codemod invariant -> throw new Error (#22435)
* Hoist error codes import to module scope
When this code was written, the error codes map (`codes.json`) was
created on-the-fly, so we had to lazily require from inside the visitor.
Because `codes.json` is now checked into source, we can import it a
single time in module scope.
* Minify error constructors in production
We use a script to minify our error messages in production. Each message
is assigned an error code, defined in `scripts/error-codes/codes.json`.
Then our build script replaces the messages with a link to our
error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92
This enables us to write helpful error messages without increasing the
bundle size.
Right now, the script only works for `invariant` calls. It does not work
if you throw an Error object. This is an old Facebookism that we don't
really need, other than the fact that our error minification script
relies on it.
So, I've updated the script to minify error constructors, too:
Input:
Error(`A ${adj} message that contains ${noun}`);
Output:
Error(formatProdErrorMessage(ERR_CODE, adj, noun));
It only works for constructors that are literally named Error, though we
could add support for other names, too.
As a next step, I will add a lint rule to enforce that errors written
this way must have a corresponding error code.
* Minify "no fallback UI specified" error in prod
This error message wasn't being minified because it doesn't use
invariant. The reason it didn't use invariant is because this particular
error is created without begin thrown — it doesn't need to be thrown
because it's located inside the error handling part of the runtime.
Now that the error minification script supports Error constructors, we
can minify it by assigning it a production error code in
`scripts/error-codes/codes.json`.
To support the use of Error constructors more generally, I will add a
lint rule that enforces each message has a corresponding error code.
* Lint rule to detect unminified errors
Adds a lint rule that detects when an Error constructor is used without
a corresponding production error code.
We already have this for `invariant`, but not for regular errors, i.e.
`throw new Error(msg)`. There's also nothing that enforces the use of
`invariant` besides convention.
There are some packages where we don't care to minify errors. These are
packages that run in environments where bundle size is not a concern,
like react-pg. I added an override in the ESLint config to ignore these.
* Temporarily add invariant codemod script
I'm adding this codemod to the repo temporarily, but I'll revert it
in the same PR. That way we don't have to check it in but it's still
accessible (via the PR) if we need it later.
* [Automated] Codemod invariant -> Error
This commit contains only automated changes:
npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*"
yarn linc --fix
yarn prettier
I will do any manual touch ups in separate commits so they're easier
to review.
* Remove temporary codemod script
This reverts the codemod script and ESLint config I added temporarily
in order to perform the invariant codemod.
* Manual touch ups
A few manual changes I made after the codemod ran.
* Enable error code transform per package
Currently we're not consistent about which packages should have their
errors minified in production and which ones should.
This adds a field to the bundle configuration to control whether to
apply the transform. We should decide what the criteria is going
forward. I think it's probably a good idea to minify any package that
gets sent over the network. So yes to modules that run in the browser,
and no to modules that run on the server and during development only.
2021-10-01 03:01:28 +08:00
|
|
|
throw new Error(
|
2017-05-24 04:22:21 +08:00
|
|
|
'getInspectorDataForViewTag() is not available in production',
|
|
|
|
|
);
|
|
|
|
|
};
|
2020-03-31 03:42:41 +08:00
|
|
|
|
|
|
|
|
getInspectorDataForViewAtPoint = (
|
|
|
|
|
findNodeHandle: (componentOrHandle: any) => ?number,
|
|
|
|
|
inspectedView: Object,
|
|
|
|
|
locationX: number,
|
|
|
|
|
locationY: number,
|
|
|
|
|
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
|
|
|
|
): void => {
|
[RFC] Codemod invariant -> throw new Error (#22435)
* Hoist error codes import to module scope
When this code was written, the error codes map (`codes.json`) was
created on-the-fly, so we had to lazily require from inside the visitor.
Because `codes.json` is now checked into source, we can import it a
single time in module scope.
* Minify error constructors in production
We use a script to minify our error messages in production. Each message
is assigned an error code, defined in `scripts/error-codes/codes.json`.
Then our build script replaces the messages with a link to our
error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92
This enables us to write helpful error messages without increasing the
bundle size.
Right now, the script only works for `invariant` calls. It does not work
if you throw an Error object. This is an old Facebookism that we don't
really need, other than the fact that our error minification script
relies on it.
So, I've updated the script to minify error constructors, too:
Input:
Error(`A ${adj} message that contains ${noun}`);
Output:
Error(formatProdErrorMessage(ERR_CODE, adj, noun));
It only works for constructors that are literally named Error, though we
could add support for other names, too.
As a next step, I will add a lint rule to enforce that errors written
this way must have a corresponding error code.
* Minify "no fallback UI specified" error in prod
This error message wasn't being minified because it doesn't use
invariant. The reason it didn't use invariant is because this particular
error is created without begin thrown — it doesn't need to be thrown
because it's located inside the error handling part of the runtime.
Now that the error minification script supports Error constructors, we
can minify it by assigning it a production error code in
`scripts/error-codes/codes.json`.
To support the use of Error constructors more generally, I will add a
lint rule that enforces each message has a corresponding error code.
* Lint rule to detect unminified errors
Adds a lint rule that detects when an Error constructor is used without
a corresponding production error code.
We already have this for `invariant`, but not for regular errors, i.e.
`throw new Error(msg)`. There's also nothing that enforces the use of
`invariant` besides convention.
There are some packages where we don't care to minify errors. These are
packages that run in environments where bundle size is not a concern,
like react-pg. I added an override in the ESLint config to ignore these.
* Temporarily add invariant codemod script
I'm adding this codemod to the repo temporarily, but I'll revert it
in the same PR. That way we don't have to check it in but it's still
accessible (via the PR) if we need it later.
* [Automated] Codemod invariant -> Error
This commit contains only automated changes:
npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*"
yarn linc --fix
yarn prettier
I will do any manual touch ups in separate commits so they're easier
to review.
* Remove temporary codemod script
This reverts the codemod script and ESLint config I added temporarily
in order to perform the invariant codemod.
* Manual touch ups
A few manual changes I made after the codemod ran.
* Enable error code transform per package
Currently we're not consistent about which packages should have their
errors minified in production and which ones should.
This adds a field to the bundle configuration to control whether to
apply the transform. We should decide what the criteria is going
forward. I think it's probably a good idea to minify any package that
gets sent over the network. So yes to modules that run in the browser,
and no to modules that run on the server and during development only.
2021-10-01 03:01:28 +08:00
|
|
|
throw new Error(
|
2020-03-31 03:42:41 +08:00
|
|
|
'getInspectorDataForViewAtPoint() is not available in production.',
|
|
|
|
|
);
|
|
|
|
|
};
|
2017-05-24 04:22:21 +08:00
|
|
|
}
|
|
|
|
|
|
2021-07-26 21:56:59 +08:00
|
|
|
export {
|
|
|
|
|
getInspectorDataForInstance,
|
|
|
|
|
getInspectorDataForViewAtPoint,
|
|
|
|
|
getInspectorDataForViewTag,
|
|
|
|
|
};
|