Skip to content

Commit f09fbb7

Browse files
committed
feat: implement has-jsx - CLI and programmatic JSX detection tool
1 parent 3783a11 commit f09fbb7

22 files changed

+3758
-0
lines changed

.github/workflows/node.js.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3+
4+
name: Node.js CI
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
strategy:
18+
matrix:
19+
node-version: [20.x, 22.x]
20+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
- name: Use Node.js ${{ matrix.node-version }}
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
cache: 'npm'
29+
- run: npm ci
30+
- run: npm run build --if-present
31+
- run: npm test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,4 @@ dist
137137
# Vite logs files
138138
vite.config.js.timestamp-*
139139
vite.config.ts.timestamp-*
140+
dist/

README.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# has-jsx
2+
3+
Detect JSX in files and strings using AST analysis.
4+
5+
## Why has-jsx?
6+
7+
Detecting JSX isn't as simple as checking file extensions. JavaScript and TypeScript files (`.js`, `.ts`) can contain JSX syntax. This tool uses [ast-grep](https://github.com/ast-grep/ast-grep)'s powerful AST parsing to reliably detect JSX node types from the [JSX specification](https://facebook.github.io/jsx/):
8+
9+
- `jsx_element` - Standard tags: `<div>...</div>`
10+
- `jsx_self_closing_element` - Self-closing: `<Component />`
11+
- `jsx_fragment` - Fragments: `<>...</>`
12+
13+
## Installation
14+
15+
```bash
16+
npm install has-jsx
17+
```
18+
19+
## CLI Usage
20+
21+
### Check Files
22+
23+
Use the `-f` or `--file` flag to check files:
24+
25+
```bash
26+
has-jsx -f src/Component.tsx
27+
# Output: JSX detected
28+
# Exit code: 0
29+
30+
has-jsx --file src/utils.ts
31+
# Output: No JSX detected
32+
# Exit code: 1
33+
```
34+
35+
### Check Strings
36+
37+
Pass source code directly without any flags:
38+
39+
```bash
40+
has-jsx "const App = () => <div>Hello</div>"
41+
# Output: JSX detected
42+
# Exit code: 0
43+
44+
has-jsx "const x = 5;"
45+
# Output: No JSX detected
46+
# Exit code: 1
47+
```
48+
49+
### Options
50+
51+
| Flag | Description |
52+
|------|-------------|
53+
| `-f, --file <path>` | Check a file for JSX |
54+
| `--verbose` | Output results as JSON |
55+
| `--quiet` | Silent mode, exit code only |
56+
| `--version` | Show version number |
57+
| `--help` | Show help |
58+
59+
### Exit Codes
60+
61+
| Code | Meaning |
62+
|------|---------|
63+
| `0` | JSX detected |
64+
| `1` | No JSX detected |
65+
| `2` | Error (file not found, read error, etc.) |
66+
67+
## Programmatic API
68+
69+
### File-based Detection
70+
71+
Check if a file contains JSX:
72+
73+
**ESM (recommended):**
74+
```javascript
75+
import hasJSX from 'has-jsx';
76+
77+
const result = await hasJSX('src/Component.tsx');
78+
console.log(result); // true or false
79+
```
80+
81+
**CommonJS:**
82+
```javascript
83+
const { hasJSX } = require('has-jsx');
84+
85+
(async () => {
86+
const result = await hasJSX('src/Component.tsx');
87+
console.log(result);
88+
})();
89+
```
90+
91+
### String-based Detection
92+
93+
Check if a string contains JSX (synchronous, no file I/O):
94+
95+
**ESM:**
96+
```javascript
97+
import { hasJSXInString } from 'has-jsx';
98+
99+
const code = `
100+
function App() {
101+
return <div>Hello World</div>;
102+
}
103+
`;
104+
105+
const result = hasJSXInString(code);
106+
console.log(result); // true
107+
```
108+
109+
**CommonJS:**
110+
```javascript
111+
const { hasJSXInString } = require('has-jsx');
112+
113+
const code = 'const App = () => <div>Hello</div>';
114+
const result = hasJSXInString(code);
115+
console.log(result); // true
116+
```
117+
118+
### API Reference
119+
120+
#### `hasJSX(filepath: string): Promise<boolean>`
121+
122+
Analyzes a file to detect JSX syntax.
123+
124+
**Parameters:**
125+
- `filepath` - Path to the file to analyze (relative or absolute)
126+
127+
**Returns:**
128+
- `Promise<boolean>` - `true` if JSX is detected, `false` otherwise
129+
130+
**Throws:**
131+
- `Error` - If file doesn't exist, is not a file, or can't be read
132+
133+
**Example:**
134+
135+
```javascript
136+
import hasJSX from 'has-jsx';
137+
138+
try {
139+
const result = await hasJSX('src/Component.tsx');
140+
if (result) {
141+
console.log('This file contains JSX');
142+
}
143+
} catch (error) {
144+
console.error('Error:', error.message);
145+
}
146+
```
147+
148+
#### `hasJSXInString(source: string): boolean`
149+
150+
Analyzes a source code string to detect JSX syntax (synchronous).
151+
152+
**Parameters:**
153+
- `source` - Source code string to analyze
154+
155+
**Returns:**
156+
- `boolean` - `true` if JSX is detected, `false` otherwise
157+
158+
**Throws:**
159+
- `TypeError` - If source is not a string
160+
161+
**Example:**
162+
163+
```javascript
164+
import { hasJSXInString } from 'has-jsx';
165+
166+
const code = 'const Button = () => <button>Click</button>';
167+
const result = hasJSXInString(code);
168+
console.log(result); // true
169+
170+
// No false positives for TypeScript generics
171+
const genericCode = 'function identity<T>(arg: T): T { return arg; }';
172+
console.log(hasJSXInString(genericCode)); // false
173+
```
174+
175+
## How It Works
176+
177+
1. **AST Parsing**: Uses [ast-grep](https://github.com/ast-grep/ast-grep) to parse files as TypeScript/TSX
178+
2. **Node Detection**: Searches the AST for JSX-specific node types defined in the [JSX specification](https://facebook.github.io/jsx/)
179+
3. **Reliable Results**: Avoids false positives from regex-based detection or angle brackets in comparisons
180+
181+
## Use Cases
182+
183+
- **Build Tools**: Determine which files need JSX transformation
184+
- **Linters**: Validate JSX usage against project rules
185+
- **Code Analysis**: Audit codebase for JSX patterns
186+
- **CI/CD**: Verify file types in automated pipelines
187+
- **Migration Tools**: Identify files during framework migrations
188+
189+
## Requirements
190+
191+
- Node.js >= 20.0.0
192+
193+
## License
194+
195+
MIT
196+
197+
## Contributing
198+
199+
Issues and pull requests welcome! Please ensure all tests pass before submitting:
200+
201+
```bash
202+
npm test
203+
```

bin/has-jsx.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env node
2+
3+
import { run } from '../src/cli.js';
4+
5+
run(process.argv).catch((error) => {
6+
console.error(error.message);
7+
process.exit(1);
8+
});

0 commit comments

Comments
 (0)