Skip to content

Commit 1ff847b

Browse files
fix(cli): missing placeholder replacement logic in mdx (#1062)
1 parent 3c58ab1 commit 1ff847b

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

.changeset/blue-cows-search.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"lingo.dev": patch
3+
---
4+
5+
fixed missing placeholder replacement logic in mdx

packages/cli/src/cli/loaders/mdx2/code-placeholder.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,4 +510,87 @@ describe("MDX Code Placeholder Loader", () => {
510510
expect(pushed).toBe(expected);
511511
});
512512
});
513+
514+
describe("placeholder replacement bugs", () => {
515+
it("should not leave placeholders when content matches", async () => {
516+
const loader = createMdxCodePlaceholderLoader();
517+
loader.setDefaultLocale("en");
518+
519+
const content = "Use the `getData()` function.";
520+
521+
// Pull and then push the same content - should work correctly
522+
const pulled = await loader.pull("en", content);
523+
const translated = pulled.replace("Use", "Utilize");
524+
const pushed = await loader.push("en", translated);
525+
526+
// Should not contain any placeholders
527+
expect(pushed).not.toMatch(/---INLINE-CODE-PLACEHOLDER-[0-9a-f]+---/);
528+
expect(pushed).not.toMatch(/---CODE-PLACEHOLDER-[0-9a-f]+---/);
529+
expect(pushed).toContain("`getData()`");
530+
expect(pushed).toContain("Utilize");
531+
});
532+
533+
it("should replace all placeholders including those from different sources", async () => {
534+
const loader = createMdxCodePlaceholderLoader();
535+
loader.setDefaultLocale("en");
536+
537+
// Simulate the exact scenario from the user's bug report
538+
const englishContent = "Use the `getData()` function.";
539+
const arabicContent = "استخدم `الحصول_على_البيانات()` الدالة.";
540+
541+
// First pull English (required as default locale)
542+
await loader.pull("en", englishContent);
543+
544+
// Pull Arabic content to create placeholders
545+
const arabicPulled = await loader.pull("ar", arabicContent);
546+
547+
// Simulate translation: translator changes text but keeps placeholder
548+
const arabicTranslated = arabicPulled.replace("استخدم", "قم بتطبيق");
549+
550+
// Push back - this should now work correctly with the fix
551+
const pushedResult = await loader.push("ar", arabicTranslated);
552+
553+
// The fix: ALL placeholders should be replaced, including Arabic ones
554+
expect(pushedResult).not.toMatch(
555+
/---INLINE-CODE-PLACEHOLDER-[0-9a-f]+---/,
556+
);
557+
expect(pushedResult).not.toMatch(/---CODE-PLACEHOLDER-[0-9a-f]+---/);
558+
559+
// The Arabic inline code should be preserved and translated text should be there
560+
expect(pushedResult).toContain("`الحصول_على_البيانات()`");
561+
expect(pushedResult).toContain("قم بتطبيق");
562+
});
563+
564+
it("should replace placeholders even when pullInput state is overwritten", async () => {
565+
const loader = createMdxCodePlaceholderLoader();
566+
loader.setDefaultLocale("en");
567+
568+
const englishContent = "Use the `getData()` function.";
569+
const arabicContent = "استخدم `الحصول_على_البيانات()` الدالة.";
570+
571+
// First pull English (required as default locale)
572+
await loader.pull("en", englishContent);
573+
574+
// Pull Arabic content to create placeholders
575+
const arabicPulled = await loader.pull("ar", arabicContent);
576+
577+
// Simulate translation: translator changes text but keeps placeholder
578+
const arabicTranslated = arabicPulled.replace("استخدم", "قم بتطبيق");
579+
580+
// Now pull English again, overwriting pullInput state
581+
// This simulates the real-world scenario where the loader state gets out of sync
582+
await loader.pull("en", englishContent);
583+
584+
// Push the Arabic translation - should work despite state being overwritten
585+
const pushedResult = await loader.push("ar", arabicTranslated);
586+
587+
// All placeholders should be replaced, even when not in current pullInput
588+
expect(pushedResult).not.toMatch(
589+
/---INLINE-CODE-PLACEHOLDER-[0-9a-f]+---/,
590+
);
591+
expect(pushedResult).not.toMatch(/---CODE-PLACEHOLDER-[0-9a-f]+---/);
592+
expect(pushedResult).toContain("`الحصول_على_البيانات()`");
593+
expect(pushedResult).toContain("قم بتطبيق");
594+
});
595+
});
513596
});

packages/cli/src/cli/loaders/mdx2/code-placeholder.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,30 @@ export default function createMdxCodePlaceholderLoader(): ILoader<
131131
string,
132132
string
133133
> {
134+
// Keep a global registry of all placeholders we've ever created
135+
// This solves the state synchronization issue
136+
const globalPlaceholderRegistry: Record<string, string> = {};
137+
134138
return createLoader({
135139
async pull(locale, input) {
136140
const response = extractCodePlaceholders(input);
141+
142+
// Register all placeholders we create so we can use them later
143+
Object.assign(globalPlaceholderRegistry, response.codePlaceholders);
144+
137145
return response.content;
138146
},
139147

140148
async push(locale, data, originalInput, originalLocale, pullInput) {
141149
const sourceInfo = extractCodePlaceholders(originalInput ?? "");
142150
const currentInfo = extractCodePlaceholders(pullInput ?? "");
143151

152+
// Use the global registry to ensure all placeholders can be replaced,
153+
// including those from previous pulls that are no longer in current state
144154
const codePlaceholders = _.merge(
145155
sourceInfo.codePlaceholders,
146156
currentInfo.codePlaceholders,
157+
globalPlaceholderRegistry, // Include ALL placeholders ever created
147158
);
148159

149160
let result = data;

0 commit comments

Comments
 (0)