@dcoder-x/plugin-shared 0.1.2 → 0.1.4
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/buildId.d.ts +5 -0
- package/dist/buildId.js +17 -0
- package/dist/extractors/ComponentContextResolver.d.ts +37 -0
- package/dist/extractors/ComponentContextResolver.js +167 -0
- package/dist/extractors/ComponentExtractor.d.ts +6 -1
- package/dist/extractors/ComponentExtractor.js +76 -1
- package/dist/extractors/FlowInferrer.d.ts +4 -2
- package/dist/extractors/FlowInferrer.js +34 -2
- package/dist/extractors/InteractionGraphExtractor.d.ts +34 -0
- package/dist/extractors/InteractionGraphExtractor.js +245 -0
- package/dist/extractors/SelectorGenerator.d.ts +6 -1
- package/dist/extractors/SelectorGenerator.js +28 -3
- package/dist/index.d.ts +6 -1
- package/dist/index.js +20 -0
- package/dist/injection/ClippyIdInjector.d.ts +12 -0
- package/dist/injection/ClippyIdInjector.js +91 -0
- package/dist/injection/HtmlTagGuards.d.ts +3 -0
- package/dist/injection/HtmlTagGuards.js +41 -0
- package/dist/injection/IdStrategy.d.ts +2 -0
- package/dist/injection/IdStrategy.js +38 -0
- package/dist/types.d.ts +102 -1
- package/dist/upload/Adapter.d.ts +3 -0
- package/dist/upload/Adapter.js +13 -0
- package/dist/upload/BackendAdapter.d.ts +30 -0
- package/dist/upload/BackendAdapter.js +42 -0
- package/dist/upload/BackendUploadAdapter.d.ts +13 -0
- package/dist/upload/BackendUploadAdapter.js +51 -0
- package/dist/upload/PackageBuilder.d.ts +32 -1
- package/dist/upload/PackageBuilder.js +143 -0
- package/dist/upload/PackageWriter.d.ts +9 -1
- package/dist/upload/PackageWriter.js +26 -0
- package/dist/upload/UploadStrategy.d.ts +11 -0
- package/dist/upload/UploadStrategy.js +40 -0
- package/dist/upload/Uploader.d.ts +6 -1
- package/dist/upload/Uploader.js +48 -3
- package/package.json +1 -1
package/dist/buildId.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveBuildId = deriveBuildId;
|
|
4
|
+
function deriveBuildId(moduleGraph) {
|
|
5
|
+
if (!moduleGraph.size) {
|
|
6
|
+
return `vite_${Date.now()}`;
|
|
7
|
+
}
|
|
8
|
+
const rows = Array.from(moduleGraph.values())
|
|
9
|
+
.map((mod) => `${mod.id}|${[...mod.importedIds].sort().join(',')}`)
|
|
10
|
+
.sort()
|
|
11
|
+
.join('\n');
|
|
12
|
+
let hash = 0;
|
|
13
|
+
for (let i = 0; i < rows.length; i++) {
|
|
14
|
+
hash = (hash * 31 + rows.charCodeAt(i)) >>> 0;
|
|
15
|
+
}
|
|
16
|
+
return `vite_${hash.toString(36)}`;
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/traverse';
|
|
2
|
+
export interface StateVariable {
|
|
3
|
+
name: string;
|
|
4
|
+
setter?: string;
|
|
5
|
+
initialValue?: string;
|
|
6
|
+
line: number;
|
|
7
|
+
}
|
|
8
|
+
export interface EventHandler {
|
|
9
|
+
name: string;
|
|
10
|
+
events: string[];
|
|
11
|
+
line: number;
|
|
12
|
+
callsStateSetters: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface ComponentContext {
|
|
15
|
+
componentName: string;
|
|
16
|
+
stateVariables: StateVariable[];
|
|
17
|
+
eventHandlers: EventHandler[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolves state declarations, setters, and event handlers within a component scope.
|
|
21
|
+
* This provides the symbol table needed for interaction graph extraction.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ComponentContextResolver {
|
|
24
|
+
/**
|
|
25
|
+
* Extract state, handlers, and their relationships from a component AST node.
|
|
26
|
+
*/
|
|
27
|
+
extractContext(componentPath: NodePath<any>, componentName: string): ComponentContext;
|
|
28
|
+
/**
|
|
29
|
+
* Extract handler name, event references, and called setters from a function.
|
|
30
|
+
*/
|
|
31
|
+
private extractHandlerInfo;
|
|
32
|
+
/**
|
|
33
|
+
* Map event handlers referenced in JSX attributes to their handler definitions.
|
|
34
|
+
* Returns which state setters a given event attribute calls.
|
|
35
|
+
*/
|
|
36
|
+
getStateSettersCalledByEvent(event: string, handlers: EventHandler[]): string[];
|
|
37
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ComponentContextResolver = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves state declarations, setters, and event handlers within a component scope.
|
|
6
|
+
* This provides the symbol table needed for interaction graph extraction.
|
|
7
|
+
*/
|
|
8
|
+
class ComponentContextResolver {
|
|
9
|
+
/**
|
|
10
|
+
* Extract state, handlers, and their relationships from a component AST node.
|
|
11
|
+
*/
|
|
12
|
+
extractContext(componentPath, componentName) {
|
|
13
|
+
const stateVariables = [];
|
|
14
|
+
const eventHandlers = [];
|
|
15
|
+
const stateSetterMap = new Map(); // setter -> state var
|
|
16
|
+
// First pass: collect useState declarations and build setter map
|
|
17
|
+
componentPath.traverse({
|
|
18
|
+
VariableDeclarator: (varPath) => {
|
|
19
|
+
const id = varPath.node.id;
|
|
20
|
+
const init = varPath.node.init;
|
|
21
|
+
// Detect useState pattern: const [state, setState] = useState(...)
|
|
22
|
+
if (id.type === 'ArrayPattern' &&
|
|
23
|
+
id.elements &&
|
|
24
|
+
id.elements.length === 2 &&
|
|
25
|
+
init?.type === 'CallExpression') {
|
|
26
|
+
const callee = init.callee;
|
|
27
|
+
if ((callee.type === 'Identifier' && callee.name === 'useState') ||
|
|
28
|
+
(callee.type === 'MemberExpression' && callee.property?.name === 'useState')) {
|
|
29
|
+
const stateId = id.elements[0];
|
|
30
|
+
const setterIdNode = id.elements[1];
|
|
31
|
+
if (stateId?.type === 'Identifier' && setterIdNode?.type === 'Identifier') {
|
|
32
|
+
const stateName = stateId.name;
|
|
33
|
+
const setterName = setterIdNode.name;
|
|
34
|
+
stateSetterMap.set(setterName, stateName);
|
|
35
|
+
stateVariables.push({
|
|
36
|
+
name: stateName,
|
|
37
|
+
setter: setterName,
|
|
38
|
+
initialValue: init.arguments?.[0]?.type === 'StringLiteral'
|
|
39
|
+
? init.arguments[0].value
|
|
40
|
+
: undefined,
|
|
41
|
+
line: varPath.node.loc?.start.line ?? 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Detect useReducer pattern: const [state, dispatch] = useReducer(...)
|
|
47
|
+
if (id.type === 'ArrayPattern' &&
|
|
48
|
+
id.elements &&
|
|
49
|
+
id.elements.length === 2 &&
|
|
50
|
+
init?.type === 'CallExpression') {
|
|
51
|
+
const callee = init.callee;
|
|
52
|
+
if ((callee.type === 'Identifier' && callee.name === 'useReducer') ||
|
|
53
|
+
(callee.type === 'MemberExpression' && callee.property?.name === 'useReducer')) {
|
|
54
|
+
const stateId = id.elements[0];
|
|
55
|
+
const dispatchIdNode = id.elements[1];
|
|
56
|
+
if (stateId?.type === 'Identifier' && dispatchIdNode?.type === 'Identifier') {
|
|
57
|
+
const stateName = stateId.name;
|
|
58
|
+
const dispatchName = dispatchIdNode.name;
|
|
59
|
+
stateSetterMap.set(dispatchName, stateName);
|
|
60
|
+
stateVariables.push({
|
|
61
|
+
name: stateName,
|
|
62
|
+
setter: dispatchName,
|
|
63
|
+
initialValue: undefined,
|
|
64
|
+
line: varPath.node.loc?.start.line ?? 0,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
// Second pass: find event handler functions and track which setters they call
|
|
72
|
+
componentPath.traverse({
|
|
73
|
+
FunctionDeclaration: (funcPath) => {
|
|
74
|
+
const handlerInfo = this.extractHandlerInfo(funcPath, stateSetterMap);
|
|
75
|
+
if (handlerInfo) {
|
|
76
|
+
handlerInfo.name = funcPath.node.id?.name || handlerInfo.name;
|
|
77
|
+
eventHandlers.push(handlerInfo);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
VariableDeclarator: (varPath) => {
|
|
81
|
+
if ((varPath.node.init?.type === 'ArrowFunctionExpression' ||
|
|
82
|
+
varPath.node.init?.type === 'FunctionExpression') &&
|
|
83
|
+
varPath.node.id?.type === 'Identifier') {
|
|
84
|
+
const handlerName = varPath.node.id.name;
|
|
85
|
+
const funcPath = varPath.get('init');
|
|
86
|
+
const handlerInfo = this.extractHandlerInfo(funcPath, stateSetterMap);
|
|
87
|
+
if (handlerInfo) {
|
|
88
|
+
handlerInfo.name = handlerName;
|
|
89
|
+
eventHandlers.push(handlerInfo);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
// Third pass: annotate which events correspond to each handler reference
|
|
95
|
+
componentPath.traverse({
|
|
96
|
+
JSXAttribute: (attrPath) => {
|
|
97
|
+
const name = attrPath.node.name?.name;
|
|
98
|
+
if (!name || !name.startsWith('on'))
|
|
99
|
+
return;
|
|
100
|
+
const value = attrPath.node.value;
|
|
101
|
+
if (!value || value.type !== 'JSXExpressionContainer')
|
|
102
|
+
return;
|
|
103
|
+
const expression = value.expression;
|
|
104
|
+
if (!expression)
|
|
105
|
+
return;
|
|
106
|
+
const handlerName = expression.type === 'Identifier' ? expression.name : null;
|
|
107
|
+
if (!handlerName)
|
|
108
|
+
return;
|
|
109
|
+
const handler = eventHandlers.find((h) => h.name === handlerName);
|
|
110
|
+
if (!handler)
|
|
111
|
+
return;
|
|
112
|
+
if (!handler.events.includes(name)) {
|
|
113
|
+
handler.events.push(name);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
componentName,
|
|
119
|
+
stateVariables,
|
|
120
|
+
eventHandlers,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Extract handler name, event references, and called setters from a function.
|
|
125
|
+
*/
|
|
126
|
+
extractHandlerInfo(funcPath, stateSetterMap) {
|
|
127
|
+
const calledSetters = new Set();
|
|
128
|
+
funcPath.traverse({
|
|
129
|
+
CallExpression(callPath) {
|
|
130
|
+
const callee = callPath.node.callee;
|
|
131
|
+
let setterName = null;
|
|
132
|
+
if (callee.type === 'Identifier') {
|
|
133
|
+
setterName = callee.name;
|
|
134
|
+
}
|
|
135
|
+
else if (callee.type === 'MemberExpression' && callee.property?.type === 'Identifier') {
|
|
136
|
+
setterName = callee.property.name;
|
|
137
|
+
}
|
|
138
|
+
if (setterName && stateSetterMap.has(setterName)) {
|
|
139
|
+
calledSetters.add(stateSetterMap.get(setterName));
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
name: funcPath.node.id?.name || 'anonymous',
|
|
145
|
+
events: [],
|
|
146
|
+
line: funcPath.node.loc?.start.line ?? 0,
|
|
147
|
+
callsStateSetters: Array.from(calledSetters),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Map event handlers referenced in JSX attributes to their handler definitions.
|
|
152
|
+
* Returns which state setters a given event attribute calls.
|
|
153
|
+
*/
|
|
154
|
+
getStateSettersCalledByEvent(event, handlers) {
|
|
155
|
+
// Simple heuristic: look for handlers named after the event
|
|
156
|
+
const candidates = handlers.filter((h) => h.name.toLowerCase().includes(event.toLowerCase().replace('on', '')) ||
|
|
157
|
+
h.events.includes(event));
|
|
158
|
+
const allSetters = new Set();
|
|
159
|
+
for (const handler of candidates) {
|
|
160
|
+
for (const setter of handler.callsStateSetters) {
|
|
161
|
+
allSetters.add(setter);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return Array.from(allSetters);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.ComponentContextResolver = ComponentContextResolver;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type { ModuleGraphSource, DiscoveredRoute, DiscoveredElement } from '../types';
|
|
1
|
+
import type { ModuleGraphSource, DiscoveredRoute, DiscoveredElement, PolicyComponent } from '../types';
|
|
2
2
|
export declare class ComponentExtractor {
|
|
3
3
|
private source;
|
|
4
4
|
private routes;
|
|
5
5
|
constructor(source: ModuleGraphSource, routes: DiscoveredRoute[]);
|
|
6
6
|
extract(): Promise<DiscoveredElement[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Extract component-level analysis including state, handlers, and interaction graphs.
|
|
9
|
+
* Returns PolicyComponent data suitable for policy document generation.
|
|
10
|
+
*/
|
|
11
|
+
extractComponents(): Promise<PolicyComponent[]>;
|
|
7
12
|
private getModuleFilesForRoute;
|
|
8
13
|
private walkWebpackGraph;
|
|
9
14
|
private findWebpackModule;
|
|
@@ -8,6 +8,8 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const parser_1 = require("@babel/parser");
|
|
10
10
|
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
11
|
+
const InteractionGraphExtractor_1 = require("./InteractionGraphExtractor");
|
|
12
|
+
const ComponentContextResolver_1 = require("./ComponentContextResolver");
|
|
11
13
|
class ComponentExtractor {
|
|
12
14
|
constructor(source, routes) {
|
|
13
15
|
this.source = source;
|
|
@@ -26,6 +28,66 @@ class ComponentExtractor {
|
|
|
26
28
|
}
|
|
27
29
|
return elements;
|
|
28
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Extract component-level analysis including state, handlers, and interaction graphs.
|
|
33
|
+
* Returns PolicyComponent data suitable for policy document generation.
|
|
34
|
+
*/
|
|
35
|
+
async extractComponents() {
|
|
36
|
+
const components = [];
|
|
37
|
+
const contextResolver = new ComponentContextResolver_1.ComponentContextResolver();
|
|
38
|
+
const graphExtractor = new InteractionGraphExtractor_1.InteractionGraphExtractor();
|
|
39
|
+
for (const route of this.routes) {
|
|
40
|
+
const moduleFiles = this.getModuleFilesForRoute(route.filePath);
|
|
41
|
+
for (const filePath of moduleFiles) {
|
|
42
|
+
const source = this.readSource(filePath);
|
|
43
|
+
if (!source)
|
|
44
|
+
continue;
|
|
45
|
+
let ast;
|
|
46
|
+
try {
|
|
47
|
+
ast = (0, parser_1.parse)(source, { sourceType: 'module', plugins: ['typescript', 'jsx'] });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// Find all component declarations and extract their analysis
|
|
53
|
+
(0, traverse_1.default)(ast, {
|
|
54
|
+
FunctionDeclaration: (path) => {
|
|
55
|
+
const componentName = path.node.id?.name;
|
|
56
|
+
if (componentName && /^[A-Z]/.test(componentName)) {
|
|
57
|
+
const context = contextResolver.extractContext(path, componentName);
|
|
58
|
+
const interactions = graphExtractor.extractInteractions(path, componentName);
|
|
59
|
+
components.push({
|
|
60
|
+
name: componentName,
|
|
61
|
+
filePath,
|
|
62
|
+
route: route.path,
|
|
63
|
+
stateVariables: context.stateVariables,
|
|
64
|
+
interactions,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
VariableDeclarator: (path) => {
|
|
69
|
+
const componentName = path.node.id?.name;
|
|
70
|
+
if (componentName &&
|
|
71
|
+
/^[A-Z]/.test(componentName) &&
|
|
72
|
+
(path.node.init?.type === 'ArrowFunctionExpression' ||
|
|
73
|
+
path.node.init?.type === 'FunctionExpression')) {
|
|
74
|
+
const funcPath = path.get('init');
|
|
75
|
+
const context = contextResolver.extractContext(funcPath, componentName);
|
|
76
|
+
const interactions = graphExtractor.extractInteractions(funcPath, componentName);
|
|
77
|
+
components.push({
|
|
78
|
+
name: componentName,
|
|
79
|
+
filePath,
|
|
80
|
+
route: route.path,
|
|
81
|
+
stateVariables: context.stateVariables,
|
|
82
|
+
interactions,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return components;
|
|
90
|
+
}
|
|
29
91
|
getModuleFilesForRoute(entryFilePath) {
|
|
30
92
|
if (this.source.type === 'webpack') {
|
|
31
93
|
return this.walkWebpackGraph(entryFilePath, this.source.compilation);
|
|
@@ -98,7 +160,19 @@ class ComponentExtractor {
|
|
|
98
160
|
const fallbackDeps = Array.isArray(mod?.dependencies) ? mod.dependencies : [];
|
|
99
161
|
const out = [];
|
|
100
162
|
for (const dep of fallbackDeps) {
|
|
101
|
-
|
|
163
|
+
let depModule = null;
|
|
164
|
+
if (graph?.getModule) {
|
|
165
|
+
depModule = graph.getModule(dep);
|
|
166
|
+
}
|
|
167
|
+
// Legacy fallback for older webpack versions.
|
|
168
|
+
if (!depModule) {
|
|
169
|
+
try {
|
|
170
|
+
depModule = dep?.module || dep?._module;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
depModule = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
102
176
|
const depPath = normalizeModulePath(depModule?.resource || depModule?.userRequest);
|
|
103
177
|
if (depPath)
|
|
104
178
|
out.push(depPath);
|
|
@@ -197,6 +271,7 @@ class ComponentExtractor {
|
|
|
197
271
|
nearbyText,
|
|
198
272
|
semanticRole: inferSemanticRole(tagName, staticProps, nearbyText),
|
|
199
273
|
interactions: inferInteractions(tagName, staticProps),
|
|
274
|
+
loc: { line: nodePath.node.loc?.start?.line ?? 0 },
|
|
200
275
|
});
|
|
201
276
|
},
|
|
202
277
|
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { DiscoveredRoute, DiscoveredElement, InferredFlow } from '../types';
|
|
1
|
+
import type { DiscoveredRoute, DiscoveredElement, InferredFlow, PolicyComponent } from '../types';
|
|
2
2
|
export declare class FlowInferrer {
|
|
3
3
|
private routes;
|
|
4
4
|
private elements;
|
|
5
|
-
|
|
5
|
+
private components?;
|
|
6
|
+
constructor(routes: DiscoveredRoute[], elements: DiscoveredElement[], components?: PolicyComponent[] | undefined);
|
|
6
7
|
infer(): InferredFlow[];
|
|
7
8
|
private buildEdgeGraph;
|
|
8
9
|
private detectLinearFlows;
|
|
10
|
+
private detectInteractionFlows;
|
|
9
11
|
private buildChain;
|
|
10
12
|
}
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FlowInferrer = void 0;
|
|
4
4
|
class FlowInferrer {
|
|
5
|
-
constructor(routes, elements) {
|
|
5
|
+
constructor(routes, elements, components) {
|
|
6
6
|
this.routes = routes;
|
|
7
7
|
this.elements = elements;
|
|
8
|
+
this.components = components;
|
|
8
9
|
}
|
|
9
10
|
infer() {
|
|
10
11
|
const edges = this.buildEdgeGraph();
|
|
11
|
-
|
|
12
|
+
const routeFlows = this.detectLinearFlows(edges);
|
|
13
|
+
const interactionFlows = this.detectInteractionFlows();
|
|
14
|
+
return [...routeFlows, ...interactionFlows];
|
|
12
15
|
}
|
|
13
16
|
buildEdgeGraph() {
|
|
14
17
|
const edges = [];
|
|
@@ -45,6 +48,7 @@ class FlowInferrer {
|
|
|
45
48
|
name: chain
|
|
46
49
|
.map((p) => p.split('/').filter(Boolean).pop() || 'home')
|
|
47
50
|
.join(' -> '),
|
|
51
|
+
page: chain[0],
|
|
48
52
|
steps: chain,
|
|
49
53
|
edges: edges.filter((e) => {
|
|
50
54
|
const s = new Set(chain);
|
|
@@ -56,6 +60,34 @@ class FlowInferrer {
|
|
|
56
60
|
}
|
|
57
61
|
return flows;
|
|
58
62
|
}
|
|
63
|
+
detectInteractionFlows() {
|
|
64
|
+
if (!this.components || this.components.length === 0) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const flows = [];
|
|
68
|
+
for (const component of this.components) {
|
|
69
|
+
for (const interaction of component.interactions || []) {
|
|
70
|
+
const routePath = component.route || '/';
|
|
71
|
+
if (!interaction.trigger?.event)
|
|
72
|
+
continue;
|
|
73
|
+
flows.push({
|
|
74
|
+
id: `flow_${flows.length + 1}`,
|
|
75
|
+
name: `${routePath} ${interaction.trigger.event}`,
|
|
76
|
+
page: routePath,
|
|
77
|
+
steps: [routePath],
|
|
78
|
+
edges: [
|
|
79
|
+
{
|
|
80
|
+
from: routePath,
|
|
81
|
+
to: routePath,
|
|
82
|
+
trigger: interaction.trigger.event,
|
|
83
|
+
confidence: 0.65,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return flows;
|
|
90
|
+
}
|
|
59
91
|
buildChain(start, adjacency, seen) {
|
|
60
92
|
if (seen.has(start))
|
|
61
93
|
return [];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/traverse';
|
|
2
|
+
import type { ComponentInteraction } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts interaction graphs from React component ASTs.
|
|
5
|
+
* Maps event handlers -> state mutations -> conditional renders and effects.
|
|
6
|
+
*/
|
|
7
|
+
export declare class InteractionGraphExtractor {
|
|
8
|
+
private contextResolver;
|
|
9
|
+
/**
|
|
10
|
+
* Extract all interactions (trigger -> effect mappings) from a component.
|
|
11
|
+
*/
|
|
12
|
+
extractInteractions(componentPath: NodePath<any>, componentName: string): ComponentInteraction[];
|
|
13
|
+
/**
|
|
14
|
+
* Extract trigger/effect pair from a JSX element with an event handler.
|
|
15
|
+
*/
|
|
16
|
+
private extractTriggerAndEffect;
|
|
17
|
+
private resolveStateSettersForAttribute;
|
|
18
|
+
private collectSettersFromNode;
|
|
19
|
+
/**
|
|
20
|
+
* Find conditional renders/effects that depend on a state variable.
|
|
21
|
+
* Looks for logical expressions and ternaries using the state var.
|
|
22
|
+
*/
|
|
23
|
+
private findConditionalEffect;
|
|
24
|
+
private findConditionalEffectInAncestors;
|
|
25
|
+
private findConditionalEffectInComponent;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a node references a specific state variable by name.
|
|
28
|
+
*/
|
|
29
|
+
private nodeReferencesState;
|
|
30
|
+
/**
|
|
31
|
+
* Extract component name from a JSX element or identifier.
|
|
32
|
+
*/
|
|
33
|
+
private extractComponentNameFromNode;
|
|
34
|
+
}
|