@inlang/sdk 0.26.5 → 0.28.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.
Files changed (60) hide show
  1. package/dist/adapter/solidAdapter.test.js +52 -4
  2. package/dist/api.d.ts +5 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/createMessageLintReportsQuery.d.ts +1 -1
  5. package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
  6. package/dist/createMessageLintReportsQuery.js +30 -22
  7. package/dist/createMessagesQuery.d.ts.map +1 -1
  8. package/dist/createMessagesQuery.js +24 -1
  9. package/dist/createMessagesQuery.test.js +46 -2
  10. package/dist/createNodeishFsWithAbsolutePaths.d.ts +3 -3
  11. package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -1
  12. package/dist/createNodeishFsWithAbsolutePaths.js +9 -1
  13. package/dist/createNodeishFsWithAbsolutePaths.test.js +7 -1
  14. package/dist/createNodeishFsWithWatcher.d.ts +3 -3
  15. package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
  16. package/dist/createNodeishFsWithWatcher.js +3 -0
  17. package/dist/errors.d.ts +14 -0
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.js +12 -0
  20. package/dist/loadProject.d.ts.map +1 -1
  21. package/dist/loadProject.js +474 -39
  22. package/dist/loadProject.test.js +93 -14
  23. package/dist/messages/variant.test.js +3 -0
  24. package/dist/resolve-modules/plugins/resolvePlugins.test.js +4 -2
  25. package/dist/storage/helper.d.ts +5 -0
  26. package/dist/storage/helper.d.ts.map +1 -0
  27. package/dist/storage/helper.js +35 -0
  28. package/dist/storage/human-id/human-readable-id.d.ts +3 -0
  29. package/dist/storage/human-id/human-readable-id.d.ts.map +1 -0
  30. package/dist/storage/human-id/human-readable-id.js +20 -0
  31. package/dist/storage/human-id/words.d.ts +5 -0
  32. package/dist/storage/human-id/words.d.ts.map +1 -0
  33. package/dist/storage/human-id/words.js +1032 -0
  34. package/dist/storage/human-id/words.test.d.ts +2 -0
  35. package/dist/storage/human-id/words.test.d.ts.map +1 -0
  36. package/dist/storage/human-id/words.test.js +25 -0
  37. package/dist/test-utilities/createMessage.d.ts +1 -0
  38. package/dist/test-utilities/createMessage.d.ts.map +1 -1
  39. package/dist/test-utilities/createMessage.js +1 -0
  40. package/dist/test-utilities/createMessage.test.js +70 -55
  41. package/package.json +16 -7
  42. package/src/adapter/solidAdapter.test.ts +76 -4
  43. package/src/api.ts +6 -0
  44. package/src/createMessageLintReportsQuery.ts +47 -35
  45. package/src/createMessagesQuery.test.ts +54 -2
  46. package/src/createMessagesQuery.ts +30 -1
  47. package/src/createNodeishFsWithAbsolutePaths.test.ts +10 -3
  48. package/src/createNodeishFsWithAbsolutePaths.ts +14 -5
  49. package/src/createNodeishFsWithWatcher.ts +6 -3
  50. package/src/errors.ts +20 -0
  51. package/src/loadProject.test.ts +108 -14
  52. package/src/loadProject.ts +657 -60
  53. package/src/messages/variant.test.ts +3 -0
  54. package/src/resolve-modules/plugins/resolvePlugins.test.ts +4 -2
  55. package/src/storage/helper.ts +48 -0
  56. package/src/storage/human-id/human-readable-id.ts +27 -0
  57. package/src/storage/human-id/words.test.ts +27 -0
  58. package/src/storage/human-id/words.ts +1035 -0
  59. package/src/test-utilities/createMessage.test.ts +72 -54
  60. package/src/test-utilities/createMessage.ts +1 -0
@@ -1,6 +1,7 @@
1
1
  import { it, expect, vi } from "vitest"
2
2
  import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
3
- import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
3
+ // import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
4
+ import type { NodeishFilesystem } from "@lix-js/fs"
4
5
 
5
6
  it("throws an error if projectPath is not an absolute path", () => {
6
7
  const relativePath = "relative/path"
@@ -27,9 +28,16 @@ it("intercepts paths correctly for readFile", async () => {
27
28
  readFile: vi.fn(),
28
29
  readdir: vi.fn(),
29
30
  mkdir: vi.fn(),
31
+ rmdir: vi.fn(),
30
32
  writeFile: vi.fn(),
31
33
  watch: vi.fn(),
32
- } satisfies Record<keyof NodeishFilesystemSubset, any>
34
+ rm: vi.fn(),
35
+ stat: vi.fn(),
36
+ lstat: vi.fn(),
37
+ symlink: vi.fn(),
38
+ unlink: vi.fn(),
39
+ readlink: vi.fn(),
40
+ } satisfies Record<keyof NodeishFilesystem, any>
33
41
 
34
42
  const interceptedFs = createNodeishFsWithAbsolutePaths({
35
43
  projectPath,
@@ -38,7 +46,6 @@ it("intercepts paths correctly for readFile", async () => {
38
46
 
39
47
  for (const [path, expectedPath] of filePaths) {
40
48
  for (const fn of Object.keys(mockNodeishFs)) {
41
- // @ts-expect-error
42
49
  await interceptedFs[fn](path)
43
50
  // @ts-expect-error
44
51
  // expect the first argument to be the expectedPath
@@ -1,5 +1,4 @@
1
- import type { NodeishFilesystemSubset } from "@inlang/plugin"
2
- import { normalizePath } from "@lix-js/fs"
1
+ import { normalizePath, type NodeishFilesystem } from "@lix-js/fs"
3
2
  import { isAbsolutePath } from "./isAbsolutePath.js"
4
3
 
5
4
  /**
@@ -10,8 +9,8 @@ import { isAbsolutePath } from "./isAbsolutePath.js"
10
9
  */
11
10
  export const createNodeishFsWithAbsolutePaths = (args: {
12
11
  projectPath: string
13
- nodeishFs: NodeishFilesystemSubset
14
- }): NodeishFilesystemSubset => {
12
+ nodeishFs: NodeishFilesystem
13
+ }): NodeishFilesystem => {
15
14
  if (!isAbsolutePath(args.projectPath)) {
16
15
  throw new Error(`Expected an absolute path but received "${args.projectPath}".`)
17
16
  }
@@ -33,11 +32,21 @@ export const createNodeishFsWithAbsolutePaths = (args: {
33
32
  readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
34
33
  args.nodeishFs.readFile(makeAbsolute(path), options),
35
34
  readdir: (path: string) => args.nodeishFs.readdir(makeAbsolute(path)),
36
- mkdir: (path: string) => args.nodeishFs.mkdir(makeAbsolute(path)),
35
+ mkdir: (path: string, options: { recursive: boolean }) =>
36
+ args.nodeishFs.mkdir(makeAbsolute(path), options),
37
37
  writeFile: (path: string, data: string) => args.nodeishFs.writeFile(makeAbsolute(path), data),
38
+ stat: (path: string) => args.nodeishFs.stat(makeAbsolute(path)),
39
+ rm: (path: string) => args.nodeishFs.rm(makeAbsolute(path)),
40
+ rmdir: (path: string) => (args.nodeishFs as any).rmdir(makeAbsolute(path)),
38
41
  watch: (
39
42
  path: string,
40
43
  options: { signal: AbortSignal | undefined; recursive: boolean | undefined }
41
44
  ) => args.nodeishFs.watch(makeAbsolute(path), options),
45
+ // This might be surprising when symlinks were intended to be relative
46
+ symlink: (target: string, path: string) =>
47
+ args.nodeishFs.symlink(makeAbsolute(target), makeAbsolute(path)),
48
+ unlink: (path: string) => args.nodeishFs.unlink(makeAbsolute(path)),
49
+ readlink: (path: string) => args.nodeishFs.readlink(makeAbsolute(path)),
50
+ lstat: (path: string) => args.nodeishFs.lstat(makeAbsolute(path)),
42
51
  }
43
52
  }
@@ -1,4 +1,4 @@
1
- import type { NodeishFilesystemSubset } from "@inlang/plugin"
1
+ import type { NodeishFilesystem } from "@lix-js/fs"
2
2
 
3
3
  /**
4
4
  * Wraps the nodeish filesystem subset with a function that intercepts paths
@@ -7,9 +7,9 @@ import type { NodeishFilesystemSubset } from "@inlang/plugin"
7
7
  * The paths are resolved from the `projectPath` argument.
8
8
  */
9
9
  export const createNodeishFsWithWatcher = (args: {
10
- nodeishFs: NodeishFilesystemSubset
10
+ nodeishFs: NodeishFilesystem
11
11
  updateMessages: () => void
12
- }): NodeishFilesystemSubset => {
12
+ }): NodeishFilesystem => {
13
13
  const pathList: string[] = []
14
14
 
15
15
  const makeWatcher = (path: string) => {
@@ -50,9 +50,12 @@ export const createNodeishFsWithWatcher = (args: {
50
50
  // @ts-expect-error
51
51
  readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
52
52
  readFileAndExtractPath(path, options),
53
+ rm: args.nodeishFs.rm,
53
54
  readdir: args.nodeishFs.readdir,
54
55
  mkdir: args.nodeishFs.mkdir,
56
+ rmdir: (args.nodeishFs as any).rmdir,
55
57
  writeFile: args.nodeishFs.writeFile,
56
58
  watch: args.nodeishFs.watch,
59
+ stat: args.nodeishFs.stat,
57
60
  }
58
61
  }
package/src/errors.ts CHANGED
@@ -50,3 +50,23 @@ export class PluginLoadMessagesError extends Error {
50
50
  this.name = "PluginLoadMessagesError"
51
51
  }
52
52
  }
53
+
54
+ export class LoadMessageError extends Error {
55
+ constructor(options: { path: string; messageId: string; cause: ErrorOptions["cause"] }) {
56
+ super(
57
+ `An error occured when loading message ${options.messageId} from path ${options.path} caused by ${options.cause}.`,
58
+ options
59
+ )
60
+ this.name = "LoadMessageError"
61
+ }
62
+ }
63
+
64
+ export class SaveMessageError extends Error {
65
+ constructor(options: { path: string; messageId: string; cause: ErrorOptions["cause"] }) {
66
+ super(
67
+ `An error occured when loading message ${options.messageId} from path ${options.path} caused by ${options.cause}.`,
68
+ options
69
+ )
70
+ this.name = "SaveMessageError"
71
+ }
72
+ }
@@ -65,6 +65,7 @@ const mockPlugin: Plugin = {
65
65
  const exampleMessages: Message[] = [
66
66
  {
67
67
  id: "a",
68
+ alias: {},
68
69
  selectors: [],
69
70
  variants: [
70
71
  {
@@ -81,6 +82,48 @@ const exampleMessages: Message[] = [
81
82
  },
82
83
  {
83
84
  id: "b",
85
+ alias: {},
86
+ selectors: [],
87
+ variants: [
88
+ {
89
+ languageTag: "en",
90
+ match: [],
91
+ pattern: [
92
+ {
93
+ type: "Text",
94
+ value: "test",
95
+ },
96
+ ],
97
+ },
98
+ ],
99
+ },
100
+ ]
101
+
102
+ const exampleAliasedMessages: Message[] = [
103
+ {
104
+ id: "raw_tapir_pause_grateful",
105
+ alias: {
106
+ default: "a",
107
+ },
108
+ selectors: [],
109
+ variants: [
110
+ {
111
+ languageTag: "en",
112
+ match: [],
113
+ pattern: [
114
+ {
115
+ type: "Text",
116
+ value: "test",
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ },
122
+ {
123
+ id: "dizzy_halibut_dial_vaguely",
124
+ alias: {
125
+ default: "b",
126
+ },
84
127
  selectors: [],
85
128
  variants: [
86
129
  {
@@ -509,6 +552,7 @@ describe("functionality", () => {
509
552
  description: mockPlugin.description,
510
553
  displayName: mockPlugin.displayName,
511
554
  module: settings.modules[0],
555
+ settingsSchema: mockPlugin.settingsSchema,
512
556
  })
513
557
 
514
558
  expect(project.installed.messageLintRules()[0]).toEqual({
@@ -517,6 +561,7 @@ describe("functionality", () => {
517
561
  displayName: mockMessageLintRule.displayName,
518
562
  module: settings.modules[1],
519
563
  level: "warning",
564
+ settingsSchema: mockMessageLintRule.settingsSchema,
520
565
  })
521
566
  })
522
567
 
@@ -561,7 +606,7 @@ describe("functionality", () => {
561
606
  description: { en: "Mock plugin description" },
562
607
  displayName: { en: "Mock Plugin" },
563
608
 
564
- loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
609
+ loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
565
610
  saveMessages: () => undefined,
566
611
  }
567
612
  const repo = await mockRepo()
@@ -613,7 +658,7 @@ describe("functionality", () => {
613
658
  description: { en: "Mock plugin description" },
614
659
  displayName: { en: "Mock Plugin" },
615
660
 
616
- loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
661
+ loadMessages: () => [{ id: "some-message", alias: {}, selectors: [], variants: [] }],
617
662
  saveMessages: () => undefined,
618
663
  }
619
664
  const repo = await mockRepo()
@@ -698,6 +743,25 @@ describe("functionality", () => {
698
743
  })
699
744
  })
700
745
 
746
+ describe("messages with aliases", () => {
747
+ it("should return the messages", async () => {
748
+ const repo = await mockRepo()
749
+ const fs = repo.nodeishFs
750
+ await fs.mkdir("/user/project.inlang", { recursive: true })
751
+ await fs.writeFile(
752
+ "/user/project.inlang/settings.json",
753
+ JSON.stringify({ ...settings, experimental: { aliases: true } })
754
+ )
755
+ const project = await loadProject({
756
+ projectPath: "/user/project.inlang",
757
+ repo,
758
+ _import,
759
+ })
760
+
761
+ expect(Object.values(project.query.messages.getAll())).toEqual(exampleAliasedMessages)
762
+ })
763
+ })
764
+
701
765
  describe("query", () => {
702
766
  it("should call saveMessages() on updates", async () => {
703
767
  const repo = await mockRepo()
@@ -744,6 +808,7 @@ describe("functionality", () => {
744
808
  where: { id: "a" },
745
809
  data: {
746
810
  id: "a",
811
+ alias: {},
747
812
  selectors: [],
748
813
  variants: [
749
814
  {
@@ -774,6 +839,7 @@ describe("functionality", () => {
774
839
  where: { id: "b" },
775
840
  data: {
776
841
  id: "b",
842
+ alias: {},
777
843
  selectors: [],
778
844
  variants: [
779
845
  {
@@ -800,7 +866,8 @@ describe("functionality", () => {
800
866
  },
801
867
  })
802
868
 
803
- await new Promise((resolve) => setTimeout(resolve, 510))
869
+ // lets wait for the next tick
870
+ await new Promise((resolve) => setTimeout(resolve, 100))
804
871
 
805
872
  expect(mockSaveFn.mock.calls.length).toBe(1)
806
873
 
@@ -809,25 +876,26 @@ describe("functionality", () => {
809
876
  expect(Object.values(mockSaveFn.mock.calls[0][0].messages)).toStrictEqual([
810
877
  {
811
878
  id: "a",
879
+ alias: {},
812
880
  selectors: [],
813
881
  variants: [
814
882
  {
815
- languageTag: "en",
883
+ languageTag: "de",
816
884
  match: [],
817
885
  pattern: [
818
886
  {
819
887
  type: "Text",
820
- value: "a en",
888
+ value: "a de",
821
889
  },
822
890
  ],
823
891
  },
824
892
  {
825
- languageTag: "de",
893
+ languageTag: "en",
826
894
  match: [],
827
895
  pattern: [
828
896
  {
829
897
  type: "Text",
830
- value: "a de",
898
+ value: "a en",
831
899
  },
832
900
  ],
833
901
  },
@@ -835,25 +903,26 @@ describe("functionality", () => {
835
903
  },
836
904
  {
837
905
  id: "b",
906
+ alias: {},
838
907
  selectors: [],
839
908
  variants: [
840
909
  {
841
- languageTag: "en",
910
+ languageTag: "de",
842
911
  match: [],
843
912
  pattern: [
844
913
  {
845
914
  type: "Text",
846
- value: "b en",
915
+ value: "b de",
847
916
  },
848
917
  ],
849
918
  },
850
919
  {
851
- languageTag: "de",
920
+ languageTag: "en",
852
921
  match: [],
853
922
  pattern: [
854
923
  {
855
924
  type: "Text",
856
- value: "b de",
925
+ value: "b en",
857
926
  },
858
927
  ],
859
928
  },
@@ -926,6 +995,20 @@ describe("functionality", () => {
926
995
 
927
996
  expect(mockSaveFn.mock.calls.length).toBe(1)
928
997
  expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4)
998
+
999
+ project.query.messages.create({ data: createMessage("fifth", { en: "fifth message" }) })
1000
+
1001
+ await new Promise((resolve) => setTimeout(resolve, 510))
1002
+
1003
+ expect(mockSaveFn.mock.calls.length).toBe(2)
1004
+ expect(mockSaveFn.mock.calls[1][0].messages).toHaveLength(5)
1005
+
1006
+ project.query.messages.delete({ where: { id: "fourth" } })
1007
+
1008
+ await new Promise((resolve) => setTimeout(resolve, 510))
1009
+
1010
+ expect(mockSaveFn.mock.calls.length).toBe(3)
1011
+ expect(mockSaveFn.mock.calls[2][0].messages).toHaveLength(4)
929
1012
  })
930
1013
  })
931
1014
 
@@ -1040,17 +1123,28 @@ describe("functionality", () => {
1040
1123
  counter = counter + 1
1041
1124
  })
1042
1125
 
1126
+ // subscribe fires once
1043
1127
  expect(counter).toBe(1)
1044
1128
 
1045
- // change file
1129
+ // saving the file without changing should not trigger a message query
1046
1130
  await fs.writeFile("./messages.json", JSON.stringify(messages))
1047
- await new Promise((resolve) => setTimeout(resolve, 0))
1131
+ await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1132
+
1133
+ // we didn't change the message we write into message.json - shouldn't change the messages
1134
+ expect(counter).toBe(1)
1135
+
1136
+ // saving the file without changing should trigger a change
1137
+ messages.data[0]!.variants[0]!.pattern[0]!.value = "changed"
1138
+ await fs.writeFile("./messages.json", JSON.stringify(messages))
1139
+ await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1048
1140
 
1049
1141
  expect(counter).toBe(2)
1050
1142
 
1143
+ messages.data[0]!.variants[0]!.pattern[0]!.value = "changed3"
1144
+
1051
1145
  // change file
1052
1146
  await fs.writeFile("./messages.json", JSON.stringify(messages))
1053
- await new Promise((resolve) => setTimeout(resolve, 0))
1147
+ await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1054
1148
 
1055
1149
  expect(counter).toBe(3)
1056
1150
  })