@inlang/sdk 0.34.9 → 0.34.10

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 (45) hide show
  1. package/dist/adapter/solidAdapter.js +1 -1
  2. package/dist/adapter/solidAdapter.test.js +60 -23
  3. package/dist/api.d.ts +15 -7
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
  6. package/dist/createMessageLintReportsQuery.js +158 -64
  7. package/dist/createMessagesQuery.d.ts.map +1 -1
  8. package/dist/createMessagesQuery.js +29 -11
  9. package/dist/lint/message/lintSingleMessage.d.ts.map +1 -1
  10. package/dist/lint/message/lintSingleMessage.js +3 -1
  11. package/dist/loadProject.d.ts.map +1 -1
  12. package/dist/loadProject.js +6 -2
  13. package/dist/loadProject.test.js +38 -25
  14. package/dist/reactivity/solid.d.ts +2 -1
  15. package/dist/reactivity/solid.d.ts.map +1 -1
  16. package/dist/reactivity/solid.js +3 -2
  17. package/dist/reactivity/solid.test.js +38 -1
  18. package/dist/v2/createMessageBundle.d.ts +25 -0
  19. package/dist/v2/createMessageBundle.d.ts.map +1 -0
  20. package/dist/v2/createMessageBundle.js +36 -0
  21. package/dist/v2/createMessageBundle.test.d.ts +2 -0
  22. package/dist/v2/createMessageBundle.test.d.ts.map +1 -0
  23. package/dist/v2/createMessageBundle.test.js +92 -0
  24. package/dist/v2/mocks/plural/bundle.d.ts +3 -0
  25. package/dist/v2/mocks/plural/bundle.d.ts.map +1 -0
  26. package/dist/v2/mocks/plural/bundle.js +140 -0
  27. package/dist/v2/mocks/plural/bundle.test.d.ts +2 -0
  28. package/dist/v2/mocks/plural/bundle.test.d.ts.map +1 -0
  29. package/dist/v2/mocks/plural/bundle.test.js +15 -0
  30. package/package.json +5 -5
  31. package/src/adapter/solidAdapter.test.ts +78 -33
  32. package/src/adapter/solidAdapter.ts +1 -1
  33. package/src/api.ts +14 -7
  34. package/src/createMessageLintReportsQuery.ts +183 -68
  35. package/src/createMessagesQuery.ts +32 -11
  36. package/src/createNodeishFsWithWatcher.ts +1 -1
  37. package/src/lint/message/lintSingleMessage.ts +4 -1
  38. package/src/loadProject.test.ts +45 -24
  39. package/src/loadProject.ts +7 -2
  40. package/src/reactivity/solid.test.ts +54 -1
  41. package/src/reactivity/solid.ts +3 -0
  42. package/src/v2/createMessageBundle.test.ts +95 -0
  43. package/src/v2/createMessageBundle.ts +43 -0
  44. package/src/v2/mocks/plural/bundle.test.ts +18 -0
  45. package/src/v2/mocks/plural/bundle.ts +142 -0
@@ -635,8 +635,10 @@ describe("functionality", () => {
635
635
 
636
636
  await new Promise((resolve) => setTimeout(resolve, 510))
637
637
 
638
- expect(await project.query.messageLintReports.getAll()).toHaveLength(1)
639
- expect((await project.query.messageLintReports.getAll())?.[0]?.ruleId).toBe(_mockLintRule.id)
638
+ expect(await project.query.messageLintReports.getAll.settled()).toHaveLength(1)
639
+ expect((await project.query.messageLintReports.getAll.settled())?.[0]?.ruleId).toBe(
640
+ _mockLintRule.id
641
+ )
640
642
  expect(project.installed.messageLintRules()).toHaveLength(1)
641
643
  })
642
644
 
@@ -1014,25 +1016,6 @@ describe("functionality", () => {
1014
1016
  })
1015
1017
 
1016
1018
  describe("lint", () => {
1017
- it.todo("should throw if lint reports are not initialized yet", async () => {
1018
- const repo = await mockRepo()
1019
- const fs = repo.nodeishFs
1020
- await fs.mkdir("/user/project", { recursive: true })
1021
- await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
1022
- const project = await loadProject({
1023
- projectPath: "/user/project/project.inlang.json",
1024
- repo,
1025
- _import,
1026
- })
1027
- // TODO: test with real lint rules
1028
- try {
1029
- const r = await project.query.messageLintReports.getAll()
1030
- expect(r).toEqual(undefined)
1031
- throw new Error("Should not reach this")
1032
- } catch (e) {
1033
- expect((e as Error).message).toBe("lint not initialized yet")
1034
- }
1035
- })
1036
1019
  it("should return the message lint reports", async () => {
1037
1020
  const settings: ProjectSettings = {
1038
1021
  sourceLanguageTag: "en",
@@ -1051,7 +1034,7 @@ describe("functionality", () => {
1051
1034
  }),
1052
1035
  })
1053
1036
  // TODO: test with real lint rules
1054
- const r = await project.query.messageLintReports.getAll()
1037
+ const r = await project.query.messageLintReports.getAll.settled()
1055
1038
  expect(r).toEqual([])
1056
1039
  })
1057
1040
  })
@@ -1083,6 +1066,23 @@ describe("functionality", () => {
1083
1066
  ],
1084
1067
  }
1085
1068
 
1069
+ const newMessage = {
1070
+ id: "test2",
1071
+ selectors: [],
1072
+ variants: [
1073
+ {
1074
+ match: [],
1075
+ languageTag: "en",
1076
+ pattern: [
1077
+ {
1078
+ type: "Text",
1079
+ value: "test",
1080
+ },
1081
+ ],
1082
+ },
1083
+ ],
1084
+ }
1085
+
1086
1086
  await fs.writeFile("./messages.json", JSON.stringify(messages))
1087
1087
 
1088
1088
  const getMessages = async (customFs: NodeishFilesystemSubset) => {
@@ -1121,9 +1121,11 @@ describe("functionality", () => {
1121
1121
  })
1122
1122
 
1123
1123
  let counter = 0
1124
+ let messageCount = 0
1124
1125
 
1125
- project.query.messages.getAll.subscribe(() => {
1126
+ project.query.messages.getAll.subscribe((messages) => {
1126
1127
  counter = counter + 1
1128
+ messageCount = messages.length
1127
1129
  })
1128
1130
 
1129
1131
  // subscribe fires once
@@ -1135,6 +1137,7 @@ describe("functionality", () => {
1135
1137
 
1136
1138
  // we didn't change the message we write into message.json - shouldn't change the messages
1137
1139
  expect(counter).toBe(1)
1140
+ expect(messageCount).toBe(1)
1138
1141
 
1139
1142
  // saving the file without changing should trigger a change
1140
1143
  messages.data[0]!.variants[0]!.pattern[0]!.value = "changed"
@@ -1142,14 +1145,32 @@ describe("functionality", () => {
1142
1145
  await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1143
1146
 
1144
1147
  expect(counter).toBe(2)
1148
+ expect(messageCount).toBe(1)
1145
1149
 
1146
1150
  messages.data[0]!.variants[0]!.pattern[0]!.value = "changed3"
1147
1151
 
1148
- // change file
1152
+ // change file - update message
1149
1153
  await fs.writeFile("./messages.json", JSON.stringify(messages))
1150
1154
  await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1151
1155
 
1152
1156
  expect(counter).toBe(3)
1157
+ expect(messageCount).toBe(1)
1158
+
1159
+ // change file - add a message
1160
+ messages.data.push(newMessage)
1161
+ await fs.writeFile("./messages.json", JSON.stringify(messages))
1162
+ await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1163
+
1164
+ expect(counter).toBe(4)
1165
+ expect(messageCount).toBe(2)
1166
+
1167
+ // change file - remove a message
1168
+ messages.data.pop()
1169
+ await fs.writeFile("./messages.json", JSON.stringify(messages))
1170
+ await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
1171
+
1172
+ expect(counter).toBe(5)
1173
+ expect(messageCount).toBe(1)
1153
1174
  })
1154
1175
  })
1155
1176
  })
@@ -12,7 +12,7 @@ import {
12
12
  ProjectSettingsFileNotFoundError,
13
13
  ProjectSettingsInvalidError,
14
14
  } from "./errors.js"
15
- import { createRoot, createSignal, createEffect } from "./reactivity/solid.js"
15
+ import { createRoot, createSignal, createEffect, batch } from "./reactivity/solid.js"
16
16
  import { createMessagesQuery } from "./createMessagesQuery.js"
17
17
  import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.js"
18
18
  import { ProjectSettings, type NodeishFilesystemSubset } from "./versionedInterfaces.js"
@@ -111,7 +111,12 @@ export async function loadProject(args: {
111
111
  pathPattern: projectPath + "/messages.json",
112
112
  }
113
113
  }
114
- _setSettings(validatedSettings)
114
+
115
+ batch(() => {
116
+ // reset the resolved modules first - since they are no longer valid at that point
117
+ setResolvedModules(undefined)
118
+ _setSettings(validatedSettings)
119
+ })
115
120
 
116
121
  writeSettingsToDisk(validatedSettings)
117
122
  return { data: undefined }
@@ -1,5 +1,13 @@
1
1
  import { describe, it, expect } from "vitest"
2
- import { createRoot, createSignal, createEffect, createMemo, createResource } from "./solid.js"
2
+ import {
3
+ createRoot,
4
+ createSignal,
5
+ createEffect,
6
+ createMemo,
7
+ createResource,
8
+ untrack,
9
+ } from "./solid.js"
10
+ import { ReactiveMap } from "./map.js"
3
11
 
4
12
  function sleep(ms: number) {
5
13
  return new Promise((resolve) => setTimeout(resolve, ms))
@@ -174,3 +182,48 @@ describe("solid", () => {
174
182
  expect(memo()).toBe("memo = 2000")
175
183
  })
176
184
  })
185
+
186
+ describe("solid", () => {
187
+ it("solid reactive map allows to use untrack", () => {
188
+ // @ts-expect-error -- ReactiveMap seem to have problems type arguments here
189
+ const reactiveMap = new ReactiveMap<string, any>()
190
+ const [plainSignal, setPlainSignal] = createSignal(0)
191
+
192
+ let shouldTriggerOnBoth = -1
193
+ createEffect(() => {
194
+ shouldTriggerOnBoth++
195
+ reactiveMap.values()
196
+ plainSignal()
197
+ })
198
+
199
+ let shouldTriggerOnReactiveMapOnly = -1
200
+ createEffect(() => {
201
+ shouldTriggerOnReactiveMapOnly++
202
+ reactiveMap.values()
203
+ untrack(() => plainSignal())
204
+ })
205
+
206
+ let shouldTriggerOnPlainSignalOnly = -1
207
+ createEffect(() => {
208
+ shouldTriggerOnPlainSignalOnly++
209
+ untrack(() => reactiveMap.values())
210
+ plainSignal()
211
+ })
212
+
213
+ expect(shouldTriggerOnBoth).toBe(0)
214
+ expect(shouldTriggerOnReactiveMapOnly).toBe(0)
215
+ expect(shouldTriggerOnPlainSignalOnly).toBe(0)
216
+
217
+ setPlainSignal(1)
218
+
219
+ expect(shouldTriggerOnBoth).toBe(1)
220
+ expect(shouldTriggerOnReactiveMapOnly).toBe(0)
221
+ expect(shouldTriggerOnPlainSignalOnly).toBe(1)
222
+
223
+ reactiveMap.set("a", "a")
224
+
225
+ expect(shouldTriggerOnBoth).toBe(2)
226
+ expect(shouldTriggerOnReactiveMapOnly).toBe(1)
227
+ expect(shouldTriggerOnPlainSignalOnly).toBe(1)
228
+ })
229
+ })
@@ -4,6 +4,7 @@ import {
4
4
  createRoot as _createRoot,
5
5
  createEffect as _createEffect,
6
6
  createResource as _createResource,
7
+ untrack as _untrack,
7
8
  observable as _observable,
8
9
  batch as _batch,
9
10
  from as _from,
@@ -23,6 +24,7 @@ const from = _from as typeof import("solid-js")["from"]
23
24
  const batch = _batch as typeof import("solid-js")["batch"]
24
25
  const getListener = _getListener as typeof import("solid-js")["getListener"]
25
26
  const onCleanup = _onCleanup as typeof import("solid-js")["onCleanup"]
27
+ const untrack = _untrack as typeof import("solid-js")["untrack"]
26
28
 
27
29
  export {
28
30
  createSignal,
@@ -35,5 +37,6 @@ export {
35
37
  batch,
36
38
  getListener,
37
39
  onCleanup,
40
+ untrack,
38
41
  DEV,
39
42
  }
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import { createMessageBundle, createMessage } from "./createMessageBundle.js"
3
+ import { MessageBundle } from "./types.js"
4
+ import { Value } from "@sinclair/typebox/value"
5
+
6
+ describe("createMessageBundle", () => {
7
+ it("creates a bundle with no messages", () => {
8
+ const bundle: unknown = createMessageBundle({ id: "no_messages", messages: [] })
9
+ expect(Value.Check(MessageBundle, bundle)).toBe(true)
10
+ expect(bundle).toEqual({
11
+ id: "no_messages",
12
+ alias: {},
13
+ messages: [],
14
+ } satisfies MessageBundle)
15
+ })
16
+
17
+ it("creates a bundle with a single text-only message", () => {
18
+ const bundle: unknown = createMessageBundle({
19
+ id: "hello_world",
20
+ messages: [createMessage({ locale: "en", text: "Hello World!" })],
21
+ })
22
+ expect(Value.Check(MessageBundle, bundle)).toBe(true)
23
+ expect(bundle).toEqual({
24
+ id: "hello_world",
25
+ alias: {},
26
+ messages: [
27
+ {
28
+ locale: "en",
29
+ declarations: [],
30
+ selectors: [],
31
+ variants: [
32
+ {
33
+ match: [],
34
+ pattern: [
35
+ {
36
+ type: "text",
37
+ value: "Hello World!",
38
+ },
39
+ ],
40
+ },
41
+ ],
42
+ },
43
+ ],
44
+ } satisfies MessageBundle)
45
+ })
46
+
47
+ it("creates a bundle with multiple text-only messages", () => {
48
+ const bundle: unknown = createMessageBundle({
49
+ id: "hello_world_2",
50
+ messages: [
51
+ createMessage({ locale: "en", text: "Hello World!" }),
52
+ createMessage({ locale: "de", text: "Hallo Welt!" }),
53
+ ],
54
+ })
55
+ expect(Value.Check(MessageBundle, bundle)).toBe(true)
56
+ expect(bundle).toEqual({
57
+ id: "hello_world_2",
58
+ alias: {},
59
+ messages: [
60
+ {
61
+ locale: "en",
62
+ declarations: [],
63
+ selectors: [],
64
+ variants: [
65
+ {
66
+ match: [],
67
+ pattern: [
68
+ {
69
+ type: "text",
70
+ value: "Hello World!",
71
+ },
72
+ ],
73
+ },
74
+ ],
75
+ },
76
+ {
77
+ locale: "de",
78
+ declarations: [],
79
+ selectors: [],
80
+ variants: [
81
+ {
82
+ match: [],
83
+ pattern: [
84
+ {
85
+ type: "text",
86
+ value: "Hallo Welt!",
87
+ },
88
+ ],
89
+ },
90
+ ],
91
+ },
92
+ ],
93
+ } satisfies MessageBundle)
94
+ })
95
+ })
@@ -0,0 +1,43 @@
1
+ import { LanguageTag, MessageBundle, Message, Text } from "./types.js"
2
+
3
+ /**
4
+ * create v2 MessageBundle
5
+ * @example createMessageBundle({
6
+ * id: "greeting",
7
+ * messages: [
8
+ * createMessage({locale: "en", text: "Hello world!"})
9
+ * createMessage({locale: "de", text: "Hallo Welt!"})
10
+ * ]
11
+ * })
12
+ */
13
+ export function createMessageBundle(args: {
14
+ id: string
15
+ messages: Message[]
16
+ alias?: MessageBundle["alias"]
17
+ }): MessageBundle {
18
+ return {
19
+ id: args.id,
20
+ alias: args.alias ?? {},
21
+ messages: args.messages,
22
+ }
23
+ }
24
+
25
+ /**
26
+ * create v2 Messsage AST with text-only pattern
27
+ * @example createMessage({locale: "en", text: "Hello world"})
28
+ */
29
+ export function createMessage(args: { locale: LanguageTag; text: string }): Message {
30
+ return {
31
+ locale: args.locale,
32
+ declarations: [],
33
+ selectors: [],
34
+ variants: [{ match: [], pattern: [toTextElement(args.text ?? "")] }],
35
+ }
36
+ }
37
+
38
+ function toTextElement(text: string): Text {
39
+ return {
40
+ type: "text",
41
+ value: text,
42
+ }
43
+ }
@@ -0,0 +1,18 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ import { describe, it, expect } from "vitest"
4
+ import { bundle } from "./bundle.js"
5
+ import { MessageBundle } from "../../types.js"
6
+ import { Value } from "@sinclair/typebox/value"
7
+
8
+ describe("mock plural messageBundle", () => {
9
+ it("is valid", () => {
10
+ const messageBundle: unknown = bundle
11
+ expect(Value.Check(MessageBundle, messageBundle)).toBe(true)
12
+
13
+ expect(bundle.messages.length).toBe(2)
14
+ expect(bundle.messages[0]!.declarations.length).toBe(1)
15
+ expect(bundle.messages[0]!.selectors.length).toBe(1)
16
+ expect(bundle.messages[0]!.variants.length).toBe(3)
17
+ })
18
+ })
@@ -0,0 +1,142 @@
1
+ import type { MessageBundle } from "../../types.js"
2
+
3
+ export const bundle: MessageBundle = {
4
+ id: "mock_bundle_human_id",
5
+ alias: {
6
+ default: "mock_bundle_alias",
7
+ },
8
+ messages: [
9
+ {
10
+ locale: "de",
11
+ declarations: [
12
+ {
13
+ type: "input",
14
+ name: "numProducts",
15
+ value: {
16
+ type: "expression",
17
+ arg: {
18
+ type: "variable",
19
+ name: "numProducts",
20
+ },
21
+ },
22
+ },
23
+ ],
24
+ selectors: [
25
+ {
26
+ type: "expression",
27
+ arg: {
28
+ type: "variable",
29
+ name: "numProducts",
30
+ },
31
+ annotation: {
32
+ type: "function",
33
+ name: "plural",
34
+ options: [],
35
+ },
36
+ },
37
+ ],
38
+ variants: [
39
+ {
40
+ match: ["zero"],
41
+ pattern: [
42
+ {
43
+ type: "text",
44
+ value: "Keine Produkte",
45
+ },
46
+ ],
47
+ },
48
+ {
49
+ match: ["one"],
50
+ pattern: [
51
+ {
52
+ type: "text",
53
+ value: "Ein Produkt",
54
+ },
55
+ ],
56
+ },
57
+ {
58
+ match: ["other"],
59
+ pattern: [
60
+ {
61
+ type: "expression",
62
+ arg: {
63
+ type: "variable",
64
+ name: "numProducts",
65
+ },
66
+ },
67
+ {
68
+ type: "text",
69
+ value: " Produkte",
70
+ },
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ {
76
+ locale: "en",
77
+ declarations: [
78
+ {
79
+ type: "input",
80
+ name: "numProducts",
81
+ value: {
82
+ type: "expression",
83
+ arg: {
84
+ type: "variable",
85
+ name: "numProducts",
86
+ },
87
+ },
88
+ },
89
+ ],
90
+ selectors: [
91
+ {
92
+ type: "expression",
93
+ arg: {
94
+ type: "variable",
95
+ name: "numProducts",
96
+ },
97
+ annotation: {
98
+ type: "function",
99
+ name: "plural",
100
+ options: [],
101
+ },
102
+ },
103
+ ],
104
+ variants: [
105
+ {
106
+ match: ["zero"],
107
+ pattern: [
108
+ {
109
+ type: "text",
110
+ value: "No Products",
111
+ },
112
+ ],
113
+ },
114
+ {
115
+ match: ["one"],
116
+ pattern: [
117
+ {
118
+ type: "text",
119
+ value: "A product",
120
+ },
121
+ ],
122
+ },
123
+ {
124
+ match: ["other"],
125
+ pattern: [
126
+ {
127
+ type: "expression",
128
+ arg: {
129
+ type: "variable",
130
+ name: "numProducts",
131
+ },
132
+ },
133
+ {
134
+ type: "text",
135
+ value: " products",
136
+ },
137
+ ],
138
+ },
139
+ ],
140
+ },
141
+ ],
142
+ }