Skip to content

Commit 3a526b8

Browse files
authored
fix(cli): bucket paths with * filenames (#500)
1 parent 7fee4cf commit 3a526b8

File tree

3 files changed

+74
-9
lines changed

3 files changed

+74
-9
lines changed

.changeset/real-deers-shave.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+
fix bucket path with \* filenames

packages/cli/src/cli/utils/buckets.spec.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { describe, it, expect, vi } from "vitest";
22
import { getBuckets } from "./buckets";
3+
import { glob, Path } from "glob";
34

45
vi.mock("glob", () => ({
5-
sync: vi.fn().mockImplementation((path) => [{ isFile: () => true, fullpath: () => path }]),
6+
glob: {
7+
sync: vi.fn(),
8+
},
69
}));
710

811
describe("getBuckets", () => {
912
const makeI18nConfig = (include: any[]) => ({
13+
$schema: "https://lingo.dev/schema/i18n.json",
1014
version: 0,
1115
locale: {
1216
source: "en",
@@ -20,6 +24,8 @@ describe("getBuckets", () => {
2024
});
2125

2226
it("should return correct buckets", () => {
27+
mockGlobSync(["src/i18n/en.json"], ["src/translations/en/messages.json"]);
28+
2329
const i18nConfig = makeI18nConfig(["src/i18n/[locale].json", "src/translations/[locale]/messages.json"]);
2430
const buckets = getBuckets(i18nConfig);
2531
expect(buckets).toEqual([
@@ -33,13 +39,55 @@ describe("getBuckets", () => {
3339
]);
3440
});
3541

42+
it("should return correct buckets for paths with asterisk", () => {
43+
mockGlobSync(
44+
["src/translations/landing.en.json", "src/translations/app.en.json", "src/translations/email.en.json"],
45+
["src/locale/landing/messages.en.json", "src/locale/app/data.en.json", "src/locale/email/custom.en.json"],
46+
["src/i18n/landing/en.messages.json", "src/i18n/app/en.data.json", "src/i18n/email/en.custom.json"],
47+
[
48+
"src/i18n/data-landing-en-strings/en.messages.json",
49+
"src/i18n/data-app-en-strings/en.data.json",
50+
"src/i18n/data-email-en-strings/en.custom.json",
51+
],
52+
);
53+
54+
const i18nConfig = makeI18nConfig([
55+
"src/translations/*.[locale].json",
56+
"src/locale/*/*.[locale].json",
57+
"src/i18n/*/[locale].*.json",
58+
"src/i18n/data-*-[locale]-*/[locale].*.json",
59+
]);
60+
const buckets = getBuckets(i18nConfig);
61+
expect(buckets).toEqual([
62+
{
63+
type: "json",
64+
config: [
65+
{ pathPattern: "src/translations/landing.[locale].json", delimiter: null },
66+
{ pathPattern: "src/translations/app.[locale].json", delimiter: null },
67+
{ pathPattern: "src/translations/email.[locale].json", delimiter: null },
68+
{ pathPattern: "src/locale/landing/messages.[locale].json", delimiter: null },
69+
{ pathPattern: "src/locale/app/data.[locale].json", delimiter: null },
70+
{ pathPattern: "src/locale/email/custom.[locale].json", delimiter: null },
71+
{ pathPattern: "src/i18n/landing/[locale].messages.json", delimiter: null },
72+
{ pathPattern: "src/i18n/app/[locale].data.json", delimiter: null },
73+
{ pathPattern: "src/i18n/email/[locale].custom.json", delimiter: null },
74+
{ pathPattern: "src/i18n/data-landing-[locale]-strings/[locale].messages.json", delimiter: null },
75+
{ pathPattern: "src/i18n/data-app-[locale]-strings/[locale].data.json", delimiter: null },
76+
{ pathPattern: "src/i18n/data-email-[locale]-strings/[locale].custom.json", delimiter: null },
77+
],
78+
},
79+
]);
80+
});
81+
3682
it("should return correct bucket with delimiter", () => {
83+
mockGlobSync(["src/i18n/en.json"]);
3784
const i18nConfig = makeI18nConfig([{ path: "src/i18n/[locale].json", delimiter: "-" }]);
3885
const buckets = getBuckets(i18nConfig);
3986
expect(buckets).toEqual([{ type: "json", config: [{ pathPattern: "src/i18n/[locale].json", delimiter: "-" }] }]);
4087
});
4188

4289
it("should return bucket with multiple locale placeholders", () => {
90+
mockGlobSync(["src/i18n/en/en.json"], ["src/en/translations/en/messages.json"]);
4391
const i18nConfig = makeI18nConfig([
4492
"src/i18n/[locale]/[locale].json",
4593
"src/[locale]/translations/[locale]/messages.json",
@@ -56,3 +104,11 @@ describe("getBuckets", () => {
56104
]);
57105
});
58106
});
107+
108+
function mockGlobSync(...args: string[][]) {
109+
args.forEach((files) => {
110+
vi.mocked(glob.sync).mockReturnValueOnce(
111+
files.map((file) => ({ isFile: () => true, fullpath: () => file }) as Path),
112+
);
113+
});
114+
}

packages/cli/src/cli/utils/buckets.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from "lodash";
22
import path from "path";
3-
import * as glob from "glob";
3+
import { glob } from "glob";
44
import { CLIError } from "./errors";
55
import { I18nConfig, resolveOverridenLocale, BucketItem } from "@lingo.dev/_spec";
66
import { bucketTypeSchema } from "@lingo.dev/_spec";
@@ -82,13 +82,17 @@ function expandPlaceholderedGlob(_pathPattern: string, sourceLocale: string): st
8282
const sourcePathChunks = sourcePath.split(path.sep);
8383
localeSegmentIndexes.forEach((localeSegmentIndex) => {
8484
// Find the position of the "[locale]" placeholder within the segment
85-
const localePlaceholderIndex = pathPatternChunks[localeSegmentIndex]?.indexOf("[locale]") ?? -1;
86-
if (localeSegmentIndex >= 0 && localePlaceholderIndex >= 0) {
87-
const placeholderedPathChunk = sourcePathChunks[localeSegmentIndex];
88-
const placeholderedSegment =
89-
placeholderedPathChunk.substring(0, localePlaceholderIndex) +
90-
"[locale]" +
91-
placeholderedPathChunk.substring(localePlaceholderIndex + sourceLocale.length);
85+
const pathPatternChunk = pathPatternChunks[localeSegmentIndex];
86+
const sourcePathChunk = sourcePathChunks[localeSegmentIndex];
87+
const regexp = new RegExp(
88+
"(" +
89+
pathPatternChunk.replaceAll(".", "\\.").replaceAll("*", ".*").replace("[locale]", `)${sourceLocale}(`) +
90+
")",
91+
);
92+
const match = sourcePathChunk.match(regexp);
93+
if (match) {
94+
const [, prefix, suffix] = match;
95+
const placeholderedSegment = prefix + "[locale]" + suffix;
9296
sourcePathChunks[localeSegmentIndex] = placeholderedSegment;
9397
}
9498
});

0 commit comments

Comments
 (0)