Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
- run: npm ci

- name: Copy original parser
run: cp -r src/grammar/jsonpath_js.js ${{ runner.temp }}
run: cp -r src/grammar/jsonpath-js.js ${{ runner.temp }}
- run: npm run build:parser
- name: Compare with original parser
run: |
if ! diff -qr src/grammar/jsonpath_js.js ${{ runner.temp }}/jsonpath_js.js; then
if ! diff -qr src/grammar/jsonpath-js.js ${{ runner.temp }}/jsonpath-js.js; then
echo 'Generated parser code is out of sync. Please run "npm run build:parser", and commit the changes.'
exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.18.1
20.19.5
27 changes: 21 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,87 +9,102 @@ This is jsonpath-js, a TypeScript library implementing RFC 9535 JSONPath specifi
## Essential Commands

### Development

- `npm install` - Install dependencies
- `npm test` - Run all tests (uses Vitest)
- `npm run lint` - Run Biome linter
- `npm run build` - Build the library using tsup
- `npm run build:parser` - Regenerate parser from Peggy grammar (required after grammar changes)

### Testing

- `npm test` - Run all tests including RFC compliance tests
- Tests are located in `tests/` with RFC compliance tests in `tests/RFC9535/`

### Submodules

- `git submodule update --init --recursive` - Initialize git submodules (required for RFC compliance tests)

## Architecture Overview

### Core Flow

1. **Parse**: JSONPath string → AST via Peggy-generated parser
2. **Execute**: AST + JSON data → Node objects (value + path pairs)
3. **Return**: Extract values or value+path pairs from final nodes

### Key Components

**Grammar System (`src/grammar/`)**

- `jsonpath.pegjs` - Peggy grammar defining JSONPath syntax
- `jsonpath_js.js` - Generated parser (auto-generated, don't edit directly)
- `jsonpath-js.js` - Generated parser (auto-generated, don't edit directly)
- `ast.d.ts` - TypeScript definitions for AST nodes
- **Important**: Always run `npm run build:parser` after modifying the grammar

**Execution Engine**
- `src/jsonpath_js.ts` - Main `JSONPathJS` class with `find()` and `paths()` methods

- `src/jsonpath-js.ts` - Main `JSONPathJS` class with `find()` and `paths()` methods
- `src/parser.ts` - Central execution engine that processes AST
- `src/parsers/` - Specialized processors for different selector types:
- `root.ts` - Main segment processing logic
- `filter_selector.ts` - Complex filtering with comparisons
- `array_slice_selector.ts` - Array slicing operations
- `function_extentions.ts` - Built-in function execution
- `filter-selector.ts` - Complex filtering with comparisons
- `array-slice-selector.ts` - Array slicing operations
- `function-extentions.ts` - Built-in function execution

**Type System**

- `src/types/node.ts` - Core Node abstraction (value + path)
- `src/types/json.d.ts` - Comprehensive JSON type definitions
- All components use Node objects to maintain path tracking

**Comparison System (`src/comparator/`)**

- Type-specific comparators for all JSON types (Array, Boolean, Numeric, Object, String)
- Implements all JSONPath comparison operators (==, !=, <, <=, >, >=)
- Critical for filter expressions

**Function System (`src/functions/`)**

- Built-in JSONPath functions: count, length, match, search, value
- Strongly-typed function definitions and execution

### Build System

- **tsup**: Modern bundler producing CommonJS and ESM outputs
- **Peggy**: Parser generator for JSONPath grammar
- **Dual format**: Both CJS and ESM with TypeScript definitions

## Development Guidelines

### Grammar Changes

1. Modify `src/grammar/jsonpath.pegjs`
2. Run `npm run build:parser` to regenerate parser
3. Update `src/grammar/ast.d.ts` if AST structure changes
4. Test thoroughly with RFC compliance suite

### Parser Changes

- Parser logic is in `src/parsers/` - each selector type has its own file
- All processing works with Node objects containing value and path
- Maintain path strings in JSONPath format (e.g., `$['users'][0]['name']`)

### Adding New Functionality

- **Selectors**: Add new selector types in `src/parsers/`
- **Functions**: Add new functions in `src/functions/`
- **Comparators**: Extend comparison logic in `src/comparator/`

### Testing

- RFC compliance tests in `tests/RFC9535/` use official JSONPath test suite
- Unit tests for each component in respective test files
- Always run full test suite before committing grammar changes

### Code Patterns

- Use Node objects for all data flow
- Type-safe AST processing throughout
- Maintain JSONPath specification compliance
- Parse-once, execute-many pattern for performance
- Parse-once, execute-many pattern for performance
15 changes: 11 additions & 4 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.0.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand All @@ -10,8 +10,8 @@
"includes": [
"**",
"!**/src/jsonpath.ts",
"!**/src/grammar/jsonpath_js.js",
"!**/src/grammar/jsonpath_js.d.ts",
"!**/src/grammar/jsonpath-js.js",
"!**/src/grammar/jsonpath-js.d.ts",
"!**/tests/RFC9535/jsonpath-compliance-test-suite/**/*"
]
},
Expand All @@ -35,7 +35,14 @@
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "error",
"noUselessElse": "error"
"noUselessElse": "error",
"useFilenamingConvention": {
"level": "error",
"options": {
"requireAscii": true,
"filenameCases": ["kebab-case"]
}
}
}
},
"includes": ["**", "!**/package.json"]
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"engines": {
"node": ">=18"
"node": ">=20"
},
"files": [
"dist",
Expand All @@ -40,7 +40,7 @@
"devDependencies": {
"@biomejs/biome": "2.2.4",
"@types/jest": "^29.5.8",
"@types/node": "^18.7.3",
"@types/node": "^20.19.5",
"lefthook": "^1.11.14",
"npm-run-all": "^4.1.5",
"peggy": "^5.0.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { JsonArray } from "../types/json";
import { isEqual } from "../utils/isEqual";
import type { ComparisonOperators } from "./ComparisonOperators";
import { isEqual } from "../utils/is-equal";

import type { ComparisonOperators } from "./comparison-operators";

// equal arrays, that is, arrays of the same length where each element of
// the first array is equal to the corresponding element of the second array, or
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ComparisonOperators } from "./ComparisonOperators";
import type { ComparisonOperators } from "./comparison-operators";

export const BooleanComparator: ComparisonOperators<boolean> = {
"=="(a, b) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Operators } from "./Operators";
import type { Operators } from "./operators";

export type Comparator<T> = (a: T, b: T) => boolean;

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { JsonObject } from "../types/json";
import { isEqual } from "../utils/isEqual";
import { isEqual } from "../utils/is-equal";
import type { ComparisonOperators } from "./ComparisonOperators";

// equal objects with no duplicate names, that is, where:
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ComparisonOperators } from "./ComparisonOperators";
import type { ComparisonOperators } from "./comparison-operators";

// a non-empty string compares less than another non-empty string
// if and only if the first string starts with a lower Unicode scalar
Expand Down
2 changes: 1 addition & 1 deletion src/functions/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
createFunctionDefinition,
NodesTypeDef,
ValueTypeDef,
} from "./function_definitions";
} from "./function-definitions";

// 2.4.5. count() Function Extension
// Parameters:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionType } from "../functions/function_types";
import { FunctionType } from "../functions/function-types";
import type { Json, JsonValue } from "../types/json";
import { isNode, isNodeList, type Node, type NodeList } from "../types/node";
import { Nothing } from "../types/nothing";
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/functions/length.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Nothing } from "../types/nothing";
import { isJsonObject } from "../utils";
import { createFunctionDefinition, ValueTypeDef } from "./function_definitions";
import type { FunctionType } from "./function_types";
import { createFunctionDefinition, ValueTypeDef } from "./function-definitions";
import type { FunctionType } from "./function-types";

// 2.4.4. length() Function Extension
// Parameters:
Expand Down
6 changes: 3 additions & 3 deletions src/functions/match.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { convertIRegexpToJsRegexp } from "../utils/convertIRegexpToJsRegexp";
import { convertIRegexpToJsRegexp } from "../utils/convert-iregexp-to-js-regexp";
import {
createFunctionDefinition,
LogicalTypeDef,
ValueTypeDef,
} from "./function_definitions";
import { convertLogicalType, FunctionType } from "./function_types";
} from "./function-definitions";
import { convertLogicalType, FunctionType } from "./function-types";

// 2.4.6. match() Function Extension
// Parameters:
Expand Down
6 changes: 3 additions & 3 deletions src/functions/search.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { convertIRegexpToJsRegexp } from "../utils/convertIRegexpToJsRegexp";
import { convertIRegexpToJsRegexp } from "../utils/convert-iregexp-to-js-regexp";
import {
createFunctionDefinition,
LogicalTypeDef,
ValueTypeDef,
} from "./function_definitions";
import { convertLogicalType, FunctionType } from "./function_types";
} from "./function-definitions";
import { convertLogicalType, FunctionType } from "./function-types";

// 2.4.7. search() Function Extension
// Parameters:
Expand Down
2 changes: 1 addition & 1 deletion src/functions/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
createFunctionDefinition,
NodesTypeDef,
ValueTypeDef,
} from "./function_definitions";
} from "./function-definitions";

// 2.4.8. value() Function Extension
// Parameters:
Expand Down
2 changes: 1 addition & 1 deletion src/grammar/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module.exports = {
input: "./src/grammar/jsonpath.pegjs",
allowedStartRules: ["JsonpathQuery"],
dts: "true",
output: "src/grammar/jsonpath_js.js",
output: "src/grammar/jsonpath-js.js",
returnTypes: {
JsonpathQuery: "import('./ast.d.ts').JsonpathQuery",
},
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { JSONPathJS } from "./jsonpath_js";
import { JSONPathJS } from "./jsonpath-js";

export { JSONPathJS };
6 changes: 3 additions & 3 deletions src/jsonpath_js.ts → src/jsonpath-js.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { JsonpathQuery } from "./grammar/ast";
import { parse } from "./grammar/jsonpath_js";
import { parse } from "./grammar/jsonpath-js";
import { run } from "./parser";
import type { Json } from "./types/json";
import { escapeMemberName } from "./utils/escapeMemberName";
import { escapeMemberName } from "./utils/escape-member-name";

type PathResult = {
value: Json;
Expand All @@ -26,7 +26,7 @@ export class JSONPathJS {
* @param query - The JSONPath query string to parse
* @throws Throws an error if the query string is invalid
*/
constructor(private query: string) {
constructor(query: string) {
const parseResult = parse(query);
this.rootNode = parseResult;
}
Expand Down
20 changes: 10 additions & 10 deletions src/parsers/filter_selector.ts → src/parsers/filter-selector.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ArrayComparator } from "../comparator/ArrayComparator";
import { BooleanComparator } from "../comparator/BooleanComparator";
import { NodeComparator } from "../comparator/NodeComparator";
import { NullComparator } from "../comparator/NullComparator";
import { NumericComparator } from "../comparator/NumericComparator";
import { ObjectComparator } from "../comparator/ObjectComparator";
import { StringComparator } from "../comparator/StringComparator";
import { FunctionType, isLogicalType } from "../functions/function_types";
import { ArrayComparator } from "../comparator/array-comparator";
import { BooleanComparator } from "../comparator/boolean-comparator";
import { NodeComparator } from "../comparator/node-comparator";
import { NullComparator } from "../comparator/null-comparator";
import { NumericComparator } from "../comparator/numeric-comparator";
import { ObjectComparator } from "../comparator/object-comparator";
import { StringComparator } from "../comparator/string-comparator";
import { FunctionType, isLogicalType } from "../functions/function-types";
import type {
Comparable,
ComparisonExpr,
Expand All @@ -25,8 +25,8 @@ import type { JsonValue } from "../types/json.d";
import type { Node, NodeList } from "../types/node";
import { Nothing } from "../types/nothing";
import { isJsonArray, isJsonObject, isJsonPrimitive } from "../utils";
import { enumerateNode } from "../utils/enumerateNode";
import { applyFunction } from "./function_extentions";
import { enumerateNode } from "../utils/enumerate-node";
import { applyFunction } from "./function-extentions";
import { applyRoot, applySegments } from "./root";

// 2.3.5. Filter Selector
Expand Down
Loading