Skip to content
Open
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*\*\/\}/);

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("`الحصول_على_البيانات()`");
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