Skip to content

Commit a241343

Browse files
committed
fix: mdx improvements
1 parent c0ee61c commit a241343

8 files changed

Lines changed: 89 additions & 123 deletions

File tree

.changeset/angry-mugs-fold.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+
mdx improvements

packages/cli/src/cli/cmd/i18n.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import chalk from "chalk";
1919
import { createTwoFilesPatch } from "diff";
2020
import inquirer from "inquirer";
2121
import externalEditor from "external-editor";
22-
import { cacheChunk, deleteCache, getNormalizedCache } from "../utils/cache";
22+
// import { cacheChunk, deleteCache, getNormalizedCache } from "../utils/cache";
2323
import updateGitignore from "../utils/update-gitignore";
2424
import createProcessor from "../processor";
2525
import { withExponentialBackoff } from "../utils/exp-backoff";
@@ -524,7 +524,7 @@ export default new Command()
524524
targetData,
525525
},
526526
(progress, sourceChunk, processedChunk) => {
527-
cacheChunk(targetLocale, sourceChunk, processedChunk);
527+
// cacheChunk(targetLocale, sourceChunk, processedChunk);
528528

529529
const progressLog = `[${sourceLocale} -> ${targetLocale}] [${Object.keys(processableData).length} entries] (${progress}%) AI localization in progress...`;
530530
if (flags.verbose) {
@@ -615,10 +615,10 @@ export default new Command()
615615
console.log();
616616
if (!hasErrors) {
617617
ora.succeed("Localization completed.");
618-
deleteCache();
619-
if (flags.verbose) {
620-
ora.info("Cache file deleted.");
621-
}
618+
// deleteCache();
619+
// if (flags.verbose) {
620+
// ora.info("Cache file deleted.");
621+
// }
622622
trackEvent(auth.id, "cmd.i18n.success", {
623623
i18nConfig,
624624
flags,

packages/cli/src/cli/loaders/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import createPhpLoader from "./php";
3131
import createVueJsonLoader from "./vue-json";
3232
import createInjectLocaleLoader from "./inject-locale";
3333
import createLockedKeysLoader from "./locked-keys";
34-
import createMdxLoader from "./mdx2";
34+
import createMdxFrontmatterSplitLoader from "./mdx2/frontmatter-split";
35+
import createMdxCodePlaceholderLoader from "./mdx2/code-placeholder";
36+
import createLocalizableMdxDocumentLoader from "./mdx2/localizable-document";
37+
import createMdxSectionsSplit2Loader from "./mdx2/sections-split-2";
3538

3639
type BucketLoaderOptions = {
3740
isCacheRestore: boolean;
@@ -110,8 +113,14 @@ export default function createBucketLoader(
110113
case "mdx":
111114
return composeLoaders(
112115
createTextFileLoader(bucketPathPattern),
113-
createPrettierLoader({ parser: "mdx", bucketPathPattern }),
114-
createMdxLoader(),
116+
createMdxCodePlaceholderLoader(),
117+
createPrettierLoader({
118+
parser: "mdx",
119+
bucketPathPattern,
120+
}),
121+
createMdxFrontmatterSplitLoader(),
122+
createMdxSectionsSplit2Loader(),
123+
createLocalizableMdxDocumentLoader(),
115124
createFlatLoader(),
116125
createSyncLoader(),
117126
createUnlocalizableLoader(

packages/cli/src/cli/loaders/mdx2/_utils.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
import { Root, RootContent } from "mdast";
2-
import { VFile } from "vfile";
3-
import { visit } from "unist-util-visit";
4-
import { fromMarkdown } from "mdast-util-from-markdown";
5-
import { gfm } from "micromark-extension-gfm";
6-
import { gfmFromMarkdown } from "mdast-util-gfm";
72

83
export function traverseMdast(
94
ast: Root | RootContent,

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

Lines changed: 18 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
1-
import {
2-
Root,
3-
Paragraph,
4-
RootContent,
5-
RootContentMap,
6-
InlineCode,
7-
Code,
8-
} from "mdast";
91
import { ILoader } from "../_types";
102
import { createLoader } from "../_utils";
113
import { PlaceholderedMdx, RawMdx } from "./_types";
12-
import { traverseMdast } from "./_utils";
134
import { md5 } from "../../utils/md5";
145
import { unified } from "unified";
156
import remarkParse from "remark-parse";
@@ -35,63 +26,19 @@ function extractCodePlaceholders(content: string): {
3526
content: string;
3627
codePlaceholders: Record<string, string>;
3728
} {
38-
let shouldSearchForPlaceholders = true;
3929
let finalContent = content;
4030
const codePlaceholders: Record<string, string> = {};
4131

42-
while (shouldSearchForPlaceholders) {
43-
const ast = parseMdast(finalContent);
32+
const codeBlockRegex = /^```.*\n([\s\S]*?)^```$/gm;
33+
const codeBlockMatches = finalContent.matchAll(codeBlockRegex);
4434

45-
traverseMdast(ast, (_node) => {
46-
shouldSearchForPlaceholders = false;
35+
for (const match of codeBlockMatches) {
36+
const codeBlock = match[0];
37+
const codeBlockHash = md5(codeBlock);
38+
const placeholderId = `---CODE_PLACEHOLDER_${codeBlockHash}---`;
4739

48-
if (_node.type === "code") {
49-
const node = _node as Code;
50-
51-
const nodeContentStartLine = node.position?.start.line;
52-
const nodeContentEndLine = node.position?.end.line;
53-
54-
if (!nodeContentStartLine || !nodeContentEndLine) {
55-
return;
56-
}
57-
58-
const nodeContentPreStartLine = nodeContentStartLine - 1;
59-
const nodeContentPostEndLine = nodeContentEndLine + 1;
60-
61-
const nodeContent = finalContent
62-
.split("\n")
63-
.slice(nodeContentPreStartLine, nodeContentPostEndLine)
64-
.join("\n");
65-
66-
const nodeContentHash = md5(nodeContent);
67-
const placeholderId = `__PLACEHOLDER_${nodeContentHash}__`;
68-
69-
codePlaceholders[placeholderId] = nodeContent;
70-
finalContent = finalContent.replace(nodeContent, placeholderId);
71-
shouldSearchForPlaceholders = true;
72-
} else if (_node.type === "inlineCode") {
73-
const node = _node as InlineCode;
74-
75-
const nodeContentStartIndex = node.position?.start.offset;
76-
const nodeContentEndIndex = node.position?.end.offset;
77-
78-
if (!nodeContentStartIndex || !nodeContentEndIndex) {
79-
return;
80-
}
81-
82-
const nodeContent = finalContent.slice(
83-
nodeContentStartIndex,
84-
nodeContentEndIndex,
85-
);
86-
87-
const nodeContentHash = md5(nodeContent);
88-
const placeholderId = `__PLACEHOLDER_${nodeContentHash}__`;
89-
90-
codePlaceholders[placeholderId] = nodeContent;
91-
finalContent = finalContent.replace(nodeContent, placeholderId);
92-
shouldSearchForPlaceholders = true;
93-
}
94-
});
40+
codePlaceholders[placeholderId] = codeBlock;
41+
finalContent = finalContent.replace(codeBlock, placeholderId);
9542
}
9643

9744
return {
@@ -101,49 +48,25 @@ function extractCodePlaceholders(content: string): {
10148
}
10249

10350
export default function createMdxCodePlaceholderLoader(): ILoader<
104-
RawMdx,
105-
PlaceholderedMdx
51+
string,
52+
string
10653
> {
10754
return createLoader({
10855
async pull(locale, input) {
109-
const { frontmatter = {}, content = "" } = input || {
110-
frontmatter: {},
111-
content: "",
112-
};
113-
114-
const { content: resultContent, codePlaceholders } =
115-
extractCodePlaceholders(content);
116-
117-
return {
118-
frontmatter,
119-
content: resultContent,
120-
codePlaceholders,
121-
};
56+
const response = extractCodePlaceholders(input);
57+
return response.content;
12258
},
12359

12460
async push(locale, data, originalInput) {
125-
// Re-create the placeholders map from the original (unmodified) input so we
126-
// can rely on a stable mapping even if `data.codePlaceholders` was lost or
127-
// tampered with by other loaders in the chain.
128-
const { codePlaceholders } = extractCodePlaceholders(
129-
originalInput?.content ?? "",
130-
);
61+
const response = extractCodePlaceholders(originalInput ?? "");
13162

132-
let finalContent = data.content;
133-
// Restore code from placeholders
134-
for (const [placeholder, original] of Object.entries(codePlaceholders)) {
135-
// Use String.replaceAll with a replacer function to restore the original code.
136-
// A replacer function is used to avoid any special replacement pattern
137-
// interpretation (e.g. "$&", "$$", etc.) that could occur if `original`
138-
// contains `$` characters.
139-
finalContent = finalContent.replaceAll(placeholder, () => original);
63+
let result = data;
64+
for (const [placeholder, original] of Object.entries(
65+
response.codePlaceholders,
66+
)) {
67+
result = result.replaceAll(placeholder, original);
14068
}
14169

142-
const result: RawMdx = {
143-
frontmatter: data.frontmatter,
144-
content: finalContent,
145-
};
146-
14770
return result;
14871
},
14972
});

packages/cli/src/cli/loaders/mdx2/index.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { ILoader } from "../_types";
2+
import { createLoader } from "../_utils";
3+
import { PlaceholderedMdx, SectionedMdx } from "./_types";
4+
import _ from "lodash";
5+
6+
export default function createMdxSectionsSplit2Loader(): ILoader<
7+
PlaceholderedMdx,
8+
SectionedMdx
9+
> {
10+
return createLoader({
11+
async pull(locale, input) {
12+
const sections = _.chain(input.content)
13+
.split("\n\n")
14+
.filter(Boolean)
15+
.map((section, index) => [index, section])
16+
.fromPairs()
17+
.value();
18+
19+
const result: SectionedMdx = {
20+
frontmatter: input.frontmatter,
21+
sections,
22+
};
23+
24+
return result;
25+
},
26+
27+
async push(locale, data, originalInput, _originalLocale, pullInput) {
28+
const content = _.chain(data.sections).values().join("\n\n").value();
29+
30+
const result: PlaceholderedMdx = {
31+
frontmatter: data.frontmatter,
32+
codePlaceholders: pullInput?.codePlaceholders || {},
33+
content,
34+
};
35+
36+
return result;
37+
},
38+
});
39+
}

packages/cli/src/cli/loaders/prettier.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createLoader } from "./_utils";
66
export type PrettierLoaderOptions = {
77
parser: Options["parser"];
88
bucketPathPattern: string;
9+
stage?: "pull" | "push" | "both";
910
alwaysFormat?: boolean;
1011
};
1112

@@ -14,6 +15,10 @@ export default function createPrettierLoader(
1415
): ILoader<string, string> {
1516
return createLoader({
1617
async pull(locale, data) {
18+
if (!["pull", "both"].includes(options.stage!)) {
19+
return data;
20+
}
21+
1722
const draftPath = options.bucketPathPattern.replaceAll(
1823
"[locale]",
1924
locale,
@@ -23,6 +28,10 @@ export default function createPrettierLoader(
2328
return await formatDataWithPrettier(data, finalPath, options);
2429
},
2530
async push(locale, data) {
31+
if (!["push", "both"].includes(options.stage!)) {
32+
return data;
33+
}
34+
2635
const draftPath = options.bucketPathPattern.replaceAll(
2736
"[locale]",
2837
locale,

0 commit comments

Comments
 (0)