@inlang/sdk 0.26.4 → 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"words.test.d.ts","sourceRoot":"","sources":["../../../src/storage/human-id/words.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { adjectives, animals, adverbs, verbs } from "./words.js";
|
|
3
|
+
const wordlists = [adjectives, animals, adverbs, verbs];
|
|
4
|
+
const allwords = [...adjectives, ...animals, ...adverbs, ...verbs];
|
|
5
|
+
describe("wordlists", () => {
|
|
6
|
+
it("should have 256 words", () => {
|
|
7
|
+
for (const wordlist of wordlists) {
|
|
8
|
+
expect(wordlist.length).toBe(256);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
it("words should be unique across lists", () => {
|
|
12
|
+
const unique = new Set(allwords);
|
|
13
|
+
expect(unique.size).toBe(256 * 4);
|
|
14
|
+
});
|
|
15
|
+
it("words should have < 10 characters", () => {
|
|
16
|
+
for (const word of allwords) {
|
|
17
|
+
expect(word.length).toBeLessThan(10);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
it("words should match /^[a-z]+$/", () => {
|
|
21
|
+
for (const word of allwords) {
|
|
22
|
+
expect(word.match(/^[a-z]+$/) !== null).toBe(true);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createMessage.d.ts","sourceRoot":"","sources":["../../src/test-utilities/createMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEvD,eAAO,MAAM,aAAa,OAAQ,MAAM,YAAY,OAAO,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC
|
|
1
|
+
{"version":3,"file":"createMessage.d.ts","sourceRoot":"","sources":["../../src/test-utilities/createMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEvD,eAAO,MAAM,aAAa,OAAQ,MAAM,YAAY,OAAO,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;;;;;;;;;;;;;;;CAkB/D,CAAA"}
|
|
@@ -3,8 +3,25 @@ import { createMessage } from "./createMessage.js";
|
|
|
3
3
|
test("should create a simple message", () => {
|
|
4
4
|
expect(createMessage("welcome", {
|
|
5
5
|
de: "Hallo inlang",
|
|
6
|
-
})).toMatchInlineSnapshot(
|
|
6
|
+
})).toMatchInlineSnapshot({
|
|
7
|
+
alias: {},
|
|
8
|
+
id: "welcome",
|
|
9
|
+
selectors: [],
|
|
10
|
+
variants: [
|
|
11
|
+
{
|
|
12
|
+
languageTag: "de",
|
|
13
|
+
match: [],
|
|
14
|
+
pattern: [
|
|
15
|
+
{
|
|
16
|
+
type: "Text",
|
|
17
|
+
value: "Hallo inlang",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
}, `
|
|
7
23
|
{
|
|
24
|
+
"alias": {},
|
|
8
25
|
"id": "welcome",
|
|
9
26
|
"selectors": [],
|
|
10
27
|
"variants": [
|
|
@@ -29,63 +46,61 @@ test("should create a message with pattern", () => {
|
|
|
29
46
|
{ type: "VariableReference", name: "name" },
|
|
30
47
|
{ type: "Text", value: '"' },
|
|
31
48
|
],
|
|
32
|
-
})).
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
`);
|
|
49
|
+
})).toStrictEqual({
|
|
50
|
+
alias: {},
|
|
51
|
+
id: "greeting",
|
|
52
|
+
selectors: [],
|
|
53
|
+
variants: [
|
|
54
|
+
{
|
|
55
|
+
languageTag: "en",
|
|
56
|
+
match: [],
|
|
57
|
+
pattern: [
|
|
58
|
+
{
|
|
59
|
+
type: "Text",
|
|
60
|
+
value: "Hi ",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "name",
|
|
64
|
+
type: "VariableReference",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "Text",
|
|
68
|
+
value: '"',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
});
|
|
58
74
|
});
|
|
59
75
|
test("should create a message with a pattern", () => {
|
|
60
76
|
expect(createMessage("welcome", {
|
|
61
77
|
en: "hello inlang",
|
|
62
78
|
de: [{ type: "Text", value: "Hallo inlang" }],
|
|
63
|
-
})).
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`);
|
|
79
|
+
})).toStrictEqual({
|
|
80
|
+
alias: {},
|
|
81
|
+
id: "welcome",
|
|
82
|
+
selectors: [],
|
|
83
|
+
variants: [
|
|
84
|
+
{
|
|
85
|
+
languageTag: "en",
|
|
86
|
+
match: [],
|
|
87
|
+
pattern: [
|
|
88
|
+
{
|
|
89
|
+
type: "Text",
|
|
90
|
+
value: "hello inlang",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
languageTag: "de",
|
|
96
|
+
match: [],
|
|
97
|
+
pattern: [
|
|
98
|
+
{
|
|
99
|
+
type: "Text",
|
|
100
|
+
value: "Hallo inlang",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
91
106
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inlang/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.27.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -21,23 +21,27 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@sinclair/typebox": "^0.31.17",
|
|
24
|
+
"debug": "^4.3.4",
|
|
24
25
|
"dedent": "1.5.1",
|
|
25
26
|
"deepmerge-ts": "^5.1.0",
|
|
27
|
+
"murmurhash3js": "^3.0.1",
|
|
26
28
|
"solid-js": "1.6.12",
|
|
27
29
|
"throttle-debounce": "^5.0.0",
|
|
28
30
|
"@inlang/json-types": "1.1.0",
|
|
31
|
+
"@inlang/message": "2.1.0",
|
|
32
|
+
"@inlang/message-lint-rule": "1.4.5",
|
|
29
33
|
"@inlang/language-tag": "1.5.1",
|
|
30
|
-
"@inlang/
|
|
31
|
-
"@inlang/
|
|
32
|
-
"@inlang/
|
|
33
|
-
"@inlang/plugin": "2.4.6",
|
|
34
|
-
"@inlang/project-settings": "2.2.3",
|
|
34
|
+
"@inlang/module": "1.2.8",
|
|
35
|
+
"@inlang/plugin": "2.4.8",
|
|
36
|
+
"@inlang/project-settings": "2.4.0",
|
|
35
37
|
"@inlang/result": "1.1.0",
|
|
36
38
|
"@inlang/translatable": "1.3.1",
|
|
37
|
-
"@lix-js/
|
|
38
|
-
"@lix-js/
|
|
39
|
+
"@lix-js/client": "0.9.0",
|
|
40
|
+
"@lix-js/fs": "0.6.0"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
43
|
+
"@types/debug": "^4.1.12",
|
|
44
|
+
"@types/murmurhash3js": "^3.0.7",
|
|
41
45
|
"@types/throttle-debounce": "5.0.0",
|
|
42
46
|
"@vitest/coverage-v8": "^0.33.0",
|
|
43
47
|
"jsdom": "22.1.0",
|
|
@@ -28,6 +28,11 @@ const config: ProjectSettings = {
|
|
|
28
28
|
},
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const configWithAliases: ProjectSettings = {
|
|
32
|
+
...config,
|
|
33
|
+
experimental: { aliases: true },
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
const mockPlugin: Plugin = {
|
|
32
37
|
id: "plugin.project.i18next",
|
|
33
38
|
description: { en: "Mock plugin description" },
|
|
@@ -41,6 +46,7 @@ const mockPlugin: Plugin = {
|
|
|
41
46
|
const exampleMessages: Message[] = [
|
|
42
47
|
{
|
|
43
48
|
id: "a",
|
|
49
|
+
alias: {},
|
|
44
50
|
selectors: [],
|
|
45
51
|
variants: [
|
|
46
52
|
{
|
|
@@ -57,6 +63,7 @@ const exampleMessages: Message[] = [
|
|
|
57
63
|
},
|
|
58
64
|
{
|
|
59
65
|
id: "b",
|
|
66
|
+
alias: {},
|
|
60
67
|
selectors: [],
|
|
61
68
|
variants: [
|
|
62
69
|
{
|
|
@@ -202,10 +209,10 @@ describe("messages", () => {
|
|
|
202
209
|
{ from }
|
|
203
210
|
)
|
|
204
211
|
|
|
205
|
-
let
|
|
212
|
+
let effectOnMessagesCounter = 0
|
|
206
213
|
createEffect(() => {
|
|
207
214
|
project.query.messages.getAll()
|
|
208
|
-
|
|
215
|
+
effectOnMessagesCounter += 1
|
|
209
216
|
})
|
|
210
217
|
|
|
211
218
|
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
|
|
@@ -215,11 +222,11 @@ describe("messages", () => {
|
|
|
215
222
|
// TODO: how can we await `setConfig` correctly
|
|
216
223
|
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
217
224
|
|
|
218
|
-
expect(
|
|
225
|
+
expect(effectOnMessagesCounter).toBe(1) // 2 times because effect creation + set
|
|
219
226
|
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
|
|
220
227
|
})
|
|
221
228
|
|
|
222
|
-
it("should react to
|
|
229
|
+
it("should react to message udpate", async () => {
|
|
223
230
|
const repo = await mockRepo()
|
|
224
231
|
const fs = repo.nodeishFs
|
|
225
232
|
await fs.mkdir("/user/project.inlang.inlang", { recursive: true })
|
|
@@ -268,6 +275,71 @@ describe("messages", () => {
|
|
|
268
275
|
},
|
|
269
276
|
})
|
|
270
277
|
|
|
278
|
+
it("should react to message udpate (with aliases)", async () => {
|
|
279
|
+
const repo = await mockRepo()
|
|
280
|
+
const fs = repo.nodeishFs
|
|
281
|
+
await fs.mkdir("/user/project.inlang.inlang", { recursive: true })
|
|
282
|
+
await fs.writeFile(
|
|
283
|
+
"/user/project.inlang.inlang/settings.json",
|
|
284
|
+
JSON.stringify(configWithAliases)
|
|
285
|
+
)
|
|
286
|
+
const project = solidAdapter(
|
|
287
|
+
await loadProject({
|
|
288
|
+
projectPath: "/user/project.inlang.inlang",
|
|
289
|
+
repo,
|
|
290
|
+
_import: $import,
|
|
291
|
+
}),
|
|
292
|
+
{ from }
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
let counter = 0
|
|
296
|
+
createEffect(() => {
|
|
297
|
+
project.query.messages.getAll()
|
|
298
|
+
counter += 1
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const messagesBefore = project.query.messages.getAll
|
|
302
|
+
expect(Object.values(messagesBefore()).length).toBe(2)
|
|
303
|
+
expect(
|
|
304
|
+
(
|
|
305
|
+
Object.values(messagesBefore())[0]?.variants.find(
|
|
306
|
+
(variant) => variant.languageTag === "en"
|
|
307
|
+
)?.pattern[0] as Text
|
|
308
|
+
).value
|
|
309
|
+
).toBe("test")
|
|
310
|
+
|
|
311
|
+
project.query.messages.update({
|
|
312
|
+
where: { id: "raw_tapir_pause_grateful" },
|
|
313
|
+
// TODO: use `createMessage` utility
|
|
314
|
+
data: {
|
|
315
|
+
...exampleMessages[0],
|
|
316
|
+
variants: [
|
|
317
|
+
{
|
|
318
|
+
languageTag: "en",
|
|
319
|
+
match: [],
|
|
320
|
+
pattern: [
|
|
321
|
+
{
|
|
322
|
+
type: "Text",
|
|
323
|
+
value: "test2",
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
expect(counter).toBe(2) // 2 times because effect creation + set
|
|
332
|
+
const messagesAfter = project.query.messages.getAll
|
|
333
|
+
expect(Object.values(messagesAfter()).length).toBe(2)
|
|
334
|
+
expect(
|
|
335
|
+
(
|
|
336
|
+
Object.values(messagesAfter())[0]?.variants.find(
|
|
337
|
+
(variant) => variant.languageTag === "en"
|
|
338
|
+
)?.pattern[0] as Text
|
|
339
|
+
).value
|
|
340
|
+
).toBe("test2")
|
|
341
|
+
})
|
|
342
|
+
|
|
271
343
|
expect(counter).toBe(2) // 2 times because effect creation + set
|
|
272
344
|
const messagesAfter = project.query.messages.getAll
|
|
273
345
|
expect(Object.values(messagesAfter()).length).toBe(2)
|
package/src/api.ts
CHANGED
|
@@ -72,6 +72,10 @@ export type MessageQueryApi = {
|
|
|
72
72
|
callback: (message: Message) => void
|
|
73
73
|
) => void
|
|
74
74
|
}
|
|
75
|
+
// use getByDefaultAlias() to resolve a message from its alias, not subscribable
|
|
76
|
+
getByDefaultAlias: ((alias: Message["alias"]["default"]) => Readonly<Message>) & {
|
|
77
|
+
subscribe: (alias: Message["alias"]["default"], callback: (message: Message) => void) => void
|
|
78
|
+
}
|
|
75
79
|
includedMessageIds: Subscribable<Message["id"][]>
|
|
76
80
|
/*
|
|
77
81
|
* getAll is depricated do not use it
|
|
@@ -10,8 +10,7 @@ import type { resolveModules } from "./resolve-modules/index.js"
|
|
|
10
10
|
import type { MessageLintReport, Message } from "./versionedInterfaces.js"
|
|
11
11
|
import { lintSingleMessage } from "./lint/index.js"
|
|
12
12
|
import { ReactiveMap } from "./reactivity/map.js"
|
|
13
|
-
import {
|
|
14
|
-
import { createEffect } from "./reactivity/solid.js"
|
|
13
|
+
import { createRoot, createEffect } from "./reactivity/solid.js"
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Creates a reactive query API for messages.
|
|
@@ -21,7 +20,6 @@ export function createMessageLintReportsQuery(
|
|
|
21
20
|
settings: () => ProjectSettings,
|
|
22
21
|
installedMessageLintRules: () => Array<InstalledMessageLintRule>,
|
|
23
22
|
resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined,
|
|
24
|
-
hasWatcher: boolean
|
|
25
23
|
): InlangProject["query"]["messageLintReports"] {
|
|
26
24
|
// @ts-expect-error
|
|
27
25
|
const index = new ReactiveMap<MessageLintReport["messageId"], MessageLintReport[]>()
|
|
@@ -41,41 +39,55 @@ export function createMessageLintReportsQuery(
|
|
|
41
39
|
|
|
42
40
|
const messages = messagesQuery.getAll() as Message[]
|
|
43
41
|
|
|
42
|
+
const trackedMessages: Map<string, () => void> = new Map()
|
|
43
|
+
|
|
44
44
|
createEffect(() => {
|
|
45
|
+
const currentMessageIds = new Set(messagesQuery.includedMessageIds())
|
|
46
|
+
|
|
47
|
+
const deletedTrackedMessages = [...trackedMessages].filter(
|
|
48
|
+
(tracked) => !currentMessageIds.has(tracked[0])
|
|
49
|
+
)
|
|
50
|
+
|
|
45
51
|
if (rulesArray) {
|
|
46
|
-
for (const messageId of
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
index.set(messageId, report.data)
|
|
52
|
+
for (const messageId of currentMessageIds) {
|
|
53
|
+
if (!trackedMessages.has(messageId)) {
|
|
54
|
+
createRoot((dispose) => {
|
|
55
|
+
createEffect(() => {
|
|
56
|
+
const message = messagesQuery.get({ where: { id: messageId } })
|
|
57
|
+
if (!message) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if (!trackedMessages?.has(messageId)) {
|
|
61
|
+
// initial effect execution - add dispose function
|
|
62
|
+
trackedMessages?.set(messageId, dispose)
|
|
58
63
|
}
|
|
64
|
+
|
|
65
|
+
lintSingleMessage({
|
|
66
|
+
rules: rulesArray,
|
|
67
|
+
settings: settingsObject(),
|
|
68
|
+
messages: messages,
|
|
69
|
+
message: message,
|
|
70
|
+
}).then((report) => {
|
|
71
|
+
if (report.errors.length === 0 && index.get(messageId) !== report.data) {
|
|
72
|
+
index.set(messageId, report.data)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
59
75
|
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{ atBegin: false }
|
|
76
|
-
)(message)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const deletedMessage of deletedTrackedMessages) {
|
|
81
|
+
const deletedMessageId = deletedMessage[0]
|
|
82
|
+
|
|
83
|
+
// call dispose to cleanup the effect
|
|
84
|
+
const messageEffectDisposeFunction = trackedMessages.get(deletedMessageId)
|
|
85
|
+
if (messageEffectDisposeFunction) {
|
|
86
|
+
messageEffectDisposeFunction()
|
|
87
|
+
trackedMessages.delete(deletedMessageId)
|
|
88
|
+
// remove lint report result
|
|
89
|
+
index.delete(deletedMessageId)
|
|
90
|
+
}
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
})
|
|
@@ -6,6 +6,7 @@ import type { Message, Text } from "@inlang/message"
|
|
|
6
6
|
import { createMessage } from "./test-utilities/createMessage.js"
|
|
7
7
|
|
|
8
8
|
const createChangeListener = async (cb: () => void) => createEffect(cb)
|
|
9
|
+
const nextTick = () => new Promise((resolve) => setTimeout(resolve, 0))
|
|
9
10
|
|
|
10
11
|
describe("create", () => {
|
|
11
12
|
it("should create a message", () => {
|
|
@@ -19,6 +20,19 @@ describe("create", () => {
|
|
|
19
20
|
expect(created).toBe(true)
|
|
20
21
|
})
|
|
21
22
|
|
|
23
|
+
it("query.getByDefaultAlias should return a message with a default alias", () => {
|
|
24
|
+
const query = createMessagesQuery(() => [])
|
|
25
|
+
expect(query.get({ where: { id: "first-message" } })).toBeUndefined()
|
|
26
|
+
|
|
27
|
+
const mockMessage = createMessage("first-message", { en: "Hello World" })
|
|
28
|
+
mockMessage.alias = { default: "first-message-alias" }
|
|
29
|
+
const created = query.create({ data: mockMessage })
|
|
30
|
+
|
|
31
|
+
expect(query.get({ where: { id: "first-message" } })).toEqual(mockMessage)
|
|
32
|
+
expect(query.getByDefaultAlias("first-message-alias")).toEqual(mockMessage)
|
|
33
|
+
expect(created).toBe(true)
|
|
34
|
+
})
|
|
35
|
+
|
|
22
36
|
it("should return false if message with id already exists", () => {
|
|
23
37
|
const query = createMessagesQuery(() => [createMessage("first-message", { en: "Hello World" })])
|
|
24
38
|
expect(query.get({ where: { id: "first-message" } })).toBeDefined()
|
|
@@ -266,8 +280,46 @@ describe("reactivity", () => {
|
|
|
266
280
|
})
|
|
267
281
|
})
|
|
268
282
|
|
|
269
|
-
describe
|
|
270
|
-
|
|
283
|
+
describe("subscribe", () => {
|
|
284
|
+
describe("get", () => {
|
|
285
|
+
it("should subscribe to `create`", async () => {
|
|
286
|
+
await createRoot(async () => {
|
|
287
|
+
const query = createMessagesQuery(() => [])
|
|
288
|
+
|
|
289
|
+
// eslint-disable-next-line unicorn/no-null
|
|
290
|
+
let message: Message | undefined | null = null
|
|
291
|
+
query.get.subscribe({ where: { id: "1" } }, (v) => {
|
|
292
|
+
void (message = v)
|
|
293
|
+
})
|
|
294
|
+
await nextTick()
|
|
295
|
+
expect(message).toBeUndefined()
|
|
296
|
+
|
|
297
|
+
query.create({ data: createMessage("1", { en: "before" }) })
|
|
298
|
+
expect(message).toBeDefined()
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
describe("getByDefaultAlias", () => {
|
|
303
|
+
it("should subscribe to `create`", async () => {
|
|
304
|
+
await createRoot(async () => {
|
|
305
|
+
const query = createMessagesQuery(() => [])
|
|
306
|
+
|
|
307
|
+
// eslint-disable-next-line unicorn/no-null
|
|
308
|
+
let message: Message | undefined | null = null
|
|
309
|
+
query.getByDefaultAlias.subscribe("message-alias", (v) => {
|
|
310
|
+
void (message = v)
|
|
311
|
+
})
|
|
312
|
+
await nextTick() // required for effect to run on reactive map
|
|
313
|
+
expect(message).toBeUndefined()
|
|
314
|
+
|
|
315
|
+
const mockMessage = createMessage("1", { en: "before" })
|
|
316
|
+
mockMessage.alias = { default: "message-alias" }
|
|
317
|
+
query.create({ data: mockMessage })
|
|
318
|
+
|
|
319
|
+
expect(message).toBeDefined()
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
})
|
|
271
323
|
})
|
|
272
324
|
|
|
273
325
|
describe("getAll", () => {
|
|
@@ -13,19 +13,35 @@ export function createMessagesQuery(
|
|
|
13
13
|
// @ts-expect-error
|
|
14
14
|
const index = new ReactiveMap<string, Message>()
|
|
15
15
|
|
|
16
|
+
// Map default alias to message
|
|
17
|
+
// Assumes that aliases are only created and deleted, not updated
|
|
18
|
+
// TODO #2346 - handle updates to aliases
|
|
19
|
+
// TODO #2346 - refine to hold messageId[], if default alias is not unique
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
const defaultAliasIndex = new ReactiveMap<string, Message>()
|
|
22
|
+
|
|
16
23
|
createEffect(() => {
|
|
17
24
|
index.clear()
|
|
18
25
|
for (const message of structuredClone(messages())) {
|
|
19
26
|
index.set(message.id, message)
|
|
27
|
+
if ("default" in message.alias) {
|
|
28
|
+
defaultAliasIndex.set(message.alias.default, message)
|
|
29
|
+
}
|
|
20
30
|
}
|
|
21
31
|
})
|
|
22
32
|
|
|
23
33
|
const get = (args: Parameters<MessageQueryApi["get"]>[0]) => index.get(args.where.id)
|
|
24
34
|
|
|
35
|
+
const getByDefaultAlias = (alias: Parameters<MessageQueryApi["getByDefaultAlias"]>[0]) =>
|
|
36
|
+
defaultAliasIndex.get(alias)
|
|
37
|
+
|
|
25
38
|
return {
|
|
26
39
|
create: ({ data }): boolean => {
|
|
27
40
|
if (index.has(data.id)) return false
|
|
28
41
|
index.set(data.id, data)
|
|
42
|
+
if ("default" in data.alias) {
|
|
43
|
+
defaultAliasIndex.set(data.alias.default, data)
|
|
44
|
+
}
|
|
29
45
|
return true
|
|
30
46
|
},
|
|
31
47
|
get: Object.assign(get, {
|
|
@@ -34,6 +50,12 @@ export function createMessagesQuery(
|
|
|
34
50
|
callback: Parameters<MessageQueryApi["get"]["subscribe"]>[1]
|
|
35
51
|
) => createSubscribable(() => get(args)).subscribe(callback),
|
|
36
52
|
}) as any,
|
|
53
|
+
getByDefaultAlias: Object.assign(getByDefaultAlias, {
|
|
54
|
+
subscribe: (
|
|
55
|
+
alias: Parameters<MessageQueryApi["getByDefaultAlias"]["subscribe"]>[0],
|
|
56
|
+
callback: Parameters<MessageQueryApi["getByDefaultAlias"]["subscribe"]>[1]
|
|
57
|
+
) => createSubscribable(() => getByDefaultAlias(alias)).subscribe(callback),
|
|
58
|
+
}) as any,
|
|
37
59
|
includedMessageIds: createSubscribable(() => {
|
|
38
60
|
return [...index.keys()]
|
|
39
61
|
}),
|
|
@@ -50,13 +72,20 @@ export function createMessagesQuery(
|
|
|
50
72
|
const message = index.get(where.id)
|
|
51
73
|
if (message === undefined) {
|
|
52
74
|
index.set(where.id, data)
|
|
75
|
+
if ("default" in data.alias) {
|
|
76
|
+
defaultAliasIndex.set(data.alias.default, data)
|
|
77
|
+
}
|
|
53
78
|
} else {
|
|
54
79
|
index.set(where.id, { ...message, ...data })
|
|
55
80
|
}
|
|
56
81
|
return true
|
|
57
82
|
},
|
|
58
83
|
delete: ({ where }): boolean => {
|
|
59
|
-
|
|
84
|
+
const message = index.get(where.id)
|
|
85
|
+
if (message === undefined) return false
|
|
86
|
+
if ("default" in message.alias) {
|
|
87
|
+
defaultAliasIndex.delete(message.alias.default)
|
|
88
|
+
}
|
|
60
89
|
index.delete(where.id)
|
|
61
90
|
return true
|
|
62
91
|
},
|
|
@@ -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
|
-
|
|
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
|