@inlang/sdk 0.26.5 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/solidAdapter.test.js +52 -4
- package/dist/api.d.ts +3 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.d.ts +1 -1
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.js +30 -22
- package/dist/createMessagesQuery.d.ts.map +1 -1
- package/dist/createMessagesQuery.js +24 -1
- package/dist/createMessagesQuery.test.js +46 -2
- package/dist/createNodeishFsWithAbsolutePaths.d.ts +3 -3
- package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -1
- package/dist/createNodeishFsWithAbsolutePaths.js +9 -1
- package/dist/createNodeishFsWithAbsolutePaths.test.js +7 -1
- package/dist/createNodeishFsWithWatcher.d.ts +3 -3
- package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
- package/dist/createNodeishFsWithWatcher.js +3 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -0
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +472 -39
- package/dist/loadProject.test.js +91 -14
- package/dist/messages/variant.test.js +3 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +4 -2
- package/dist/storage/helper.d.ts +5 -0
- package/dist/storage/helper.d.ts.map +1 -0
- package/dist/storage/helper.js +35 -0
- package/dist/storage/human-id/human-readable-id.d.ts +3 -0
- package/dist/storage/human-id/human-readable-id.d.ts.map +1 -0
- package/dist/storage/human-id/human-readable-id.js +20 -0
- package/dist/storage/human-id/words.d.ts +5 -0
- package/dist/storage/human-id/words.d.ts.map +1 -0
- package/dist/storage/human-id/words.js +1032 -0
- package/dist/storage/human-id/words.test.d.ts +2 -0
- package/dist/storage/human-id/words.test.d.ts.map +1 -0
- package/dist/storage/human-id/words.test.js +25 -0
- package/dist/test-utilities/createMessage.d.ts +1 -0
- package/dist/test-utilities/createMessage.d.ts.map +1 -1
- package/dist/test-utilities/createMessage.js +1 -0
- package/dist/test-utilities/createMessage.test.js +70 -55
- package/package.json +12 -8
- package/src/adapter/solidAdapter.test.ts +76 -4
- package/src/api.ts +4 -0
- package/src/createMessageLintReportsQuery.ts +46 -34
- package/src/createMessagesQuery.test.ts +54 -2
- package/src/createMessagesQuery.ts +30 -1
- package/src/createNodeishFsWithAbsolutePaths.test.ts +10 -3
- package/src/createNodeishFsWithAbsolutePaths.ts +14 -5
- package/src/createNodeishFsWithWatcher.ts +6 -3
- package/src/errors.ts +20 -0
- package/src/loadProject.test.ts +106 -14
- package/src/loadProject.ts +655 -60
- package/src/messages/variant.test.ts +3 -0
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +4 -2
- package/src/storage/helper.ts +48 -0
- package/src/storage/human-id/human-readable-id.ts +27 -0
- package/src/storage/human-id/words.test.ts +27 -0
- package/src/storage/human-id/words.ts +1035 -0
- package/src/test-utilities/createMessage.test.ts +72 -54
- package/src/test-utilities/createMessage.ts +1 -0
package/dist/loadProject.test.js
CHANGED
|
@@ -45,6 +45,7 @@ const mockPlugin = {
|
|
|
45
45
|
const exampleMessages = [
|
|
46
46
|
{
|
|
47
47
|
id: "a",
|
|
48
|
+
alias: {},
|
|
48
49
|
selectors: [],
|
|
49
50
|
variants: [
|
|
50
51
|
{
|
|
@@ -61,6 +62,47 @@ const exampleMessages = [
|
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
64
|
id: "b",
|
|
65
|
+
alias: {},
|
|
66
|
+
selectors: [],
|
|
67
|
+
variants: [
|
|
68
|
+
{
|
|
69
|
+
languageTag: "en",
|
|
70
|
+
match: [],
|
|
71
|
+
pattern: [
|
|
72
|
+
{
|
|
73
|
+
type: "Text",
|
|
74
|
+
value: "test",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
const exampleAliasedMessages = [
|
|
82
|
+
{
|
|
83
|
+
id: "raw_tapir_pause_grateful",
|
|
84
|
+
alias: {
|
|
85
|
+
default: "a",
|
|
86
|
+
},
|
|
87
|
+
selectors: [],
|
|
88
|
+
variants: [
|
|
89
|
+
{
|
|
90
|
+
languageTag: "en",
|
|
91
|
+
match: [],
|
|
92
|
+
pattern: [
|
|
93
|
+
{
|
|
94
|
+
type: "Text",
|
|
95
|
+
value: "test",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "dizzy_halibut_dial_vaguely",
|
|
103
|
+
alias: {
|
|
104
|
+
default: "b",
|
|
105
|
+
},
|
|
64
106
|
selectors: [],
|
|
65
107
|
variants: [
|
|
66
108
|
{
|
|
@@ -449,7 +491,7 @@ describe("functionality", () => {
|
|
|
449
491
|
id: "plugin.project.i18next",
|
|
450
492
|
description: { en: "Mock plugin description" },
|
|
451
493
|
displayName: { en: "Mock Plugin" },
|
|
452
|
-
loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
|
|
494
|
+
loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
|
|
453
495
|
saveMessages: () => undefined,
|
|
454
496
|
};
|
|
455
497
|
const repo = await mockRepo();
|
|
@@ -492,7 +534,7 @@ describe("functionality", () => {
|
|
|
492
534
|
id: "plugin.project.i18next",
|
|
493
535
|
description: { en: "Mock plugin description" },
|
|
494
536
|
displayName: { en: "Mock Plugin" },
|
|
495
|
-
loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
|
|
537
|
+
loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
|
|
496
538
|
saveMessages: () => undefined,
|
|
497
539
|
};
|
|
498
540
|
const repo = await mockRepo();
|
|
@@ -563,6 +605,20 @@ describe("functionality", () => {
|
|
|
563
605
|
expect(Object.values(project.query.messages.getAll())).toEqual(exampleMessages);
|
|
564
606
|
});
|
|
565
607
|
});
|
|
608
|
+
describe("messages with aliases", () => {
|
|
609
|
+
it("should return the messages", async () => {
|
|
610
|
+
const repo = await mockRepo();
|
|
611
|
+
const fs = repo.nodeishFs;
|
|
612
|
+
await fs.mkdir("/user/project.inlang", { recursive: true });
|
|
613
|
+
await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify({ ...settings, experimental: { aliases: true } }));
|
|
614
|
+
const project = await loadProject({
|
|
615
|
+
projectPath: "/user/project.inlang",
|
|
616
|
+
repo,
|
|
617
|
+
_import,
|
|
618
|
+
});
|
|
619
|
+
expect(Object.values(project.query.messages.getAll())).toEqual(exampleAliasedMessages);
|
|
620
|
+
});
|
|
621
|
+
});
|
|
566
622
|
describe("query", () => {
|
|
567
623
|
it("should call saveMessages() on updates", async () => {
|
|
568
624
|
const repo = await mockRepo();
|
|
@@ -600,6 +656,7 @@ describe("functionality", () => {
|
|
|
600
656
|
where: { id: "a" },
|
|
601
657
|
data: {
|
|
602
658
|
id: "a",
|
|
659
|
+
alias: {},
|
|
603
660
|
selectors: [],
|
|
604
661
|
variants: [
|
|
605
662
|
{
|
|
@@ -629,6 +686,7 @@ describe("functionality", () => {
|
|
|
629
686
|
where: { id: "b" },
|
|
630
687
|
data: {
|
|
631
688
|
id: "b",
|
|
689
|
+
alias: {},
|
|
632
690
|
selectors: [],
|
|
633
691
|
variants: [
|
|
634
692
|
{
|
|
@@ -654,31 +712,33 @@ describe("functionality", () => {
|
|
|
654
712
|
],
|
|
655
713
|
},
|
|
656
714
|
});
|
|
657
|
-
|
|
715
|
+
// lets wait for the next tick
|
|
716
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
658
717
|
expect(mockSaveFn.mock.calls.length).toBe(1);
|
|
659
718
|
expect(mockSaveFn.mock.calls[0][0].settings).toStrictEqual(settings);
|
|
660
719
|
expect(Object.values(mockSaveFn.mock.calls[0][0].messages)).toStrictEqual([
|
|
661
720
|
{
|
|
662
721
|
id: "a",
|
|
722
|
+
alias: {},
|
|
663
723
|
selectors: [],
|
|
664
724
|
variants: [
|
|
665
725
|
{
|
|
666
|
-
languageTag: "
|
|
726
|
+
languageTag: "de",
|
|
667
727
|
match: [],
|
|
668
728
|
pattern: [
|
|
669
729
|
{
|
|
670
730
|
type: "Text",
|
|
671
|
-
value: "a
|
|
731
|
+
value: "a de",
|
|
672
732
|
},
|
|
673
733
|
],
|
|
674
734
|
},
|
|
675
735
|
{
|
|
676
|
-
languageTag: "
|
|
736
|
+
languageTag: "en",
|
|
677
737
|
match: [],
|
|
678
738
|
pattern: [
|
|
679
739
|
{
|
|
680
740
|
type: "Text",
|
|
681
|
-
value: "a
|
|
741
|
+
value: "a en",
|
|
682
742
|
},
|
|
683
743
|
],
|
|
684
744
|
},
|
|
@@ -686,25 +746,26 @@ describe("functionality", () => {
|
|
|
686
746
|
},
|
|
687
747
|
{
|
|
688
748
|
id: "b",
|
|
749
|
+
alias: {},
|
|
689
750
|
selectors: [],
|
|
690
751
|
variants: [
|
|
691
752
|
{
|
|
692
|
-
languageTag: "
|
|
753
|
+
languageTag: "de",
|
|
693
754
|
match: [],
|
|
694
755
|
pattern: [
|
|
695
756
|
{
|
|
696
757
|
type: "Text",
|
|
697
|
-
value: "b
|
|
758
|
+
value: "b de",
|
|
698
759
|
},
|
|
699
760
|
],
|
|
700
761
|
},
|
|
701
762
|
{
|
|
702
|
-
languageTag: "
|
|
763
|
+
languageTag: "en",
|
|
703
764
|
match: [],
|
|
704
765
|
pattern: [
|
|
705
766
|
{
|
|
706
767
|
type: "Text",
|
|
707
|
-
value: "b
|
|
768
|
+
value: "b en",
|
|
708
769
|
},
|
|
709
770
|
],
|
|
710
771
|
},
|
|
@@ -766,6 +827,14 @@ describe("functionality", () => {
|
|
|
766
827
|
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
767
828
|
expect(mockSaveFn.mock.calls.length).toBe(1);
|
|
768
829
|
expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4);
|
|
830
|
+
project.query.messages.create({ data: createMessage("fifth", { en: "fifth message" }) });
|
|
831
|
+
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
832
|
+
expect(mockSaveFn.mock.calls.length).toBe(2);
|
|
833
|
+
expect(mockSaveFn.mock.calls[1][0].messages).toHaveLength(5);
|
|
834
|
+
project.query.messages.delete({ where: { id: "fourth" } });
|
|
835
|
+
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
836
|
+
expect(mockSaveFn.mock.calls.length).toBe(3);
|
|
837
|
+
expect(mockSaveFn.mock.calls[2][0].messages).toHaveLength(4);
|
|
769
838
|
});
|
|
770
839
|
});
|
|
771
840
|
describe("lint", () => {
|
|
@@ -868,14 +937,22 @@ describe("functionality", () => {
|
|
|
868
937
|
project.query.messages.getAll.subscribe(() => {
|
|
869
938
|
counter = counter + 1;
|
|
870
939
|
});
|
|
940
|
+
// subscribe fires once
|
|
871
941
|
expect(counter).toBe(1);
|
|
872
|
-
//
|
|
942
|
+
// saving the file without changing should not trigger a message query
|
|
873
943
|
await fs.writeFile("./messages.json", JSON.stringify(messages));
|
|
874
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
944
|
+
await new Promise((resolve) => setTimeout(resolve, 200)); // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
945
|
+
// we didn't change the message we write into message.json - shouldn't change the messages
|
|
946
|
+
expect(counter).toBe(1);
|
|
947
|
+
// saving the file without changing should trigger a change
|
|
948
|
+
messages.data[0].variants[0].pattern[0].value = "changed";
|
|
949
|
+
await fs.writeFile("./messages.json", JSON.stringify(messages));
|
|
950
|
+
await new Promise((resolve) => setTimeout(resolve, 200)); // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
875
951
|
expect(counter).toBe(2);
|
|
952
|
+
messages.data[0].variants[0].pattern[0].value = "changed3";
|
|
876
953
|
// change file
|
|
877
954
|
await fs.writeFile("./messages.json", JSON.stringify(messages));
|
|
878
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
955
|
+
await new Promise((resolve) => setTimeout(resolve, 200)); // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
879
956
|
expect(counter).toBe(3);
|
|
880
957
|
});
|
|
881
958
|
});
|
|
@@ -32,6 +32,7 @@ describe("getVariant", () => {
|
|
|
32
32
|
test("it should not throw error if selector is empty and match", () => {
|
|
33
33
|
const mockMessage = {
|
|
34
34
|
id: "mockMessage",
|
|
35
|
+
alias: {},
|
|
35
36
|
selectors: [],
|
|
36
37
|
variants: [
|
|
37
38
|
{
|
|
@@ -57,6 +58,7 @@ describe("getVariant", () => {
|
|
|
57
58
|
test("it should not throw error if selector is empty, return undefined", () => {
|
|
58
59
|
const mockMessage = {
|
|
59
60
|
id: "mockMessage",
|
|
61
|
+
alias: {},
|
|
60
62
|
selectors: [],
|
|
61
63
|
variants: [
|
|
62
64
|
{
|
|
@@ -326,6 +328,7 @@ describe("updateVariant", () => {
|
|
|
326
328
|
const getMockMessage = () => {
|
|
327
329
|
return {
|
|
328
330
|
id: "first-message",
|
|
331
|
+
alias: {},
|
|
329
332
|
selectors: [
|
|
330
333
|
{ type: "VariableReference", name: "gender" },
|
|
331
334
|
{ type: "VariableReference", name: "guestOther" },
|
|
@@ -57,7 +57,9 @@ describe("loadMessages", () => {
|
|
|
57
57
|
id: "plugin.namespace.placeholder",
|
|
58
58
|
description: { en: "My plugin description" },
|
|
59
59
|
displayName: { en: "My plugin" },
|
|
60
|
-
loadMessages: async () => [
|
|
60
|
+
loadMessages: async () => [
|
|
61
|
+
{ id: "test", alias: {}, expressions: [], selectors: [], variants: [] },
|
|
62
|
+
],
|
|
61
63
|
};
|
|
62
64
|
const resolved = await resolvePlugins({
|
|
63
65
|
plugins: [mockPlugin],
|
|
@@ -67,7 +69,7 @@ describe("loadMessages", () => {
|
|
|
67
69
|
expect(await resolved.data.loadMessages({
|
|
68
70
|
settings: {},
|
|
69
71
|
nodeishFs: {},
|
|
70
|
-
})).toEqual([{ id: "test", expressions: [], selectors: [], variants: [] }]);
|
|
72
|
+
})).toEqual([{ id: "test", alias: {}, expressions: [], selectors: [], variants: [] }]);
|
|
71
73
|
});
|
|
72
74
|
it("should collect an error if function is defined twice in multiple plugins", async () => {
|
|
73
75
|
const mockPlugin = {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Message } from "../versionedInterfaces.js";
|
|
2
|
+
export declare function getMessageIdFromPath(path: string): string | undefined;
|
|
3
|
+
export declare function getPathFromMessageId(id: string): string;
|
|
4
|
+
export declare function stringifyMessage(message: Message): string;
|
|
5
|
+
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/storage/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AAI5D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,sBAahD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAG9C;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,UAuBhD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Message, Variant } from "../versionedInterfaces.js";
|
|
2
|
+
const fileExtension = ".json";
|
|
3
|
+
export function getMessageIdFromPath(path) {
|
|
4
|
+
if (!path.endsWith(fileExtension)) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
const cleanedPath = path.replace(/\/$/, ""); // This regex matches a trailing slash and replaces it with an empty string
|
|
8
|
+
const messageFileName = cleanedPath.split("/").join("_"); // we split by the first leading namespace or _ separator - make sure slashes don't exit in the id
|
|
9
|
+
// const messageFileName = pathParts.at(-1)!
|
|
10
|
+
const lastDotIndex = messageFileName.lastIndexOf(".");
|
|
11
|
+
// Extract until the last dot (excluding the dot)
|
|
12
|
+
return messageFileName.slice(0, Math.max(0, lastDotIndex));
|
|
13
|
+
}
|
|
14
|
+
export function getPathFromMessageId(id) {
|
|
15
|
+
const path = id.replace("_", "/") + fileExtension;
|
|
16
|
+
return path;
|
|
17
|
+
}
|
|
18
|
+
export function stringifyMessage(message) {
|
|
19
|
+
// create a new object do specify key output order
|
|
20
|
+
const messageWithSortedKeys = {};
|
|
21
|
+
for (const key of Object.keys(message).sort()) {
|
|
22
|
+
messageWithSortedKeys[key] = message[key];
|
|
23
|
+
}
|
|
24
|
+
// lets order variants as well
|
|
25
|
+
messageWithSortedKeys["variants"] = messageWithSortedKeys["variants"].sort((variantA, variantB) => {
|
|
26
|
+
// First, compare by language
|
|
27
|
+
const languageComparison = variantA.languageTag.localeCompare(variantB.languageTag);
|
|
28
|
+
// If languages are the same, compare by match
|
|
29
|
+
if (languageComparison === 0) {
|
|
30
|
+
return variantA.match.join("-").localeCompare(variantB.match.join("-"));
|
|
31
|
+
}
|
|
32
|
+
return languageComparison;
|
|
33
|
+
});
|
|
34
|
+
return JSON.stringify(messageWithSortedKeys, undefined, 4);
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-readable-id.d.ts","sourceRoot":"","sources":["../../../src/storage/human-id/human-readable-id.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,WAI5B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,UAgB5D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// we use murmur for best distribution https://medium.com/miro-engineering/choosing-a-hash-function-to-solve-a-data-sharding-problem-c656259e2b54
|
|
2
|
+
import murmurhash3 from "murmurhash3js";
|
|
3
|
+
import { adjectives, animals, adverbs, verbs } from "./words.js";
|
|
4
|
+
export function randomHumanId() {
|
|
5
|
+
return `${adjectives[Math.floor(Math.random() * 256)]}_${adjectives[Math.floor(Math.random() * 256)]}_${animals[Math.floor(Math.random() * 256)]}_${verbs[Math.floor(Math.random() * 256)]}`;
|
|
6
|
+
}
|
|
7
|
+
export function humanIdHash(value, offset = 0) {
|
|
8
|
+
// Seed value can be any arbitrary value
|
|
9
|
+
const seed = 42;
|
|
10
|
+
// Use MurmurHash3 to produce a 32-bit hash
|
|
11
|
+
const hash32 = murmurhash3.x86.hash32(value, seed);
|
|
12
|
+
// Add 1 and take modulo 2^32 to fit within 32 bits
|
|
13
|
+
const hash32WithOffset = (hash32 + offset) >>> 0;
|
|
14
|
+
// Extract four 8-bit parts
|
|
15
|
+
const part1 = (hash32WithOffset >>> 24) & 0xff;
|
|
16
|
+
const part2 = (hash32WithOffset >>> 16) & 0xff;
|
|
17
|
+
const part3 = (hash32WithOffset >>> 8) & 0xff;
|
|
18
|
+
const part4 = hash32WithOffset & 0xff;
|
|
19
|
+
return `${adjectives[part1]}_${animals[part2]}_${verbs[part3]}_${adverbs[part4]}`;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"words.d.ts","sourceRoot":"","sources":["../../../src/storage/human-id/words.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAiQnB,CAAA;AAED,eAAO,MAAM,UAAU,UAiQtB,CAAA;AAED,eAAO,MAAM,OAAO,UAiQnB,CAAA;AAED,eAAO,MAAM,KAAK,UAiQjB,CAAA"}
|