Skip to content

Commit d467371

Browse files
Merge branch 'main' into best/aisdk-provider-upgrades
2 parents 166e6e1 + 389190b commit d467371

File tree

9 files changed

+274
-5
lines changed

9 files changed

+274
-5
lines changed

.changeset/plenty-lemons-remain.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

demo/adonisjs/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# adonis
22

3+
## 0.0.39
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`9c338a8`](https://github.com/lingodotdev/lingo.dev/commit/9c338a8c5fab77c386d74700a6055c73d06daafd)]:
8+
- lingo.dev@0.116.5
9+
310
## 0.0.38
411

512
### Patch Changes

demo/adonisjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "adonis",
3-
"version": "0.0.38",
3+
"version": "0.0.39",
44
"private": true,
55
"type": "module",
66
"license": "UNLICENSED",

demo/next-app/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# next-app
22

3+
## 0.2.91
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`9c338a8`](https://github.com/lingodotdev/lingo.dev/commit/9c338a8c5fab77c386d74700a6055c73d06daafd)]:
8+
- lingo.dev@0.116.5
9+
310
## 0.2.90
411

512
### Patch Changes

demo/next-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "next-app",
3-
"version": "0.2.90",
3+
"version": "0.2.91",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

packages/cli/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# lingo.dev
22

3+
## 0.116.5
4+
5+
### Patch Changes
6+
7+
- [#1626](https://github.com/lingodotdev/lingo.dev/pull/1626) [`9c338a8`](https://github.com/lingodotdev/lingo.dev/commit/9c338a8c5fab77c386d74700a6055c73d06daafd) Thanks [@vrcprl](https://github.com/vrcprl)! - preserve YAML literal block scalars without backslash escaping
8+
39
## 0.116.4
410

511
### Patch Changes

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "lingo.dev",
3-
"version": "0.116.4",
3+
"version": "0.116.5",
44
"description": "Lingo.dev CLI",
55
"private": false,
66
"publishConfig": {
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import { describe, expect, it } from "vitest";
2+
import createYamlLoader from "./yaml";
3+
4+
describe("yaml loader", () => {
5+
it("pull should parse valid YAML format", async () => {
6+
const loader = createYamlLoader();
7+
loader.setDefaultLocale("en");
8+
const yamlInput = `hello: Hello
9+
world: World
10+
nested:
11+
key: value`;
12+
13+
const result = await loader.pull("en", yamlInput);
14+
expect(result).toEqual({
15+
hello: "Hello",
16+
world: "World",
17+
nested: {
18+
key: "value",
19+
},
20+
});
21+
});
22+
23+
it("pull should handle empty input", async () => {
24+
const loader = createYamlLoader();
25+
loader.setDefaultLocale("en");
26+
const result = await loader.pull("en", "");
27+
expect(result).toEqual({});
28+
});
29+
30+
it("pull should parse YAML with literal block scalars", async () => {
31+
const loader = createYamlLoader();
32+
loader.setDefaultLocale("en");
33+
const yamlInput = `description: |
34+
This is a multi-line
35+
description with
36+
- bullet points
37+
- and more items`;
38+
39+
const result = await loader.pull("en", yamlInput);
40+
expect(result).toEqual({
41+
description:
42+
"This is a multi-line\ndescription with\n- bullet points\n- and more items\n",
43+
});
44+
});
45+
46+
it("push should preserve literal block scalar format when input uses it", async () => {
47+
const loader = createYamlLoader();
48+
loader.setDefaultLocale("en");
49+
50+
// Input with literal block scalar
51+
const yamlInput = `system_prompt: |
52+
You are a compliance expert.
53+
Standard Rules:
54+
1. **Rule One**
55+
- Do not make guarantees
56+
- Flag phrases: "guaranteed returns"
57+
2. **Rule Two**
58+
- Past performance disclaimer`;
59+
60+
await loader.pull("en", yamlInput);
61+
62+
const data = {
63+
system_prompt: `You are a compliance expert.
64+
Standard Rules:
65+
1. **Rule One**
66+
- Do not make guarantees
67+
- Flag phrases: "guaranteed returns"
68+
2. **Rule Two**
69+
- Past performance disclaimer`,
70+
};
71+
72+
const result = await loader.push("en", data, yamlInput);
73+
74+
// Should NOT contain backslash escaping before dashes
75+
expect(result).not.toContain("\\ -");
76+
77+
// Should use literal block scalar format (|- or |)
78+
expect(result).toMatch(/system_prompt:\s*\|[-+]?/);
79+
80+
// Verify the content is correctly formatted
81+
expect(result).toContain(" - Do not make guarantees");
82+
expect(result).toContain(" - Flag phrases:");
83+
expect(result).toContain(" - Past performance disclaimer");
84+
});
85+
86+
it("push should NOT use backslash escaping for indented dashes with literal block scalars", async () => {
87+
const loader = createYamlLoader();
88+
loader.setDefaultLocale("en");
89+
90+
// Original input uses literal block scalar
91+
const originalInput = `content: |
92+
List of items:
93+
- First item
94+
- Second item
95+
- Nested item`;
96+
97+
await loader.pull("en", originalInput);
98+
99+
const data = {
100+
content: `List of items:
101+
- First item
102+
- Second item
103+
- Nested item`,
104+
};
105+
106+
const result = await loader.push("en", data, originalInput);
107+
108+
// Critical: Should NOT have backslash escaping
109+
expect(result).not.toMatch(/^\s*\\\s+-/m);
110+
expect(result).not.toContain("\\ -");
111+
112+
// Should preserve literal block scalar
113+
expect(result).toMatch(/content:\s*\|[-+]?/);
114+
});
115+
116+
it("push should use QUOTE_DOUBLE when original input has quoted strings", async () => {
117+
const loader = createYamlLoader();
118+
loader.setDefaultLocale("en");
119+
120+
// Input with double-quoted strings (no literal block scalars)
121+
const yamlInput = `"hello": "Hello World"
122+
"world": "Another string"`;
123+
124+
await loader.pull("en", yamlInput);
125+
126+
const data = {
127+
hello: "Hello World",
128+
world: "Another string",
129+
};
130+
131+
const result = await loader.push("en", data, yamlInput);
132+
133+
// Should use double quotes
134+
expect(result).toContain('"hello"');
135+
expect(result).toContain('"Hello World"');
136+
});
137+
138+
it("push should default to PLAIN format when no style indicators", async () => {
139+
const loader = createYamlLoader();
140+
loader.setDefaultLocale("en");
141+
142+
const yamlInput = `hello: Hello World
143+
world: Another string`;
144+
145+
await loader.pull("en", yamlInput);
146+
147+
const data = {
148+
hello: "Hello World",
149+
world: "Another string",
150+
};
151+
152+
const result = await loader.push("en", data, yamlInput);
153+
154+
// Should use plain format (no quotes, no literal scalars for simple strings)
155+
expect(result).toContain("hello: Hello World");
156+
expect(result).toContain("world: Another string");
157+
});
158+
159+
it("push should handle complex content with literal block scalars and quotes", async () => {
160+
const loader = createYamlLoader();
161+
loader.setDefaultLocale("en");
162+
163+
// Input that has both literal block scalars and embedded quotes
164+
const yamlInput = `system_prompt: |
165+
Rules:
166+
1. **Rule**
167+
- Do not use "guaranteed" language
168+
- Flag: "no risk"
169+
user_prompt: Simple text`;
170+
171+
await loader.pull("en", yamlInput);
172+
173+
const data = {
174+
system_prompt: `Rules:
175+
1. **Rule**
176+
- Do not use "guaranteed" language
177+
- Flag: "no risk"`,
178+
user_prompt: "Simple text",
179+
};
180+
181+
const result = await loader.push("en", data, yamlInput);
182+
183+
// Should detect literal block scalar and use PLAIN format
184+
expect(result).toMatch(/system_prompt:\s*\|[-+]?/);
185+
186+
// Should NOT have backslash escaping
187+
expect(result).not.toContain("\\ -");
188+
189+
// Embedded quotes should be preserved in the content
190+
expect(result).toContain('"guaranteed"');
191+
expect(result).toContain('"no risk"');
192+
});
193+
194+
it("pull should handle YAML with comments", async () => {
195+
const loader = createYamlLoader();
196+
loader.setDefaultLocale("en");
197+
const yamlInput = `# This is a comment
198+
hello: Hello # inline comment
199+
world: World`;
200+
201+
const result = await loader.pull("en", yamlInput);
202+
expect(result).toEqual({
203+
hello: "Hello",
204+
world: "World",
205+
});
206+
});
207+
208+
it("push should handle empty object", async () => {
209+
const loader = createYamlLoader();
210+
loader.setDefaultLocale("en");
211+
await loader.pull("en", "{}");
212+
213+
const result = await loader.push("en", {});
214+
expect(result.trim()).toBe("{}");
215+
});
216+
217+
it("push should handle arrays", async () => {
218+
const loader = createYamlLoader();
219+
loader.setDefaultLocale("en");
220+
221+
const yamlInput = `items:
222+
- first
223+
- second
224+
- third`;
225+
226+
await loader.pull("en", yamlInput);
227+
228+
const data = {
229+
items: ["first", "second", "third"],
230+
};
231+
232+
const result = await loader.push("en", data, yamlInput);
233+
234+
// Verify it parses back correctly
235+
const reparsed = await loader.pull("en", result);
236+
expect(reparsed).toEqual(data);
237+
});
238+
});

packages/cli/src/cli/loaders/yaml.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ function getStringType(
4242
): ToStringOptions["defaultStringType"] {
4343
if (yamlString) {
4444
const lines = yamlString.split("\n");
45+
46+
// Check if the file uses literal block scalars (|, |-, |+)
47+
const hasLiteralBlockScalar = lines.find((line) => {
48+
const trimmedLine = line.trim();
49+
return trimmedLine.match(/:\s*\|[-+]?\s*$/);
50+
});
51+
52+
// If literal block scalars are used, always use PLAIN to preserve them
53+
if (hasLiteralBlockScalar) {
54+
return "PLAIN";
55+
}
56+
57+
// Otherwise, check for double quotes on string values
4558
const hasDoubleQuotes = lines.find((line) => {
4659
const trimmedLine = line.trim();
4760
return (

0 commit comments

Comments
 (0)