@react-devtools-plus/kit 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +254 -12
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +254 -12
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -105,9 +105,28 @@ function getFiberFromElement(element) {
|
|
|
105
105
|
//#endregion
|
|
106
106
|
//#region src/core/context/index.ts
|
|
107
107
|
/**
|
|
108
|
+
* Properties that can cause prototype pollution and should be filtered out
|
|
109
|
+
* SuperJSON will throw an error if these properties are present in the object
|
|
110
|
+
*/
|
|
111
|
+
const DANGEROUS_PROPERTIES$1 = new Set([
|
|
112
|
+
"constructor",
|
|
113
|
+
"__proto__",
|
|
114
|
+
"prototype"
|
|
115
|
+
]);
|
|
116
|
+
/**
|
|
117
|
+
* Check if a property key is safe to serialize (not a prototype pollution risk)
|
|
118
|
+
*/
|
|
119
|
+
function isSafePropertyKey$1(key) {
|
|
120
|
+
return !DANGEROUS_PROPERTIES$1.has(key);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
108
123
|
* Serialize a value into a displayable PropValue
|
|
124
|
+
* @param value - The value to serialize
|
|
125
|
+
* @param depth - Current recursion depth
|
|
126
|
+
* @param maxDepth - Maximum recursion depth
|
|
127
|
+
* @param context - Optional context to collect warnings about filtered properties
|
|
109
128
|
*/
|
|
110
|
-
function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
129
|
+
function serializeValue$1(value, depth = 0, maxDepth = 6, context) {
|
|
111
130
|
if (value === null) return {
|
|
112
131
|
type: "null",
|
|
113
132
|
value: "null"
|
|
@@ -150,7 +169,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
150
169
|
if (Array.isArray(value)) {
|
|
151
170
|
const children = {};
|
|
152
171
|
value.forEach((item, index) => {
|
|
153
|
-
children[String(index)] = serializeValue$1(item, depth + 1, maxDepth);
|
|
172
|
+
children[String(index)] = serializeValue$1(item, depth + 1, maxDepth, context);
|
|
154
173
|
});
|
|
155
174
|
return {
|
|
156
175
|
type: "array",
|
|
@@ -166,10 +185,13 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
166
185
|
value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
|
|
167
186
|
};
|
|
168
187
|
}
|
|
169
|
-
const
|
|
188
|
+
const allKeys = Object.keys(value);
|
|
189
|
+
const safeKeys = allKeys.filter(isSafePropertyKey$1);
|
|
190
|
+
const filteredKeys = allKeys.filter((key) => !isSafePropertyKey$1(key));
|
|
191
|
+
if (context && filteredKeys.length > 0) filteredKeys.forEach((key) => context.filteredProperties.add(key));
|
|
170
192
|
const children = {};
|
|
171
|
-
for (const key of
|
|
172
|
-
children[key] = serializeValue$1(value[key], depth + 1, maxDepth);
|
|
193
|
+
for (const key of safeKeys) try {
|
|
194
|
+
children[key] = serializeValue$1(value[key], depth + 1, maxDepth, context);
|
|
173
195
|
} catch (_unused) {
|
|
174
196
|
children[key] = {
|
|
175
197
|
type: "unknown",
|
|
@@ -179,7 +201,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
179
201
|
return {
|
|
180
202
|
type: "object",
|
|
181
203
|
value: `Object`,
|
|
182
|
-
preview:
|
|
204
|
+
preview: safeKeys.length > 0 ? `{${safeKeys.slice(0, 3).join(", ")}${safeKeys.length > 3 ? ", ..." : ""}}` : "{}",
|
|
183
205
|
children: Object.keys(children).length > 0 ? children : void 0
|
|
184
206
|
};
|
|
185
207
|
}
|
|
@@ -189,6 +211,23 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
189
211
|
};
|
|
190
212
|
}
|
|
191
213
|
/**
|
|
214
|
+
* Serialize a context value and collect any warnings
|
|
215
|
+
*/
|
|
216
|
+
function serializeContextValue(value) {
|
|
217
|
+
const context = { filteredProperties: /* @__PURE__ */ new Set() };
|
|
218
|
+
const propValue = serializeValue$1(value, 0, 6, context);
|
|
219
|
+
const warnings = [];
|
|
220
|
+
for (const property of context.filteredProperties) warnings.push({
|
|
221
|
+
type: "filtered_property",
|
|
222
|
+
property,
|
|
223
|
+
message: `Property "${property}" was hidden to prevent prototype pollution errors. This does not affect your application.`
|
|
224
|
+
});
|
|
225
|
+
return {
|
|
226
|
+
propValue,
|
|
227
|
+
warnings
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
192
231
|
* Get the display name of a Context
|
|
193
232
|
*/
|
|
194
233
|
function getContextDisplayName(fiber) {
|
|
@@ -264,10 +303,11 @@ function buildProviderInfo(fiber, visited) {
|
|
|
264
303
|
const consumers = [];
|
|
265
304
|
if (fiber.child) findConsumers(fiber.child, contextType, consumers, /* @__PURE__ */ new WeakSet());
|
|
266
305
|
const uniqueConsumers = consumers.filter((consumer, index, self) => index === self.findIndex((c) => c.id === consumer.id));
|
|
306
|
+
const { propValue, warnings } = serializeContextValue(getContextValue(fiber));
|
|
267
307
|
const info = {
|
|
268
308
|
id: getFiberId(fiber),
|
|
269
309
|
name: getContextDisplayName(fiber),
|
|
270
|
-
value:
|
|
310
|
+
value: propValue,
|
|
271
311
|
fiberId: getFiberId(fiber),
|
|
272
312
|
consumerCount: uniqueConsumers.length,
|
|
273
313
|
consumers: uniqueConsumers,
|
|
@@ -276,7 +316,8 @@ function buildProviderInfo(fiber, visited) {
|
|
|
276
316
|
fileName: fiber._debugSource.fileName,
|
|
277
317
|
lineNumber: fiber._debugSource.lineNumber,
|
|
278
318
|
columnNumber: fiber._debugSource.columnNumber
|
|
279
|
-
} : void 0
|
|
319
|
+
} : void 0,
|
|
320
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
280
321
|
};
|
|
281
322
|
const findNestedProviders = (child) => {
|
|
282
323
|
var _child$type;
|
|
@@ -361,6 +402,21 @@ function getAllContexts(root) {
|
|
|
361
402
|
//#endregion
|
|
362
403
|
//#region src/core/fiber/details.ts
|
|
363
404
|
/**
|
|
405
|
+
* Properties that can cause prototype pollution and should be filtered out
|
|
406
|
+
* SuperJSON throws: "Detected property constructor. This is a property pollution risk"
|
|
407
|
+
*/
|
|
408
|
+
const DANGEROUS_PROPERTIES = new Set([
|
|
409
|
+
"constructor",
|
|
410
|
+
"__proto__",
|
|
411
|
+
"prototype"
|
|
412
|
+
]);
|
|
413
|
+
/**
|
|
414
|
+
* Check if a property key is safe to serialize (not a prototype pollution risk)
|
|
415
|
+
*/
|
|
416
|
+
function isSafePropertyKey(key) {
|
|
417
|
+
return !DANGEROUS_PROPERTIES.has(key);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
364
420
|
* Serialize a value into a displayable PropValue
|
|
365
421
|
*/
|
|
366
422
|
function serializeValue(value, depth = 0, maxDepth = 8) {
|
|
@@ -422,7 +478,7 @@ function serializeValue(value, depth = 0, maxDepth = 8) {
|
|
|
422
478
|
value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
|
|
423
479
|
};
|
|
424
480
|
}
|
|
425
|
-
const keys = Object.keys(value);
|
|
481
|
+
const keys = Object.keys(value).filter(isSafePropertyKey);
|
|
426
482
|
const children = {};
|
|
427
483
|
for (const key of keys) try {
|
|
428
484
|
children[key] = serializeValue(value[key], depth + 1, maxDepth);
|
|
@@ -1586,7 +1642,7 @@ function extractRouteFromElement(element, parentPath = "") {
|
|
|
1586
1642
|
return routeInfo;
|
|
1587
1643
|
}
|
|
1588
1644
|
/**
|
|
1589
|
-
* Extract routes from Routes component's children prop
|
|
1645
|
+
* Extract routes from Routes component's children prop (React Router v6)
|
|
1590
1646
|
*/
|
|
1591
1647
|
function extractRoutesFromProps(props) {
|
|
1592
1648
|
const routes = [];
|
|
@@ -1602,6 +1658,181 @@ function extractRoutesFromProps(props) {
|
|
|
1602
1658
|
return routes;
|
|
1603
1659
|
}
|
|
1604
1660
|
/**
|
|
1661
|
+
* Get component name from React Router v5 Route props
|
|
1662
|
+
*/
|
|
1663
|
+
function getV5ComponentName(props) {
|
|
1664
|
+
if (props.component) return props.component.name || props.component.displayName;
|
|
1665
|
+
if (props.render) return "render()";
|
|
1666
|
+
if (typeof props.children === "function") return "children()";
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Check if a component is likely a React Router v5 Route by its props
|
|
1670
|
+
*/
|
|
1671
|
+
function isLikelyV5Route(props) {
|
|
1672
|
+
if (!props) return false;
|
|
1673
|
+
const hasPath = typeof props.path === "string" || props.path === void 0;
|
|
1674
|
+
const hasRouteProps = props.component || props.render || props.children !== void 0;
|
|
1675
|
+
const hasExactOrStrict = props.exact !== void 0 || props.strict !== void 0;
|
|
1676
|
+
const hasLocation = props.location !== void 0;
|
|
1677
|
+
const hasComputedMatch = props.computedMatch !== void 0;
|
|
1678
|
+
return hasPath && hasRouteProps || hasExactOrStrict || hasComputedMatch || hasLocation;
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Check if a component is likely a React Router v5 Switch by its props/children
|
|
1682
|
+
*/
|
|
1683
|
+
function isLikelyV5Switch(props) {
|
|
1684
|
+
if (!props || !props.children) return false;
|
|
1685
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1686
|
+
for (const child of children) if (child && child.props) {
|
|
1687
|
+
if (isLikelyV5Route(child.props)) return true;
|
|
1688
|
+
}
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Check if a component is likely a React Router v5 Redirect by its props
|
|
1693
|
+
*/
|
|
1694
|
+
function isLikelyV5Redirect(props) {
|
|
1695
|
+
if (!props) return false;
|
|
1696
|
+
return typeof props.to === "string" || typeof props.to === "object" && props.to !== null;
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* Check if a fiber node is a React Router v5 context provider
|
|
1700
|
+
* React Router v5 uses __RouterContext with value containing { history, location, match, staticContext }
|
|
1701
|
+
*/
|
|
1702
|
+
function isRouterContextProvider(fiber) {
|
|
1703
|
+
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
1704
|
+
if (!(props === null || props === void 0 ? void 0 : props.value)) return false;
|
|
1705
|
+
const value = props.value;
|
|
1706
|
+
return !!(value.history && value.location && typeof value.match === "object");
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Check if a fiber node is a React Router v5 Route context provider
|
|
1710
|
+
* Route wraps children in a context provider with match info
|
|
1711
|
+
*/
|
|
1712
|
+
function isRouteContextProvider(fiber) {
|
|
1713
|
+
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
1714
|
+
if (!(props === null || props === void 0 ? void 0 : props.value)) return false;
|
|
1715
|
+
const value = props.value;
|
|
1716
|
+
return !!(value.match && typeof value.match.path === "string");
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Extract routes from React Router v5 by traversing fiber tree and finding Route context providers
|
|
1720
|
+
* This works even when component names are minified
|
|
1721
|
+
*/
|
|
1722
|
+
function extractRoutesFromFiberV5(fiber) {
|
|
1723
|
+
const routes = [];
|
|
1724
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
1725
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
1726
|
+
function traverse$1(node) {
|
|
1727
|
+
if (!node || visited.has(node)) return;
|
|
1728
|
+
visited.add(node);
|
|
1729
|
+
const props = node.memoizedProps || node.pendingProps;
|
|
1730
|
+
if (isRouteContextProvider(node)) {
|
|
1731
|
+
const match = props.value.match;
|
|
1732
|
+
const path = match.path || match.url || "/";
|
|
1733
|
+
if (!seenPaths.has(path)) {
|
|
1734
|
+
seenPaths.add(path);
|
|
1735
|
+
const routeInfo = {
|
|
1736
|
+
path,
|
|
1737
|
+
params: match.params ? Object.keys(match.params) : void 0,
|
|
1738
|
+
exact: match.isExact
|
|
1739
|
+
};
|
|
1740
|
+
if (node.child) {
|
|
1741
|
+
const childName = getDisplayName(node.child);
|
|
1742
|
+
if (childName && childName !== "Anonymous" && childName.length > 1) routeInfo.element = childName;
|
|
1743
|
+
}
|
|
1744
|
+
routes.push(routeInfo);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (props === null || props === void 0 ? void 0 : props.children) {
|
|
1748
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1749
|
+
let hasRouteChildren = false;
|
|
1750
|
+
for (const child of children) if (child && child.props && isLikelyV5Route(child.props)) {
|
|
1751
|
+
hasRouteChildren = true;
|
|
1752
|
+
const route = extractRouteFromElementV5(child);
|
|
1753
|
+
if (route && !seenPaths.has(route.path)) {
|
|
1754
|
+
seenPaths.add(route.path);
|
|
1755
|
+
routes.push(route);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
if (hasRouteChildren) return;
|
|
1759
|
+
}
|
|
1760
|
+
if (node.child) traverse$1(node.child);
|
|
1761
|
+
if (node.sibling) traverse$1(node.sibling);
|
|
1762
|
+
}
|
|
1763
|
+
traverse$1(fiber);
|
|
1764
|
+
return routes;
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Extract route info from a React Router v5 Route element (JSX props)
|
|
1768
|
+
*/
|
|
1769
|
+
function extractRouteFromElementV5(element, parentPath = "") {
|
|
1770
|
+
if (!element || !element.props) return null;
|
|
1771
|
+
const props = element.props;
|
|
1772
|
+
const path = props.path || "*";
|
|
1773
|
+
let fullPath = path;
|
|
1774
|
+
if (path !== "*" && !path.startsWith("/") && parentPath) fullPath = `${parentPath}/${path}`.replace(/\/+/g, "/");
|
|
1775
|
+
const params = extractParamsFromPath(path);
|
|
1776
|
+
const childRoutes = [];
|
|
1777
|
+
if (props.children && typeof props.children !== "function") {
|
|
1778
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1779
|
+
for (const child of children) if (child && child.type) {
|
|
1780
|
+
var _child$type3;
|
|
1781
|
+
const typeName = child.type.name || ((_child$type3 = child.type) === null || _child$type3 === void 0 ? void 0 : _child$type3.displayName);
|
|
1782
|
+
const isRoute = typeName === "Route" || isLikelyV5Route(child.props);
|
|
1783
|
+
const isSwitch = typeName === "Switch" || isLikelyV5Switch(child.props);
|
|
1784
|
+
const isRedirect = typeName === "Redirect" || isLikelyV5Redirect(child.props);
|
|
1785
|
+
if (isRoute && !isRedirect) {
|
|
1786
|
+
const childRoute = extractRouteFromElementV5(child, fullPath);
|
|
1787
|
+
if (childRoute) childRoutes.push(childRoute);
|
|
1788
|
+
} else if (isSwitch) {
|
|
1789
|
+
const switchChildren = extractRoutesFromSwitchProps(child.props, fullPath);
|
|
1790
|
+
childRoutes.push(...switchChildren);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
const hasChildren = childRoutes.length > 0;
|
|
1795
|
+
const routeInfo = {
|
|
1796
|
+
path: fullPath,
|
|
1797
|
+
element: getV5ComponentName(props),
|
|
1798
|
+
exact: props.exact === true,
|
|
1799
|
+
strict: props.strict === true,
|
|
1800
|
+
isLayout: hasChildren,
|
|
1801
|
+
isLazy: isLazyElement(props.component),
|
|
1802
|
+
params: params.length > 0 ? params : void 0
|
|
1803
|
+
};
|
|
1804
|
+
if (hasChildren) routeInfo.children = childRoutes;
|
|
1805
|
+
return routeInfo;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Extract routes from Switch component's children prop (React Router v5)
|
|
1809
|
+
*/
|
|
1810
|
+
function extractRoutesFromSwitchProps(props, parentPath = "") {
|
|
1811
|
+
const routes = [];
|
|
1812
|
+
if (!(props === null || props === void 0 ? void 0 : props.children)) return routes;
|
|
1813
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1814
|
+
for (const child of children) if (child && child.type) {
|
|
1815
|
+
const typeName = child.type.name || child.type.displayName;
|
|
1816
|
+
const isRoute = typeName === "Route" || isLikelyV5Route(child.props);
|
|
1817
|
+
const isRedirect = typeName === "Redirect" || isLikelyV5Redirect(child.props);
|
|
1818
|
+
if (isRoute && !isRedirect) {
|
|
1819
|
+
const route = extractRouteFromElementV5(child, parentPath);
|
|
1820
|
+
if (route) routes.push(route);
|
|
1821
|
+
} else if (isRedirect) {
|
|
1822
|
+
var _child$props, _child$props2, _child$props3;
|
|
1823
|
+
const to = (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.to;
|
|
1824
|
+
const toPath = typeof to === "string" ? to : (to === null || to === void 0 ? void 0 : to.pathname) || "unknown";
|
|
1825
|
+
const redirectInfo = {
|
|
1826
|
+
path: ((_child$props2 = child.props) === null || _child$props2 === void 0 ? void 0 : _child$props2.from) || "*",
|
|
1827
|
+
element: `Redirect → ${toPath}`,
|
|
1828
|
+
exact: ((_child$props3 = child.props) === null || _child$props3 === void 0 ? void 0 : _child$props3.exact) === true
|
|
1829
|
+
};
|
|
1830
|
+
routes.push(redirectInfo);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
return routes;
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1605
1836
|
* Extract route configuration from fiber tree
|
|
1606
1837
|
*/
|
|
1607
1838
|
function extractRoutesFromFiber(fiber) {
|
|
@@ -1636,7 +1867,7 @@ function extractRoutesFromFiber(fiber) {
|
|
|
1636
1867
|
return routes;
|
|
1637
1868
|
}
|
|
1638
1869
|
/**
|
|
1639
|
-
* Find Routes component and extract route configuration
|
|
1870
|
+
* Find Routes/Switch component and extract route configuration
|
|
1640
1871
|
*/
|
|
1641
1872
|
function findAndExtractRoutes(fiber) {
|
|
1642
1873
|
if (!fiber) return [];
|
|
@@ -1654,6 +1885,13 @@ function findAndExtractRoutes(fiber) {
|
|
|
1654
1885
|
return;
|
|
1655
1886
|
}
|
|
1656
1887
|
}
|
|
1888
|
+
if (name === "Switch" || isLikelyV5Switch(props)) {
|
|
1889
|
+
const switchRoutes = extractRoutesFromSwitchProps(props);
|
|
1890
|
+
if (switchRoutes.length > 0) {
|
|
1891
|
+
routes = switchRoutes;
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1657
1895
|
if (name === "DataRoutes" || name === "RoutesRenderer") {
|
|
1658
1896
|
const fiberRoutes = extractRoutesFromFiber(node);
|
|
1659
1897
|
if (fiberRoutes.length > 0) routes.push(...fiberRoutes);
|
|
@@ -1662,6 +1900,7 @@ function findAndExtractRoutes(fiber) {
|
|
|
1662
1900
|
if (node.sibling) traverse$1(node.sibling);
|
|
1663
1901
|
}
|
|
1664
1902
|
traverse$1(fiber);
|
|
1903
|
+
if (routes.length === 0) routes = extractRoutesFromFiberV5(fiber);
|
|
1665
1904
|
return routes;
|
|
1666
1905
|
}
|
|
1667
1906
|
/**
|
|
@@ -1769,7 +2008,10 @@ function detectRouterType(fiber) {
|
|
|
1769
2008
|
if (!node || visited.has(node)) return null;
|
|
1770
2009
|
visited.add(node);
|
|
1771
2010
|
const name = getDisplayName(node);
|
|
1772
|
-
|
|
2011
|
+
const props = node.memoizedProps || node.pendingProps;
|
|
2012
|
+
if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Switch" || name === "Route") return "react-router";
|
|
2013
|
+
if (isLikelyV5Switch(props) || isLikelyV5Route(props)) return "react-router";
|
|
2014
|
+
if (isRouterContextProvider(node) || isRouteContextProvider(node)) return "react-router";
|
|
1773
2015
|
if (node.child) {
|
|
1774
2016
|
const result = traverse$1(node.child);
|
|
1775
2017
|
if (result) return result;
|
package/dist/index.d.cts
CHANGED
|
@@ -148,6 +148,16 @@ declare global {
|
|
|
148
148
|
}
|
|
149
149
|
//#endregion
|
|
150
150
|
//#region src/core/context/index.d.ts
|
|
151
|
+
/**
|
|
152
|
+
* Warning about filtered properties in context values
|
|
153
|
+
*/
|
|
154
|
+
interface ContextWarning {
|
|
155
|
+
type: 'filtered_property';
|
|
156
|
+
/** The property name that was filtered */
|
|
157
|
+
property: string;
|
|
158
|
+
/** Human-readable message */
|
|
159
|
+
message: string;
|
|
160
|
+
}
|
|
151
161
|
/**
|
|
152
162
|
* Information about a Context Provider
|
|
153
163
|
*/
|
|
@@ -171,6 +181,8 @@ interface ContextProviderInfo {
|
|
|
171
181
|
lineNumber: number;
|
|
172
182
|
columnNumber: number;
|
|
173
183
|
};
|
|
184
|
+
/** Warnings about the context value (e.g., filtered properties) */
|
|
185
|
+
warnings?: ContextWarning[];
|
|
174
186
|
}
|
|
175
187
|
/**
|
|
176
188
|
* Information about a Context Consumer
|
|
@@ -386,6 +398,10 @@ interface RouteInfo {
|
|
|
386
398
|
hasErrorBoundary?: boolean;
|
|
387
399
|
/** Dynamic segments in the path (e.g., :id, :userId) */
|
|
388
400
|
params?: string[];
|
|
401
|
+
/** Whether this route requires exact match (React Router v5) */
|
|
402
|
+
exact?: boolean;
|
|
403
|
+
/** Whether this route uses strict matching (React Router v5) */
|
|
404
|
+
strict?: boolean;
|
|
389
405
|
}
|
|
390
406
|
interface MatchedRoute {
|
|
391
407
|
path: string;
|
|
@@ -463,4 +479,4 @@ declare function createRpcServer<RemoteFunctions = Record<string, never>, LocalF
|
|
|
463
479
|
//#region src/messaging/presets/iframe/context.d.ts
|
|
464
480
|
declare function setIframeServerContext(iframe: HTMLIFrameElement | null): void;
|
|
465
481
|
//#endregion
|
|
466
|
-
export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
|
|
482
|
+
export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, ContextWarning, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
|
package/dist/index.d.ts
CHANGED
|
@@ -148,6 +148,16 @@ declare global {
|
|
|
148
148
|
}
|
|
149
149
|
//#endregion
|
|
150
150
|
//#region src/core/context/index.d.ts
|
|
151
|
+
/**
|
|
152
|
+
* Warning about filtered properties in context values
|
|
153
|
+
*/
|
|
154
|
+
interface ContextWarning {
|
|
155
|
+
type: 'filtered_property';
|
|
156
|
+
/** The property name that was filtered */
|
|
157
|
+
property: string;
|
|
158
|
+
/** Human-readable message */
|
|
159
|
+
message: string;
|
|
160
|
+
}
|
|
151
161
|
/**
|
|
152
162
|
* Information about a Context Provider
|
|
153
163
|
*/
|
|
@@ -171,6 +181,8 @@ interface ContextProviderInfo {
|
|
|
171
181
|
lineNumber: number;
|
|
172
182
|
columnNumber: number;
|
|
173
183
|
};
|
|
184
|
+
/** Warnings about the context value (e.g., filtered properties) */
|
|
185
|
+
warnings?: ContextWarning[];
|
|
174
186
|
}
|
|
175
187
|
/**
|
|
176
188
|
* Information about a Context Consumer
|
|
@@ -386,6 +398,10 @@ interface RouteInfo {
|
|
|
386
398
|
hasErrorBoundary?: boolean;
|
|
387
399
|
/** Dynamic segments in the path (e.g., :id, :userId) */
|
|
388
400
|
params?: string[];
|
|
401
|
+
/** Whether this route requires exact match (React Router v5) */
|
|
402
|
+
exact?: boolean;
|
|
403
|
+
/** Whether this route uses strict matching (React Router v5) */
|
|
404
|
+
strict?: boolean;
|
|
389
405
|
}
|
|
390
406
|
interface MatchedRoute {
|
|
391
407
|
path: string;
|
|
@@ -463,4 +479,4 @@ declare function createRpcServer<RemoteFunctions = Record<string, never>, LocalF
|
|
|
463
479
|
//#region src/messaging/presets/iframe/context.d.ts
|
|
464
480
|
declare function setIframeServerContext(iframe: HTMLIFrameElement | null): void;
|
|
465
481
|
//#endregion
|
|
466
|
-
export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
|
|
482
|
+
export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, ContextWarning, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
|
package/dist/index.js
CHANGED
|
@@ -81,9 +81,28 @@ function getFiberFromElement(element) {
|
|
|
81
81
|
//#endregion
|
|
82
82
|
//#region src/core/context/index.ts
|
|
83
83
|
/**
|
|
84
|
+
* Properties that can cause prototype pollution and should be filtered out
|
|
85
|
+
* SuperJSON will throw an error if these properties are present in the object
|
|
86
|
+
*/
|
|
87
|
+
const DANGEROUS_PROPERTIES$1 = new Set([
|
|
88
|
+
"constructor",
|
|
89
|
+
"__proto__",
|
|
90
|
+
"prototype"
|
|
91
|
+
]);
|
|
92
|
+
/**
|
|
93
|
+
* Check if a property key is safe to serialize (not a prototype pollution risk)
|
|
94
|
+
*/
|
|
95
|
+
function isSafePropertyKey$1(key) {
|
|
96
|
+
return !DANGEROUS_PROPERTIES$1.has(key);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
84
99
|
* Serialize a value into a displayable PropValue
|
|
100
|
+
* @param value - The value to serialize
|
|
101
|
+
* @param depth - Current recursion depth
|
|
102
|
+
* @param maxDepth - Maximum recursion depth
|
|
103
|
+
* @param context - Optional context to collect warnings about filtered properties
|
|
85
104
|
*/
|
|
86
|
-
function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
105
|
+
function serializeValue$1(value, depth = 0, maxDepth = 6, context) {
|
|
87
106
|
if (value === null) return {
|
|
88
107
|
type: "null",
|
|
89
108
|
value: "null"
|
|
@@ -126,7 +145,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
126
145
|
if (Array.isArray(value)) {
|
|
127
146
|
const children = {};
|
|
128
147
|
value.forEach((item, index) => {
|
|
129
|
-
children[String(index)] = serializeValue$1(item, depth + 1, maxDepth);
|
|
148
|
+
children[String(index)] = serializeValue$1(item, depth + 1, maxDepth, context);
|
|
130
149
|
});
|
|
131
150
|
return {
|
|
132
151
|
type: "array",
|
|
@@ -142,10 +161,13 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
142
161
|
value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
|
|
143
162
|
};
|
|
144
163
|
}
|
|
145
|
-
const
|
|
164
|
+
const allKeys = Object.keys(value);
|
|
165
|
+
const safeKeys = allKeys.filter(isSafePropertyKey$1);
|
|
166
|
+
const filteredKeys = allKeys.filter((key) => !isSafePropertyKey$1(key));
|
|
167
|
+
if (context && filteredKeys.length > 0) filteredKeys.forEach((key) => context.filteredProperties.add(key));
|
|
146
168
|
const children = {};
|
|
147
|
-
for (const key of
|
|
148
|
-
children[key] = serializeValue$1(value[key], depth + 1, maxDepth);
|
|
169
|
+
for (const key of safeKeys) try {
|
|
170
|
+
children[key] = serializeValue$1(value[key], depth + 1, maxDepth, context);
|
|
149
171
|
} catch (_unused) {
|
|
150
172
|
children[key] = {
|
|
151
173
|
type: "unknown",
|
|
@@ -155,7 +177,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
155
177
|
return {
|
|
156
178
|
type: "object",
|
|
157
179
|
value: `Object`,
|
|
158
|
-
preview:
|
|
180
|
+
preview: safeKeys.length > 0 ? `{${safeKeys.slice(0, 3).join(", ")}${safeKeys.length > 3 ? ", ..." : ""}}` : "{}",
|
|
159
181
|
children: Object.keys(children).length > 0 ? children : void 0
|
|
160
182
|
};
|
|
161
183
|
}
|
|
@@ -165,6 +187,23 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
|
|
|
165
187
|
};
|
|
166
188
|
}
|
|
167
189
|
/**
|
|
190
|
+
* Serialize a context value and collect any warnings
|
|
191
|
+
*/
|
|
192
|
+
function serializeContextValue(value) {
|
|
193
|
+
const context = { filteredProperties: /* @__PURE__ */ new Set() };
|
|
194
|
+
const propValue = serializeValue$1(value, 0, 6, context);
|
|
195
|
+
const warnings = [];
|
|
196
|
+
for (const property of context.filteredProperties) warnings.push({
|
|
197
|
+
type: "filtered_property",
|
|
198
|
+
property,
|
|
199
|
+
message: `Property "${property}" was hidden to prevent prototype pollution errors. This does not affect your application.`
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
propValue,
|
|
203
|
+
warnings
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
168
207
|
* Get the display name of a Context
|
|
169
208
|
*/
|
|
170
209
|
function getContextDisplayName(fiber) {
|
|
@@ -240,10 +279,11 @@ function buildProviderInfo(fiber, visited) {
|
|
|
240
279
|
const consumers = [];
|
|
241
280
|
if (fiber.child) findConsumers(fiber.child, contextType, consumers, /* @__PURE__ */ new WeakSet());
|
|
242
281
|
const uniqueConsumers = consumers.filter((consumer, index, self) => index === self.findIndex((c) => c.id === consumer.id));
|
|
282
|
+
const { propValue, warnings } = serializeContextValue(getContextValue(fiber));
|
|
243
283
|
const info = {
|
|
244
284
|
id: getFiberId(fiber),
|
|
245
285
|
name: getContextDisplayName(fiber),
|
|
246
|
-
value:
|
|
286
|
+
value: propValue,
|
|
247
287
|
fiberId: getFiberId(fiber),
|
|
248
288
|
consumerCount: uniqueConsumers.length,
|
|
249
289
|
consumers: uniqueConsumers,
|
|
@@ -252,7 +292,8 @@ function buildProviderInfo(fiber, visited) {
|
|
|
252
292
|
fileName: fiber._debugSource.fileName,
|
|
253
293
|
lineNumber: fiber._debugSource.lineNumber,
|
|
254
294
|
columnNumber: fiber._debugSource.columnNumber
|
|
255
|
-
} : void 0
|
|
295
|
+
} : void 0,
|
|
296
|
+
warnings: warnings.length > 0 ? warnings : void 0
|
|
256
297
|
};
|
|
257
298
|
const findNestedProviders = (child) => {
|
|
258
299
|
var _child$type;
|
|
@@ -337,6 +378,21 @@ function getAllContexts(root) {
|
|
|
337
378
|
//#endregion
|
|
338
379
|
//#region src/core/fiber/details.ts
|
|
339
380
|
/**
|
|
381
|
+
* Properties that can cause prototype pollution and should be filtered out
|
|
382
|
+
* SuperJSON throws: "Detected property constructor. This is a property pollution risk"
|
|
383
|
+
*/
|
|
384
|
+
const DANGEROUS_PROPERTIES = new Set([
|
|
385
|
+
"constructor",
|
|
386
|
+
"__proto__",
|
|
387
|
+
"prototype"
|
|
388
|
+
]);
|
|
389
|
+
/**
|
|
390
|
+
* Check if a property key is safe to serialize (not a prototype pollution risk)
|
|
391
|
+
*/
|
|
392
|
+
function isSafePropertyKey(key) {
|
|
393
|
+
return !DANGEROUS_PROPERTIES.has(key);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
340
396
|
* Serialize a value into a displayable PropValue
|
|
341
397
|
*/
|
|
342
398
|
function serializeValue(value, depth = 0, maxDepth = 8) {
|
|
@@ -398,7 +454,7 @@ function serializeValue(value, depth = 0, maxDepth = 8) {
|
|
|
398
454
|
value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
|
|
399
455
|
};
|
|
400
456
|
}
|
|
401
|
-
const keys = Object.keys(value);
|
|
457
|
+
const keys = Object.keys(value).filter(isSafePropertyKey);
|
|
402
458
|
const children = {};
|
|
403
459
|
for (const key of keys) try {
|
|
404
460
|
children[key] = serializeValue(value[key], depth + 1, maxDepth);
|
|
@@ -1562,7 +1618,7 @@ function extractRouteFromElement(element, parentPath = "") {
|
|
|
1562
1618
|
return routeInfo;
|
|
1563
1619
|
}
|
|
1564
1620
|
/**
|
|
1565
|
-
* Extract routes from Routes component's children prop
|
|
1621
|
+
* Extract routes from Routes component's children prop (React Router v6)
|
|
1566
1622
|
*/
|
|
1567
1623
|
function extractRoutesFromProps(props) {
|
|
1568
1624
|
const routes = [];
|
|
@@ -1578,6 +1634,181 @@ function extractRoutesFromProps(props) {
|
|
|
1578
1634
|
return routes;
|
|
1579
1635
|
}
|
|
1580
1636
|
/**
|
|
1637
|
+
* Get component name from React Router v5 Route props
|
|
1638
|
+
*/
|
|
1639
|
+
function getV5ComponentName(props) {
|
|
1640
|
+
if (props.component) return props.component.name || props.component.displayName;
|
|
1641
|
+
if (props.render) return "render()";
|
|
1642
|
+
if (typeof props.children === "function") return "children()";
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Check if a component is likely a React Router v5 Route by its props
|
|
1646
|
+
*/
|
|
1647
|
+
function isLikelyV5Route(props) {
|
|
1648
|
+
if (!props) return false;
|
|
1649
|
+
const hasPath = typeof props.path === "string" || props.path === void 0;
|
|
1650
|
+
const hasRouteProps = props.component || props.render || props.children !== void 0;
|
|
1651
|
+
const hasExactOrStrict = props.exact !== void 0 || props.strict !== void 0;
|
|
1652
|
+
const hasLocation = props.location !== void 0;
|
|
1653
|
+
const hasComputedMatch = props.computedMatch !== void 0;
|
|
1654
|
+
return hasPath && hasRouteProps || hasExactOrStrict || hasComputedMatch || hasLocation;
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Check if a component is likely a React Router v5 Switch by its props/children
|
|
1658
|
+
*/
|
|
1659
|
+
function isLikelyV5Switch(props) {
|
|
1660
|
+
if (!props || !props.children) return false;
|
|
1661
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1662
|
+
for (const child of children) if (child && child.props) {
|
|
1663
|
+
if (isLikelyV5Route(child.props)) return true;
|
|
1664
|
+
}
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Check if a component is likely a React Router v5 Redirect by its props
|
|
1669
|
+
*/
|
|
1670
|
+
function isLikelyV5Redirect(props) {
|
|
1671
|
+
if (!props) return false;
|
|
1672
|
+
return typeof props.to === "string" || typeof props.to === "object" && props.to !== null;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Check if a fiber node is a React Router v5 context provider
|
|
1676
|
+
* React Router v5 uses __RouterContext with value containing { history, location, match, staticContext }
|
|
1677
|
+
*/
|
|
1678
|
+
function isRouterContextProvider(fiber) {
|
|
1679
|
+
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
1680
|
+
if (!(props === null || props === void 0 ? void 0 : props.value)) return false;
|
|
1681
|
+
const value = props.value;
|
|
1682
|
+
return !!(value.history && value.location && typeof value.match === "object");
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Check if a fiber node is a React Router v5 Route context provider
|
|
1686
|
+
* Route wraps children in a context provider with match info
|
|
1687
|
+
*/
|
|
1688
|
+
function isRouteContextProvider(fiber) {
|
|
1689
|
+
const props = fiber.memoizedProps || fiber.pendingProps;
|
|
1690
|
+
if (!(props === null || props === void 0 ? void 0 : props.value)) return false;
|
|
1691
|
+
const value = props.value;
|
|
1692
|
+
return !!(value.match && typeof value.match.path === "string");
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Extract routes from React Router v5 by traversing fiber tree and finding Route context providers
|
|
1696
|
+
* This works even when component names are minified
|
|
1697
|
+
*/
|
|
1698
|
+
function extractRoutesFromFiberV5(fiber) {
|
|
1699
|
+
const routes = [];
|
|
1700
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
1701
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
1702
|
+
function traverse$1(node) {
|
|
1703
|
+
if (!node || visited.has(node)) return;
|
|
1704
|
+
visited.add(node);
|
|
1705
|
+
const props = node.memoizedProps || node.pendingProps;
|
|
1706
|
+
if (isRouteContextProvider(node)) {
|
|
1707
|
+
const match = props.value.match;
|
|
1708
|
+
const path = match.path || match.url || "/";
|
|
1709
|
+
if (!seenPaths.has(path)) {
|
|
1710
|
+
seenPaths.add(path);
|
|
1711
|
+
const routeInfo = {
|
|
1712
|
+
path,
|
|
1713
|
+
params: match.params ? Object.keys(match.params) : void 0,
|
|
1714
|
+
exact: match.isExact
|
|
1715
|
+
};
|
|
1716
|
+
if (node.child) {
|
|
1717
|
+
const childName = getDisplayName(node.child);
|
|
1718
|
+
if (childName && childName !== "Anonymous" && childName.length > 1) routeInfo.element = childName;
|
|
1719
|
+
}
|
|
1720
|
+
routes.push(routeInfo);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (props === null || props === void 0 ? void 0 : props.children) {
|
|
1724
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1725
|
+
let hasRouteChildren = false;
|
|
1726
|
+
for (const child of children) if (child && child.props && isLikelyV5Route(child.props)) {
|
|
1727
|
+
hasRouteChildren = true;
|
|
1728
|
+
const route = extractRouteFromElementV5(child);
|
|
1729
|
+
if (route && !seenPaths.has(route.path)) {
|
|
1730
|
+
seenPaths.add(route.path);
|
|
1731
|
+
routes.push(route);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
if (hasRouteChildren) return;
|
|
1735
|
+
}
|
|
1736
|
+
if (node.child) traverse$1(node.child);
|
|
1737
|
+
if (node.sibling) traverse$1(node.sibling);
|
|
1738
|
+
}
|
|
1739
|
+
traverse$1(fiber);
|
|
1740
|
+
return routes;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Extract route info from a React Router v5 Route element (JSX props)
|
|
1744
|
+
*/
|
|
1745
|
+
function extractRouteFromElementV5(element, parentPath = "") {
|
|
1746
|
+
if (!element || !element.props) return null;
|
|
1747
|
+
const props = element.props;
|
|
1748
|
+
const path = props.path || "*";
|
|
1749
|
+
let fullPath = path;
|
|
1750
|
+
if (path !== "*" && !path.startsWith("/") && parentPath) fullPath = `${parentPath}/${path}`.replace(/\/+/g, "/");
|
|
1751
|
+
const params = extractParamsFromPath(path);
|
|
1752
|
+
const childRoutes = [];
|
|
1753
|
+
if (props.children && typeof props.children !== "function") {
|
|
1754
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1755
|
+
for (const child of children) if (child && child.type) {
|
|
1756
|
+
var _child$type3;
|
|
1757
|
+
const typeName = child.type.name || ((_child$type3 = child.type) === null || _child$type3 === void 0 ? void 0 : _child$type3.displayName);
|
|
1758
|
+
const isRoute = typeName === "Route" || isLikelyV5Route(child.props);
|
|
1759
|
+
const isSwitch = typeName === "Switch" || isLikelyV5Switch(child.props);
|
|
1760
|
+
const isRedirect = typeName === "Redirect" || isLikelyV5Redirect(child.props);
|
|
1761
|
+
if (isRoute && !isRedirect) {
|
|
1762
|
+
const childRoute = extractRouteFromElementV5(child, fullPath);
|
|
1763
|
+
if (childRoute) childRoutes.push(childRoute);
|
|
1764
|
+
} else if (isSwitch) {
|
|
1765
|
+
const switchChildren = extractRoutesFromSwitchProps(child.props, fullPath);
|
|
1766
|
+
childRoutes.push(...switchChildren);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
const hasChildren = childRoutes.length > 0;
|
|
1771
|
+
const routeInfo = {
|
|
1772
|
+
path: fullPath,
|
|
1773
|
+
element: getV5ComponentName(props),
|
|
1774
|
+
exact: props.exact === true,
|
|
1775
|
+
strict: props.strict === true,
|
|
1776
|
+
isLayout: hasChildren,
|
|
1777
|
+
isLazy: isLazyElement(props.component),
|
|
1778
|
+
params: params.length > 0 ? params : void 0
|
|
1779
|
+
};
|
|
1780
|
+
if (hasChildren) routeInfo.children = childRoutes;
|
|
1781
|
+
return routeInfo;
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Extract routes from Switch component's children prop (React Router v5)
|
|
1785
|
+
*/
|
|
1786
|
+
function extractRoutesFromSwitchProps(props, parentPath = "") {
|
|
1787
|
+
const routes = [];
|
|
1788
|
+
if (!(props === null || props === void 0 ? void 0 : props.children)) return routes;
|
|
1789
|
+
const children = Array.isArray(props.children) ? props.children : [props.children];
|
|
1790
|
+
for (const child of children) if (child && child.type) {
|
|
1791
|
+
const typeName = child.type.name || child.type.displayName;
|
|
1792
|
+
const isRoute = typeName === "Route" || isLikelyV5Route(child.props);
|
|
1793
|
+
const isRedirect = typeName === "Redirect" || isLikelyV5Redirect(child.props);
|
|
1794
|
+
if (isRoute && !isRedirect) {
|
|
1795
|
+
const route = extractRouteFromElementV5(child, parentPath);
|
|
1796
|
+
if (route) routes.push(route);
|
|
1797
|
+
} else if (isRedirect) {
|
|
1798
|
+
var _child$props, _child$props2, _child$props3;
|
|
1799
|
+
const to = (_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.to;
|
|
1800
|
+
const toPath = typeof to === "string" ? to : (to === null || to === void 0 ? void 0 : to.pathname) || "unknown";
|
|
1801
|
+
const redirectInfo = {
|
|
1802
|
+
path: ((_child$props2 = child.props) === null || _child$props2 === void 0 ? void 0 : _child$props2.from) || "*",
|
|
1803
|
+
element: `Redirect → ${toPath}`,
|
|
1804
|
+
exact: ((_child$props3 = child.props) === null || _child$props3 === void 0 ? void 0 : _child$props3.exact) === true
|
|
1805
|
+
};
|
|
1806
|
+
routes.push(redirectInfo);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
return routes;
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1581
1812
|
* Extract route configuration from fiber tree
|
|
1582
1813
|
*/
|
|
1583
1814
|
function extractRoutesFromFiber(fiber) {
|
|
@@ -1612,7 +1843,7 @@ function extractRoutesFromFiber(fiber) {
|
|
|
1612
1843
|
return routes;
|
|
1613
1844
|
}
|
|
1614
1845
|
/**
|
|
1615
|
-
* Find Routes component and extract route configuration
|
|
1846
|
+
* Find Routes/Switch component and extract route configuration
|
|
1616
1847
|
*/
|
|
1617
1848
|
function findAndExtractRoutes(fiber) {
|
|
1618
1849
|
if (!fiber) return [];
|
|
@@ -1630,6 +1861,13 @@ function findAndExtractRoutes(fiber) {
|
|
|
1630
1861
|
return;
|
|
1631
1862
|
}
|
|
1632
1863
|
}
|
|
1864
|
+
if (name === "Switch" || isLikelyV5Switch(props)) {
|
|
1865
|
+
const switchRoutes = extractRoutesFromSwitchProps(props);
|
|
1866
|
+
if (switchRoutes.length > 0) {
|
|
1867
|
+
routes = switchRoutes;
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1633
1871
|
if (name === "DataRoutes" || name === "RoutesRenderer") {
|
|
1634
1872
|
const fiberRoutes = extractRoutesFromFiber(node);
|
|
1635
1873
|
if (fiberRoutes.length > 0) routes.push(...fiberRoutes);
|
|
@@ -1638,6 +1876,7 @@ function findAndExtractRoutes(fiber) {
|
|
|
1638
1876
|
if (node.sibling) traverse$1(node.sibling);
|
|
1639
1877
|
}
|
|
1640
1878
|
traverse$1(fiber);
|
|
1879
|
+
if (routes.length === 0) routes = extractRoutesFromFiberV5(fiber);
|
|
1641
1880
|
return routes;
|
|
1642
1881
|
}
|
|
1643
1882
|
/**
|
|
@@ -1745,7 +1984,10 @@ function detectRouterType(fiber) {
|
|
|
1745
1984
|
if (!node || visited.has(node)) return null;
|
|
1746
1985
|
visited.add(node);
|
|
1747
1986
|
const name = getDisplayName(node);
|
|
1748
|
-
|
|
1987
|
+
const props = node.memoizedProps || node.pendingProps;
|
|
1988
|
+
if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Switch" || name === "Route") return "react-router";
|
|
1989
|
+
if (isLikelyV5Switch(props) || isLikelyV5Route(props)) return "react-router";
|
|
1990
|
+
if (isRouterContextProvider(node) || isRouteContextProvider(node)) return "react-router";
|
|
1749
1991
|
if (node.child) {
|
|
1750
1992
|
const result = traverse$1(node.child);
|
|
1751
1993
|
if (result) return result;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-devtools-plus/kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.6",
|
|
5
5
|
"author": "wzc520pyfm",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"mitt": "^3.0.1",
|
|
28
28
|
"perfect-debounce": "^2.0.0",
|
|
29
29
|
"superjson": "^2.2.2",
|
|
30
|
-
"@react-devtools-plus/shared": "^0.9.
|
|
30
|
+
"@react-devtools-plus/shared": "^0.9.6"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^24.7.2",
|