Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/polite-jobs-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"lingo.dev": patch
---

fix MDX code placeholder
46 changes: 23 additions & 23 deletions packages/cli/src/cli/loaders/mdx2/code-placeholder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import createMdxCodePlaceholderLoader from "./code-placeholder";
import dedent from "dedent";
import { md5 } from "../../utils/md5";

const PLACEHOLDER_REGEX = /\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/g;
const PLACEHOLDER_REGEX = /CODE_PLACEHOLDER_[0-9a-f]+_END/g;

const sampleContent = dedent`
Paragraph with some code:
Expand All @@ -19,8 +19,8 @@ describe("MDX Code Placeholder Loader", () => {

it("should replace fenced code with placeholder on pull", async () => {
const result = await loader.pull("en", sampleContent);
const hash = md5('```js\nconsole.log("foo");\n```');
const expected = `Paragraph with some code:\n\n{/* CODE_PLACEHOLDER_${hash} */}`;
const hash = md5('```js\nconsole.log("foo");\n```').slice(0, 16);
const expected = `Paragraph with some code:\n\nCODE_PLACEHOLDER_${hash}_END`;
expect(result.trim()).toBe(expected);
});

Expand Down Expand Up @@ -406,8 +406,8 @@ describe("MDX Code Placeholder Loader", () => {
it("should replace inline code with placeholder on pull", async () => {
const md = "This is some `inline()` code.";
const pulled = await loader.pull("en", md);
const hash = md5("`inline()`");
const expected = `This is some {/* INLINE_CODE_PLACEHOLDER_${hash} */} code.`;
const hash = md5("`inline()`").slice(0, 16);
const expected = `This is some INLINE_CODE_PLACEHOLDER_${hash}_END code.`;
expect(pulled).toBe(expected);
});

Expand Down Expand Up @@ -535,7 +535,7 @@ describe("MDX Code Placeholder Loader", () => {
const pushed = await loader.push("en", translated);

// Should not contain any placeholders
expect(pushed).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushed).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);

// Should preserve all special $ characters exactly as they were
expect(pushed).toContain('const price = "$100";');
Expand All @@ -556,7 +556,7 @@ describe("MDX Code Placeholder Loader", () => {
const pushed = await loader.push("en", translated);

// Should not contain any placeholders
expect(pushed).not.toMatch(/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushed).not.toMatch(/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/);

// Should preserve all special $ characters
expect(pushed).toContain("`$price`");
Expand All @@ -577,8 +577,8 @@ describe("MDX Code Placeholder Loader", () => {
const pushed = await loader.push("en", translated);

// Should not contain any placeholders
expect(pushed).not.toMatch(/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushed).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushed).not.toMatch(/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/);
expect(pushed).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);
expect(pushed).toContain("`getData()`");
expect(pushed).toContain("Utilize");
});
Expand All @@ -605,7 +605,7 @@ describe("MDX Code Placeholder Loader", () => {

// The fix: ALL placeholders should be replaced, including Arabic ones
expect(pushedResult).not.toMatch(
/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/,
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);

Comment on lines 607 to 611
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale assertion checking old placeholder format that no longer exists.

Line 610 still checks for the old {/* CODE_PLACEHOLDER_... */} format. Since code-block placeholders now use CODE_PLACEHOLDER_<hash>_END, this assertion will always pass trivially and doesn't verify anything useful. It should check the new format instead:

Suggested fix
       expect(pushedResult).not.toMatch(
         /INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
       );
-      expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
+      expect(pushedResult).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(pushedResult).not.toMatch(
/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/,
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushedResult).not.toMatch(
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);
🤖 Prompt for AI Agents
In `@packages/cli/src/cli/loaders/mdx2/code-placeholder.spec.ts` around lines 607
- 611, Replace the stale assertion that checks for the old block comment
placeholder with one that asserts the new placeholder format is not present:
update the second expect that currently reads against /\{\/\*
CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/ to instead assert
pushedResult.not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/); locate the assertion
referencing pushedResult in code-placeholder.spec.ts and swap the regex
accordingly so the test verifies the new placeholder format.

Expand Down Expand Up @@ -639,7 +639,7 @@ describe("MDX Code Placeholder Loader", () => {

// All placeholders should be replaced, even when not in current pullInput
expect(pushedResult).not.toMatch(
/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/,
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushedResult).toContain("`الحصول_على_البيانات()`");
Comment on lines 641 to 645
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same stale old-format assertion as above.

Line 644 has the same issue — checking for the defunct {/* ... */} format instead of the new CODE_PLACEHOLDER_<hash>_END format.

Suggested fix
       expect(pushedResult).not.toMatch(
         /INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
       );
-      expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
+      expect(pushedResult).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(pushedResult).not.toMatch(
/\{\/\* INLINE_CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/,
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/);
expect(pushedResult).toContain("`الحصول_على_البيانات()`");
expect(pushedResult).not.toMatch(
/INLINE_CODE_PLACEHOLDER_[0-9a-f]+_END/,
);
expect(pushedResult).not.toMatch(/CODE_PLACEHOLDER_[0-9a-f]+_END/);
expect(pushedResult).toContain("`الحصول_على_البيانات()`");
🤖 Prompt for AI Agents
In `@packages/cli/src/cli/loaders/mdx2/code-placeholder.spec.ts` around lines 641
- 645, The test contains a stale assertion checking for the old block-comment
format (/\{\/\* CODE_PLACEHOLDER_[0-9a-f]+\s*\*\/\}/) against the variable
pushedResult; replace that assertion with one that ensures the new end-marker
format is not present, e.g. change the second expect to verify pushedResult does
not match /CODE_PLACEHOLDER_[0-9a-f]+_END/ so the test asserts absence of the
new CODE_PLACEHOLDER_<hash>_END pattern (refer to pushedResult and the spec in
code-placeholder.spec.ts).

Expand Down Expand Up @@ -737,7 +737,7 @@ describe("MDX Code Placeholder Loader", () => {
// The code block should be present and not replaced with placeholder
expect(dePushed).toContain("```bash");
expect(dePushed).toContain("npm install");
expect(dePushed).not.toMatch(/\{\/\* CODE_PLACEHOLDER_/);
expect(dePushed).not.toMatch(/CODE_PLACEHOLDER_/);
});

it("should preserve double newlines around placeholders for section splitting", async () => {
Expand All @@ -764,7 +764,7 @@ describe("MDX Code Placeholder Loader", () => {
const pulled = await loader.pull("en", md);

// Verify placeholders are surrounded by double newlines for proper section splitting
const placeholders = pulled.match(/\{\/\* CODE_PLACEHOLDER_[a-f0-9]+\s*\*\/\}/g);
const placeholders = pulled.match(/CODE_PLACEHOLDER_[a-f0-9]+_END/g);
expect(placeholders).toHaveLength(2);

// Check that each placeholder has double newlines around it
Expand Down Expand Up @@ -814,12 +814,12 @@ describe("adjacent code blocks bug", () => {
console.log("___");

// The bug: placeholder is concatenated with "typescript" from next block
const bugPattern = /\{\/\* CODE_PLACEHOLDER_[a-f0-9]+\s*\*\/\}typescript/;
const bugPattern = /CODE_PLACEHOLDER_[a-f0-9]+\_ENDtypescript/;
expect(pulled).not.toMatch(bugPattern);

// Should have proper separation
expect(pulled).toMatch(
/\{\/\* CODE_PLACEHOLDER_[a-f0-9]+\s*\*\/\}\n\n\{\/\* CODE_PLACEHOLDER_[a-f0-9]+\s*\*\/\}/,
/CODE_PLACEHOLDER_[a-f0-9]+_END\n\nCODE_PLACEHOLDER_[a-f0-9]+_END/,
);
});
});
Expand Down Expand Up @@ -885,13 +885,13 @@ describe("placeholder format edge cases (regression)", () => {
const koreanMdx = `\`코드\` 디렉토리와 그 내용을 이해합니다.`;
const pulled = await loader.pull("ko", koreanMdx);

expect(pulled).toMatch(/^\{\/\* INLINE_CODE_PLACEHOLDER/);
expect(pulled).toMatch(/^INLINE_CODE_PLACEHOLDER/);

// Simulate frontmatter-split: matter.stringify with placeholder at start
const matter = require('gray-matter');
const mdxDocument = matter.stringify(pulled, { title: 'Test' });

expect(mdxDocument).toContain('{/*');
expect(mdxDocument).toContain('INLINE_CODE_PLACEHOLDER');

const restored = await loader.push("ko", pulled, koreanMdx, "en", koreanMdx);
expect(restored).toBe(koreanMdx);
Expand All @@ -918,8 +918,8 @@ describe("placeholder format edge cases (regression)", () => {
const processor = unified().use(remarkParse).use(remarkStringify);
const parsed = processor.stringify(processor.parse(pulled));

// JSX comments get escaped by Markdown but structure is preserved
// Underscores in placeholder names get escaped: INLINE\_CODE\_PLACEHOLDER
// Tag-style placeholders are preserved through markdown parsing
// Underscores in placeholder names may get escaped: INLINE\_CODE\_PLACEHOLDER
expect(parsed).toMatch(/INLINE[_\\]*CODE[_\\]*PLACEHOLDER/);
expect(parsed).not.toContain('***'); // Not parsed as bold-italic
expect(parsed).not.toMatch(/___CODE.*___/); // Not using underscore format
Expand All @@ -942,15 +942,15 @@ describe("placeholder format edge cases (regression)", () => {
`;

const pulled = await loader.pull("en", mdContent);
const placeholders = pulled.match(/\{\/\* INLINE_CODE_PLACEHOLDER_[a-f0-9]+\s*\*\/\}/g);
const placeholders = pulled.match(/INLINE_CODE_PLACEHOLDER_[a-f0-9]+_END/g);
expect(placeholders).toHaveLength(3);

const matter = require('gray-matter');
const mdxDoc = matter.stringify(pulled, {
title: 'Multiple Placeholders'
});

expect(mdxDoc).toContain('{/*');
expect(mdxDoc).toContain('INLINE_CODE_PLACEHOLDER');

const restored = await loader.push("en", pulled, mdContent, "en", mdContent);
expect(restored).toBe(mdContent);
Expand All @@ -970,13 +970,13 @@ describe("placeholder format edge cases (regression)", () => {
`;

const pulled = await loader.pull("en", mdContent);
expect(pulled).toMatch(/^\{\/\* CODE_PLACEHOLDER/);
expect(pulled).toMatch(/^CODE_PLACEHOLDER/);

const matter = require('gray-matter');
const mdxDoc = matter.stringify(pulled, { title: 'Code First' });

expect(mdxDoc).toContain('title: Code First');
expect(mdxDoc).toContain('{/*');
expect(mdxDoc).toContain('CODE_PLACEHOLDER');

const restored = await loader.push("en", pulled, mdContent, "en", mdContent);
expect(restored).toBe(mdContent);
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/cli/loaders/mdx2/code-placeholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ function extractCodePlaceholders(content: string): {
const codeBlockMatches = finalContent.matchAll(fenceRegex);
for (const match of codeBlockMatches) {
const codeBlock = match[0];
const codeBlockHash = md5(codeBlock);
const placeholder = `{/* CODE_PLACEHOLDER_${codeBlockHash} */}`;
const codeBlockHash = md5(codeBlock).slice(0, 16);
const placeholder = `CODE_PLACEHOLDER_${codeBlockHash}_END`;

codePlaceholders[placeholder] = codeBlock;

Expand All @@ -112,8 +112,8 @@ function extractCodePlaceholders(content: string): {
const inlineCodeMatches = finalContent.matchAll(inlineCodeRegex);
for (const match of inlineCodeMatches) {
const inlineCode = match[0];
const inlineCodeHash = md5(inlineCode);
const placeholder = `{/* INLINE_CODE_PLACEHOLDER_${inlineCodeHash} */}`;
const inlineCodeHash = md5(inlineCode).slice(0, 16);
const placeholder = `INLINE_CODE_PLACEHOLDER_${inlineCodeHash}_END`;
codePlaceholders[placeholder] = inlineCode;
const replacement = placeholder;
finalContent = finalContent.replace(inlineCode, () => replacement);
Expand Down