@inlang/sdk 0.10.0 → 0.11.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 +12 -8
- package/dist/loadProject.d.ts +4 -1
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +13 -4
- package/dist/loadProject.test.js +122 -43
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts +6 -0
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts.map +1 -0
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.js +20 -0
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts +2 -0
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts.map +1 -0
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.js +85 -0
- package/dist/resolve-modules/import.d.ts.map +1 -1
- package/dist/resolve-modules/import.js +3 -2
- package/dist/resolve-modules/plugins/types.d.ts +2 -2
- package/dist/resolve-modules/plugins/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapter/solidAdapter.test.ts +12 -8
- package/src/loadProject.test.ts +132 -43
- package/src/loadProject.ts +17 -4
- package/src/resolve-modules/createNodeishFsWithAbsolutePaths.test.ts +102 -0
- package/src/resolve-modules/createNodeishFsWithAbsolutePaths.ts +30 -0
- package/src/resolve-modules/import.ts +3 -2
- package/src/resolve-modules/plugins/types.ts +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeishFilesystem
|
|
1
|
+
import type { NodeishFilesystem } from "@lix-js/fs";
|
|
2
2
|
import type { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginHasInvalidSchemaError, PluginUsesReservedNamespaceError, PluginsDoNotProvideLoadOrSaveMessagesError } from "./errors.js";
|
|
3
3
|
import type { Message } from "@inlang/message";
|
|
4
4
|
import type { CustomApiInlangIdeExtension, Plugin } from "@inlang/plugin";
|
|
@@ -8,7 +8,7 @@ import type { ProjectSettings } from "@inlang/project-settings";
|
|
|
8
8
|
*
|
|
9
9
|
* - only uses minimally required functions to decrease the API footprint on the ecosystem.
|
|
10
10
|
*/
|
|
11
|
-
export type NodeishFilesystemSubset = Pick<
|
|
11
|
+
export type NodeishFilesystemSubset = Pick<NodeishFilesystem, "readFile" | "readdir" | "mkdir" | "writeFile">;
|
|
12
12
|
/**
|
|
13
13
|
* Function that resolves (imports and initializes) the plugins.
|
|
14
14
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EACX,mCAAmC,EACnC,6CAA6C,EAC7C,6CAA6C,EAC7C,uBAAuB,EACvB,2BAA2B,EAC3B,gCAAgC,EAChC,0CAA0C,EAC1C,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE/D;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACzC,iBAAiB,EACjB,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAC9C,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACtB,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAK,OAAO,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,EAAE,KAAK,CACV,mCAAmC,GACnC,6CAA6C,GAC7C,6CAA6C,GAC7C,uBAAuB,GACvB,2BAA2B,GAC3B,gCAAgC,GAChC,0CAA0C,CAC5C,CAAA;CACD,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAA;IACrF,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChG;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,MAAM,CAAC,OAAO,MAAM,IAAI,MAAM,EAAE,GAAG,WAAW,MAAM,IAAI,MAAM,EAAE,EAAE,OAAO,CAAC,GAAG;QACvF,yBAAyB,CAAC,EAAE,2BAA2B,CAAA;KACvD,CAAA;CACD,CAAA"}
|
package/package.json
CHANGED
|
@@ -88,10 +88,11 @@ const $import: ImportFunction = async (name) => ({
|
|
|
88
88
|
describe("config", () => {
|
|
89
89
|
it("should react to changes in config", async () => {
|
|
90
90
|
const fs = createNodeishMemoryFs()
|
|
91
|
-
await fs.
|
|
91
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
92
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config))
|
|
92
93
|
const project = solidAdapter(
|
|
93
94
|
await loadProject({
|
|
94
|
-
settingsFilePath: "
|
|
95
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
95
96
|
nodeishFs: fs,
|
|
96
97
|
_import: $import,
|
|
97
98
|
}),
|
|
@@ -118,10 +119,11 @@ describe("config", () => {
|
|
|
118
119
|
describe("installed", () => {
|
|
119
120
|
it("react to changes that are unrelated to installed items", async () => {
|
|
120
121
|
const fs = createNodeishMemoryFs()
|
|
121
|
-
await fs.
|
|
122
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
123
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config))
|
|
122
124
|
const project = solidAdapter(
|
|
123
125
|
await loadProject({
|
|
124
|
-
settingsFilePath: "
|
|
126
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
125
127
|
nodeishFs: fs,
|
|
126
128
|
_import: $import,
|
|
127
129
|
}),
|
|
@@ -181,10 +183,11 @@ describe("messages", () => {
|
|
|
181
183
|
|
|
182
184
|
const mockImport: ImportFunction = async () => ({ default: mockPlugin })
|
|
183
185
|
|
|
184
|
-
await fs.
|
|
186
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
187
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(mockConfig))
|
|
185
188
|
const project = solidAdapter(
|
|
186
189
|
await loadProject({
|
|
187
|
-
settingsFilePath: "
|
|
190
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
188
191
|
nodeishFs: fs,
|
|
189
192
|
_import: mockImport,
|
|
190
193
|
}),
|
|
@@ -210,10 +213,11 @@ describe("messages", () => {
|
|
|
210
213
|
|
|
211
214
|
it("should react to changes in messages", async () => {
|
|
212
215
|
const fs = createNodeishMemoryFs()
|
|
213
|
-
await fs.
|
|
216
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
217
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config))
|
|
214
218
|
const project = solidAdapter(
|
|
215
219
|
await loadProject({
|
|
216
|
-
settingsFilePath: "
|
|
220
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
217
221
|
nodeishFs: fs,
|
|
218
222
|
_import: $import,
|
|
219
223
|
}),
|
package/src/loadProject.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ProjectSettingsInvalidError,
|
|
11
11
|
} from "./errors.js"
|
|
12
12
|
import { createNodeishMemoryFs } from "@lix-js/fs"
|
|
13
|
+
import { createMessage } from "./test-utilities/createMessage.js"
|
|
13
14
|
|
|
14
15
|
// ------------------------------------------------------------------------------------------------
|
|
15
16
|
|
|
@@ -99,9 +100,10 @@ describe("initialization", () => {
|
|
|
99
100
|
describe("settings", () => {
|
|
100
101
|
it("should return an error if settings file is not found", async () => {
|
|
101
102
|
const fs = createNodeishMemoryFs()
|
|
103
|
+
fs.mkdir("/user/project", { recursive: true })
|
|
102
104
|
|
|
103
105
|
const project = await loadProject({
|
|
104
|
-
settingsFilePath: "
|
|
106
|
+
settingsFilePath: "/user/project/test.json",
|
|
105
107
|
nodeishFs: fs,
|
|
106
108
|
_import,
|
|
107
109
|
})
|
|
@@ -111,10 +113,11 @@ describe("initialization", () => {
|
|
|
111
113
|
|
|
112
114
|
it("should return an error if settings file is not a valid JSON", async () => {
|
|
113
115
|
const fs = await createNodeishMemoryFs()
|
|
114
|
-
await fs.
|
|
116
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
117
|
+
await fs.writeFile("/user/project/project.inlang.json", "invalid json")
|
|
115
118
|
|
|
116
119
|
const project = await loadProject({
|
|
117
|
-
settingsFilePath: "
|
|
120
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
118
121
|
nodeishFs: fs,
|
|
119
122
|
_import,
|
|
120
123
|
})
|
|
@@ -124,10 +127,11 @@ describe("initialization", () => {
|
|
|
124
127
|
|
|
125
128
|
it("should return an error if settings file is does not match schema", async () => {
|
|
126
129
|
const fs = await createNodeishMemoryFs()
|
|
127
|
-
await fs.
|
|
130
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
131
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({}))
|
|
128
132
|
|
|
129
133
|
const project = await loadProject({
|
|
130
|
-
settingsFilePath: "
|
|
134
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
131
135
|
nodeishFs: fs,
|
|
132
136
|
_import,
|
|
133
137
|
})
|
|
@@ -137,9 +141,10 @@ describe("initialization", () => {
|
|
|
137
141
|
|
|
138
142
|
it("should return the parsed settings", async () => {
|
|
139
143
|
const fs = await createNodeishMemoryFs()
|
|
140
|
-
await fs.
|
|
144
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
145
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
141
146
|
const project = await loadProject({
|
|
142
|
-
settingsFilePath: "
|
|
147
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
143
148
|
nodeishFs: fs,
|
|
144
149
|
_import,
|
|
145
150
|
})
|
|
@@ -150,22 +155,27 @@ describe("initialization", () => {
|
|
|
150
155
|
it("should not re-write the settings to disk when initializing", async () => {
|
|
151
156
|
const fs = await createNodeishMemoryFs()
|
|
152
157
|
const settingsWithDeifferentFormatting = JSON.stringify(settings, undefined, 4)
|
|
153
|
-
await fs.
|
|
158
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
159
|
+
await fs.writeFile("/user/project/project.inlang.json", settingsWithDeifferentFormatting)
|
|
154
160
|
|
|
155
161
|
const project = await loadProject({
|
|
156
|
-
settingsFilePath: "
|
|
162
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
157
163
|
nodeishFs: fs,
|
|
158
164
|
_import,
|
|
159
165
|
})
|
|
160
166
|
|
|
161
|
-
const settingsOnDisk = await fs.readFile("
|
|
167
|
+
const settingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
|
|
168
|
+
encoding: "utf-8",
|
|
169
|
+
})
|
|
162
170
|
expect(settingsOnDisk).toBe(settingsWithDeifferentFormatting)
|
|
163
171
|
|
|
164
172
|
project.setSettings(project.settings())
|
|
165
173
|
// TODO: how can we await `setsettings` correctly
|
|
166
174
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
167
175
|
|
|
168
|
-
const newsettingsOnDisk = await fs.readFile("
|
|
176
|
+
const newsettingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
|
|
177
|
+
encoding: "utf-8",
|
|
178
|
+
})
|
|
169
179
|
expect(newsettingsOnDisk).not.toBe(settingsWithDeifferentFormatting)
|
|
170
180
|
})
|
|
171
181
|
})
|
|
@@ -178,10 +188,11 @@ describe("initialization", () => {
|
|
|
178
188
|
} satisfies InlangModule)
|
|
179
189
|
|
|
180
190
|
const fs = createNodeishMemoryFs()
|
|
181
|
-
await fs.
|
|
191
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
192
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
182
193
|
|
|
183
194
|
const project = await loadProject({
|
|
184
|
-
settingsFilePath: "
|
|
195
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
185
196
|
nodeishFs: fs,
|
|
186
197
|
_import: $badImport,
|
|
187
198
|
})
|
|
@@ -210,9 +221,10 @@ describe("functionality", () => {
|
|
|
210
221
|
describe("settings", () => {
|
|
211
222
|
it("should return the settings", async () => {
|
|
212
223
|
const fs = await createNodeishMemoryFs()
|
|
213
|
-
await fs.
|
|
224
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
225
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
214
226
|
const project = await loadProject({
|
|
215
|
-
settingsFilePath: "
|
|
227
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
216
228
|
nodeishFs: fs,
|
|
217
229
|
_import,
|
|
218
230
|
})
|
|
@@ -222,9 +234,10 @@ describe("functionality", () => {
|
|
|
222
234
|
|
|
223
235
|
it("should set a new settings", async () => {
|
|
224
236
|
const fs = await createNodeishMemoryFs()
|
|
225
|
-
await fs.
|
|
237
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
238
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
226
239
|
const project = await loadProject({
|
|
227
|
-
settingsFilePath: "
|
|
240
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
228
241
|
nodeishFs: fs,
|
|
229
242
|
_import,
|
|
230
243
|
})
|
|
@@ -247,9 +260,10 @@ describe("functionality", () => {
|
|
|
247
260
|
describe("setSettings", () => {
|
|
248
261
|
it("should fail if settings is not valid", async () => {
|
|
249
262
|
const fs = await createNodeishMemoryFs()
|
|
250
|
-
await fs.
|
|
263
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
264
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
251
265
|
const project = await loadProject({
|
|
252
|
-
settingsFilePath: "
|
|
266
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
253
267
|
nodeishFs: fs,
|
|
254
268
|
_import,
|
|
255
269
|
})
|
|
@@ -261,14 +275,15 @@ describe("functionality", () => {
|
|
|
261
275
|
|
|
262
276
|
it("should write settings to disk", async () => {
|
|
263
277
|
const fs = await createNodeishMemoryFs()
|
|
264
|
-
await fs.
|
|
278
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
279
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
265
280
|
const project = await loadProject({
|
|
266
|
-
settingsFilePath: "
|
|
281
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
267
282
|
nodeishFs: fs,
|
|
268
283
|
_import,
|
|
269
284
|
})
|
|
270
285
|
|
|
271
|
-
const before = await fs.readFile("
|
|
286
|
+
const before = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" })
|
|
272
287
|
expect(before).toBeDefined()
|
|
273
288
|
|
|
274
289
|
const result = project.setSettings({ ...settings, languageTags: [] })
|
|
@@ -278,7 +293,7 @@ describe("functionality", () => {
|
|
|
278
293
|
// TODO: how to wait for fs.writeFile to finish?
|
|
279
294
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
280
295
|
|
|
281
|
-
const after = await fs.readFile("
|
|
296
|
+
const after = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" })
|
|
282
297
|
expect(after).toBeDefined()
|
|
283
298
|
expect(after).not.toBe(before)
|
|
284
299
|
})
|
|
@@ -292,9 +307,10 @@ describe("functionality", () => {
|
|
|
292
307
|
languageTags: ["en"],
|
|
293
308
|
modules: ["plugin.js", "lintRule.js"],
|
|
294
309
|
}
|
|
295
|
-
await fs.
|
|
310
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
311
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
296
312
|
const project = await loadProject({
|
|
297
|
-
settingsFilePath: "
|
|
313
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
298
314
|
nodeishFs: fs,
|
|
299
315
|
_import,
|
|
300
316
|
})
|
|
@@ -324,10 +340,11 @@ describe("functionality", () => {
|
|
|
324
340
|
modules: ["plugin.js", "lintRule.js"],
|
|
325
341
|
}
|
|
326
342
|
|
|
327
|
-
await fs.
|
|
343
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
344
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
328
345
|
|
|
329
346
|
const project = await loadProject({
|
|
330
|
-
settingsFilePath: "
|
|
347
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
331
348
|
nodeishFs: fs,
|
|
332
349
|
_import,
|
|
333
350
|
})
|
|
@@ -357,8 +374,9 @@ describe("functionality", () => {
|
|
|
357
374
|
saveMessages: () => undefined,
|
|
358
375
|
}
|
|
359
376
|
const fs = await createNodeishMemoryFs()
|
|
377
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
360
378
|
await fs.writeFile(
|
|
361
|
-
"
|
|
379
|
+
"/user/project/project.inlang.json",
|
|
362
380
|
JSON.stringify({
|
|
363
381
|
sourceLanguageTag: "en",
|
|
364
382
|
languageTags: ["en"],
|
|
@@ -372,7 +390,7 @@ describe("functionality", () => {
|
|
|
372
390
|
} satisfies InlangModule
|
|
373
391
|
}
|
|
374
392
|
const project = await loadProject({
|
|
375
|
-
settingsFilePath: "
|
|
393
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
376
394
|
nodeishFs: fs,
|
|
377
395
|
_import,
|
|
378
396
|
})
|
|
@@ -405,8 +423,9 @@ describe("functionality", () => {
|
|
|
405
423
|
saveMessages: () => undefined,
|
|
406
424
|
}
|
|
407
425
|
const fs = await createNodeishMemoryFs()
|
|
426
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
408
427
|
await fs.writeFile(
|
|
409
|
-
"
|
|
428
|
+
"/user/project/project.inlang.json",
|
|
410
429
|
JSON.stringify({
|
|
411
430
|
sourceLanguageTag: "en",
|
|
412
431
|
languageTags: ["en"],
|
|
@@ -420,7 +439,7 @@ describe("functionality", () => {
|
|
|
420
439
|
}
|
|
421
440
|
|
|
422
441
|
const project = await loadProject({
|
|
423
|
-
settingsFilePath: "
|
|
442
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
424
443
|
nodeishFs: fs,
|
|
425
444
|
_import,
|
|
426
445
|
})
|
|
@@ -436,9 +455,10 @@ describe("functionality", () => {
|
|
|
436
455
|
describe("errors", () => {
|
|
437
456
|
it("should return the errors", async () => {
|
|
438
457
|
const fs = await createNodeishMemoryFs()
|
|
439
|
-
await fs.
|
|
458
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
459
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
440
460
|
const project = await loadProject({
|
|
441
|
-
settingsFilePath: "
|
|
461
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
442
462
|
nodeishFs: fs,
|
|
443
463
|
_import,
|
|
444
464
|
})
|
|
@@ -451,9 +471,10 @@ describe("functionality", () => {
|
|
|
451
471
|
describe("customApi", () => {
|
|
452
472
|
it("should return the app specific api", async () => {
|
|
453
473
|
const fs = await createNodeishMemoryFs()
|
|
454
|
-
await fs.
|
|
474
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
475
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
455
476
|
const project = await loadProject({
|
|
456
|
-
settingsFilePath: "
|
|
477
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
457
478
|
nodeishFs: fs,
|
|
458
479
|
_import,
|
|
459
480
|
})
|
|
@@ -467,9 +488,10 @@ describe("functionality", () => {
|
|
|
467
488
|
describe("messages", () => {
|
|
468
489
|
it("should return the messages", async () => {
|
|
469
490
|
const fs = await createNodeishMemoryFs()
|
|
470
|
-
await fs.
|
|
491
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
492
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
471
493
|
const project = await loadProject({
|
|
472
|
-
settingsFilePath: "
|
|
494
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
473
495
|
nodeishFs: fs,
|
|
474
496
|
_import,
|
|
475
497
|
})
|
|
@@ -491,7 +513,8 @@ describe("functionality", () => {
|
|
|
491
513
|
},
|
|
492
514
|
}
|
|
493
515
|
|
|
494
|
-
await fs.
|
|
516
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
517
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
495
518
|
|
|
496
519
|
await fs.mkdir("./resources")
|
|
497
520
|
|
|
@@ -512,7 +535,7 @@ describe("functionality", () => {
|
|
|
512
535
|
}
|
|
513
536
|
|
|
514
537
|
const project = await loadProject({
|
|
515
|
-
settingsFilePath: "
|
|
538
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
516
539
|
nodeishFs: fs,
|
|
517
540
|
_import,
|
|
518
541
|
})
|
|
@@ -639,14 +662,79 @@ describe("functionality", () => {
|
|
|
639
662
|
},
|
|
640
663
|
])
|
|
641
664
|
})
|
|
665
|
+
|
|
666
|
+
/*
|
|
667
|
+
* Passing all messages to saveMessages() simplifies plugins by an order of magnitude.
|
|
668
|
+
*
|
|
669
|
+
* The alternative would be to pass only the messages that changed to saveMessages().
|
|
670
|
+
* But, this would require plugins to maintain a separate data structure of messages
|
|
671
|
+
* and create optimizations, leading to (unjustified) complexity for plugin authors.
|
|
672
|
+
*
|
|
673
|
+
* Pros:
|
|
674
|
+
* - plugins don't need to transform the data (time complexity).
|
|
675
|
+
* - plugins don't to maintain a separate data structure (space complexity).
|
|
676
|
+
* - plugin authors don't need to deal with optimizations (ecosystem complexity).
|
|
677
|
+
*
|
|
678
|
+
* Cons:
|
|
679
|
+
* - Might be slow for a large number of messages. The requirement hasn't popped up yet though.
|
|
680
|
+
*/
|
|
681
|
+
it("should pass all messages, regardless of which message changed, to saveMessages()", async () => {
|
|
682
|
+
const fs = createNodeishMemoryFs()
|
|
683
|
+
|
|
684
|
+
const settings: ProjectSettings = {
|
|
685
|
+
sourceLanguageTag: "en",
|
|
686
|
+
languageTags: ["en", "de"],
|
|
687
|
+
modules: ["plugin.js"],
|
|
688
|
+
"plugin.project.json": {
|
|
689
|
+
pathPattern: "./resources/{languageTag}.json",
|
|
690
|
+
},
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
694
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
695
|
+
|
|
696
|
+
const mockSaveFn = vi.fn()
|
|
697
|
+
|
|
698
|
+
const _mockPlugin: Plugin = {
|
|
699
|
+
id: "plugin.project.json",
|
|
700
|
+
description: "Mock plugin description",
|
|
701
|
+
displayName: "Mock Plugin",
|
|
702
|
+
loadMessages: () => [
|
|
703
|
+
createMessage("first", { en: "first message" }),
|
|
704
|
+
createMessage("second", { en: "second message" }),
|
|
705
|
+
createMessage("third", { en: "third message" }),
|
|
706
|
+
],
|
|
707
|
+
saveMessages: mockSaveFn,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const _import = async () => {
|
|
711
|
+
return {
|
|
712
|
+
default: _mockPlugin,
|
|
713
|
+
} satisfies InlangModule
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const project = await loadProject({
|
|
717
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
718
|
+
nodeishFs: fs,
|
|
719
|
+
_import,
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
project.query.messages.create({ data: createMessage("fourth", { en: "fourth message" }) })
|
|
723
|
+
|
|
724
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
725
|
+
|
|
726
|
+
expect(mockSaveFn.mock.calls.length).toBe(1)
|
|
727
|
+
expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4)
|
|
728
|
+
})
|
|
642
729
|
})
|
|
643
730
|
|
|
644
731
|
describe("lint", () => {
|
|
645
732
|
it.todo("should throw if lint reports are not initialized yet", async () => {
|
|
646
733
|
const fs = await createNodeishMemoryFs()
|
|
647
|
-
await fs.
|
|
734
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
735
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
648
736
|
const project = await loadProject({
|
|
649
|
-
settingsFilePath: "
|
|
737
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
650
738
|
nodeishFs: fs,
|
|
651
739
|
_import,
|
|
652
740
|
})
|
|
@@ -665,9 +753,10 @@ describe("functionality", () => {
|
|
|
665
753
|
modules: ["lintRule.js"],
|
|
666
754
|
}
|
|
667
755
|
const fs = createNodeishMemoryFs()
|
|
668
|
-
await fs.
|
|
756
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
757
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
669
758
|
const project = await loadProject({
|
|
670
|
-
settingsFilePath: "
|
|
759
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
671
760
|
nodeishFs: fs,
|
|
672
761
|
_import: async () => ({
|
|
673
762
|
default: mockMessageLintRule,
|
package/src/loadProject.ts
CHANGED
|
@@ -21,14 +21,18 @@ import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.j
|
|
|
21
21
|
import { ProjectSettings, Message, type NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
22
22
|
import { tryCatch, type Result } from "@inlang/result"
|
|
23
23
|
import { migrateIfOutdated } from "@inlang/project-settings/migration"
|
|
24
|
+
import { createNodeishFsWithAbsolutePaths } from "./resolve-modules/createNodeishFsWithAbsolutePaths.js"
|
|
24
25
|
|
|
25
26
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Creates an inlang instance.
|
|
29
30
|
*
|
|
30
|
-
* -
|
|
31
|
+
* @param settingsFilePath - Absolute path to the inlang settings file.
|
|
32
|
+
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
33
|
+
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
31
34
|
* and supporting legacy resolvedModules such as CJS.
|
|
35
|
+
* @param _capture - Use `_capture` to capture events for analytics.
|
|
32
36
|
*
|
|
33
37
|
*/
|
|
34
38
|
export const loadProject = async (args: {
|
|
@@ -40,11 +44,18 @@ export const loadProject = async (args: {
|
|
|
40
44
|
return await createRoot(async () => {
|
|
41
45
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable()
|
|
42
46
|
|
|
47
|
+
// -- absolute path env -----------------------------------------------
|
|
48
|
+
|
|
49
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
50
|
+
nodeishFs: args.nodeishFs,
|
|
51
|
+
basePath: args.settingsFilePath.replace(/(.*?)[^/]*\..*$/, "$1"), // get directory of settings file
|
|
52
|
+
})
|
|
53
|
+
|
|
43
54
|
// -- settings ------------------------------------------------------------
|
|
44
55
|
|
|
45
56
|
const [settings, _setSettings] = createSignal<ProjectSettings>()
|
|
46
57
|
createEffect(() => {
|
|
47
|
-
loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs
|
|
58
|
+
loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs })
|
|
48
59
|
.then((settings) => {
|
|
49
60
|
setSettings(settings)
|
|
50
61
|
// rename settings to get a convenient access to the data in Posthog
|
|
@@ -58,7 +69,7 @@ export const loadProject = async (args: {
|
|
|
58
69
|
// TODO: create FS watcher and update settings on change
|
|
59
70
|
|
|
60
71
|
const writeSettingsToDisk = skipFirst((settings: ProjectSettings) =>
|
|
61
|
-
_writeSettingsToDisk({ nodeishFs
|
|
72
|
+
_writeSettingsToDisk({ nodeishFs, settings })
|
|
62
73
|
)
|
|
63
74
|
|
|
64
75
|
const setSettings = (settings: ProjectSettings): Result<void, ProjectSettingsInvalidError> => {
|
|
@@ -88,7 +99,7 @@ export const loadProject = async (args: {
|
|
|
88
99
|
const _settings = settings()
|
|
89
100
|
if (!_settings) return
|
|
90
101
|
|
|
91
|
-
resolveModules({ settings: _settings, nodeishFs
|
|
102
|
+
resolveModules({ settings: _settings, nodeishFs, _import: args._import })
|
|
92
103
|
.then((resolvedModules) => {
|
|
93
104
|
setResolvedModules(resolvedModules)
|
|
94
105
|
})
|
|
@@ -327,3 +338,5 @@ export function createSubscribable<T>(signal: () => T): Subscribable<T> {
|
|
|
327
338
|
},
|
|
328
339
|
})
|
|
329
340
|
}
|
|
341
|
+
|
|
342
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
2
|
+
import { describe, it, expect } from "vitest"
|
|
3
|
+
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
4
|
+
|
|
5
|
+
describe("createNodeishFsWithAbsolutePaths", () => {
|
|
6
|
+
it("throws an error if basePath is not an absolute path", () => {
|
|
7
|
+
const invalidBasePath = "relative/path"
|
|
8
|
+
const nodeishFs: NodeishFilesystemSubset = {
|
|
9
|
+
// @ts-expect-error
|
|
10
|
+
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
11
|
+
readdir: async () => Promise.resolve([]),
|
|
12
|
+
mkdir: async () => Promise.resolve(""),
|
|
13
|
+
writeFile: async () => Promise.resolve(),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
expect(() =>
|
|
17
|
+
createNodeishFsWithAbsolutePaths({ basePath: invalidBasePath, nodeishFs })
|
|
18
|
+
).toThrowError("The argument `settingsFilePath` of `loadProject()` must be an absolute path.")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("intercepts paths correctly for readFile", async () => {
|
|
22
|
+
const basePath = "/absolute/path"
|
|
23
|
+
const filePath = "file.txt"
|
|
24
|
+
const expectedPath = "/absolute/path/file.txt"
|
|
25
|
+
|
|
26
|
+
const nodeishFs: NodeishFilesystemSubset = {
|
|
27
|
+
// @ts-expect-error
|
|
28
|
+
readFile: async (path) => {
|
|
29
|
+
expect(path).toBe(expectedPath)
|
|
30
|
+
return Promise.resolve(new Uint8Array(0))
|
|
31
|
+
},
|
|
32
|
+
readdir: async () => Promise.resolve([]),
|
|
33
|
+
mkdir: async () => Promise.resolve(""),
|
|
34
|
+
writeFile: async () => Promise.resolve(),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
38
|
+
await interceptedFs.readFile(filePath, { encoding: "utf-8" })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("intercepts paths correctly for readdir", async () => {
|
|
42
|
+
const basePath = "/absolute/path"
|
|
43
|
+
const dirPath = "dir"
|
|
44
|
+
const expectedPath = "/absolute/path/dir"
|
|
45
|
+
|
|
46
|
+
const nodeishFs: NodeishFilesystemSubset = {
|
|
47
|
+
// @ts-expect-error
|
|
48
|
+
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
49
|
+
readdir: async (path) => {
|
|
50
|
+
expect(path).toBe(expectedPath)
|
|
51
|
+
return Promise.resolve([])
|
|
52
|
+
},
|
|
53
|
+
mkdir: async () => Promise.resolve(""),
|
|
54
|
+
writeFile: async () => Promise.resolve(),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
58
|
+
await interceptedFs.readdir(dirPath)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("intercepts paths correctly for mkdir", async () => {
|
|
62
|
+
const basePath = "/absolute/path"
|
|
63
|
+
const dirPath = "newDir"
|
|
64
|
+
const expectedPath = "/absolute/path/newDir"
|
|
65
|
+
|
|
66
|
+
const nodeishFs: NodeishFilesystemSubset = {
|
|
67
|
+
// @ts-expect-error
|
|
68
|
+
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
69
|
+
readdir: async () => Promise.resolve([]),
|
|
70
|
+
mkdir: async (path) => {
|
|
71
|
+
expect(path).toBe(expectedPath)
|
|
72
|
+
return Promise.resolve("")
|
|
73
|
+
},
|
|
74
|
+
writeFile: async () => Promise.resolve(),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
78
|
+
await interceptedFs.mkdir(dirPath)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it("intercepts paths correctly for writeFile", async () => {
|
|
82
|
+
const basePath = "/absolute/path"
|
|
83
|
+
const filePath = "file.txt"
|
|
84
|
+
const expectedPath = "/absolute/path/file.txt"
|
|
85
|
+
const data = "Hello, World!"
|
|
86
|
+
|
|
87
|
+
const nodeishFs: NodeishFilesystemSubset = {
|
|
88
|
+
// @ts-expect-error
|
|
89
|
+
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
90
|
+
readdir: async () => Promise.resolve([]),
|
|
91
|
+
mkdir: async () => Promise.resolve(""),
|
|
92
|
+
writeFile: async (path, content) => {
|
|
93
|
+
expect(path).toBe(expectedPath)
|
|
94
|
+
expect(content).toBe(data)
|
|
95
|
+
return Promise.resolve()
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
100
|
+
await interceptedFs.writeFile(filePath, data)
|
|
101
|
+
})
|
|
102
|
+
})
|