Skip to content

Commit 8369879

Browse files
authored
Merge branch 'master' into fix/increase-test-coverage-for-bfs-and-cycles
2 parents 9f91cce + 985a969 commit 8369879

File tree

5 files changed

+190
-33
lines changed

5 files changed

+190
-33
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [1.11.0](https://github.com/json-schema-tools/traverse/compare/1.10.4...1.11.0) (2025-06-20)
2+
3+
4+
### Features
5+
6+
* support additional schema keywords ([6c0a7fa](https://github.com/json-schema-tools/traverse/commit/6c0a7fa714a2640ba6da7bfa3f2597ff42ffe71c))
7+
18
## [1.10.4](https://github.com/json-schema-tools/traverse/compare/1.10.3...1.10.4) (2024-05-07)
29

310

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,29 @@ traverse(mySchema, (schemaOrSubschema) => {
5454
});
5555
```
5656

57-
## API Docs
57+
### Advanced Options
58+
59+
`traverse` accepts an optional options object as the third argument. Some useful
60+
flags include:
5861

59-
https://json-schema-tools.github.io/traverse/
62+
- `bfs` - process schemas in a breadth first order
63+
- `skipFirstMutation` - do not call the mutation function on the root schema
64+
- `mergeNotMutate` - merge the mutation result back into the original schema
6065

61-
## Features Todo
66+
```js
67+
traverse(mySchema, (schemaOrSubschema) => {
68+
console.log(schemaOrSubschema.title);
69+
}, {
70+
bfs: true,
71+
skipFirstMutation: true,
72+
mergeNotMutate: true,
73+
});
74+
```
75+
76+
## API Docs
6277

63-
- unevaluatedItems (https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.3.1.3)
64-
- unevaluatedProperties (https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.3.2.4)
65-
- contains (https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.3.1.4)
66-
- propertyNames (https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.3.2.5)
78+
The full TypeDoc generated API documentation is available at
79+
[https://json-schema-tools.github.io/traverse/](https://json-schema-tools.github.io/traverse/).
6780

6881
### Contributing
6982

src/index.test.ts

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,109 @@ describe("traverse", () => {
622622
});
623623
});
624624

625+
it("traverses contains", () => {
626+
const testSchema: any = {
627+
contains: { type: "string" }
628+
};
629+
const mockMutation = jest.fn((mockS) => mockS);
630+
631+
traverse(testSchema, mockMutation);
632+
633+
expect(mockMutation).nthCalledWith(
634+
1,
635+
testSchema.contains,
636+
expect.anything(),
637+
expect.anything(),
638+
expect.anything(),
639+
);
640+
expect(mockMutation).nthCalledWith(
641+
2,
642+
testSchema,
643+
expect.anything(),
644+
expect.anything(),
645+
undefined,
646+
);
647+
648+
expect(mockMutation).toHaveBeenCalledTimes(2);
649+
});
650+
651+
it("traverses propertyNames", () => {
652+
const testSchema: any = {
653+
propertyNames: { type: "string" }
654+
};
655+
const mockMutation = jest.fn((mockS) => mockS);
656+
657+
traverse(testSchema, mockMutation);
658+
659+
expect(mockMutation).nthCalledWith(
660+
1,
661+
testSchema.propertyNames,
662+
expect.anything(),
663+
expect.anything(),
664+
expect.anything(),
665+
);
666+
expect(mockMutation).nthCalledWith(
667+
2,
668+
testSchema,
669+
expect.anything(),
670+
expect.anything(),
671+
undefined,
672+
);
673+
674+
expect(mockMutation).toHaveBeenCalledTimes(2);
675+
});
676+
677+
it("traverses unevaluatedItems as boolean", () => {
678+
const testSchema: any = {
679+
unevaluatedItems: false,
680+
};
681+
const mockMutation = jest.fn((mockS) => mockS);
682+
683+
traverse(testSchema, mockMutation);
684+
685+
expect(mockMutation).nthCalledWith(
686+
1,
687+
testSchema.unevaluatedItems,
688+
expect.anything(),
689+
expect.anything(),
690+
expect.anything(),
691+
);
692+
expect(mockMutation).nthCalledWith(
693+
2,
694+
testSchema,
695+
expect.anything(),
696+
expect.anything(),
697+
undefined,
698+
);
699+
700+
expect(mockMutation).toHaveBeenCalledTimes(2);
701+
});
702+
703+
it("traverses unevaluatedProperties as schema", () => {
704+
const testSchema: any = {
705+
unevaluatedProperties: { type: "string" },
706+
};
707+
const mockMutation = jest.fn((mockS) => mockS);
708+
709+
traverse(testSchema, mockMutation);
710+
711+
expect(mockMutation).nthCalledWith(
712+
1,
713+
testSchema.unevaluatedProperties,
714+
expect.anything(),
715+
expect.anything(),
716+
expect.anything(),
717+
);
718+
expect(mockMutation).nthCalledWith(
719+
2,
720+
testSchema,
721+
expect.anything(),
722+
expect.anything(),
723+
undefined,
724+
);
725+
726+
expect(mockMutation).toHaveBeenCalledTimes(2);
727+
});
625728

626729
describe("schema.type being an array", () => {
627730
it("allows type to be an array", () => {
@@ -1092,8 +1195,6 @@ describe("traverse", () => {
10921195
);
10931196
});
10941197
});
1095-
});
1096-
10971198
describe("Mutability settings", () => {
10981199
it("defaults to being immutable", () => {
10991200
const s = {
@@ -1266,3 +1367,4 @@ describe("Mutability settings", () => {
12661367
});
12671368
});
12681369
});
1370+
});

src/index.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { JSONSchema, JSONSchemaObject, PatternProperties } from "@json-schema-tools/meta-schema";
2+
import { jsonPathStringify, isCycle, last } from "./utils";
23

34
/**
45
* Signature of the mutation method passed to traverse.
@@ -49,28 +50,6 @@ export const defaultOptions: TraverseOptions = {
4950
bfs: false,
5051
};
5152

52-
const jsonPathStringify = (s: string[]) => {
53-
return s.map((i) => {
54-
if (i === "") {
55-
return '$';
56-
} else {
57-
return `.${i}`;
58-
}
59-
}).join("");
60-
};
61-
62-
const isCycle = (s: JSONSchema, recursiveStack: JSONSchema[]): JSONSchema | false => {
63-
const foundInRecursiveStack = recursiveStack.find((recSchema) => recSchema === s);
64-
if (foundInRecursiveStack) {
65-
return foundInRecursiveStack;
66-
}
67-
return false;
68-
};
69-
70-
const last = (i: JSONSchema[], skip = 1): JSONSchema => {
71-
return i[i.length - skip];
72-
};
73-
7453
/**
7554
* Traverse all subschema of a schema, calling the mutator function with each.
7655
* The mutator is called on leaf nodes first.
@@ -238,6 +217,20 @@ export default function traverse(
238217
);
239218
}
240219

220+
if (schema.contains !== undefined) {
221+
mutableSchema.contains = rec(
222+
schema.contains,
223+
[...pathStack, "contains"],
224+
);
225+
}
226+
227+
if (schema.unevaluatedItems !== undefined) {
228+
mutableSchema.unevaluatedItems = rec(
229+
schema.unevaluatedItems,
230+
[...pathStack, "unevaluatedItems"],
231+
);
232+
}
233+
241234
if (schema.properties !== undefined) {
242235
const sProps: { [key: string]: JSONSchema } = schema.properties;
243236
const mutableProps: { [key: string]: JSONSchema } = {};
@@ -263,6 +256,20 @@ export default function traverse(
263256
if (schema.additionalProperties !== undefined && !!schema.additionalProperties === true) {
264257
mutableSchema.additionalProperties = rec(schema.additionalProperties, [...pathStack, "additionalProperties"]);
265258
}
259+
260+
if (schema.propertyNames !== undefined) {
261+
mutableSchema.propertyNames = rec(
262+
schema.propertyNames,
263+
[...pathStack, "propertyNames"],
264+
);
265+
}
266+
267+
if (schema.unevaluatedProperties !== undefined && !!schema.unevaluatedProperties === true) {
268+
mutableSchema.unevaluatedProperties = rec(
269+
schema.unevaluatedProperties,
270+
[...pathStack, "unevaluatedProperties"],
271+
);
272+
}
266273
}
267274

268275
if (opts.skipFirstMutation === true && depth === 0) {
@@ -273,11 +280,11 @@ export default function traverse(
273280
mutableStack.pop();
274281
return mutableSchema;
275282
} else {
276-
const isCycle = cycleSet.indexOf(schema) !== -1
283+
const isCycleNode = cycleSet.indexOf(schema) !== -1
277284
mutableStack.pop();
278285
return mutation(
279286
mutableSchema,
280-
isCycle,
287+
isCycleNode,
281288
jsonPathStringify(pathStack),
282289
last(mutableStack)
283290
);

src/utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { JSONSchema } from "@json-schema-tools/meta-schema";
2+
3+
export const jsonPathStringify = (s: string[]): string => {
4+
return s
5+
.map((i) => {
6+
if (i === "") {
7+
return "$";
8+
} else {
9+
return `.${i}`;
10+
}
11+
})
12+
.join("");
13+
};
14+
15+
export const isCycle = (
16+
s: JSONSchema,
17+
recursiveStack: JSONSchema[],
18+
): JSONSchema | false => {
19+
const foundInRecursiveStack = recursiveStack.find((recSchema) => recSchema === s);
20+
if (foundInRecursiveStack) {
21+
return foundInRecursiveStack;
22+
}
23+
return false;
24+
};
25+
26+
export const last = (i: JSONSchema[], skip = 1): JSONSchema => {
27+
return i[i.length - skip];
28+
};

0 commit comments

Comments
 (0)