Skip to content

Commit f12a0f4

Browse files
Merge branch 'main' into docs/pt-br-readme-fix
2 parents 34799ac + 809f5b1 commit f12a0f4

File tree

10 files changed

+182
-8
lines changed

10 files changed

+182
-8
lines changed

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.50
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`77cf56e`](https://github.com/lingodotdev/lingo.dev/commit/77cf56e57725c680d071c6f5bc310e77c8ead463)]:
8+
9+
310
## 0.0.49
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.49",
3+
"version": "0.0.50",
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.102
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`77cf56e`](https://github.com/lingodotdev/lingo.dev/commit/77cf56e57725c680d071c6f5bc310e77c8ead463)]:
8+
9+
310
## 0.2.101
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.101",
3+
"version": "0.2.102",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

demo/react-router-app/CHANGELOG.md

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

3+
## 1.0.11
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`77cf56e`](https://github.com/lingodotdev/lingo.dev/commit/77cf56e57725c680d071c6f5bc310e77c8ead463)]:
8+
9+
310
## 1.0.10
411

512
### Patch Changes

demo/react-router-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-router-app",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"private": true,
55
"type": "module",
66
"scripts": {

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.117.10
4+
5+
### Patch Changes
6+
7+
- [#1658](https://github.com/lingodotdev/lingo.dev/pull/1658) [`77cf56e`](https://github.com/lingodotdev/lingo.dev/commit/77cf56e57725c680d071c6f5bc310e77c8ead463) Thanks [@vrcprl](https://github.com/vrcprl)! - fix mjml format issue
8+
39
## 0.117.9
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.117.9",
3+
"version": "0.117.10",
44
"description": "Lingo.dev CLI",
55
"private": false,
66
"publishConfig": {

packages/cli/src/cli/loaders/mjml.spec.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,4 +697,140 @@ describe("mjml loader", () => {
697697
expect(output).toMatch(/Comience con (<\/span>)?<strong>/);
698698
expect(output).not.toContain("Comience con<strong>");
699699
});
700+
701+
test("should handle empty input string in pull", async () => {
702+
const loader = createMjmlLoader();
703+
loader.setDefaultLocale("en");
704+
705+
const result = await loader.pull("en", "");
706+
707+
expect(result).toEqual({});
708+
});
709+
710+
test("should handle whitespace-only input in pull", async () => {
711+
const loader = createMjmlLoader();
712+
loader.setDefaultLocale("en");
713+
714+
const result = await loader.pull("en", " \n\t ");
715+
716+
expect(result).toEqual({});
717+
});
718+
719+
test("should handle empty input string in push", async () => {
720+
const loader = createMjmlLoader();
721+
loader.setDefaultLocale("en");
722+
723+
// Need to pull first to initialize state
724+
await loader.pull("en", "");
725+
const output = await loader.push("es", {}, "");
726+
727+
expect(output).toBe("");
728+
});
729+
730+
test("should handle whitespace-only input in push", async () => {
731+
const loader = createMjmlLoader();
732+
loader.setDefaultLocale("en");
733+
734+
// Need to pull first to initialize state
735+
await loader.pull("en", " \n\t ");
736+
const output = await loader.push("es", {}, " \n\t ");
737+
738+
expect(output).toBe(" \n\t ");
739+
});
740+
741+
test("should not add XML declaration to output", async () => {
742+
const loader = createMjmlLoader();
743+
loader.setDefaultLocale("en");
744+
745+
const inputWithoutDeclaration = `<mjml>
746+
<mj-body>
747+
<mj-section>
748+
<mj-column>
749+
<mj-text>Hello World</mj-text>
750+
</mj-column>
751+
</mj-section>
752+
</mj-body>
753+
</mjml>`;
754+
755+
await loader.pull("en", inputWithoutDeclaration);
756+
757+
const translations = {
758+
"mjml/mj-body/0/mj-section/0/mj-column/0/mj-text/0": "Hola Mundo",
759+
};
760+
761+
const output = await loader.push("es", translations, inputWithoutDeclaration);
762+
763+
// Should NOT start with XML declaration
764+
expect(output).not.toMatch(/^<\?xml/);
765+
// Should start with <mjml>
766+
expect(output.trim()).toMatch(/^<mjml>/);
767+
expect(output).toContain("Hola Mundo");
768+
});
769+
770+
test("should handle input with XML declaration and not duplicate it", async () => {
771+
const loader = createMjmlLoader();
772+
loader.setDefaultLocale("en");
773+
774+
const inputWithDeclaration = `<?xml version="1.0" encoding="UTF-8"?>
775+
<mjml>
776+
<mj-body>
777+
<mj-section>
778+
<mj-column>
779+
<mj-text>Hello World</mj-text>
780+
</mj-column>
781+
</mj-section>
782+
</mj-body>
783+
</mjml>`;
784+
785+
await loader.pull("en", inputWithDeclaration);
786+
787+
const translations = {
788+
"mjml/mj-body/0/mj-section/0/mj-column/0/mj-text/0": "Hola Mundo",
789+
};
790+
791+
const output = await loader.push("es", translations, inputWithDeclaration);
792+
793+
// Should not duplicate XML declaration
794+
const declarationMatches = output.match(/<\?xml/g);
795+
expect(declarationMatches).toBeNull(); // No XML declaration in output
796+
expect(output).toContain("Hola Mundo");
797+
});
798+
799+
test("should match structure of source file without XML declaration", async () => {
800+
const loader = createMjmlLoader();
801+
loader.setDefaultLocale("en");
802+
803+
// Source file format (en-US)
804+
const sourceInput = `<mjml>
805+
<mj-body>
806+
<mj-section padding="26px 0px 86px 45px">
807+
<mj-column>
808+
<mj-image
809+
src="cid:logo.png"
810+
alt="GitProtect logo"
811+
width="140px"
812+
height="35px"
813+
padding="0"
814+
align="left"
815+
/>
816+
</mj-column>
817+
</mj-section>
818+
</mj-body>
819+
</mjml>`;
820+
821+
await loader.pull("en", sourceInput);
822+
823+
const translations = {
824+
"mjml/mj-body/0/mj-section/0/mj-column/0/mj-image/0#alt": "Logo de GitProtect",
825+
};
826+
827+
const output = await loader.push("es", translations, sourceInput);
828+
829+
// Generated file should match source structure
830+
expect(output.trim()).toMatch(/^<mjml>/);
831+
expect(output).not.toMatch(/^<\?xml/);
832+
expect(output).toContain("Logo de GitProtect");
833+
expect(output).toContain("<mjml>");
834+
expect(output).toContain("</mjml>");
835+
});
700836
});

packages/cli/src/cli/loaders/mjml.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ export default function createMjmlLoader(): ILoader<
3434
async pull(locale, input) {
3535
const result: Record<string, any> = {};
3636

37+
// Handle empty input
38+
if (!input || input.trim() === "") {
39+
return result;
40+
}
41+
3742
try {
3843
const parsed = await parseStringPromise(input, {
3944
explicitArray: true,
@@ -88,8 +93,13 @@ export default function createMjmlLoader(): ILoader<
8893
},
8994

9095
async push(locale, data, originalInput) {
96+
// Handle empty input
97+
if (!originalInput || originalInput.trim() === "") {
98+
return originalInput || "";
99+
}
100+
91101
try {
92-
const parsed = await parseStringPromise(originalInput || "", {
102+
const parsed = await parseStringPromise(originalInput, {
93103
explicitArray: true,
94104
explicitChildren: true,
95105
preserveChildrenOrder: true,
@@ -275,13 +285,14 @@ function convertDomToXmlNode(domNode: any): any {
275285
}
276286

277287
function serializeMjml(parsed: any): string {
278-
const xmlDec = '<?xml version="1.0" encoding="UTF-8"?>\n';
279-
280288
const rootKey = Object.keys(parsed).find(key => !key.startsWith("_") && !key.startsWith("$"));
281289
const rootNode = rootKey ? parsed[rootKey] : parsed;
282290

283291
const body = serializeElement(rootNode);
284-
return xmlDec + body;
292+
293+
// Don't add XML declaration - xml2js already preserves it in the parsed object
294+
// or it will be added by the consumer if needed
295+
return body;
285296
}
286297

287298
function serializeElement(node: any, indent: string = ""): string {

0 commit comments

Comments
 (0)