react-native-mcp-kit 4.2.0 → 4.2.1
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/babel/testIdPlugin/collectHooks.d.ts +13 -0
- package/dist/babel/testIdPlugin/collectHooks.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/collectHooks.js +112 -0
- package/dist/babel/testIdPlugin/collectHooks.js.map +1 -0
- package/dist/babel/testIdPlugin/constants.d.ts +6 -0
- package/dist/babel/testIdPlugin/constants.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/constants.js +49 -0
- package/dist/babel/testIdPlugin/constants.js.map +1 -0
- package/dist/babel/testIdPlugin/helpers.d.ts +7 -0
- package/dist/babel/testIdPlugin/helpers.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/helpers.js +77 -0
- package/dist/babel/testIdPlugin/helpers.js.map +1 -0
- package/dist/babel/testIdPlugin/hocUnwrap.d.ts +4 -0
- package/dist/babel/testIdPlugin/hocUnwrap.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/hocUnwrap.js +94 -0
- package/dist/babel/testIdPlugin/hocUnwrap.js.map +1 -0
- package/dist/babel/{testIdPlugin.d.ts → testIdPlugin/index.d.ts} +1 -1
- package/dist/babel/testIdPlugin/index.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/index.js +205 -0
- package/dist/babel/testIdPlugin/index.js.map +1 -0
- package/dist/babel/testIdPlugin/inject.d.ts +7 -0
- package/dist/babel/testIdPlugin/inject.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/inject.js +86 -0
- package/dist/babel/testIdPlugin/inject.js.map +1 -0
- package/dist/babel/testIdPlugin/types.d.ts +59 -0
- package/dist/babel/testIdPlugin/types.d.ts.map +1 -0
- package/dist/babel/testIdPlugin/types.js +3 -0
- package/dist/babel/testIdPlugin/types.js.map +1 -0
- package/dist/bin/ios-hid +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/fiberTree/hooks/extract.d.ts +6 -0
- package/dist/modules/fiberTree/hooks/extract.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/extract.js +187 -0
- package/dist/modules/fiberTree/hooks/extract.js.map +1 -0
- package/dist/modules/fiberTree/hooks/flatten.d.ts +4 -0
- package/dist/modules/fiberTree/hooks/flatten.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/flatten.js +81 -0
- package/dist/modules/fiberTree/hooks/flatten.js.map +1 -0
- package/dist/modules/fiberTree/hooks/index.d.ts +5 -0
- package/dist/modules/fiberTree/hooks/index.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/index.js +10 -0
- package/dist/modules/fiberTree/hooks/index.js.map +1 -0
- package/dist/modules/fiberTree/hooks/options.d.ts +9 -0
- package/dist/modules/fiberTree/hooks/options.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/options.js +27 -0
- package/dist/modules/fiberTree/hooks/options.js.map +1 -0
- package/dist/modules/fiberTree/hooks/patterns.d.ts +2 -0
- package/dist/modules/fiberTree/hooks/patterns.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/patterns.js +26 -0
- package/dist/modules/fiberTree/hooks/patterns.js.map +1 -0
- package/dist/modules/fiberTree/hooks/shape.d.ts +3 -0
- package/dist/modules/fiberTree/hooks/shape.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/shape.js +66 -0
- package/dist/modules/fiberTree/hooks/shape.js.map +1 -0
- package/dist/modules/fiberTree/hooks/slotCount.d.ts +2 -0
- package/dist/modules/fiberTree/hooks/slotCount.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/slotCount.js +93 -0
- package/dist/modules/fiberTree/hooks/slotCount.js.map +1 -0
- package/dist/modules/fiberTree/{hooks.d.ts → hooks/types.d.ts} +1 -34
- package/dist/modules/fiberTree/hooks/types.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/types.js +3 -0
- package/dist/modules/fiberTree/hooks/types.js.map +1 -0
- package/dist/modules/fiberTree/hooks/value.d.ts +2 -0
- package/dist/modules/fiberTree/hooks/value.d.ts.map +1 -0
- package/dist/modules/fiberTree/hooks/value.js +89 -0
- package/dist/modules/fiberTree/hooks/value.js.map +1 -0
- package/dist/modules/fiberTree/utils/constants.d.ts +8 -0
- package/dist/modules/fiberTree/utils/constants.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/constants.js +15 -0
- package/dist/modules/fiberTree/utils/constants.js.map +1 -0
- package/dist/modules/fiberTree/utils/index.d.ts +9 -0
- package/dist/modules/fiberTree/utils/index.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/index.js +34 -0
- package/dist/modules/fiberTree/utils/index.js.map +1 -0
- package/dist/modules/fiberTree/utils/match.d.ts +4 -0
- package/dist/modules/fiberTree/utils/match.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/match.js +169 -0
- package/dist/modules/fiberTree/utils/match.js.map +1 -0
- package/dist/modules/fiberTree/utils/naming.d.ts +4 -0
- package/dist/modules/fiberTree/utils/naming.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/naming.js +52 -0
- package/dist/modules/fiberTree/utils/naming.js.map +1 -0
- package/dist/modules/fiberTree/utils/native.d.ts +5 -0
- package/dist/modules/fiberTree/utils/native.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/native.js +101 -0
- package/dist/modules/fiberTree/utils/native.js.map +1 -0
- package/dist/modules/fiberTree/utils/projection.d.ts +9 -0
- package/dist/modules/fiberTree/utils/projection.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/projection.js +75 -0
- package/dist/modules/fiberTree/utils/projection.js.map +1 -0
- package/dist/modules/fiberTree/utils/root.d.ts +4 -0
- package/dist/modules/fiberTree/utils/root.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/root.js +30 -0
- package/dist/modules/fiberTree/utils/root.js.map +1 -0
- package/dist/modules/fiberTree/utils/screen.d.ts +10 -0
- package/dist/modules/fiberTree/utils/screen.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/screen.js +39 -0
- package/dist/modules/fiberTree/utils/screen.js.map +1 -0
- package/dist/modules/fiberTree/utils/serialize.d.ts +4 -0
- package/dist/modules/fiberTree/utils/serialize.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/serialize.js +102 -0
- package/dist/modules/fiberTree/utils/serialize.js.map +1 -0
- package/dist/modules/fiberTree/utils/traverse.d.ts +12 -0
- package/dist/modules/fiberTree/utils/traverse.d.ts.map +1 -0
- package/dist/modules/fiberTree/utils/traverse.js +107 -0
- package/dist/modules/fiberTree/utils/traverse.js.map +1 -0
- package/dist/server/dispatch.d.ts +15 -0
- package/dist/server/dispatch.d.ts.map +1 -0
- package/dist/server/dispatch.js +121 -0
- package/dist/server/dispatch.js.map +1 -0
- package/dist/server/helpers.d.ts +97 -0
- package/dist/server/helpers.d.ts.map +1 -0
- package/dist/server/helpers.js +162 -0
- package/dist/server/helpers.js.map +1 -0
- package/dist/server/host/deviceResolver/byClient.d.ts +5 -0
- package/dist/server/host/deviceResolver/byClient.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/byClient.js +171 -0
- package/dist/server/host/deviceResolver/byClient.js.map +1 -0
- package/dist/server/host/deviceResolver/byId.d.ts +5 -0
- package/dist/server/host/deviceResolver/byId.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/byId.js +81 -0
- package/dist/server/host/deviceResolver/byId.js.map +1 -0
- package/dist/server/host/deviceResolver/enrich.d.ts +5 -0
- package/dist/server/host/deviceResolver/enrich.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/enrich.js +123 -0
- package/dist/server/host/deviceResolver/enrich.js.map +1 -0
- package/dist/server/host/deviceResolver/index.d.ts +13 -0
- package/dist/server/host/deviceResolver/index.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/index.js +65 -0
- package/dist/server/host/deviceResolver/index.js.map +1 -0
- package/dist/server/host/deviceResolver/list.d.ts +10 -0
- package/dist/server/host/deviceResolver/list.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/list.js +119 -0
- package/dist/server/host/deviceResolver/list.js.map +1 -0
- package/dist/server/host/deviceResolver/scan.d.ts +4 -0
- package/dist/server/host/deviceResolver/scan.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/scan.js +102 -0
- package/dist/server/host/deviceResolver/scan.js.map +1 -0
- package/dist/server/host/{deviceResolver.d.ts → deviceResolver/types.d.ts} +1 -16
- package/dist/server/host/deviceResolver/types.d.ts.map +1 -0
- package/dist/server/host/deviceResolver/types.js +3 -0
- package/dist/server/host/deviceResolver/types.js.map +1 -0
- package/dist/server/host/tools/input/android.d.ts +15 -0
- package/dist/server/host/tools/input/android.d.ts.map +1 -0
- package/dist/server/host/tools/input/android.js +88 -0
- package/dist/server/host/tools/input/android.js.map +1 -0
- package/dist/server/host/tools/input/constants.d.ts +14 -0
- package/dist/server/host/tools/input/constants.d.ts.map +1 -0
- package/dist/server/host/tools/input/constants.js +41 -0
- package/dist/server/host/tools/input/constants.js.map +1 -0
- package/dist/server/host/tools/input/drag.d.ts +4 -0
- package/dist/server/host/tools/input/drag.d.ts.map +1 -0
- package/dist/server/host/tools/input/drag.js +76 -0
- package/dist/server/host/tools/input/drag.js.map +1 -0
- package/dist/server/host/tools/input/index.d.ts +8 -0
- package/dist/server/host/tools/input/index.d.ts.map +1 -0
- package/dist/server/host/tools/input/index.js +18 -0
- package/dist/server/host/tools/input/index.js.map +1 -0
- package/dist/server/host/tools/input/longPress.d.ts +4 -0
- package/dist/server/host/tools/input/longPress.d.ts.map +1 -0
- package/dist/server/host/tools/input/longPress.js +49 -0
- package/dist/server/host/tools/input/longPress.js.map +1 -0
- package/dist/server/host/tools/input/pressKey.d.ts +4 -0
- package/dist/server/host/tools/input/pressKey.d.ts.map +1 -0
- package/dist/server/host/tools/input/pressKey.js +42 -0
- package/dist/server/host/tools/input/pressKey.js.map +1 -0
- package/dist/server/host/tools/input/swipe.d.ts +4 -0
- package/dist/server/host/tools/input/swipe.d.ts.map +1 -0
- package/dist/server/host/tools/input/swipe.js +71 -0
- package/dist/server/host/tools/input/swipe.js.map +1 -0
- package/dist/server/host/tools/input/tap.d.ts +4 -0
- package/dist/server/host/tools/input/tap.d.ts.map +1 -0
- package/dist/server/host/tools/input/tap.js +49 -0
- package/dist/server/host/tools/input/tap.js.map +1 -0
- package/dist/server/host/tools/input/typeText.d.ts +4 -0
- package/dist/server/host/tools/input/typeText.d.ts.map +1 -0
- package/dist/server/host/tools/input/typeText.js +52 -0
- package/dist/server/host/tools/input/typeText.js.map +1 -0
- package/dist/server/host/tools/input/typeTextBatch.d.ts +4 -0
- package/dist/server/host/tools/input/typeTextBatch.d.ts.map +1 -0
- package/dist/server/host/tools/input/typeTextBatch.js +106 -0
- package/dist/server/host/tools/input/typeTextBatch.js.map +1 -0
- package/dist/server/instructions.d.ts +2 -0
- package/dist/server/instructions.d.ts.map +1 -0
- package/dist/server/instructions.js +74 -0
- package/dist/server/instructions.js.map +1 -0
- package/dist/server/mcpServer.d.ts +0 -12
- package/dist/server/mcpServer.d.ts.map +1 -1
- package/dist/server/mcpServer.js +26 -877
- package/dist/server/mcpServer.js.map +1 -1
- package/dist/server/predicate.d.ts +31 -0
- package/dist/server/predicate.d.ts.map +1 -0
- package/dist/server/predicate.js +104 -0
- package/dist/server/predicate.js.map +1 -0
- package/dist/server/tools/assert.d.ts +4 -0
- package/dist/server/tools/assert.d.ts.map +1 -0
- package/dist/server/tools/assert.js +79 -0
- package/dist/server/tools/assert.js.map +1 -0
- package/dist/server/tools/call.d.ts +4 -0
- package/dist/server/tools/call.d.ts.map +1 -0
- package/dist/server/tools/call.js +38 -0
- package/dist/server/tools/call.js.map +1 -0
- package/dist/server/tools/connectionStatus.d.ts +4 -0
- package/dist/server/tools/connectionStatus.d.ts.map +1 -0
- package/dist/server/tools/connectionStatus.js +41 -0
- package/dist/server/tools/connectionStatus.js.map +1 -0
- package/dist/server/tools/describeTool.d.ts +4 -0
- package/dist/server/tools/describeTool.d.ts.map +1 -0
- package/dist/server/tools/describeTool.js +130 -0
- package/dist/server/tools/describeTool.js.map +1 -0
- package/dist/server/tools/listTools.d.ts +4 -0
- package/dist/server/tools/listTools.d.ts.map +1 -0
- package/dist/server/tools/listTools.js +109 -0
- package/dist/server/tools/listTools.js.map +1 -0
- package/dist/server/tools/waitUntil.d.ts +4 -0
- package/dist/server/tools/waitUntil.d.ts.map +1 -0
- package/dist/server/tools/waitUntil.js +119 -0
- package/dist/server/tools/waitUntil.js.map +1 -0
- package/package.json +2 -2
- package/dist/babel/testIdPlugin.d.ts.map +0 -1
- package/dist/babel/testIdPlugin.js +0 -585
- package/dist/babel/testIdPlugin.js.map +0 -1
- package/dist/modules/fiberTree/hooks.d.ts.map +0 -1
- package/dist/modules/fiberTree/hooks.js +0 -550
- package/dist/modules/fiberTree/hooks.js.map +0 -1
- package/dist/modules/fiberTree/utils.d.ts +0 -39
- package/dist/modules/fiberTree/utils.d.ts.map +0 -1
- package/dist/modules/fiberTree/utils.js +0 -641
- package/dist/modules/fiberTree/utils.js.map +0 -1
- package/dist/server/host/deviceResolver.d.ts.map +0 -1
- package/dist/server/host/deviceResolver.js +0 -621
- package/dist/server/host/deviceResolver.js.map +0 -1
- package/dist/server/host/tools/input.d.ts +0 -10
- package/dist/server/host/tools/input.d.ts.map +0 -1
- package/dist/server/host/tools/input.js +0 -503
- package/dist/server/host/tools/input.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type NodePath, type types as BabelTypes } from '@babel/core';
|
|
2
|
+
import { type CollectedHook } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Walk a function body collecting hook-call metadata in source order. Names
|
|
5
|
+
* come from the consuming binding:
|
|
6
|
+
* const [count, setCount] = useState(0) → "count"
|
|
7
|
+
* const memoValue = useMemo(...) → "memoValue"
|
|
8
|
+
* useEffect(...) → "effect:<index>"
|
|
9
|
+
* The `kind` is taken from HOOK_KIND for built-ins, or "Custom" for any
|
|
10
|
+
* other identifier matching `/^use[A-Z]/`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const collectHooksInBody: (body: NodePath<BabelTypes.Node>, t: typeof BabelTypes, shortFile: string, separator: string) => CollectedHook[];
|
|
13
|
+
//# sourceMappingURL=collectHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectHooks.d.ts","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/collectHooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,SACvB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAC5B,OAAO,UAAU,aACT,MAAM,aACN,MAAM,KAChB,aAAa,EAmGf,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectHooksInBody = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
/**
|
|
6
|
+
* Walk a function body collecting hook-call metadata in source order. Names
|
|
7
|
+
* come from the consuming binding:
|
|
8
|
+
* const [count, setCount] = useState(0) → "count"
|
|
9
|
+
* const memoValue = useMemo(...) → "memoValue"
|
|
10
|
+
* useEffect(...) → "effect:<index>"
|
|
11
|
+
* The `kind` is taken from HOOK_KIND for built-ins, or "Custom" for any
|
|
12
|
+
* other identifier matching `/^use[A-Z]/`.
|
|
13
|
+
*/
|
|
14
|
+
const collectHooksInBody = (body, t, shortFile, separator) => {
|
|
15
|
+
const hooks = [];
|
|
16
|
+
const anonCounters = {};
|
|
17
|
+
body.traverse({
|
|
18
|
+
CallExpression(callPath) {
|
|
19
|
+
const callee = callPath.node.callee;
|
|
20
|
+
// Three call shapes:
|
|
21
|
+
// useState(x) — direct identifier
|
|
22
|
+
// React.useState(x) — MemberExpression (compiled react libs)
|
|
23
|
+
// _React2.useState(x) — MemberExpression (bundler-mangled)
|
|
24
|
+
let hookIdent;
|
|
25
|
+
let isMemberCall = false;
|
|
26
|
+
let memberObject;
|
|
27
|
+
if (t.isIdentifier(callee)) {
|
|
28
|
+
hookIdent = callee.name;
|
|
29
|
+
}
|
|
30
|
+
else if (t.isMemberExpression(callee) &&
|
|
31
|
+
!callee.computed &&
|
|
32
|
+
t.isIdentifier(callee.property)) {
|
|
33
|
+
hookIdent = callee.property.name;
|
|
34
|
+
isMemberCall = true;
|
|
35
|
+
if (t.isIdentifier(callee.object))
|
|
36
|
+
memberObject = callee.object.name;
|
|
37
|
+
}
|
|
38
|
+
if (!hookIdent || !constants_1.HOOK_NAME_RE.test(hookIdent))
|
|
39
|
+
return;
|
|
40
|
+
// For the `<obj>.use(...)` form, only accept calls with a React-like
|
|
41
|
+
// namespace as the object — otherwise `database.use(middleware)` and
|
|
42
|
+
// friends light up. `useXxx` member calls (`React.useState`) keep
|
|
43
|
+
// working through the same gate since their property name is unique
|
|
44
|
+
// enough on its own; the filter just prunes ambiguous bare `use`.
|
|
45
|
+
if (isMemberCall && hookIdent === 'use' && !constants_1.REACT_NAMESPACE_RE.test(memberObject ?? '')) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const kind = constants_1.HOOK_KIND[hookIdent] ?? 'Custom';
|
|
49
|
+
// Resolve hook name from the consuming binding when possible.
|
|
50
|
+
let hookName;
|
|
51
|
+
const parent = callPath.parent;
|
|
52
|
+
if (t.isVariableDeclarator(parent)) {
|
|
53
|
+
const id = parent.id;
|
|
54
|
+
if (t.isIdentifier(id)) {
|
|
55
|
+
hookName = id.name;
|
|
56
|
+
}
|
|
57
|
+
else if (t.isArrayPattern(id)) {
|
|
58
|
+
// const [value, setter] = useState(0) → pick the first binding
|
|
59
|
+
const first = id.elements[0];
|
|
60
|
+
if (first && t.isIdentifier(first)) {
|
|
61
|
+
hookName = first.name;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (t.isObjectPattern(id)) {
|
|
65
|
+
// const { foo } = useCustomHook() — rare, name it after the first key
|
|
66
|
+
const firstProp = id.properties[0];
|
|
67
|
+
if (firstProp && t.isObjectProperty(firstProp) && t.isIdentifier(firstProp.key)) {
|
|
68
|
+
hookName = firstProp.key.name;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!hookName) {
|
|
73
|
+
// Naked hook call (typical for useEffect). Generate a positional name
|
|
74
|
+
// keyed by the hook kind so "effect:0" / "effect:1" don't collide
|
|
75
|
+
// with state hooks if user has multiple effects.
|
|
76
|
+
const counterKey = kind.toLowerCase();
|
|
77
|
+
const index = anonCounters[counterKey] ?? 0;
|
|
78
|
+
anonCounters[counterKey] = index + 1;
|
|
79
|
+
hookName = `${counterKey}:${index}`;
|
|
80
|
+
}
|
|
81
|
+
const entry = { hook: hookIdent, kind, name: hookName };
|
|
82
|
+
const line = callPath.node.loc?.start.line;
|
|
83
|
+
if (line) {
|
|
84
|
+
entry.mcpId = `${hookName}${separator}${shortFile}${separator}${line}`;
|
|
85
|
+
}
|
|
86
|
+
if (kind === 'Custom' && !isMemberCall) {
|
|
87
|
+
// Only attach a function reference for direct-identifier calls where
|
|
88
|
+
// we can verify the binding is module-scoped (import / top-level
|
|
89
|
+
// const/let/var/function). MemberExpression calls (React.useX) are
|
|
90
|
+
// always built-in hooks anyway — kind ends up mapped, no expansion
|
|
91
|
+
// needed. Local/parameter names would throw ReferenceError at the
|
|
92
|
+
// injection site (which is module-level).
|
|
93
|
+
const binding = callPath.scope.getBinding(hookIdent);
|
|
94
|
+
if (binding && binding.scope.block.type === 'Program') {
|
|
95
|
+
entry.fnIdent = hookIdent;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
hooks.push(entry);
|
|
99
|
+
},
|
|
100
|
+
Function(innerPath) {
|
|
101
|
+
// Don't descend into nested functions — their hooks are not this
|
|
102
|
+
// component's hooks. `useCallback(() => {...})` is still traversed
|
|
103
|
+
// because the wrapper call is a call expression visited before we
|
|
104
|
+
// skip into the body (we only skip the nested function's own
|
|
105
|
+
// declaration/body traversal).
|
|
106
|
+
innerPath.skip();
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
return hooks;
|
|
110
|
+
};
|
|
111
|
+
exports.collectHooksInBody = collectHooksInBody;
|
|
112
|
+
//# sourceMappingURL=collectHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectHooks.js","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/collectHooks.ts"],"names":[],"mappings":";;;AAEA,2CAA0E;AAG1E;;;;;;;;GAQG;AACI,MAAM,kBAAkB,GAAG,CAChC,IAA+B,EAC/B,CAAoB,EACpB,SAAiB,EACjB,SAAiB,EACA,EAAE;IACnB,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,MAAM,YAAY,GAA2B,EAAE,CAAC;IAEhD,IAAI,CAAC,QAAQ,CAAC;QACZ,cAAc,CAAC,QAAQ;YACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC,qBAAqB;YACrB,8CAA8C;YAC9C,mEAAmE;YACnE,+DAA+D;YAC/D,IAAI,SAA6B,CAAC;YAClC,IAAI,YAAY,GAAG,KAAK,CAAC;YACzB,IAAI,YAAgC,CAAC;YACrC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC;YAC1B,CAAC;iBAAM,IACL,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC;gBAC5B,CAAC,MAAM,CAAC,QAAQ;gBAChB,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,EAC/B,CAAC;gBACD,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACjC,YAAY,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC;oBAAE,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;YACvE,CAAC;YACD,IAAI,CAAC,SAAS,IAAI,CAAC,wBAAY,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,OAAO;YACxD,qEAAqE;YACrE,qEAAqE;YACrE,kEAAkE;YAClE,oEAAoE;YACpE,kEAAkE;YAClE,IAAI,YAAY,IAAI,SAAS,KAAK,KAAK,IAAI,CAAC,8BAAkB,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;gBACxF,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,qBAAS,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC;YAE9C,8DAA8D;YAC9D,IAAI,QAA4B,CAAC;YACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC/B,IAAI,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnC,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;gBACrB,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;oBACvB,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC;gBACrB,CAAC;qBAAM,IAAI,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC,EAAE,CAAC;oBAChC,gEAAgE;oBAChE,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAC7B,IAAI,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;wBACnC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;oBACxB,CAAC;gBACH,CAAC;qBAAM,IAAI,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC;oBACjC,sEAAsE;oBACtE,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;oBACnC,IAAI,SAAS,IAAI,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;wBAChF,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,sEAAsE;gBACtE,kEAAkE;gBAClE,iDAAiD;gBACjD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC5C,YAAY,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;gBACrC,QAAQ,GAAG,GAAG,UAAU,IAAI,KAAK,EAAE,CAAC;YACtC,CAAC;YAED,MAAM,KAAK,GAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;YACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC;YAC3C,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,CAAC,KAAK,GAAG,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,EAAE,CAAC;YACzE,CAAC;YACD,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;gBACvC,qEAAqE;gBACrE,iEAAiE;gBACjE,mEAAmE;gBACnE,mEAAmE;gBACnE,kEAAkE;gBAClE,0CAA0C;gBAC1C,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;gBACrD,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACtD,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;gBAC5B,CAAC;YACH,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QACD,QAAQ,CAAC,SAAS;YAChB,iEAAiE;YACjE,mEAAmE;YACnE,kEAAkE;YAClE,6DAA6D;YAC7D,+BAA+B;YAC/B,SAAS,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAxGW,QAAA,kBAAkB,sBAwG7B"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DEFAULT_ATTR = "data-mcp-id";
|
|
2
|
+
export declare const DEFAULT_EXCLUDE: string[];
|
|
3
|
+
export declare const HOOK_KIND: Record<string, string>;
|
|
4
|
+
export declare const HOOK_NAME_RE: RegExp;
|
|
5
|
+
export declare const REACT_NAMESPACE_RE: RegExp;
|
|
6
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,eAAO,MAAM,eAAe,UAO3B,CAAC;AAQF,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmB5C,CAAC;AAIF,eAAO,MAAM,YAAY,QAAkB,CAAC;AAQ5C,eAAO,MAAM,kBAAkB,QAAoB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REACT_NAMESPACE_RE = exports.HOOK_NAME_RE = exports.HOOK_KIND = exports.DEFAULT_EXCLUDE = exports.DEFAULT_ATTR = void 0;
|
|
4
|
+
exports.DEFAULT_ATTR = 'data-mcp-id';
|
|
5
|
+
exports.DEFAULT_EXCLUDE = [
|
|
6
|
+
'Fragment',
|
|
7
|
+
'React.Fragment',
|
|
8
|
+
'React.StrictMode',
|
|
9
|
+
'React.Suspense',
|
|
10
|
+
'StrictMode',
|
|
11
|
+
'Suspense',
|
|
12
|
+
];
|
|
13
|
+
// Map of built-in React hook names → agent-friendly `kind` labels. Anything
|
|
14
|
+
// matching the hook-name detector (`use` exact or `/^use[A-Z]/`) that is
|
|
15
|
+
// NOT in this table is treated as "Custom".
|
|
16
|
+
//
|
|
17
|
+
// react-dom-only hooks (`useFormStatus`, `useFormState`) are intentionally
|
|
18
|
+
// omitted — this library targets React Native, where neither exists.
|
|
19
|
+
exports.HOOK_KIND = {
|
|
20
|
+
use: 'Use',
|
|
21
|
+
useActionState: 'ActionState',
|
|
22
|
+
useCallback: 'Callback',
|
|
23
|
+
useContext: 'Context',
|
|
24
|
+
useDebugValue: 'DebugValue',
|
|
25
|
+
useDeferredValue: 'DeferredValue',
|
|
26
|
+
useEffect: 'Effect',
|
|
27
|
+
useId: 'Id',
|
|
28
|
+
useImperativeHandle: 'ImperativeHandle',
|
|
29
|
+
useInsertionEffect: 'InsertionEffect',
|
|
30
|
+
useLayoutEffect: 'LayoutEffect',
|
|
31
|
+
useMemo: 'Memo',
|
|
32
|
+
useOptimistic: 'Optimistic',
|
|
33
|
+
useReducer: 'Reducer',
|
|
34
|
+
useRef: 'Ref',
|
|
35
|
+
useState: 'State',
|
|
36
|
+
useSyncExternalStore: 'SyncExternalStore',
|
|
37
|
+
useTransition: 'Transition',
|
|
38
|
+
};
|
|
39
|
+
// Hook-name detector. Accepts `use` exact (React 19's `use(promise|context)`)
|
|
40
|
+
// AND the classic `use[A-Z]\w*` pattern.
|
|
41
|
+
exports.HOOK_NAME_RE = /^use([A-Z]|$)/;
|
|
42
|
+
// For the MemberExpression form (`X.use(...)`) we need to filter out
|
|
43
|
+
// unrelated method calls — `database.use(middleware)`, `app.use(router)` etc.
|
|
44
|
+
// Allow only object names that look like React-namespace bindings: literal
|
|
45
|
+
// `React` / `react` and the common bundler-mangled forms (`_react`,
|
|
46
|
+
// `_React2`, `_react3`, …). Same heuristic for any hook, but matters most
|
|
47
|
+
// for bare `use` whose name is otherwise too generic to disambiguate.
|
|
48
|
+
exports.REACT_NAMESPACE_RE = /^_?[Rr]eact\d*$/;
|
|
49
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,YAAY,GAAG,aAAa,CAAC;AAE7B,QAAA,eAAe,GAAG;IAC7B,UAAU;IACV,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,YAAY;IACZ,UAAU;CACX,CAAC;AAEF,4EAA4E;AAC5E,yEAAyE;AACzE,4CAA4C;AAC5C,EAAE;AACF,2EAA2E;AAC3E,qEAAqE;AACxD,QAAA,SAAS,GAA2B;IAC/C,GAAG,EAAE,KAAK;IACV,cAAc,EAAE,aAAa;IAC7B,WAAW,EAAE,UAAU;IACvB,UAAU,EAAE,SAAS;IACrB,aAAa,EAAE,YAAY;IAC3B,gBAAgB,EAAE,eAAe;IACjC,SAAS,EAAE,QAAQ;IACnB,KAAK,EAAE,IAAI;IACX,mBAAmB,EAAE,kBAAkB;IACvC,kBAAkB,EAAE,iBAAiB;IACrC,eAAe,EAAE,cAAc;IAC/B,OAAO,EAAE,MAAM;IACf,aAAa,EAAE,YAAY;IAC3B,UAAU,EAAE,SAAS;IACrB,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,OAAO;IACjB,oBAAoB,EAAE,mBAAmB;IACzC,aAAa,EAAE,YAAY;CAC5B,CAAC;AAEF,8EAA8E;AAC9E,yCAAyC;AAC5B,QAAA,YAAY,GAAG,eAAe,CAAC;AAE5C,qEAAqE;AACrE,8EAA8E;AAC9E,2EAA2E;AAC3E,oEAAoE;AACpE,0EAA0E;AAC1E,sEAAsE;AACzD,QAAA,kBAAkB,GAAG,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type NodePath, type types as BabelTypes } from '@babel/core';
|
|
2
|
+
export declare const isCapitalized: (name: string | undefined) => boolean;
|
|
3
|
+
export declare const getShortFile: (filename: string | null | undefined) => string;
|
|
4
|
+
export declare const isCustomHookName: (name: string | undefined) => boolean;
|
|
5
|
+
export declare const bodyUsesJSX: (bodyPath: NodePath<BabelTypes.Node>) => boolean;
|
|
6
|
+
export declare const bodyCallsHook: (bodyPath: NodePath<BabelTypes.Node>, t: typeof BabelTypes) => boolean;
|
|
7
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAQtE,eAAO,MAAM,aAAa,SAAU,MAAM,GAAG,SAAS,KAAG,OAOxD,CAAC;AAMF,eAAO,MAAM,YAAY,aAAc,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAMlE,CAAC;AAMF,eAAO,MAAM,gBAAgB,SAAU,MAAM,GAAG,SAAS,KAAG,OAE3D,CAAC;AAIF,eAAO,MAAM,WAAW,aAAc,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAAG,OAWjE,CAAC;AAEF,eAAO,MAAM,aAAa,aACd,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAChC,OAAO,UAAU,KACnB,OAwBF,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bodyCallsHook = exports.bodyUsesJSX = exports.isCustomHookName = exports.getShortFile = exports.isCapitalized = void 0;
|
|
4
|
+
// Cheap, conservative component detector: the binding name starts with a
|
|
5
|
+
// capital letter and the function body mentions JSX somewhere. Covers
|
|
6
|
+
// `function LoginForm() { return <View />; }` and
|
|
7
|
+
// `const LoginForm = (props) => <View />;`. HOC-wrapped components
|
|
8
|
+
// (memo / forwardRef / observer) are handled in `VariableDeclarator` by
|
|
9
|
+
// unwrapping the call expression first via `findInnerFunctionBodyPath`.
|
|
10
|
+
const isCapitalized = (name) => {
|
|
11
|
+
return (!!name &&
|
|
12
|
+
name.length > 0 &&
|
|
13
|
+
name[0] === name[0]?.toUpperCase() &&
|
|
14
|
+
name[0] !== name[0]?.toLowerCase());
|
|
15
|
+
};
|
|
16
|
+
exports.isCapitalized = isCapitalized;
|
|
17
|
+
// Strip the absolute path prefix (anything up to and including the project's
|
|
18
|
+
// `/src/`) and drop the .ts/.tsx/.js/.jsx extension. Same shape components
|
|
19
|
+
// use inside `data-mcp-id` so a hook's `mcpId` and a JSX element's
|
|
20
|
+
// `data-mcp-id` look interchangeable to an agent reading them.
|
|
21
|
+
const getShortFile = (filename) => {
|
|
22
|
+
const file = filename ?? 'unknown';
|
|
23
|
+
const relative = file.includes('/src/')
|
|
24
|
+
? (file.split('/src/').pop() ?? file)
|
|
25
|
+
: (file.split('/').pop() ?? file);
|
|
26
|
+
return relative.replace(/\.(tsx?|jsx?)$/, '');
|
|
27
|
+
};
|
|
28
|
+
exports.getShortFile = getShortFile;
|
|
29
|
+
// Is this a candidate custom hook definition? Name matches `use[A-Z]...`
|
|
30
|
+
// and the body contains at least one built-in or custom hook call. Matches
|
|
31
|
+
// userland hooks AND library hook sources that happen to pass through our
|
|
32
|
+
// babel transform (Metro runs plugins on node_modules by default).
|
|
33
|
+
const isCustomHookName = (name) => {
|
|
34
|
+
return !!name && /^use[A-Z]/.test(name);
|
|
35
|
+
};
|
|
36
|
+
exports.isCustomHookName = isCustomHookName;
|
|
37
|
+
// Heuristic: the function body must reference JSX. Otherwise it's just a
|
|
38
|
+
// plain capitalized helper that happens to share the naming convention.
|
|
39
|
+
const bodyUsesJSX = (bodyPath) => {
|
|
40
|
+
let uses = false;
|
|
41
|
+
bodyPath.traverse({
|
|
42
|
+
JSXElement() {
|
|
43
|
+
uses = true;
|
|
44
|
+
},
|
|
45
|
+
JSXFragment() {
|
|
46
|
+
uses = true;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return uses;
|
|
50
|
+
};
|
|
51
|
+
exports.bodyUsesJSX = bodyUsesJSX;
|
|
52
|
+
const bodyCallsHook = (bodyPath, t) => {
|
|
53
|
+
let hit = false;
|
|
54
|
+
bodyPath.traverse({
|
|
55
|
+
CallExpression(callPath) {
|
|
56
|
+
if (hit)
|
|
57
|
+
return;
|
|
58
|
+
const callee = callPath.node.callee;
|
|
59
|
+
if (t.isIdentifier(callee) && /^use[A-Z]/.test(callee.name)) {
|
|
60
|
+
hit = true;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (t.isMemberExpression(callee) &&
|
|
64
|
+
!callee.computed &&
|
|
65
|
+
t.isIdentifier(callee.property) &&
|
|
66
|
+
/^use[A-Z]/.test(callee.property.name)) {
|
|
67
|
+
hit = true;
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
Function(innerPath) {
|
|
71
|
+
innerPath.skip();
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
return hit;
|
|
75
|
+
};
|
|
76
|
+
exports.bodyCallsHook = bodyCallsHook;
|
|
77
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type NodePath, type types as BabelTypes } from '@babel/core';
|
|
2
|
+
export declare const findInnerFunctionBodyPath: (startPath: NodePath<BabelTypes.Node>, t: typeof BabelTypes) => NodePath<BabelTypes.Node> | null;
|
|
3
|
+
export declare const findInnerIdentifier: (startPath: NodePath<BabelTypes.Node>, t: typeof BabelTypes) => NodePath<BabelTypes.Identifier> | null;
|
|
4
|
+
//# sourceMappingURL=hocUnwrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hocUnwrap.d.ts","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/hocUnwrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAmFtE,eAAO,MAAM,yBAAyB,cACzB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KACjC,OAAO,UAAU,KACnB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,IAO9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,cACnB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KACjC,OAAO,UAAU,KACnB,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,IAIpC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findInnerIdentifier = exports.findInnerFunctionBodyPath = void 0;
|
|
4
|
+
// Recognize callee shapes that look like an HOC application without locking
|
|
5
|
+
// to a hardcoded name list. Two shapes pass:
|
|
6
|
+
// 1. `memo(fn)` / `forwardRef(fn)` / `withAuth(fn)` — bare Identifier callee.
|
|
7
|
+
// 2. `React.memo(fn)` / `Mobx.observer(fn)` — MemberExpression where the
|
|
8
|
+
// object identifier resolves to a module (import) binding. This rules
|
|
9
|
+
// out `arr.map(fn)` and similar local-method calls, which would
|
|
10
|
+
// otherwise produce harmless-but-noisy metadata on a non-component
|
|
11
|
+
// binding. Property access on a deep chain (`a.b.c(fn)`) is rejected.
|
|
12
|
+
const isLikelyHocCallee = (callPath, t) => {
|
|
13
|
+
const callee = callPath.node.callee;
|
|
14
|
+
if (t.isIdentifier(callee))
|
|
15
|
+
return true;
|
|
16
|
+
if (t.isMemberExpression(callee) &&
|
|
17
|
+
!callee.computed &&
|
|
18
|
+
t.isIdentifier(callee.property) &&
|
|
19
|
+
t.isIdentifier(callee.object)) {
|
|
20
|
+
const binding = callPath.scope.getBinding(callee.object.name);
|
|
21
|
+
return !!binding && binding.kind === 'module';
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
25
|
+
// Walk down a CallExpression chain (`memo(forwardRef(...))`,
|
|
26
|
+
// `withAuth(observer(...))`, etc.) to whatever the chain bottoms out at.
|
|
27
|
+
// Returns null if no unwrap happened (the start was not a recognized HOC
|
|
28
|
+
// chain). Each link must look like a plausible HOC application via
|
|
29
|
+
// `isLikelyHocCallee`, so non-HOC calls that happen to take a function
|
|
30
|
+
// argument (e.g. `arr.map(fn)`) don't trigger metadata attachment on the
|
|
31
|
+
// outer binding.
|
|
32
|
+
//
|
|
33
|
+
// Transparently steps through `AssignmentExpression` and `SequenceExpression`
|
|
34
|
+
// wrappers — react-refresh injects `_c = <fn>` and similar synthetic
|
|
35
|
+
// expression wraps around the inner component during its `VariableDeclaration`
|
|
36
|
+
// enter visitor (which runs before our `VariableDeclarator` visitor). Without
|
|
37
|
+
// the step-through, our unwrap would dead-end at the AssignmentExpression
|
|
38
|
+
// and miss the inner arrow / function expression entirely.
|
|
39
|
+
const unwrapTransparentExpr = (path) => {
|
|
40
|
+
let cur = path;
|
|
41
|
+
// Bound the loop to defend against pathological ASTs.
|
|
42
|
+
for (let i = 0; i < 8; i++) {
|
|
43
|
+
if (cur.isAssignmentExpression() && cur.node.operator === '=') {
|
|
44
|
+
cur = cur.get('right');
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (cur.isSequenceExpression()) {
|
|
48
|
+
const exprs = cur.get('expressions');
|
|
49
|
+
if (exprs.length === 0)
|
|
50
|
+
break;
|
|
51
|
+
cur = exprs[exprs.length - 1];
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
return cur;
|
|
57
|
+
};
|
|
58
|
+
const unwrapHocChainToBottom = (startPath, t) => {
|
|
59
|
+
let cur = unwrapTransparentExpr(startPath);
|
|
60
|
+
let unwrapped = false;
|
|
61
|
+
while (cur.isCallExpression() && cur.node.arguments.length > 0 && isLikelyHocCallee(cur, t)) {
|
|
62
|
+
const arg0 = cur.get('arguments.0');
|
|
63
|
+
if (!arg0.node)
|
|
64
|
+
return null;
|
|
65
|
+
cur = unwrapTransparentExpr(arg0);
|
|
66
|
+
unwrapped = true;
|
|
67
|
+
}
|
|
68
|
+
return unwrapped ? cur : null;
|
|
69
|
+
};
|
|
70
|
+
// Two consumers of the unwrap walk:
|
|
71
|
+
// 1. Inline-function case (`memo(() => <JSX />)`) — find the body so we can
|
|
72
|
+
// collect hooks and attach metadata directly to the outer binding.
|
|
73
|
+
// 2. Identifier-ref case (`memo(InnerFn)`) — record `{ outer, inner }` so a
|
|
74
|
+
// deferred copy `Outer.__mcp_hooks = Inner.__mcp_hooks` can be emitted
|
|
75
|
+
// at Program:exit. The inline case can't use the deferred copy approach
|
|
76
|
+
// because there's no name to copy from.
|
|
77
|
+
const findInnerFunctionBodyPath = (startPath, t) => {
|
|
78
|
+
const bottom = unwrapHocChainToBottom(startPath, t);
|
|
79
|
+
if (!bottom)
|
|
80
|
+
return null;
|
|
81
|
+
if (bottom.isArrowFunctionExpression() || bottom.isFunctionExpression()) {
|
|
82
|
+
return bottom.get('body');
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
exports.findInnerFunctionBodyPath = findInnerFunctionBodyPath;
|
|
87
|
+
const findInnerIdentifier = (startPath, t) => {
|
|
88
|
+
const bottom = unwrapHocChainToBottom(startPath, t);
|
|
89
|
+
if (!bottom)
|
|
90
|
+
return null;
|
|
91
|
+
return bottom.isIdentifier() ? bottom : null;
|
|
92
|
+
};
|
|
93
|
+
exports.findInnerIdentifier = findInnerIdentifier;
|
|
94
|
+
//# sourceMappingURL=hocUnwrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hocUnwrap.js","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/hocUnwrap.ts"],"names":[],"mappings":";;;AAEA,4EAA4E;AAC5E,6CAA6C;AAC7C,gFAAgF;AAChF,2EAA2E;AAC3E,2EAA2E;AAC3E,qEAAqE;AACrE,wEAAwE;AACxE,2EAA2E;AAC3E,MAAM,iBAAiB,GAAG,CACxB,QAA6C,EAC7C,CAAoB,EACX,EAAE;IACX,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;IACpC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,IACE,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC5B,CAAC,MAAM,CAAC,QAAQ;QAChB,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC/B,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,EAC7B,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,6DAA6D;AAC7D,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AACnE,uEAAuE;AACvE,yEAAyE;AACzE,iBAAiB;AACjB,EAAE;AACF,8EAA8E;AAC9E,qEAAqE;AACrE,+EAA+E;AAC/E,8EAA8E;AAC9E,0EAA0E;AAC1E,2DAA2D;AAC3D,MAAM,qBAAqB,GAAG,CAAC,IAA+B,EAA6B,EAAE;IAC3F,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,sDAAsD;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,sBAAsB,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YAC9D,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAA8B,CAAC;YACpD,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,aAAa,CAAsC,CAAC;YAC1E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM;YAC9B,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAA8B,CAAC;YAC3D,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAC7B,SAAoC,EACpC,CAAoB,EACc,EAAE;IACpC,IAAI,GAAG,GAA8B,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACtE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,OAAO,GAAG,CAAC,gBAAgB,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,iBAAiB,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,aAAa,CAA8B,CAAC;QACjE,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5B,GAAG,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAClC,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAChC,CAAC,CAAC;AAEF,oCAAoC;AACpC,8EAA8E;AAC9E,wEAAwE;AACxE,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,6CAA6C;AACtC,MAAM,yBAAyB,GAAG,CACvC,SAAoC,EACpC,CAAoB,EACc,EAAE;IACpC,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,CAAC,yBAAyB,EAAE,IAAI,MAAM,CAAC,oBAAoB,EAAE,EAAE,CAAC;QACxE,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAA8B,CAAC;IACzD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAVW,QAAA,yBAAyB,6BAUpC;AAEK,MAAM,mBAAmB,GAAG,CACjC,SAAoC,EACpC,CAAoB,EACoB,EAAE;IAC1C,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAE,MAA0C,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC,CAAC;AAPW,QAAA,mBAAmB,uBAO9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/babel/testIdPlugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,KAAK,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAoBtF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IAAE,KAAK,EAAE,OAAO,UAAU,CAAA;CAAE,GAAG,SAAS,CAgN1F"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = testIdPlugin;
|
|
4
|
+
const collectHooks_1 = require("./collectHooks");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
const hocUnwrap_1 = require("./hocUnwrap");
|
|
8
|
+
const inject_1 = require("./inject");
|
|
9
|
+
function testIdPlugin({ types: t }) {
|
|
10
|
+
return {
|
|
11
|
+
name: 'react-native-mcp-test-id',
|
|
12
|
+
visitor: {
|
|
13
|
+
// === Part 2: attach __mcp_hooks metadata. ===
|
|
14
|
+
// Two candidate shapes per function declaration:
|
|
15
|
+
// 1. Component — capitalized name + JSX in body (React component).
|
|
16
|
+
// 2. Custom hook — name matches /^use[A-Z]/ + body contains hook calls.
|
|
17
|
+
// The VariableDeclarator visitor below covers the
|
|
18
|
+
// `const Foo = (...) => {...}` / `const useX = (...) => {...}` forms,
|
|
19
|
+
// plus HOC-wrapped components: `const Foo = anyHoc((props) => <JSX />)`
|
|
20
|
+
// and nested chains thereof.
|
|
21
|
+
FunctionDeclaration(path, state) {
|
|
22
|
+
const id = path.node.id;
|
|
23
|
+
if (!id)
|
|
24
|
+
return;
|
|
25
|
+
const bodyPath = path.get('body');
|
|
26
|
+
const pluginState = state;
|
|
27
|
+
const opts = (state.opts ?? {});
|
|
28
|
+
const separator = opts.separator ?? ':';
|
|
29
|
+
const shortFile = (0, helpers_1.getShortFile)(state.filename);
|
|
30
|
+
// Component: capitalized name + (JSX in body OR hook calls in body).
|
|
31
|
+
// The hook-call branch covers components that legitimately return
|
|
32
|
+
// null / non-JSX values — portal-like, context-only, or
|
|
33
|
+
// imperative-handle wrappers. Under the Rules of Hooks, only
|
|
34
|
+
// components and custom hooks may call hooks, so a capitalized
|
|
35
|
+
// function that calls them is unambiguously a component.
|
|
36
|
+
if ((0, helpers_1.isCapitalized)(id.name) && ((0, helpers_1.bodyUsesJSX)(bodyPath) || (0, helpers_1.bodyCallsHook)(bodyPath, t))) {
|
|
37
|
+
const hooks = (0, collectHooks_1.collectHooksInBody)(bodyPath, t, shortFile, separator);
|
|
38
|
+
(0, inject_1.queueHooksAssignment)(pluginState, path, id.name, hooks);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Custom hook: name matches use[A-Z] + body calls at least one hook.
|
|
42
|
+
if ((0, helpers_1.isCustomHookName)(id.name) && (0, helpers_1.bodyCallsHook)(bodyPath, t)) {
|
|
43
|
+
const hooks = (0, collectHooks_1.collectHooksInBody)(bodyPath, t, shortFile, separator);
|
|
44
|
+
(0, inject_1.queueHooksAssignment)(pluginState, path, id.name, hooks);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
// === Part 1: stamp data-mcp-id on capitalized JSX elements. ===
|
|
48
|
+
JSXOpeningElement(path, state) {
|
|
49
|
+
const opts = (state.opts ?? {});
|
|
50
|
+
const attrName = opts.attr ?? constants_1.DEFAULT_ATTR;
|
|
51
|
+
const separator = opts.separator ?? ':';
|
|
52
|
+
const exclude = opts.exclude ?? constants_1.DEFAULT_EXCLUDE;
|
|
53
|
+
const include = opts.include;
|
|
54
|
+
const nameNode = path.node.name;
|
|
55
|
+
let componentName;
|
|
56
|
+
if (t.isJSXIdentifier(nameNode)) {
|
|
57
|
+
componentName = nameNode.name;
|
|
58
|
+
}
|
|
59
|
+
else if (t.isJSXMemberExpression(nameNode)) {
|
|
60
|
+
componentName = `${nameNode.object.name}.${nameNode.property.name}`;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (componentName[0] === componentName[0]?.toLowerCase())
|
|
66
|
+
return;
|
|
67
|
+
if (include && !include.includes(componentName))
|
|
68
|
+
return;
|
|
69
|
+
if (exclude.includes(componentName))
|
|
70
|
+
return;
|
|
71
|
+
const shortFile = (0, helpers_1.getShortFile)(state.filename);
|
|
72
|
+
const line = path.node.loc?.start.line ?? 0;
|
|
73
|
+
const generatedId = `${componentName}${separator}${shortFile}${separator}${line}`;
|
|
74
|
+
const existingAttr = path.node.attributes.find((attr) => {
|
|
75
|
+
return t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: attrName });
|
|
76
|
+
});
|
|
77
|
+
if (existingAttr && t.isJSXAttribute(existingAttr)) {
|
|
78
|
+
if (t.isStringLiteral(existingAttr.value)) {
|
|
79
|
+
const existing = existingAttr.value.value;
|
|
80
|
+
existingAttr.value = t.stringLiteral(`${existing}${separator}${shortFile}${separator}${line}`);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
path.node.attributes.push(t.jsxAttribute(t.jsxIdentifier(attrName), t.stringLiteral(generatedId)));
|
|
85
|
+
},
|
|
86
|
+
// === Part 3: drain deferred-injection queue at module end. ===
|
|
87
|
+
// Runs after all other plugins' visitors (in particular react-refresh's
|
|
88
|
+
// replaceWith on HOC-wrapped components) have finished mutating the
|
|
89
|
+
// tree. We dedupe by `outer` name to defend against multiple visitor
|
|
90
|
+
// passes queuing the same component twice, then push every queued
|
|
91
|
+
// statement onto the END of the Program body. End-of-body is the only
|
|
92
|
+
// placement that's safe across react-compiler + worklets + unistyles:
|
|
93
|
+
// earlier plugins can rewrite a `const Foo = ...` declaration into a
|
|
94
|
+
// hoisted `var Foo = ...` and slip extracted helpers (`_worklet_..._init_data`,
|
|
95
|
+
// memoization scaffolding) BEFORE Foo's source line, leaving an
|
|
96
|
+
// insertAfter-target-position holding a stale `undefined` binding.
|
|
97
|
+
Program: {
|
|
98
|
+
exit(programPath, state) {
|
|
99
|
+
const pluginState = state;
|
|
100
|
+
const queue = pluginState.pendingInjects;
|
|
101
|
+
if (!queue || queue.length === 0)
|
|
102
|
+
return;
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
for (const entry of queue) {
|
|
105
|
+
if (seen.has(entry.outer))
|
|
106
|
+
continue;
|
|
107
|
+
seen.add(entry.outer);
|
|
108
|
+
if (entry.kind === 'assignment') {
|
|
109
|
+
programPath.pushContainer('body', (0, inject_1.buildAssignmentStmt)(entry.outer, entry.hooks, t));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
programPath.pushContainer('body', (0, inject_1.buildHooksGetterStmt)(entry.outer, entry.inner, t));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
pluginState.pendingInjects = [];
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
VariableDeclarator(path, state) {
|
|
119
|
+
const id = path.node.id;
|
|
120
|
+
if (!t.isIdentifier(id))
|
|
121
|
+
return;
|
|
122
|
+
let initNode = path.node.init;
|
|
123
|
+
if (!initNode)
|
|
124
|
+
return;
|
|
125
|
+
const pluginState = state;
|
|
126
|
+
// Unwrap TypeScript casts: `as Foo`, `as const`, `<Foo>x` (legacy
|
|
127
|
+
// angle-bracket assertion), and `x satisfies Foo`. Without this,
|
|
128
|
+
// `const Wrapped = memo(arrow) as { ... }` fails our HOC unwrap
|
|
129
|
+
// because we'd see TSAsExpression instead of the inner CallExpression.
|
|
130
|
+
// Pattern in the wild: 21vek's PageFlashListWithBanner /
|
|
131
|
+
// ListProductRow / RangeFilter use `as` to attach generic call
|
|
132
|
+
// signatures + displayName onto memo'd components.
|
|
133
|
+
let initPathForUnwrap = path.get('init');
|
|
134
|
+
for (let i = 0; i < 4; i++) {
|
|
135
|
+
if (t.isTSAsExpression(initNode) ||
|
|
136
|
+
t.isTSTypeAssertion(initNode) ||
|
|
137
|
+
t.isTSSatisfiesExpression(initNode) ||
|
|
138
|
+
t.isTSNonNullExpression(initNode)) {
|
|
139
|
+
initPathForUnwrap = initPathForUnwrap.get('expression');
|
|
140
|
+
initNode = initPathForUnwrap.node;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
const init = initNode;
|
|
146
|
+
// Resolve the function body to inspect. Three shapes:
|
|
147
|
+
// const Foo = (props) => <JSX /> — direct arrow / fn
|
|
148
|
+
// const Foo = memo((props) => <JSX />) — single-HOC wrap
|
|
149
|
+
// const Foo = memo(forwardRef((p, r) => ...)) — nested HOC chain
|
|
150
|
+
// For HOC wrappers, attaching `Foo.__mcp_hooks = [...]` on the outer
|
|
151
|
+
// binding works because `memo()` / `forwardRef()` return plain JS
|
|
152
|
+
// objects that React stores in `fiber.type` — the runtime read path
|
|
153
|
+
// is identical to the bare-component case. The unwrap is name-
|
|
154
|
+
// agnostic (any Identifier or import-namespaced MemberExpression
|
|
155
|
+
// callee qualifies), so custom HOCs like `withAuth(...)` work too.
|
|
156
|
+
let bodyPath = null;
|
|
157
|
+
if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
|
|
158
|
+
bodyPath = initPathForUnwrap.get('body');
|
|
159
|
+
}
|
|
160
|
+
else if (t.isCallExpression(init)) {
|
|
161
|
+
bodyPath = (0, hocUnwrap_1.findInnerFunctionBodyPath)(initPathForUnwrap, t);
|
|
162
|
+
// Identifier-ref HOC case: `const Foo = memo(InnerFn)`. The inner
|
|
163
|
+
// arg is a name, not an inline function — so we can't statically
|
|
164
|
+
// collect hooks here. Instead, install a getter on `Foo` that
|
|
165
|
+
// forwards to `Inner.__mcp_hooks` at read time.
|
|
166
|
+
if (!bodyPath && (0, helpers_1.isCapitalized)(id.name)) {
|
|
167
|
+
const inner = (0, hocUnwrap_1.findInnerIdentifier)(initPathForUnwrap, t);
|
|
168
|
+
if (inner && inner.scope.getBinding(inner.node.name)) {
|
|
169
|
+
const stmt = path.getStatementParent();
|
|
170
|
+
if (stmt) {
|
|
171
|
+
(0, inject_1.queueHooksGetter)(pluginState, stmt, id.name, inner.node.name);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!bodyPath || !bodyPath.node)
|
|
177
|
+
return;
|
|
178
|
+
const opts = (state.opts ?? {});
|
|
179
|
+
const separator = opts.separator ?? ':';
|
|
180
|
+
const shortFile = (0, helpers_1.getShortFile)(state.filename);
|
|
181
|
+
// Component: capitalized + (JSX in body OR hook calls). Same
|
|
182
|
+
// rationale as FunctionDeclaration above — Rules of Hooks pin a
|
|
183
|
+
// capitalized hook-calling function to "component", so JSX is
|
|
184
|
+
// sufficient but not necessary (covers `return null` portals).
|
|
185
|
+
if ((0, helpers_1.isCapitalized)(id.name) && ((0, helpers_1.bodyUsesJSX)(bodyPath) || (0, helpers_1.bodyCallsHook)(bodyPath, t))) {
|
|
186
|
+
const hooks = (0, collectHooks_1.collectHooksInBody)(bodyPath, t, shortFile, separator);
|
|
187
|
+
const statement = path.getStatementParent();
|
|
188
|
+
if (!statement)
|
|
189
|
+
return;
|
|
190
|
+
(0, inject_1.queueHooksAssignment)(pluginState, statement, id.name, hooks);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Custom hook: use[A-Z] + body calls hooks.
|
|
194
|
+
if ((0, helpers_1.isCustomHookName)(id.name) && (0, helpers_1.bodyCallsHook)(bodyPath, t)) {
|
|
195
|
+
const hooks = (0, collectHooks_1.collectHooksInBody)(bodyPath, t, shortFile, separator);
|
|
196
|
+
const statement = path.getStatementParent();
|
|
197
|
+
if (!statement)
|
|
198
|
+
return;
|
|
199
|
+
(0, inject_1.queueHooksAssignment)(pluginState, statement, id.name, hooks);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=index.js.map
|