From 23d8549e42fa99709aa77272bb35dcc20f2752f2 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 3 Jul 2026 11:55:51 +0200 Subject: [PATCH 1/5] Update dependency js-yaml to v5 --- packages/lib-common/package.json | 2 +- packages/lib-common/src/testUtil/serialize.ts | 75 +++++-------------- packages/lib-node-common/package.json | 2 +- pnpm-lock.yaml | 16 +++- 4 files changed, 33 insertions(+), 62 deletions(-) diff --git a/packages/lib-common/package.json b/packages/lib-common/package.json index 42549c1381..395d2ed797 100644 --- a/packages/lib-common/package.json +++ b/packages/lib-common/package.json @@ -29,7 +29,7 @@ "@types/tinycolor2": "^1.4.6", "cross-spawn": "^7.0.6", "fast-check": "^4.8.0", - "js-yaml": "^4.2.0", + "js-yaml": "^5.2.1", "web-tree-sitter": "^0.26.10" } } diff --git a/packages/lib-common/src/testUtil/serialize.ts b/packages/lib-common/src/testUtil/serialize.ts index 870476879a..1e518bd5df 100644 --- a/packages/lib-common/src/testUtil/serialize.ts +++ b/packages/lib-common/src/testUtil/serialize.ts @@ -2,69 +2,32 @@ // This file ensures that simple objects and arrays (ie without array or object // children) will be serialized inline, and also ensures that "fullTargets" will be inlined as well -import { dump, Type, DEFAULT_SCHEMA } from "js-yaml"; -import type { DumpOptions } from "js-yaml"; +import { dump, visit } from "js-yaml"; +import type { Document, MappingNode, Node, SequenceNode } from "js-yaml"; -class CustomDump { - constructor( - private readonly data: unknown, - private readonly opts: DumpOptions, - ) {} - - represent(): string { - let result = dump(this.data, { replacer, schema, ...this.opts }); - result = result.trim(); - if (result.includes("\n")) { - result = `\n${result}`; - } - return result; - } +function hasOnlyScalarChildren(node: MappingNode | SequenceNode): boolean { + return node.kind === "mapping" + ? node.items.every(({ value }) => value.kind === "scalar") + : node.items.every((item) => item.kind === "scalar"); } -const customDumpType = new Type("!format", { - kind: "scalar", - resolve: () => false, - instanceOf: CustomDump, - represent: (d: unknown) => (d as CustomDump).represent(), -}); - -const schema = DEFAULT_SCHEMA.extend({ implicit: [customDumpType] }); - -const isObject = (value: unknown): value is object => - typeof value === "object" && value != null; - -function hasSimpleChildren(value: unknown): boolean { - if (isObject(value)) { - return Object.values(value).every( - (value) => !isObject(value) && !Array.isArray(value), - ); - } - if (Array.isArray(value)) { - return value.every((value) => !isObject(value) && !Array.isArray(value)); - } - return false; +function inlineSimpleCollections(documents: Document[]): void { + visit(documents, (node: Node, { depth }) => { + if (depth > 0 && isCollectionNode(node) && hasOnlyScalarChildren(node)) { + node.style.flow = true; + } + }); } -function replacer(key: string, value: unknown): unknown { - // top-level, don't change this - if (key === "") { - return value; - } - - if (hasSimpleChildren(value)) { - return new CustomDump(value, { flowLevel: 0 }); - } - - // default - return value; +function isCollectionNode(node: Node): node is MappingNode | SequenceNode { + return node.kind === "mapping" || node.kind === "sequence"; } export function serialize(obj: unknown): string { - const dump = new CustomDump(obj, { + const serialized = dump(obj, { noRefs: true, - quotingType: '"', - }) - .represent() - .trim(); - return `${dump}\n`; + quoteStyle: "double", + transform: inlineSimpleCollections, + }).trim(); + return `${serialized}\n`; } diff --git a/packages/lib-node-common/package.json b/packages/lib-node-common/package.json index f8fb76609a..e42fca566a 100644 --- a/packages/lib-node-common/package.json +++ b/packages/lib-node-common/package.json @@ -21,6 +21,6 @@ "devDependencies": { "@types/js-yaml": "^4.0.9", "@types/lodash-es": "^4.17.12", - "js-yaml": "^4.2.0" + "js-yaml": "^5.2.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 546b0fd0c9..bd374483ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -397,8 +397,8 @@ importers: specifier: ^4.8.0 version: 4.8.0 js-yaml: - specifier: ^4.2.0 - version: 4.3.0 + specifier: ^5.2.1 + version: 5.2.1 web-tree-sitter: specifier: ^0.26.10 version: 0.26.10 @@ -516,8 +516,8 @@ importers: specifier: ^4.17.12 version: 4.17.12 js-yaml: - specifier: ^4.2.0 - version: 4.3.0 + specifier: ^5.2.1 + version: 5.2.1 packages/lib-sentence-parser: devDependencies: @@ -7743,6 +7743,10 @@ packages: resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true + js-yaml@5.2.1: + resolution: {integrity: sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==} + hasBin: true + jsdom@26.1.0: resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} engines: {node: '>=18'} @@ -20059,6 +20063,10 @@ snapshots: dependencies: argparse: 2.0.1 + js-yaml@5.2.1: + dependencies: + argparse: 2.0.1 + jsdom@26.1.0: dependencies: cssstyle: 4.6.0 From 48c93b8c05ef85b9f65f73ad29fdc92e525bb079 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 3 Jul 2026 12:02:28 +0200 Subject: [PATCH 2/5] Change quote style --- packages/lib-common/src/testUtil/serialize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib-common/src/testUtil/serialize.ts b/packages/lib-common/src/testUtil/serialize.ts index 1e518bd5df..d9060dfa08 100644 --- a/packages/lib-common/src/testUtil/serialize.ts +++ b/packages/lib-common/src/testUtil/serialize.ts @@ -26,7 +26,7 @@ function isCollectionNode(node: Node): node is MappingNode | SequenceNode { export function serialize(obj: unknown): string { const serialized = dump(obj, { noRefs: true, - quoteStyle: "double", + quoteStyle: "single", transform: inlineSimpleCollections, }).trim(); return `${serialized}\n`; From 5a37bd49f691f14da86d632bac63cf120341e35a Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 3 Jul 2026 13:04:05 +0200 Subject: [PATCH 3/5] Restore old serializer behavior --- packages/lib-common/src/testUtil/serialize.ts | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/packages/lib-common/src/testUtil/serialize.ts b/packages/lib-common/src/testUtil/serialize.ts index d9060dfa08..58bcf6a5a8 100644 --- a/packages/lib-common/src/testUtil/serialize.ts +++ b/packages/lib-common/src/testUtil/serialize.ts @@ -2,20 +2,52 @@ // This file ensures that simple objects and arrays (ie without array or object // children) will be serialized inline, and also ensures that "fullTargets" will be inlined as well -import { dump, visit } from "js-yaml"; +import { CORE_SCHEMA, dump, visit, YAML11_SCHEMA } from "js-yaml"; import type { Document, MappingNode, Node, SequenceNode } from "js-yaml"; -function hasOnlyScalarChildren(node: MappingNode | SequenceNode): boolean { - return node.kind === "mapping" - ? node.items.every(({ value }) => value.kind === "scalar") - : node.items.every((item) => item.kind === "scalar"); +export function serialize(obj: unknown): string { + return dump(toSerializableObject(obj), { + noRefs: true, + quoteStyle: "double", + schema: CORE_SCHEMA, + transform: inlineSimpleCollections, + }); +} + +function toSerializableObject(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map(toSerializableObject); + } + + if (value == null || typeof value !== "object" || value instanceof Date) { + return value; + } + + return Object.fromEntries( + Object.entries(value).map(([key, value]) => [ + key, + toSerializableObject(value), + ]), + ); } function inlineSimpleCollections(documents: Document[]): void { - visit(documents, (node: Node, { depth }) => { + visit(documents, (node: Node, { depth, isKey, parent }) => { if (depth > 0 && isCollectionNode(node) && hasOnlyScalarChildren(node)) { node.style.flow = true; } + + if ( + node.kind === "scalar" && + node.tag === "tag:yaml.org,2002:str" && + !isKey && + parent?.style.flow === true && + !node.value.includes("\n") && + !node.value.includes("\r") && + scalarNeedsQuotesInFlow(node.value) + ) { + node.style.singleQuoted = true; + } }); } @@ -23,11 +55,18 @@ function isCollectionNode(node: Node): node is MappingNode | SequenceNode { return node.kind === "mapping" || node.kind === "sequence"; } -export function serialize(obj: unknown): string { - const serialized = dump(obj, { - noRefs: true, - quoteStyle: "single", - transform: inlineSimpleCollections, - }).trim(); - return `${serialized}\n`; +function hasOnlyScalarChildren(node: MappingNode | SequenceNode): boolean { + return node.kind === "mapping" + ? node.items.every(({ value }) => value.kind === "scalar") + : node.items.every((item) => item.kind === "scalar"); +} + +function scalarNeedsQuotesInFlow(value: string): boolean { + return dump([value], { + flowLevel: 0, + quoteStyle: "double", + schema: YAML11_SCHEMA, + }) + .trim() + .startsWith('["'); } From 2f13f9c8fb3bd6fda76a6446851306ccd4322b0c Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 3 Jul 2026 13:29:50 +0200 Subject: [PATCH 4/5] Added comments --- packages/lib-common/src/testUtil/serialize.ts | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/lib-common/src/testUtil/serialize.ts b/packages/lib-common/src/testUtil/serialize.ts index 58bcf6a5a8..cdb954c355 100644 --- a/packages/lib-common/src/testUtil/serialize.ts +++ b/packages/lib-common/src/testUtil/serialize.ts @@ -1,14 +1,15 @@ -// From https://github.com/nodeca/js-yaml/issues/586#issuecomment-814310104 // This file ensures that simple objects and arrays (ie without array or object // children) will be serialized inline, and also ensures that "fullTargets" will be inlined as well -import { CORE_SCHEMA, dump, visit, YAML11_SCHEMA } from "js-yaml"; import type { Document, MappingNode, Node, SequenceNode } from "js-yaml"; +import { CORE_SCHEMA, dump, visit, YAML11_SCHEMA } from "js-yaml"; export function serialize(obj: unknown): string { return dump(toSerializableObject(obj), { noRefs: true, quoteStyle: "double", + // CORE_SCHEMA preserves existing fixture output for numeric-looking strings + // like `1_01_001`, which js-yaml's default schema would quote. schema: CORE_SCHEMA, transform: inlineSimpleCollections, }); @@ -19,7 +20,7 @@ function toSerializableObject(value: unknown): unknown { return value.map(toSerializableObject); } - if (value == null || typeof value !== "object" || value instanceof Date) { + if (value == null || typeof value !== "object") { return value; } @@ -33,17 +34,21 @@ function toSerializableObject(value: unknown): unknown { function inlineSimpleCollections(documents: Document[]): void { visit(documents, (node: Node, { depth, isKey, parent }) => { + // Keep nested simple objects and arrays in flow style, eg `{line: 0}` and + // `[default.a]`, while leaving top-level mappings in block style. if (depth > 0 && isCollectionNode(node) && hasOnlyScalarChildren(node)) { node.style.flow = true; } + // Existing fixtures use single quotes for flow scalar values that need + // quoting, eg `{character: ']'}`. Do not apply this to keys, non-string + // scalars, block scalars, or multiline strings. if ( - node.kind === "scalar" && - node.tag === "tag:yaml.org,2002:str" && !isKey && parent?.style.flow === true && + node.kind === "scalar" && + node.tag === "tag:yaml.org,2002:str" && !node.value.includes("\n") && - !node.value.includes("\r") && scalarNeedsQuotesInFlow(node.value) ) { node.style.singleQuoted = true; @@ -57,16 +62,20 @@ function isCollectionNode(node: Node): node is MappingNode | SequenceNode { function hasOnlyScalarChildren(node: MappingNode | SequenceNode): boolean { return node.kind === "mapping" - ? node.items.every(({ value }) => value.kind === "scalar") + ? node.items.every((item) => item.value.kind === "scalar") : node.items.every((item) => item.kind === "scalar"); } function scalarNeedsQuotesInFlow(value: string): boolean { + // Dump the value inside a flow array so js-yaml applies the same scalar + // rules it would use for an inline collection. If the result starts with + // `["`, the value cannot be emitted plainly in flow style, so we switch the + // actual fixture value to single quotes to match the existing fixtures. return dump([value], { flowLevel: 0, quoteStyle: "double", + // Use YAML 1.1 schema to preserve existing fixtures where legacy + // boolean-like strings such as `y` and `n` are quoted in flow collections. schema: YAML11_SCHEMA, - }) - .trim() - .startsWith('["'); + }).startsWith('["'); } From 72142fcc560a7b2437c4cd0464a215f9f611cf89 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 3 Jul 2026 13:35:13 +0200 Subject: [PATCH 5/5] Small cleanup --- packages/lib-common/src/testUtil/serialize.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/lib-common/src/testUtil/serialize.ts b/packages/lib-common/src/testUtil/serialize.ts index cdb954c355..3342402833 100644 --- a/packages/lib-common/src/testUtil/serialize.ts +++ b/packages/lib-common/src/testUtil/serialize.ts @@ -45,7 +45,8 @@ function inlineSimpleCollections(documents: Document[]): void { // scalars, block scalars, or multiline strings. if ( !isKey && - parent?.style.flow === true && + parent != null && + parent.style.flow && node.kind === "scalar" && node.tag === "tag:yaml.org,2002:str" && !node.value.includes("\n") &&