Skip to content

Commit de0b97f

Browse files
committed
Commit add JSX support
1 parent 4822e1b commit de0b97f

5 files changed

Lines changed: 81 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [3.0.4] - 2026-05-15
4+
5+
- Add JSX parsing support by falling back to meriyah's `jsx` option when non-JSX parsing fails (tries both module and script modes)
6+
37
## [3.0.3] - 2026-03-07
48

59
- Fix problem with resolving TemplateElement values in filters

CLAUDE.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
npm run build # compile CJS + ESM into lib/ (via tsup)
9+
npm run watch # build in watch mode during development
10+
npm run typecheck # tsc --noEmit
11+
npm run lint # eslint --fix
12+
npm run check # lint + typecheck
13+
npm test # jest --ci
14+
npm run testWatch # jest --watchAll
15+
```
16+
17+
Run a single test file: `npx jest tests/query.test.ts`
18+
19+
Consumers import from `lib/`, not `src/` — always run `npm run build` before testing integration or publishing.
20+
21+
## Architecture
22+
23+
**Purpose:** `query(code, query)` and `multiQuery(code, namedQueries)` parse JavaScript source and run a compact XPath-inspired query language over the resulting AST.
24+
25+
**Execution flow:**
26+
1. `parseSource` (in `src/index.ts`) parses JS via `meriyah` — tries `module: true`, falls back to `module: false, webcompat: true`.
27+
2. `createTraverser()` walks the AST once, building `NodePath` wrappers (via `createNodePath`, backed by a WeakMap) and registering scopes/bindings.
28+
3. The query string is tokenized and parsed by `src/parseQuery.ts` into a tree of `QNode`s.
29+
4. `createQuerier()` evaluates QNodes against NodePaths, memoizing subquery results by `(QNode, NodePath)` pair.
30+
31+
**Key source files:**
32+
- [src/index.ts](src/index.ts) — traversal, binding registration (`registerBindings`, `getBinding`), querier, built-in `functions` map (`join`, `concat`, `first`, `nthchild`), public API
33+
- [src/parseQuery.ts](src/parseQuery.ts) — tokenizer and parser for the query DSL; defines `AvailableFunction` type
34+
- [src/nodeutils.ts](src/nodeutils.ts)`VISITOR_KEYS`, type predicates (`isIdentifier`, `isScopable`, etc.), `NodePath` helpers
35+
- [src/utils.ts](src/utils.ts) — small generic utilities
36+
37+
**Build output:** `tsup` produces both CJS (`lib/index.js`) and ESM (`lib/index.mjs`) plus `.d.ts`/`.d.mts` files. The `exports` map in `package.json` routes `require`/`import` to the right build.
38+
39+
## Key conventions
40+
41+
**Adding a query language function** (e.g. `/fn:uppercase(sel)`):
42+
1. Add implementation to the `functions` map in `src/index.ts`.
43+
2. The `AvailableFunction` type in `parseQuery.ts` is derived from `typeof functions` — no manual update needed unless you change the type structure.
44+
3. Add tests in `tests/`.
45+
46+
**Bindings and scopes:** Scope IDs are stored on AST nodes as `extra.scopeId`. The `nodePathMap` WeakMap ties AST nodes to their `NodePath`. Do not mutate node identity or add per-node state — this silently breaks memoization and binding lookups.
47+
48+
**Performance (large/minified files):**
49+
- Use `multiQuery` instead of repeated `query` calls — the traverser is designed for a single pass over multiple queries.
50+
- Prefer child selectors (`/Type`) over descendant (`//Type`) when the depth is known.
51+
- Avoid `../` (parent) filters in hot paths — they cause extra traversal and defeat memoization.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "astronomical",
3-
"version": "3.0.3",
3+
"version": "3.0.4",
44
"type": "commonjs",
55
"description": "offers a way to query a Javascript AST to find specific patterns using a syntax somewhat similar to XPath.",
66
"sideEffects": false,

src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,15 @@ export function parseSource(source: string, optimize: boolean = true) : ASTNode
655655
try {
656656
return parseScript(source, { module: true, next: true, ...parsingOptions });
657657
} catch {
658-
return parseScript(source, { module: false, next: true, ...parsingOptions, webcompat: true });
658+
try {
659+
return parseScript(source, { module: false, next: true, ...parsingOptions, webcompat: true });
660+
} catch {
661+
try {
662+
return parseScript(source, { module: true, next: true, ...parsingOptions, jsx: true });
663+
} catch {
664+
return parseScript(source, { module: false, next: true, ...parsingOptions, webcompat: true, jsx: true });
665+
}
666+
}
659667
}
660668
}
661669

tests/query.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,22 @@ describe('testing index file', () => {
331331
expect(nodes.length).toEqual(2);
332332
})
333333

334+
test("should parse JSX and find element names", () => {
335+
const code = `import React from 'react';
336+
const App = () => <div className="app"><span>Hello</span></div>;`;
337+
const nodes = query(code, "//JSXOpeningElement/:name/:name");
338+
expect(nodes).toEqual(["div", "span"]);
339+
});
340+
341+
test("should parse JSX module with import/export and find element names", () => {
342+
const code = `import React from 'react';
343+
export default function App() {
344+
return <h1>Hello</h1>;
345+
}`;
346+
const nodes = query(code, "//JSXOpeningElement/:name/:name");
347+
expect(nodes).toEqual(["h1"]);
348+
});
349+
334350
});
335351

336352

0 commit comments

Comments
 (0)