Skip to content

Commit 767f128

Browse files
committed
perf - use explicit isCircular code to avoid slow JSON.stringify settings
1 parent 48d9ee9 commit 767f128

File tree

2 files changed

+32
-17
lines changed

2 files changed

+32
-17
lines changed

src/core/lib/is-circular.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* is-circular.ts
3+
*
4+
* Copyright (C) 2025 Posit Software, PBC
5+
*/
6+
7+
// deno-lint-ignore no-explicit-any
8+
export const isCircular = (obj: any): unknown => {
9+
const objectSet = new WeakSet();
10+
// deno-lint-ignore no-explicit-any
11+
const detect = (obj: any): boolean => {
12+
if (obj && typeof obj === "object") {
13+
if (objectSet.has(obj)) {
14+
return true;
15+
}
16+
objectSet.add(obj);
17+
for (const key in obj) {
18+
if (Object.hasOwn(obj, key) && detect(obj[key])) {
19+
return true;
20+
}
21+
}
22+
objectSet.delete(obj);
23+
}
24+
return false;
25+
};
26+
return detect(obj);
27+
};

src/core/lib/yaml-intelligence/annotated-yaml.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { QuartoJSONSchema } from "./js-yaml-schema.ts";
2121
import { createSourceContext } from "../yaml-validation/errors.ts";
2222
import { tidyverseInfo } from "../errors.ts";
2323
import { InternalError } from "../error.ts";
24+
import { isCircular } from "../is-circular.ts";
2425

2526
// deno-lint-ignore no-explicit-any
2627
type TreeSitterParse = any;
@@ -268,23 +269,10 @@ export function buildJsYamlAnnotation(mappedYaml: MappedString) {
268269
);
269270
}
270271

271-
// console.log(results[0]);
272-
try {
273-
JSON.stringify(results[0]); // this is here so that we throw on circular structures
274-
} catch (e) {
275-
if (e.message.match("invalid string length")) {
276-
// https://github.com/quarto-dev/quarto-cli/issues/10504
277-
// It seems to be relatively easy to hit string length limits in
278-
// JSON.stringify. Since this call is only here to check for circular
279-
// structures, we chose to ignore this error, even though it's not
280-
// ideal
281-
} else if (e.message.match(/circular structure/)) {
282-
throw new InternalError(
283-
`Circular structure detected in parsed yaml: ${e.message}`,
284-
);
285-
} else {
286-
287-
}
272+
if (isCircular(results[0])) {
273+
throw new InternalError(
274+
`Circular structure detected in yaml`,
275+
);
288276
}
289277
return postProcessAnnotation(results[0]);
290278
}

0 commit comments

Comments
 (0)