@uicontract/parser-react 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 UIC Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @uicontract/parser-react
2
+
3
+ React and Next.js parser for discovering interactive UI elements.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @uicontract/parser-react
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { ReactParser } from '@uicontract/parser-react';
15
+
16
+ const parser = new ReactParser();
17
+
18
+ // Detect whether a project uses React
19
+ const isReact = await parser.detect('/path/to/my-app');
20
+
21
+ // Discover all interactive elements
22
+ const { elements, warnings, metadata } = await parser.discover('/path/to/my-app', {
23
+ include: ['src/**/*.{tsx,jsx}'],
24
+ });
25
+
26
+ console.log(`Found ${elements.length} elements in ${metadata.filesScanned} files`);
27
+ // elements: RawElement[] ready for @uicontract/namer
28
+ ```
29
+
30
+ ## API
31
+
32
+ - **`ReactParser`**: Implements the `Parser` interface from `@uicontract/core`.
33
+ - **`detect(dir)`**: Returns `true` if the directory contains a React project.
34
+ - **`discover(dir, options)`**: AST-parses `.tsx` and `.jsx` files using `@babel/parser` and `@babel/traverse`. Returns `{ elements, warnings, metadata }`.
35
+
36
+ Discovered element types: `button`, `a` (link), `input`, `select`, `textarea`, `form`.
37
+
38
+ Supported patterns: JSX, TSX, `forwardRef`, `memo`, HOCs, dynamic imports, conditional rendering, file-based routing (Next.js).
39
+
40
+ Unexpected syntax produces a `warning` entry rather than throwing, so partial results are always returned.
41
+
42
+ ## Part of UIC
43
+
44
+ This package is part of [UIC (UI Contracts)](https://github.com/sherifkozman/uicontract) — making web app UIs machine-readable.
45
+
46
+ ## License
47
+
48
+ [MIT](../../LICENSE)
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Context extraction helpers for the React AST visitor.
3
+ * Each function extracts a specific piece of metadata from a JSX element's path.
4
+ */
5
+ import type { NodePath } from '@babel/traverse';
6
+ import * as t from '@babel/types';
7
+ /**
8
+ * Walk up the path ancestry to find the name of the enclosing React component.
9
+ * Handles function declarations, arrow functions assigned to variables,
10
+ * React.memo(), React.forwardRef(), and HOC wrappers.
11
+ */
12
+ export declare function extractComponentName(nodePath: NodePath): string | null;
13
+ /**
14
+ * Infer a Next.js route from a file path.
15
+ * Files under an `app/` directory named `page.tsx` or `layout.tsx` map to routes.
16
+ * All other files return null.
17
+ */
18
+ export declare function extractRoute(filePath: string, projectRoot: string): string | null;
19
+ /**
20
+ * Extract the best human-readable label for a JSX interactive element.
21
+ * Priority: aria-label > children text > placeholder
22
+ */
23
+ export declare function extractLabel(openingPath: NodePath<t.JSXOpeningElement>): string | null;
24
+ /** Extract the event handler name from event props like onClick, onSubmit, etc. */
25
+ export declare function extractHandler(openingPath: NodePath<t.JSXOpeningElement>): string | null;
26
+ /** Collect all data-* attributes from a JSX opening element. */
27
+ export declare function extractDataAttributes(openingPath: NodePath<t.JSXOpeningElement>): Record<string, string>;
28
+ /**
29
+ * Returns true if the path is nested inside a ConditionalExpression (ternary)
30
+ * or a LogicalExpression with && or ||.
31
+ */
32
+ export declare function isConditional(nodePath: NodePath): boolean;
33
+ /**
34
+ * Returns true if the path is nested inside a .map() or similar array iteration call.
35
+ */
36
+ export declare function isDynamic(nodePath: NodePath): boolean;
37
+ //# sourceMappingURL=context-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-extractor.d.ts","sourceRoot":"","sources":["../src/context-extractor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAMlC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CA4DtE;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBjF;AAwDD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,MAAM,GAAG,IAAI,CAyBtF;AAMD,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,MAAM,GAAG,IAAI,CAyBxF;AAMD,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYxG;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAkBzD;AAID;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAgBrD"}
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Context extraction helpers for the React AST visitor.
3
+ * Each function extracts a specific piece of metadata from a JSX element's path.
4
+ */
5
+ import * as path from 'node:path';
6
+ import * as t from '@babel/types';
7
+ // ---------------------------------------------------------------------------
8
+ // Component name
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Walk up the path ancestry to find the name of the enclosing React component.
12
+ * Handles function declarations, arrow functions assigned to variables,
13
+ * React.memo(), React.forwardRef(), and HOC wrappers.
14
+ */
15
+ export function extractComponentName(nodePath) {
16
+ let current = nodePath.parentPath;
17
+ while (current) {
18
+ const node = current.node;
19
+ // Named function declaration: `function MyComponent() { ... }`
20
+ if (t.isFunctionDeclaration(node) && node.id) {
21
+ return node.id.name;
22
+ }
23
+ // Arrow function or function expression assigned to variable:
24
+ // `const MyComponent = () => ...`
25
+ if ((t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) &&
26
+ t.isVariableDeclarator(current.parent) &&
27
+ t.isIdentifier(current.parent.id)) {
28
+ return current.parent.id.name;
29
+ }
30
+ // Named function expression inside call (React.memo, React.forwardRef, HOC):
31
+ // `React.memo(function MyComponent() { ... })`
32
+ if (t.isFunctionExpression(node) && node.id) {
33
+ return node.id.name;
34
+ }
35
+ // Arrow/function inside a call expression (HOC wrapper, memo, forwardRef):
36
+ // `const Foo = withAuth(() => <div>...</div>)`
37
+ // `const Bar = React.memo(() => <div>...</div>)`
38
+ if ((t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) &&
39
+ t.isCallExpression(current.parent)) {
40
+ const callPath = current.parentPath;
41
+ if (callPath && t.isVariableDeclarator(callPath.parent) && t.isIdentifier(callPath.parent.id)) {
42
+ return callPath.parent.id.name;
43
+ }
44
+ // Nested calls: `React.memo(React.forwardRef(() => ...))`
45
+ if (callPath?.parentPath && t.isCallExpression(callPath.parentPath.node)) {
46
+ const outerCallPath = callPath.parentPath;
47
+ if (outerCallPath && t.isVariableDeclarator(outerCallPath.parent) && t.isIdentifier(outerCallPath.parent.id)) {
48
+ return outerCallPath.parent.id.name;
49
+ }
50
+ }
51
+ }
52
+ // Class component: `class MyComponent extends React.Component { ... }`
53
+ if (t.isClassDeclaration(node) && node.id) {
54
+ return node.id.name;
55
+ }
56
+ if (t.isClassExpression(node) && node.id) {
57
+ return node.id.name;
58
+ }
59
+ current = current.parentPath;
60
+ }
61
+ return null;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Route extraction (Next.js App Router)
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * Infer a Next.js route from a file path.
68
+ * Files under an `app/` directory named `page.tsx` or `layout.tsx` map to routes.
69
+ * All other files return null.
70
+ */
71
+ export function extractRoute(filePath, projectRoot) {
72
+ const rel = path.relative(projectRoot, filePath).replace(/\\/g, '/');
73
+ // Look for the app/ directory segment
74
+ const appIndex = rel.indexOf('app/');
75
+ if (appIndex === -1)
76
+ return null;
77
+ const afterApp = rel.slice(appIndex + 'app/'.length);
78
+ const fileName = path.basename(afterApp);
79
+ if (fileName !== 'page.tsx' && fileName !== 'page.jsx' && fileName !== 'layout.tsx' && fileName !== 'layout.jsx') {
80
+ return null;
81
+ }
82
+ const dirPart = path.dirname(afterApp).replace(/\\/g, '/');
83
+ if (dirPart === '.') {
84
+ return '/';
85
+ }
86
+ return '/' + dirPart;
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Label extraction
90
+ // ---------------------------------------------------------------------------
91
+ /** Try to extract a string value from a JSXAttribute's value node. */
92
+ function jsxAttrStringValue(attr) {
93
+ if (t.isStringLiteral(attr.value)) {
94
+ return attr.value.value;
95
+ }
96
+ if (t.isJSXExpressionContainer(attr.value)) {
97
+ const expr = attr.value.expression;
98
+ if (t.isStringLiteral(expr)) {
99
+ return expr.value;
100
+ }
101
+ if (t.isTemplateLiteral(expr) && expr.quasis.length === 1) {
102
+ const quasi = expr.quasis[0];
103
+ return quasi?.value.cooked ?? quasi?.value.raw ?? null;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+ /** Get the string name of a JSX attribute. */
109
+ function attrName(attr) {
110
+ if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
111
+ return attr.name.name;
112
+ }
113
+ return null;
114
+ }
115
+ /** Find an attribute by name in a JSX opening element. */
116
+ function findAttr(node, name) {
117
+ for (const attr of node.attributes) {
118
+ if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === name) {
119
+ return attr;
120
+ }
121
+ }
122
+ return undefined;
123
+ }
124
+ /** Extract visible text from JSX children (shallow — only literals). */
125
+ function childrenText(jsxElement) {
126
+ const texts = [];
127
+ for (const child of jsxElement.children) {
128
+ if (t.isJSXText(child)) {
129
+ const trimmed = child.value.trim();
130
+ if (trimmed)
131
+ texts.push(trimmed);
132
+ }
133
+ else if (t.isJSXExpressionContainer(child) && t.isStringLiteral(child.expression)) {
134
+ texts.push(child.expression.value);
135
+ }
136
+ }
137
+ return texts.length > 0 ? texts.join(' ') : null;
138
+ }
139
+ /**
140
+ * Extract the best human-readable label for a JSX interactive element.
141
+ * Priority: aria-label > children text > placeholder
142
+ */
143
+ export function extractLabel(openingPath) {
144
+ const node = openingPath.node;
145
+ // 1. aria-label
146
+ const ariaLabel = findAttr(node, 'aria-label');
147
+ if (ariaLabel) {
148
+ const val = jsxAttrStringValue(ariaLabel);
149
+ if (val)
150
+ return val;
151
+ }
152
+ // 2. children text — need access to the parent JSXElement
153
+ const parent = openingPath.parent;
154
+ if (t.isJSXElement(parent)) {
155
+ const text = childrenText(parent);
156
+ if (text)
157
+ return text;
158
+ }
159
+ // 3. placeholder
160
+ const placeholder = findAttr(node, 'placeholder');
161
+ if (placeholder) {
162
+ const val = jsxAttrStringValue(placeholder);
163
+ if (val)
164
+ return val;
165
+ }
166
+ return null;
167
+ }
168
+ // ---------------------------------------------------------------------------
169
+ // Handler extraction
170
+ // ---------------------------------------------------------------------------
171
+ /** Extract the event handler name from event props like onClick, onSubmit, etc. */
172
+ export function extractHandler(openingPath) {
173
+ const eventProps = ['onClick', 'onSubmit', 'onChange', 'onInput', 'onFocus', 'onBlur', 'onKeyDown', 'onKeyUp', 'onKeyPress'];
174
+ for (const attr of openingPath.node.attributes) {
175
+ if (!t.isJSXAttribute(attr))
176
+ continue;
177
+ const name = attrName(attr);
178
+ if (!name || !eventProps.includes(name))
179
+ continue;
180
+ if (!t.isJSXExpressionContainer(attr.value))
181
+ continue;
182
+ const expr = attr.value.expression;
183
+ // onClick={handleFoo}
184
+ if (t.isIdentifier(expr)) {
185
+ return expr.name;
186
+ }
187
+ // onClick={this.handleFoo}
188
+ if (t.isMemberExpression(expr) && t.isIdentifier(expr.property)) {
189
+ return expr.property.name;
190
+ }
191
+ // Inline arrow / complex expression → null
192
+ }
193
+ return null;
194
+ }
195
+ // ---------------------------------------------------------------------------
196
+ // Data attribute extraction
197
+ // ---------------------------------------------------------------------------
198
+ /** Collect all data-* attributes from a JSX opening element. */
199
+ export function extractDataAttributes(openingPath) {
200
+ const result = {};
201
+ for (const attr of openingPath.node.attributes) {
202
+ if (!t.isJSXAttribute(attr))
203
+ continue;
204
+ const name = attrName(attr);
205
+ if (!name || !name.startsWith('data-'))
206
+ continue;
207
+ const val = jsxAttrStringValue(attr);
208
+ result[name] = val ?? '';
209
+ }
210
+ return result;
211
+ }
212
+ // ---------------------------------------------------------------------------
213
+ // Conditional / Dynamic detection
214
+ // ---------------------------------------------------------------------------
215
+ /**
216
+ * Returns true if the path is nested inside a ConditionalExpression (ternary)
217
+ * or a LogicalExpression with && or ||.
218
+ */
219
+ export function isConditional(nodePath) {
220
+ let current = nodePath.parentPath;
221
+ while (current) {
222
+ if (t.isConditionalExpression(current.node))
223
+ return true;
224
+ if (t.isLogicalExpression(current.node) && (current.node.operator === '&&' || current.node.operator === '||')) {
225
+ return true;
226
+ }
227
+ // Stop at function boundaries
228
+ if (t.isFunctionDeclaration(current.node) ||
229
+ t.isFunctionExpression(current.node) ||
230
+ t.isArrowFunctionExpression(current.node)) {
231
+ break;
232
+ }
233
+ current = current.parentPath;
234
+ }
235
+ return false;
236
+ }
237
+ const DYNAMIC_CALLEES = new Set(['map', 'flatMap', 'reduce', 'filter', 'forEach', 'find', 'findIndex']);
238
+ /**
239
+ * Returns true if the path is nested inside a .map() or similar array iteration call.
240
+ */
241
+ export function isDynamic(nodePath) {
242
+ let current = nodePath.parentPath;
243
+ while (current) {
244
+ if (t.isCallExpression(current.node)) {
245
+ const callee = current.node.callee;
246
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
247
+ if (DYNAMIC_CALLEES.has(callee.property.name))
248
+ return true;
249
+ }
250
+ }
251
+ // Stop at function boundaries (but not arrow functions passed as callbacks)
252
+ if (t.isFunctionDeclaration(current.node)) {
253
+ break;
254
+ }
255
+ current = current.parentPath;
256
+ }
257
+ return false;
258
+ }
259
+ //# sourceMappingURL=context-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-extractor.js","sourceRoot":"","sources":["../src/context-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * AST visitor that discovers interactive JSX elements in a parsed file.
3
+ */
4
+ import type { RawElement } from '@uicontract/core';
5
+ /** Parse a React/JSX/TS source file and return discovered interactive elements. */
6
+ export declare function parseFile(source: string, absoluteFilePath: string, projectRoot: string): RawElement[];
7
+ //# sourceMappingURL=element-visitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-visitor.d.ts","sourceRoot":"","sources":["../src/element-visitor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,kBAAkB,CAAC;AA+D3E,mFAAmF;AACnF,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,GAClB,UAAU,EAAE,CAiEd"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * AST visitor that discovers interactive JSX elements in a parsed file.
3
+ */
4
+ import { parse } from '@babel/parser';
5
+ import _traverse from '@babel/traverse';
6
+ import * as t from '@babel/types';
7
+ import { extractComponentName, extractRoute, extractLabel, extractHandler, extractDataAttributes, isConditional, isDynamic, } from './context-extractor.js';
8
+ // @babel/traverse ships as CJS; handle default import for ESM interop
9
+ const traverse = (_traverse.default ?? _traverse);
10
+ /** Interactive HTML element tag names that are always captured. */
11
+ const ALWAYS_INTERACTIVE = new Set([
12
+ 'button',
13
+ 'input',
14
+ 'select',
15
+ 'textarea',
16
+ 'a',
17
+ 'form',
18
+ ]);
19
+ /** JSX event handler prop names that make any element interactive. */
20
+ const EVENT_HANDLER_PROPS = new Set([
21
+ 'onClick',
22
+ 'onSubmit',
23
+ 'onChange',
24
+ 'onInput',
25
+ 'onFocus',
26
+ 'onBlur',
27
+ 'onKeyDown',
28
+ 'onKeyUp',
29
+ 'onKeyPress',
30
+ ]);
31
+ /** Elements that can appear in InteractiveElementType but aren't always interactive. */
32
+ const GENERIC_INTERACTIVE_TYPES = new Set(['div', 'span', 'img', 'label']);
33
+ /** All valid InteractiveElementType values. */
34
+ const VALID_ELEMENT_TYPES = new Set([
35
+ 'button', 'input', 'select', 'textarea', 'a', 'form',
36
+ 'div', 'span', 'img', 'label',
37
+ ]);
38
+ function toInteractiveType(tagName) {
39
+ if (VALID_ELEMENT_TYPES.has(tagName)) {
40
+ return tagName;
41
+ }
42
+ return null;
43
+ }
44
+ /** Check whether a JSX opening element has any event handler props. */
45
+ function hasEventHandlerProp(node) {
46
+ for (const attr of node.attributes) {
47
+ if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
48
+ if (EVENT_HANDLER_PROPS.has(attr.name.name))
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ }
54
+ /** Parse a React/JSX/TS source file and return discovered interactive elements. */
55
+ export function parseFile(source, absoluteFilePath, projectRoot) {
56
+ const ast = parse(source, {
57
+ sourceType: 'module',
58
+ plugins: [
59
+ 'typescript',
60
+ 'jsx',
61
+ 'decorators-legacy',
62
+ 'classProperties',
63
+ 'optionalChaining',
64
+ 'nullishCoalescingOperator',
65
+ ],
66
+ });
67
+ const elements = [];
68
+ traverse(ast, {
69
+ JSXOpeningElement(nodePath) {
70
+ const nameNode = nodePath.node.name;
71
+ // Only handle plain HTML elements (lowercase tag names)
72
+ if (!t.isJSXIdentifier(nameNode))
73
+ return;
74
+ const tagName = nameNode.name;
75
+ const isAlwaysInteractive = ALWAYS_INTERACTIVE.has(tagName);
76
+ const isGenericWithHandler = GENERIC_INTERACTIVE_TYPES.has(tagName) && hasEventHandlerProp(nodePath.node);
77
+ if (!isAlwaysInteractive && !isGenericWithHandler)
78
+ return;
79
+ const elementType = toInteractiveType(tagName);
80
+ if (!elementType)
81
+ return;
82
+ const loc = nodePath.node.loc;
83
+ const line = loc?.start.line ?? 0;
84
+ const column = (loc?.start.column ?? 0) + 1; // convert 0-based to 1-based
85
+ const componentName = extractComponentName(nodePath);
86
+ const route = extractRoute(absoluteFilePath, projectRoot);
87
+ const label = extractLabel(nodePath);
88
+ const handler = extractHandler(nodePath);
89
+ const attributes = extractDataAttributes(nodePath);
90
+ const conditional = isConditional(nodePath);
91
+ const dynamic = isDynamic(nodePath);
92
+ const relFilePath = absoluteFilePath
93
+ .replace(/\\/g, '/')
94
+ .replace(projectRoot.replace(/\\/g, '/').replace(/\/?$/, '/'), '');
95
+ elements.push({
96
+ type: elementType,
97
+ filePath: relFilePath,
98
+ line,
99
+ column,
100
+ componentName,
101
+ route,
102
+ label,
103
+ handler,
104
+ attributes,
105
+ conditional,
106
+ dynamic,
107
+ });
108
+ },
109
+ });
110
+ return elements;
111
+ }
112
+ //# sourceMappingURL=element-visitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-visitor.js","sourceRoot":"","sources":["../src/element-visitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * File discovery for React/JSX projects.
3
+ * Finds .tsx/.jsx/.ts/.js files respecting include/exclude patterns and maxDepth.
4
+ */
5
+ import type { ParserOptions } from '@uicontract/core';
6
+ /**
7
+ * Discovers React/JSX source files in the given directory.
8
+ * Returns absolute paths.
9
+ */
10
+ export declare function discoverFiles(dir: string, options: ParserOptions): Promise<string[]>;
11
+ //# sourceMappingURL=file-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-discovery.d.ts","sourceRoot":"","sources":["../src/file-discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA4FtD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ1F"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * File discovery for React/JSX projects.
3
+ * Finds .tsx/.jsx/.ts/.js files respecting include/exclude patterns and maxDepth.
4
+ */
5
+ import * as fs from 'node:fs/promises';
6
+ import * as path from 'node:path';
7
+ const DEFAULT_INCLUDE = ['**/*.tsx', '**/*.jsx'];
8
+ const DEFAULT_EXCLUDE = [
9
+ '**/node_modules/**',
10
+ '**/dist/**',
11
+ '**/.next/**',
12
+ '**/build/**',
13
+ '**/.cache/**',
14
+ '**/coverage/**',
15
+ ];
16
+ const DEFAULT_MAX_DEPTH = 20;
17
+ /**
18
+ * Converts a glob pattern segment to a RegExp source string.
19
+ * Supports `**`, `*`, `?`, and literal characters.
20
+ */
21
+ function globToRegex(glob) {
22
+ let pattern = '^';
23
+ let i = 0;
24
+ while (i < glob.length) {
25
+ const char = glob[i];
26
+ if (char === '*' && glob[i + 1] === '*') {
27
+ // ** matches any number of path segments including none
28
+ pattern += '(?:.+/)?';
29
+ i += 2;
30
+ // skip trailing slash if present
31
+ if (glob[i] === '/')
32
+ i++;
33
+ }
34
+ else if (char === '*') {
35
+ // * matches any character except /
36
+ pattern += '[^/]*';
37
+ i++;
38
+ }
39
+ else if (char === '?') {
40
+ pattern += '[^/]';
41
+ i++;
42
+ }
43
+ else if ('.+^${}()|[]\\'.includes(char ?? '')) {
44
+ pattern += '\\' + char;
45
+ i++;
46
+ }
47
+ else {
48
+ pattern += char;
49
+ i++;
50
+ }
51
+ }
52
+ pattern += '$';
53
+ return new RegExp(pattern);
54
+ }
55
+ function matchesAny(relPath, patterns) {
56
+ const normalized = relPath.replace(/\\/g, '/');
57
+ return patterns.some((glob) => {
58
+ const regex = globToRegex(glob);
59
+ return regex.test(normalized);
60
+ });
61
+ }
62
+ /**
63
+ * Recursively walks a directory and collects files that match include patterns
64
+ * and don't match exclude patterns, up to maxDepth levels deep.
65
+ */
66
+ async function walkDir(dir, rootDir, include, exclude, maxDepth, currentDepth, results) {
67
+ if (currentDepth > maxDepth)
68
+ return;
69
+ let entries;
70
+ try {
71
+ entries = await fs.readdir(dir, { withFileTypes: true });
72
+ }
73
+ catch {
74
+ // Unreadable directory — skip silently
75
+ return;
76
+ }
77
+ for (const entry of entries) {
78
+ const fullPath = path.join(dir, entry.name);
79
+ const relPath = path.relative(rootDir, fullPath).replace(/\\/g, '/');
80
+ if (matchesAny(relPath, exclude))
81
+ continue;
82
+ if (entry.isDirectory()) {
83
+ await walkDir(fullPath, rootDir, include, exclude, maxDepth, currentDepth + 1, results);
84
+ }
85
+ else if (entry.isFile() && matchesAny(relPath, include)) {
86
+ results.push(fullPath);
87
+ }
88
+ }
89
+ }
90
+ /**
91
+ * Discovers React/JSX source files in the given directory.
92
+ * Returns absolute paths.
93
+ */
94
+ export async function discoverFiles(dir, options) {
95
+ const include = options.include ?? DEFAULT_INCLUDE;
96
+ const exclude = options.exclude ?? DEFAULT_EXCLUDE;
97
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
98
+ const results = [];
99
+ await walkDir(dir, dir, include, exclude, maxDepth, 0, results);
100
+ return results;
101
+ }
102
+ //# sourceMappingURL=file-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-discovery.js","sourceRoot":"","sources":["../src/file-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @uicontract/parser-react — React/JSX parser for UIC.
3
+ * Implements the Parser interface from @uicontract/core.
4
+ */
5
+ import type { Parser, DiscoveryResult, ParserOptions } from '@uicontract/core';
6
+ export declare const VERSION = "0.0.0";
7
+ /** The React/Next.js parser implementation. */
8
+ export declare class ReactParser implements Parser {
9
+ readonly framework = "react";
10
+ detect(dir: string): Promise<boolean>;
11
+ discover(dir: string, options: ParserOptions): Promise<DiscoveryResult>;
12
+ }
13
+ export declare const reactParser: ReactParser;
14
+ export { discoverFiles } from './file-discovery.js';
15
+ export { parseFile } from './element-visitor.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,aAAa,EAA6B,MAAM,kBAAkB,CAAC;AAI1G,eAAO,MAAM,OAAO,UAAU,CAAC;AA4B/B,+CAA+C;AAC/C,qBAAa,WAAY,YAAW,MAAM;IACxC,QAAQ,CAAC,SAAS,WAAW;IAEvB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC;CAiC9E;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC;AAG7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @uicontract/parser-react — React/JSX parser for UIC.
3
+ * Implements the Parser interface from @uicontract/core.
4
+ */
5
+ import * as fs from 'node:fs/promises';
6
+ import * as path from 'node:path';
7
+ import { discoverFiles } from './file-discovery.js';
8
+ import { parseFile } from './element-visitor.js';
9
+ export const VERSION = '0.0.0';
10
+ /** Detect whether a directory likely contains a React project. */
11
+ async function detectReact(dir) {
12
+ // Check for package.json with react dependency
13
+ try {
14
+ const pkgPath = path.join(dir, 'package.json');
15
+ const raw = await fs.readFile(pkgPath, 'utf-8');
16
+ const pkg = JSON.parse(raw);
17
+ const deps = {
18
+ ...(typeof pkg['dependencies'] === 'object' && pkg['dependencies'] !== null ? pkg['dependencies'] : {}),
19
+ ...(typeof pkg['devDependencies'] === 'object' && pkg['devDependencies'] !== null ? pkg['devDependencies'] : {}),
20
+ ...(typeof pkg['peerDependencies'] === 'object' && pkg['peerDependencies'] !== null ? pkg['peerDependencies'] : {}),
21
+ };
22
+ if ('react' in deps)
23
+ return true;
24
+ }
25
+ catch {
26
+ // No package.json or unreadable — fall through to file check
27
+ }
28
+ // Fallback: any .tsx or .jsx file present
29
+ try {
30
+ const files = await discoverFiles(dir, { include: ['**/*.tsx', '**/*.jsx'], maxDepth: 3 });
31
+ return files.length > 0;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ /** The React/Next.js parser implementation. */
38
+ export class ReactParser {
39
+ framework = 'react';
40
+ async detect(dir) {
41
+ return detectReact(dir);
42
+ }
43
+ async discover(dir, options) {
44
+ const startTime = Date.now();
45
+ const warnings = [];
46
+ const elements = [];
47
+ let filesSkipped = 0;
48
+ const files = await discoverFiles(dir, options);
49
+ for (const file of files) {
50
+ try {
51
+ const source = await fs.readFile(file, 'utf-8');
52
+ const fileElements = parseFile(source, file, dir);
53
+ elements.push(...fileElements);
54
+ }
55
+ catch (error) {
56
+ filesSkipped++;
57
+ warnings.push({
58
+ code: 'PARSE_ERROR',
59
+ message: `Failed to parse: ${error instanceof Error ? error.message : String(error)}`,
60
+ filePath: path.relative(dir, file),
61
+ });
62
+ }
63
+ }
64
+ return {
65
+ elements,
66
+ warnings,
67
+ metadata: {
68
+ filesScanned: files.length,
69
+ filesSkipped,
70
+ scanDurationMs: Date.now() - startTime,
71
+ },
72
+ };
73
+ }
74
+ }
75
+ export const reactParser = new ReactParser();
76
+ // Re-export internal modules for consumers that need direct access
77
+ export { discoverFiles } from './file-discovery.js';
78
+ export { parseFile } from './element-visitor.js';
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@uicontract/parser-react",
3
+ "version": "0.1.0",
4
+ "description": "React and Next.js parser for discovering interactive UI elements",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "UIC Contributors",
8
+ "homepage": "https://github.com/sherifkozman/uicontract/tree/main/packages/parser-react",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/sherifkozman/uicontract.git",
12
+ "directory": "packages/parser-react"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/sherifkozman/uicontract/issues"
16
+ },
17
+ "keywords": [
18
+ "uic",
19
+ "ui-contracts",
20
+ "parser",
21
+ "react",
22
+ "nextjs",
23
+ "ast"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "main": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js"
37
+ }
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "LICENSE",
42
+ "README.md"
43
+ ],
44
+ "dependencies": {
45
+ "@babel/parser": "^7.29.0",
46
+ "@babel/traverse": "^7.29.0",
47
+ "@babel/types": "^7.29.0",
48
+ "@uicontract/core": "0.1.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/babel__traverse": "^7.28.0"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -b",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit",
57
+ "lint": "eslint src"
58
+ }
59
+ }