@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.
- package/dist/adapter/solidAdapter.js +1 -1
- package/dist/adapter/solidAdapter.test.js +60 -23
- package/dist/api.d.ts +15 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.js +158 -64
- package/dist/createMessagesQuery.d.ts.map +1 -1
- package/dist/createMessagesQuery.js +29 -11
- package/dist/lint/message/lintSingleMessage.d.ts.map +1 -1
- package/dist/lint/message/lintSingleMessage.js +3 -1
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +6 -2
- package/dist/loadProject.test.js +38 -25
- package/dist/reactivity/solid.d.ts +2 -1
- package/dist/reactivity/solid.d.ts.map +1 -1
- package/dist/reactivity/solid.js +3 -2
- package/dist/reactivity/solid.test.js +38 -1
- package/dist/v2/createMessageBundle.d.ts +25 -0
- package/dist/v2/createMessageBundle.d.ts.map +1 -0
- package/dist/v2/createMessageBundle.js +36 -0
- package/dist/v2/createMessageBundle.test.d.ts +2 -0
- package/dist/v2/createMessageBundle.test.d.ts.map +1 -0
- package/dist/v2/createMessageBundle.test.js +92 -0
- package/dist/v2/mocks/plural/bundle.d.ts +3 -0
- package/dist/v2/mocks/plural/bundle.d.ts.map +1 -0
- package/dist/v2/mocks/plural/bundle.js +140 -0
- package/dist/v2/mocks/plural/bundle.test.d.ts +2 -0
- package/dist/v2/mocks/plural/bundle.test.d.ts.map +1 -0
- package/dist/v2/mocks/plural/bundle.test.js +15 -0
- package/package.json +5 -5
- package/src/adapter/solidAdapter.test.ts +78 -33
- package/src/adapter/solidAdapter.ts +1 -1
- package/src/api.ts +14 -7
- package/src/createMessageLintReportsQuery.ts +183 -68
- package/src/createMessagesQuery.ts +32 -11
- package/src/createNodeishFsWithWatcher.ts +1 -1
- package/src/lint/message/lintSingleMessage.ts +4 -1
- package/src/loadProject.test.ts +45 -24
- package/src/loadProject.ts +7 -2
- package/src/reactivity/solid.test.ts +54 -1
- package/src/reactivity/solid.ts +3 -0
- package/src/v2/createMessageBundle.test.ts +95 -0
- package/src/v2/createMessageBundle.ts +43 -0
- package/src/v2/mocks/plural/bundle.test.ts +18 -0
- package/src/v2/mocks/plural/bundle.ts +142 -0
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
import { describe, it, expect } from "vitest"
|
|
3
3
|
import type { ImportFunction } from "../resolve-modules/index.js"
|
|
4
|
-
import {
|
|
5
|
-
createResource,
|
|
6
|
-
createSignal,
|
|
7
|
-
createEffect,
|
|
8
|
-
from,
|
|
9
|
-
createRoot,
|
|
10
|
-
} from "../reactivity/solid.js"
|
|
4
|
+
import { createEffect, from, createRoot } from "../reactivity/solid.js"
|
|
11
5
|
import { solidAdapter } from "./solidAdapter.js"
|
|
12
6
|
import { loadProject } from "../loadProject.js"
|
|
13
7
|
import { mockRepo } from "@lix-js/client"
|
|
@@ -19,10 +13,6 @@ import type {
|
|
|
19
13
|
Text,
|
|
20
14
|
} from "../versionedInterfaces.js"
|
|
21
15
|
|
|
22
|
-
function sleep(ms: number) {
|
|
23
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
// ------------------------------------------------------------------------------------------------
|
|
27
17
|
|
|
28
18
|
const config: ProjectSettings = {
|
|
@@ -170,8 +160,8 @@ describe("installed", () => {
|
|
|
170
160
|
// TODO: how can we await `setConfig` correctly
|
|
171
161
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
172
162
|
|
|
173
|
-
expect(counterPlugins).toBe(
|
|
174
|
-
expect(counterLint).toBe(
|
|
163
|
+
expect(counterPlugins).toBe(3) // 3 times because effect creation + setSettings, setResolvedModules
|
|
164
|
+
expect(counterLint).toBe(3) // 3 times because effect creation + setSettings, setResolvedModules
|
|
175
165
|
})
|
|
176
166
|
})
|
|
177
167
|
|
|
@@ -219,7 +209,7 @@ describe("messages", () => {
|
|
|
219
209
|
{ from }
|
|
220
210
|
)
|
|
221
211
|
|
|
222
|
-
let effectOnMessagesCounter =
|
|
212
|
+
let effectOnMessagesCounter = -1
|
|
223
213
|
createEffect(() => {
|
|
224
214
|
project.query.messages.getAll()
|
|
225
215
|
effectOnMessagesCounter += 1
|
|
@@ -232,7 +222,7 @@ describe("messages", () => {
|
|
|
232
222
|
// TODO: how can we await `setConfig` correctly
|
|
233
223
|
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
234
224
|
|
|
235
|
-
expect(effectOnMessagesCounter).toBe(
|
|
225
|
+
expect(effectOnMessagesCounter).toBe(3) // 3 = setSetting, loadMessage (2x - one per message)
|
|
236
226
|
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
|
|
237
227
|
})
|
|
238
228
|
|
|
@@ -363,6 +353,65 @@ describe("messages", () => {
|
|
|
363
353
|
})
|
|
364
354
|
|
|
365
355
|
describe("lint", () => {
|
|
356
|
+
it("should react to changes in config", async () => {
|
|
357
|
+
await createRoot(async () => {
|
|
358
|
+
const repo = await mockRepo()
|
|
359
|
+
const fs = repo.nodeishFs
|
|
360
|
+
await fs.mkdir("/user/project.inlang", { recursive: true })
|
|
361
|
+
await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(config))
|
|
362
|
+
const project = solidAdapter(
|
|
363
|
+
await loadProject({
|
|
364
|
+
projectPath: "/user/project.inlang",
|
|
365
|
+
repo,
|
|
366
|
+
_import: $import,
|
|
367
|
+
}),
|
|
368
|
+
{ from }
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
// set counter to -1 since creating effect will execute once
|
|
372
|
+
let counter = -1
|
|
373
|
+
createEffect(() => {
|
|
374
|
+
project.query.messageLintReports.getAll()
|
|
375
|
+
counter += 1
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
expect(counter).toBe(0)
|
|
379
|
+
|
|
380
|
+
// wait for all int reports beeing executed
|
|
381
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
382
|
+
|
|
383
|
+
// 1 because we batch the two lint rules
|
|
384
|
+
expect(counter).toBe(1)
|
|
385
|
+
|
|
386
|
+
const currentSettings = project.settings()!
|
|
387
|
+
const newConfig = { ...currentSettings, languageTags: ["en", "de"] }
|
|
388
|
+
project.setSettings(newConfig)
|
|
389
|
+
|
|
390
|
+
// set settings trigges synchronous -> +1
|
|
391
|
+
expect(counter).toBe(2)
|
|
392
|
+
|
|
393
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
394
|
+
|
|
395
|
+
// 4 = 2 + batched(1 (effect->settings) and 1 (effect->Resolved Plugin))
|
|
396
|
+
expect(counter).toBe(3)
|
|
397
|
+
expect(project.query.messageLintReports.getAll()).toEqual([])
|
|
398
|
+
|
|
399
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
400
|
+
|
|
401
|
+
const newConfig2 = { ...project.settings()!, languageTags: ["en", "de", "fr"] }
|
|
402
|
+
project.setSettings(newConfig2)
|
|
403
|
+
|
|
404
|
+
// set settings trigges synchronous -> +1
|
|
405
|
+
expect(counter).toBe(4)
|
|
406
|
+
|
|
407
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
408
|
+
|
|
409
|
+
// 5 -> 4 + 1 new lint report batch
|
|
410
|
+
expect(counter).toBe(5)
|
|
411
|
+
expect(project.query.messageLintReports.getAll()).toEqual([])
|
|
412
|
+
})
|
|
413
|
+
})
|
|
414
|
+
|
|
366
415
|
it.todo("should react to changes to packages")
|
|
367
416
|
it.todo("should react to changes to modules")
|
|
368
417
|
|
|
@@ -381,23 +430,16 @@ describe("lint", () => {
|
|
|
381
430
|
{ from }
|
|
382
431
|
)
|
|
383
432
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
let counter = 0
|
|
387
|
-
|
|
433
|
+
let counter = -1 // -1 to start counting after the initial effect
|
|
388
434
|
createEffect(() => {
|
|
389
|
-
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
const [lintReports] = createResource(messages, async () => {
|
|
393
|
-
const reports = await project.query.messageLintReports.getAll()
|
|
435
|
+
project.query.messageLintReports.getAll()
|
|
394
436
|
counter += 1
|
|
395
|
-
return reports
|
|
396
437
|
})
|
|
438
|
+
expect(counter).toBe(0)
|
|
397
439
|
|
|
398
|
-
await
|
|
440
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
441
|
+
// 1 -> lint rules are batched - we expect one signal on getAll
|
|
399
442
|
expect(counter).toBe(1)
|
|
400
|
-
expect(lintReports()).toStrictEqual([])
|
|
401
443
|
|
|
402
444
|
project.query.messages.update({
|
|
403
445
|
where: { id: "a" },
|
|
@@ -406,10 +448,11 @@ describe("lint", () => {
|
|
|
406
448
|
variants: [{ languageTag: "en", match: [], pattern: [{ type: "Text", value: "new" }] }],
|
|
407
449
|
},
|
|
408
450
|
})
|
|
451
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
409
452
|
|
|
410
|
-
|
|
411
|
-
expect(counter).toBe(
|
|
412
|
-
expect(
|
|
453
|
+
// 1 -> previous two messages + 0 - report results are the same - no effect
|
|
454
|
+
expect(counter).toBe(1)
|
|
455
|
+
expect(project.query.messageLintReports.getAll()).toEqual([])
|
|
413
456
|
|
|
414
457
|
project.query.messages.update({
|
|
415
458
|
where: { id: "a" },
|
|
@@ -419,9 +462,11 @@ describe("lint", () => {
|
|
|
419
462
|
},
|
|
420
463
|
})
|
|
421
464
|
|
|
422
|
-
await
|
|
423
|
-
|
|
424
|
-
|
|
465
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
466
|
+
|
|
467
|
+
// 2 -> the updated message does not update the lint rules - result is the same
|
|
468
|
+
expect(counter).toBe(1)
|
|
469
|
+
expect(project.query.messageLintReports.getAll()).toEqual([])
|
|
425
470
|
})
|
|
426
471
|
})
|
|
427
472
|
})
|
|
@@ -39,7 +39,7 @@ export const solidAdapter = (
|
|
|
39
39
|
},
|
|
40
40
|
messageLintReports: {
|
|
41
41
|
get: project.query.messageLintReports.get,
|
|
42
|
-
getAll: project.query.messageLintReports.getAll,
|
|
42
|
+
getAll: convert(project.query.messageLintReports.getAll),
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
45
|
} satisfies InlangProjectWithSolidAdapter
|
package/src/api.ts
CHANGED
|
@@ -82,9 +82,9 @@ export type Subscribable<Value> = {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export type MessageQueryDelegate = {
|
|
85
|
-
onMessageCreate: (messageId: string, message: Message) => void
|
|
86
|
-
onMessageUpdate: (messageId: string, message: Message) => void
|
|
87
|
-
onMessageDelete: (messageId: string) => void
|
|
85
|
+
onMessageCreate: (messageId: string, message: Message, messages: Message[]) => void
|
|
86
|
+
onMessageUpdate: (messageId: string, message: Message, messages: Message[]) => void
|
|
87
|
+
onMessageDelete: (messageId: string, messages: Message[]) => void
|
|
88
88
|
onLoaded: (messages: Message[]) => void
|
|
89
89
|
onCleanup: () => void
|
|
90
90
|
}
|
|
@@ -109,12 +109,19 @@ export type MessageQueryApi = {
|
|
|
109
109
|
update: (args: { where: { id: Message["id"] }; data: Partial<Message> }) => boolean
|
|
110
110
|
upsert: (args: { where: { id: Message["id"] }; data: Message }) => void
|
|
111
111
|
delete: (args: { where: { id: Message["id"] } }) => boolean
|
|
112
|
-
setDelegate: (delegate: MessageQueryDelegate) => void
|
|
112
|
+
setDelegate: (delegate: MessageQueryDelegate | undefined, callOnLoad: boolean) => void
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export type MessageLintReportsQueryApi = {
|
|
116
|
-
getAll:
|
|
117
|
-
|
|
116
|
+
getAll: Subscribable<MessageLintReport[]> & {
|
|
117
|
+
settled: () => Promise<MessageLintReport[]>
|
|
118
|
+
}
|
|
119
|
+
get: ((args: {
|
|
118
120
|
where: { messageId: MessageLintReport["messageId"] }
|
|
119
|
-
}) =>
|
|
121
|
+
}) => Readonly<MessageLintReport[]>) & {
|
|
122
|
+
subscribe: (
|
|
123
|
+
args: { where: { messageId: MessageLintReport["messageId"] } },
|
|
124
|
+
callback: (MessageLintRules: Readonly<MessageLintReport[]>) => void
|
|
125
|
+
) => void
|
|
126
|
+
}
|
|
120
127
|
}
|
|
@@ -10,13 +10,13 @@ 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
|
|
|
13
|
+
import { ReactiveMap } from "./reactivity/map.js"
|
|
14
|
+
import { createMemo, onCleanup, untrack, batch } from "./reactivity/solid.js"
|
|
15
|
+
import { createSubscribable } from "./loadProject.js"
|
|
16
|
+
|
|
13
17
|
import _debug from "debug"
|
|
14
18
|
const debug = _debug("sdk:lintReports")
|
|
15
19
|
|
|
16
|
-
function sleep(ms: number) {
|
|
17
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
20
|
/**
|
|
21
21
|
* Creates a non-reactive async query API for lint reports.
|
|
22
22
|
* e.g. used in editor/.../Listheader.tsx
|
|
@@ -30,81 +30,196 @@ export function createMessageLintReportsQuery(
|
|
|
30
30
|
installedMessageLintRules: () => Array<InstalledMessageLintRule>,
|
|
31
31
|
resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined
|
|
32
32
|
): InlangProject["query"]["messageLintReports"] {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
// @ts-expect-error Reactive map seems to have a problem with Type aliases here
|
|
34
|
+
const index = new ReactiveMap<MessageLintReport["messageId"], MessageLintReport[]>()
|
|
35
|
+
|
|
36
|
+
debug("resetting settledReports")
|
|
37
|
+
let settledReports = Promise.resolve()
|
|
38
|
+
|
|
39
|
+
let currentBatchEnd: undefined | (() => Promise<void>) = undefined
|
|
40
|
+
|
|
41
|
+
const updatedReports: { [messageId: string]: MessageLintReport[] } = {}
|
|
42
|
+
|
|
43
|
+
// triggered whenever settings or resolved modules changes
|
|
44
|
+
createMemo(() => {
|
|
45
|
+
// we clear the index independent from the change for
|
|
46
|
+
index.clear()
|
|
47
|
+
|
|
48
|
+
onCleanup(() => {
|
|
49
|
+
messagesQuery.setDelegate(undefined, false)
|
|
50
|
+
})
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
// settings is a signal - effect will be called whenever settings changes
|
|
53
|
+
const _settings = settings()
|
|
54
|
+
if (!_settings) return
|
|
55
|
+
|
|
56
|
+
const _resolvedModules = resolvedModules()
|
|
57
|
+
if (!_resolvedModules) return
|
|
58
|
+
|
|
59
|
+
// resolvedModules is a signal - effect will be called whenever resolvedModules changes
|
|
60
|
+
const rulesArray = _resolvedModules.messageLintRules
|
|
61
|
+
|
|
62
|
+
const messageLintRuleLevels = Object.fromEntries(
|
|
63
|
+
installedMessageLintRules().map((rule) => [rule.id, rule.level])
|
|
64
|
+
)
|
|
65
|
+
const settingsObject = () => {
|
|
66
|
+
return {
|
|
67
|
+
...settings(),
|
|
68
|
+
messageLintRuleLevels,
|
|
69
|
+
}
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
debug("
|
|
62
|
-
|
|
72
|
+
const sheduleLintMessage = (message: Message, messages: Message[]) => {
|
|
73
|
+
debug("shedule Lint for message:", message.id)
|
|
74
|
+
|
|
75
|
+
const updateOutstandingReportsOnLast = async () => {
|
|
76
|
+
if (currentBatchEnd !== updateOutstandingReportsOnLast) {
|
|
77
|
+
debug("skip triggering reactivy", message.id)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
debug("finished queue - trigger reactivity", message.id)
|
|
81
|
+
|
|
82
|
+
batch(() => {
|
|
83
|
+
for (const [id, reports] of Object.entries(updatedReports)) {
|
|
84
|
+
const currentReports = index.get(id)
|
|
85
|
+
// we only update the report if it differs from the known one - to not trigger reactivity
|
|
86
|
+
if (!reportsEqual(currentReports, reports)) {
|
|
87
|
+
debug("lint reports for message: ", id, " now n:", reports.length)
|
|
88
|
+
index.set(id, reports)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
63
92
|
}
|
|
93
|
+
|
|
94
|
+
currentBatchEnd = updateOutstandingReportsOnLast
|
|
95
|
+
|
|
96
|
+
const scheduledLint = lintSingleMessage({
|
|
97
|
+
rules: rulesArray,
|
|
98
|
+
settings: settingsObject(),
|
|
99
|
+
messages: messages,
|
|
100
|
+
message: message,
|
|
101
|
+
}).then((reportsResult) => {
|
|
102
|
+
if (reportsResult.errors.length === 0) {
|
|
103
|
+
debug("lint reports for message: ", message.id, "n:", reportsResult.data.length)
|
|
104
|
+
updatedReports[message.id] = reportsResult.data
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return updateOutstandingReportsOnLast()
|
|
108
|
+
})
|
|
109
|
+
settledReports = settledReports.then(() => scheduledLint)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// setup delegate of message query
|
|
113
|
+
const messageQueryChangeDelegate: MessageQueryDelegate = {
|
|
114
|
+
onCleanup: () => {
|
|
115
|
+
// NOTE: we could cancel all running lint rules - but results get overritten anyway
|
|
116
|
+
index.clear()
|
|
117
|
+
},
|
|
118
|
+
onLoaded: (messages: Message[]) => {
|
|
119
|
+
// for (const message of messages) {
|
|
120
|
+
// lintMessage(message, messages)
|
|
121
|
+
// }
|
|
122
|
+
debug("sheduluing Lint for all messages - on load")
|
|
123
|
+
batch(() => {
|
|
124
|
+
debug("sheduluing Lint for all messages - subsquencial call?")
|
|
125
|
+
for (const message of messages) {
|
|
126
|
+
// NOTE: this potentually creates thousands of promisses we could create a promise that batches linting
|
|
127
|
+
// NOTE: this produces a lot of signals - we could batch the
|
|
128
|
+
sheduleLintMessage(message, messages)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
},
|
|
132
|
+
onMessageCreate: (messageId: string, message: Message, messages: Message[]) => {
|
|
133
|
+
// NOTE: unhandled promise rejection (as before the refactor) but won't tackle this in this pr
|
|
134
|
+
// TODO MESDK-105 reevaluate all lint's instead of those for the messsage that where created
|
|
135
|
+
debug("shedule Lint for message - onMessageCreate", message.id)
|
|
136
|
+
sheduleLintMessage(message, messages)
|
|
137
|
+
},
|
|
138
|
+
onMessageUpdate: (messageId: string, message: Message, messages: Message[]) => {
|
|
139
|
+
// NOTE: unhandled promise rejection (as before the refactor) but won't tackle this in this pr
|
|
140
|
+
// TODO MESDK-105 reevaluate all lint's instead of those for the messsage that changed
|
|
141
|
+
debug("shedule Lint for message - onMessageUpdate", message.id)
|
|
142
|
+
sheduleLintMessage(message, messages)
|
|
143
|
+
},
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO MESDK-105 we gonna need the mesage Property for evaluation
|
|
145
|
+
onMessageDelete: (messageId: string, _messages: Message[]) => {
|
|
146
|
+
index.delete(messageId)
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
untrack(() => {
|
|
151
|
+
messagesQuery.setDelegate(messageQueryChangeDelegate, true)
|
|
64
152
|
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const get = (args: Parameters<MessageLintReportsQueryApi["get"]>[0]) => {
|
|
156
|
+
debug("get", args.where.messageId)
|
|
157
|
+
return structuredClone(index.get(args.where.messageId))
|
|
65
158
|
}
|
|
66
159
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
lintMessage(message, messages)
|
|
160
|
+
const getAll = () => {
|
|
161
|
+
const flatValues = [...index.values()].flat()
|
|
162
|
+
debug("getAll", flatValues.length)
|
|
163
|
+
return structuredClone(flatValues.length === 0 ? [] : flatValues) as MessageLintReport[]
|
|
72
164
|
}
|
|
73
165
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
onMessageUpdate: (messageId: string, message: Message) => {
|
|
88
|
-
lintMessage(message, messages)
|
|
89
|
-
},
|
|
90
|
-
onMessageDelete: (messageId: string) => {
|
|
91
|
-
index.delete(messageId)
|
|
92
|
-
},
|
|
166
|
+
return {
|
|
167
|
+
getAll: Object.assign(createSubscribable(getAll), {
|
|
168
|
+
settled: async () => {
|
|
169
|
+
await settledReports
|
|
170
|
+
return getAll()
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
get: Object.assign(get, {
|
|
174
|
+
subscribe: (
|
|
175
|
+
args: Parameters<MessageLintReportsQueryApi["get"]["subscribe"]>[0],
|
|
176
|
+
callback: Parameters<MessageLintReportsQueryApi["get"]["subscribe"]>[1]
|
|
177
|
+
) => createSubscribable(() => get(args)).subscribe(callback),
|
|
178
|
+
}) as any,
|
|
93
179
|
}
|
|
180
|
+
}
|
|
94
181
|
|
|
95
|
-
|
|
182
|
+
function reportsEqual(
|
|
183
|
+
reportsA: MessageLintReport[] | undefined,
|
|
184
|
+
reportsB: MessageLintReport[] | undefined
|
|
185
|
+
) {
|
|
186
|
+
if (reportsA === undefined && reportsB === undefined) {
|
|
187
|
+
return true
|
|
188
|
+
} else if (reportsA === undefined || reportsB === undefined) {
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
96
191
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return
|
|
108
|
-
}
|
|
192
|
+
if (reportsA.length !== reportsB.length) {
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const [i, element] of reportsA.entries()) {
|
|
197
|
+
if (element?.languageTag !== reportsB[i]?.languageTag) {
|
|
198
|
+
return false
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (element?.level !== reportsB[i]?.level) {
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (element?.ruleId !== reportsB[i]?.ruleId) {
|
|
206
|
+
return false
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (typeof element?.body !== typeof reportsB[i]?.body) {
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (typeof element?.body === "string") {
|
|
214
|
+
if (reportsB[i]?.body !== reportsB[i]?.body) {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
// NOTE: this was the fastest way to check if both bodies are equal - we can optimize that later if needed
|
|
219
|
+
if (JSON.stringify(element?.body) !== JSON.stringify(element?.body)) {
|
|
220
|
+
return false
|
|
221
|
+
}
|
|
222
|
+
}
|
|
109
223
|
}
|
|
224
|
+
return true
|
|
110
225
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { Message } from "@inlang/message"
|
|
2
2
|
import { ReactiveMap } from "./reactivity/map.js"
|
|
3
|
-
import { createEffect } from "./reactivity/solid.js"
|
|
3
|
+
import { createEffect, onCleanup } from "./reactivity/solid.js"
|
|
4
4
|
import { createSubscribable } from "./loadProject.js"
|
|
5
5
|
import type { InlangProject, MessageQueryApi, MessageQueryDelegate } from "./api.js"
|
|
6
6
|
import type { ResolvedPluginApi } from "./resolve-modules/plugins/types.js"
|
|
7
7
|
import type { resolveModules } from "./resolve-modules/resolveModules.js"
|
|
8
8
|
import { createNodeishFsWithWatcher } from "./createNodeishFsWithWatcher.js"
|
|
9
9
|
import type { NodeishFilesystem } from "@lix-js/fs"
|
|
10
|
-
import { onCleanup } from "solid-js"
|
|
11
10
|
import { stringifyMessage } from "./storage/helper.js"
|
|
12
11
|
import { acquireFileLock } from "./persistence/filelock/acquireFileLock.js"
|
|
13
12
|
import _debug from "debug"
|
|
@@ -62,14 +61,18 @@ export function createMessagesQuery({
|
|
|
62
61
|
}: createMessagesQueryParameters): InlangProject["query"]["messages"] {
|
|
63
62
|
// @ts-expect-error
|
|
64
63
|
const index = new ReactiveMap<string, Message>()
|
|
64
|
+
let loaded = false
|
|
65
65
|
|
|
66
66
|
// filepath for the lock folder
|
|
67
67
|
const messageLockDirPath = projectPath + "/messagelock"
|
|
68
68
|
|
|
69
69
|
let delegate: MessageQueryDelegate | undefined = undefined
|
|
70
70
|
|
|
71
|
-
const setDelegate = (newDelegate: MessageQueryDelegate) => {
|
|
71
|
+
const setDelegate = (newDelegate: MessageQueryDelegate | undefined, onLoad: boolean) => {
|
|
72
72
|
delegate = newDelegate
|
|
73
|
+
if (newDelegate && loaded && onLoad) {
|
|
74
|
+
newDelegate.onLoaded([...index.values()] as Message[])
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// Map default alias to message
|
|
@@ -94,6 +97,7 @@ export function createMessagesQuery({
|
|
|
94
97
|
// we clear the index independent from the change for
|
|
95
98
|
index.clear()
|
|
96
99
|
defaultAliasIndex.clear()
|
|
100
|
+
loaded = false
|
|
97
101
|
|
|
98
102
|
// Load messages -> use settings to subscribe to signals from the settings
|
|
99
103
|
const _settings = settings()
|
|
@@ -143,7 +147,7 @@ export function createMessagesQuery({
|
|
|
143
147
|
messageLockDirPath,
|
|
144
148
|
messageStates,
|
|
145
149
|
index,
|
|
146
|
-
delegate
|
|
150
|
+
undefined /* delegate - we don't pass it here since we will call onLoaded instead */,
|
|
147
151
|
_settings, // NOTE we bang here - we don't expect the settings to become null during the livetime of a project
|
|
148
152
|
resolvedPluginApi
|
|
149
153
|
)
|
|
@@ -154,6 +158,7 @@ export function createMessagesQuery({
|
|
|
154
158
|
.then(() => {
|
|
155
159
|
onInitialMessageLoadResult()
|
|
156
160
|
delegate?.onLoaded([...index.values()])
|
|
161
|
+
loaded = true
|
|
157
162
|
})
|
|
158
163
|
})
|
|
159
164
|
|
|
@@ -203,7 +208,7 @@ export function createMessagesQuery({
|
|
|
203
208
|
}
|
|
204
209
|
|
|
205
210
|
messageStates.messageDirtyFlags[data.id] = true
|
|
206
|
-
delegate?.onMessageCreate(data.id, index.get(data.id))
|
|
211
|
+
delegate?.onMessageCreate(data.id, index.get(data.id), [...index.values()])
|
|
207
212
|
scheduleSave()
|
|
208
213
|
return true
|
|
209
214
|
},
|
|
@@ -230,7 +235,7 @@ export function createMessagesQuery({
|
|
|
230
235
|
if (message === undefined) return false
|
|
231
236
|
index.set(where.id, { ...message, ...data })
|
|
232
237
|
messageStates.messageDirtyFlags[where.id] = true
|
|
233
|
-
delegate?.
|
|
238
|
+
delegate?.onMessageUpdate(where.id, index.get(data.id), [...index.values()])
|
|
234
239
|
scheduleSave()
|
|
235
240
|
return true
|
|
236
241
|
},
|
|
@@ -242,11 +247,11 @@ export function createMessagesQuery({
|
|
|
242
247
|
defaultAliasIndex.set(data.alias.default, data)
|
|
243
248
|
}
|
|
244
249
|
messageStates.messageDirtyFlags[where.id] = true
|
|
245
|
-
delegate?.onMessageCreate(data.id, index.get(data.id))
|
|
250
|
+
delegate?.onMessageCreate(data.id, index.get(data.id), [...index.values()])
|
|
246
251
|
} else {
|
|
247
252
|
index.set(where.id, { ...message, ...data })
|
|
248
253
|
messageStates.messageDirtyFlags[where.id] = true
|
|
249
|
-
delegate?.onMessageUpdate(data.id, index.get(data.id))
|
|
254
|
+
delegate?.onMessageUpdate(data.id, index.get(data.id), [...index.values()])
|
|
250
255
|
}
|
|
251
256
|
scheduleSave()
|
|
252
257
|
return true
|
|
@@ -259,7 +264,7 @@ export function createMessagesQuery({
|
|
|
259
264
|
}
|
|
260
265
|
index.delete(where.id)
|
|
261
266
|
messageStates.messageDirtyFlags[where.id] = true
|
|
262
|
-
delegate?.onMessageDelete(where.id)
|
|
267
|
+
delegate?.onMessageDelete(where.id, [...index.values()])
|
|
263
268
|
scheduleSave()
|
|
264
269
|
return true
|
|
265
270
|
},
|
|
@@ -323,6 +328,8 @@ async function loadMessagesViaPlugin(
|
|
|
323
328
|
|
|
324
329
|
let loadedMessageCount = 0
|
|
325
330
|
|
|
331
|
+
const deletedMessages = new Set(messages.keys())
|
|
332
|
+
|
|
326
333
|
for (const loadedMessage of loadedMessages) {
|
|
327
334
|
const loadedMessageClone = structuredClone(loadedMessage)
|
|
328
335
|
|
|
@@ -338,6 +345,8 @@ async function loadMessagesViaPlugin(
|
|
|
338
345
|
// - this could be the case if one edits the aliase manualy
|
|
339
346
|
throw new Error("more than one message with the same id or alias found ")
|
|
340
347
|
} else if (currentMessages.length === 1) {
|
|
348
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- length has checked beforhand
|
|
349
|
+
deletedMessages.delete(currentMessages[0]!.id)
|
|
341
350
|
// update message in place - leave message id and alias untouched
|
|
342
351
|
loadedMessageClone.alias = {} as any
|
|
343
352
|
|
|
@@ -364,7 +373,7 @@ async function loadMessagesViaPlugin(
|
|
|
364
373
|
messages.set(loadedMessageClone.id, loadedMessageClone)
|
|
365
374
|
// NOTE could use hash instead of the whole object JSON to save memory...
|
|
366
375
|
messageState.messageLoadHash[loadedMessageClone.id] = importedEnecoded
|
|
367
|
-
delegate?.onMessageUpdate(loadedMessageClone.id, loadedMessageClone)
|
|
376
|
+
delegate?.onMessageUpdate(loadedMessageClone.id, loadedMessageClone, [...messages.values()])
|
|
368
377
|
loadedMessageCount++
|
|
369
378
|
} else {
|
|
370
379
|
// message with the given alias does not exist so far
|
|
@@ -392,7 +401,7 @@ async function loadMessagesViaPlugin(
|
|
|
392
401
|
// we don't have to check - done before hand if (messages.has(loadedMessageClone.id)) return false
|
|
393
402
|
messages.set(loadedMessageClone.id, loadedMessageClone)
|
|
394
403
|
messageState.messageLoadHash[loadedMessageClone.id] = importedEnecoded
|
|
395
|
-
delegate?.onMessageUpdate(loadedMessageClone.id, loadedMessageClone)
|
|
404
|
+
delegate?.onMessageUpdate(loadedMessageClone.id, loadedMessageClone, [...messages.values()])
|
|
396
405
|
loadedMessageCount++
|
|
397
406
|
}
|
|
398
407
|
if (loadedMessageCount > maxMessagesPerTick) {
|
|
@@ -403,6 +412,18 @@ async function loadMessagesViaPlugin(
|
|
|
403
412
|
loadedMessageCount = 0
|
|
404
413
|
}
|
|
405
414
|
}
|
|
415
|
+
|
|
416
|
+
loadedMessageCount = 0
|
|
417
|
+
for (const deletedMessageId of deletedMessages) {
|
|
418
|
+
messages.delete(deletedMessageId)
|
|
419
|
+
delegate?.onMessageDelete(deletedMessageId, [...messages.values()])
|
|
420
|
+
loadedMessageCount++
|
|
421
|
+
if (loadedMessageCount > maxMessagesPerTick) {
|
|
422
|
+
await sleep(0)
|
|
423
|
+
loadedMessageCount = 0
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
406
427
|
await releaseLock(fs as NodeishFilesystem, lockDirPath, "loadMessage", lockTime)
|
|
407
428
|
lockTime = undefined
|
|
408
429
|
|
|
@@ -50,5 +50,8 @@ export const lintSingleMessage = async (args: {
|
|
|
50
50
|
|
|
51
51
|
await Promise.all(promises)
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// we sort the reports by rule id to allow us to easyly compare both
|
|
54
|
+
const sortedReports = reports.sort((r1, r2) => r1.ruleId.localeCompare(r2.ruleId))
|
|
55
|
+
|
|
56
|
+
return { data: sortedReports, errors }
|
|
54
57
|
}
|