@inlang/sdk 0.10.0 → 0.12.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/createNodeishFsWithAbsolutePaths.d.ts +13 -0
- package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -0
- package/dist/createNodeishFsWithAbsolutePaths.js +29 -0
- package/dist/createNodeishFsWithAbsolutePaths.test.d.ts +2 -0
- package/dist/createNodeishFsWithAbsolutePaths.test.d.ts.map +1 -0
- package/dist/createNodeishFsWithAbsolutePaths.test.js +37 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -0
- package/dist/loadProject.d.ts +4 -1
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +18 -5
- package/dist/loadProject.test.js +134 -44
- 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/createNodeishFsWithAbsolutePaths.test.ts +47 -0
- package/src/createNodeishFsWithAbsolutePaths.ts +40 -0
- package/src/errors.ts +7 -0
- package/src/loadProject.test.ts +148 -43
- package/src/loadProject.ts +25 -4
- package/src/resolve-modules/import.ts +3 -2
- package/src/resolve-modules/plugins/types.ts +2 -2
|
@@ -72,9 +72,10 @@ const $import = async (name) => ({
|
|
|
72
72
|
describe("config", () => {
|
|
73
73
|
it("should react to changes in config", async () => {
|
|
74
74
|
const fs = createNodeishMemoryFs();
|
|
75
|
-
await fs.
|
|
75
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
76
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
|
|
76
77
|
const project = solidAdapter(await loadProject({
|
|
77
|
-
settingsFilePath: "
|
|
78
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
78
79
|
nodeishFs: fs,
|
|
79
80
|
_import: $import,
|
|
80
81
|
}), { from });
|
|
@@ -95,9 +96,10 @@ describe("config", () => {
|
|
|
95
96
|
describe("installed", () => {
|
|
96
97
|
it("react to changes that are unrelated to installed items", async () => {
|
|
97
98
|
const fs = createNodeishMemoryFs();
|
|
98
|
-
await fs.
|
|
99
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
100
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
|
|
99
101
|
const project = solidAdapter(await loadProject({
|
|
100
|
-
settingsFilePath: "
|
|
102
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
101
103
|
nodeishFs: fs,
|
|
102
104
|
_import: $import,
|
|
103
105
|
}), { from });
|
|
@@ -147,9 +149,10 @@ describe("messages", () => {
|
|
|
147
149
|
saveMessages: () => undefined,
|
|
148
150
|
};
|
|
149
151
|
const mockImport = async () => ({ default: mockPlugin });
|
|
150
|
-
await fs.
|
|
152
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
153
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(mockConfig));
|
|
151
154
|
const project = solidAdapter(await loadProject({
|
|
152
|
-
settingsFilePath: "
|
|
155
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
153
156
|
nodeishFs: fs,
|
|
154
157
|
_import: mockImport,
|
|
155
158
|
}), { from });
|
|
@@ -167,9 +170,10 @@ describe("messages", () => {
|
|
|
167
170
|
});
|
|
168
171
|
it("should react to changes in messages", async () => {
|
|
169
172
|
const fs = createNodeishMemoryFs();
|
|
170
|
-
await fs.
|
|
173
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
174
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
|
|
171
175
|
const project = solidAdapter(await loadProject({
|
|
172
|
-
settingsFilePath: "
|
|
176
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
173
177
|
nodeishFs: fs,
|
|
174
178
|
_import: $import,
|
|
175
179
|
}), { from });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NodeishFilesystemSubset } from "@inlang/plugin";
|
|
2
|
+
export declare const isAbsolutePath: (path: string) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
5
|
+
* and prepends the base path.
|
|
6
|
+
*
|
|
7
|
+
* The paths are resolved from the `settingsFilePath` argument.
|
|
8
|
+
*/
|
|
9
|
+
export declare const createNodeishFsWithAbsolutePaths: (args: {
|
|
10
|
+
settingsFilePath: string;
|
|
11
|
+
nodeishFs: NodeishFilesystemSubset;
|
|
12
|
+
}) => NodeishFilesystemSubset;
|
|
13
|
+
//# sourceMappingURL=createNodeishFsWithAbsolutePaths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNodeishFsWithAbsolutePaths.d.ts","sourceRoot":"","sources":["../src/createNodeishFsWithAbsolutePaths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAG7D,eAAO,MAAM,cAAc,SAAU,MAAM,YAAwB,CAAA;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,SAAU;IACtD,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAG,uBAyBH,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { normalizePath } from "@lix-js/fs";
|
|
2
|
+
export const isAbsolutePath = (path) => /^[/\\]/.test(path);
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
5
|
+
* and prepends the base path.
|
|
6
|
+
*
|
|
7
|
+
* The paths are resolved from the `settingsFilePath` argument.
|
|
8
|
+
*/
|
|
9
|
+
export const createNodeishFsWithAbsolutePaths = (args) => {
|
|
10
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
11
|
+
throw new Error("The argument `settingsFilePath` must be an absolute path.");
|
|
12
|
+
}
|
|
13
|
+
// get the base path of the settings file by
|
|
14
|
+
// removing the file name from the path
|
|
15
|
+
const bathPath = args.settingsFilePath.split("/").slice(0, -1).join("/");
|
|
16
|
+
const makeAbsolute = (path) => {
|
|
17
|
+
if (isAbsolutePath(path)) {
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
return normalizePath(bathPath + "/" + path);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
// @ts-expect-error
|
|
24
|
+
readFile: (path, options) => args.nodeishFs.readFile(makeAbsolute(path), options),
|
|
25
|
+
readdir: (path) => args.nodeishFs.readdir(makeAbsolute(path)),
|
|
26
|
+
mkdir: (path) => args.nodeishFs.mkdir(makeAbsolute(path)),
|
|
27
|
+
writeFile: (path, data) => args.nodeishFs.writeFile(makeAbsolute(path), data),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNodeishFsWithAbsolutePaths.test.d.ts","sourceRoot":"","sources":["../src/createNodeishFsWithAbsolutePaths.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { it, expect, vi } from "vitest";
|
|
2
|
+
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js";
|
|
3
|
+
it("throws an error if settingsFilePath is not an absolute path", () => {
|
|
4
|
+
const relativePath = "relative/path";
|
|
5
|
+
expect(() => createNodeishFsWithAbsolutePaths({ settingsFilePath: relativePath, nodeishFs: {} })).toThrow();
|
|
6
|
+
});
|
|
7
|
+
it("intercepts paths correctly for readFile", async () => {
|
|
8
|
+
const settingsFilePath = `/Users/samuel/Documents/paraglide/example/project.inlang.json`;
|
|
9
|
+
const filePaths = [
|
|
10
|
+
["file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
11
|
+
["./file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
12
|
+
["./folder/file.txt", `/Users/samuel/Documents/paraglide/example/folder/file.txt`],
|
|
13
|
+
["../file.txt", `/Users/samuel/Documents/paraglide/file.txt`],
|
|
14
|
+
["../folder/file.txt", `/Users/samuel/Documents/paraglide/folder/file.txt`],
|
|
15
|
+
["../../file.txt", `/Users/samuel/Documents/file.txt`],
|
|
16
|
+
["../../../file.txt", `/Users/samuel/file.txt`],
|
|
17
|
+
];
|
|
18
|
+
const mockNodeishFs = {
|
|
19
|
+
readFile: vi.fn(),
|
|
20
|
+
readdir: vi.fn(),
|
|
21
|
+
mkdir: vi.fn(),
|
|
22
|
+
writeFile: vi.fn(),
|
|
23
|
+
};
|
|
24
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({
|
|
25
|
+
settingsFilePath,
|
|
26
|
+
nodeishFs: mockNodeishFs,
|
|
27
|
+
});
|
|
28
|
+
for (const [path, expectedPath] of filePaths) {
|
|
29
|
+
for (const fn of Object.keys(mockNodeishFs)) {
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
await interceptedFs[fn](path);
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
// expect the first argument to be the expectedPath
|
|
34
|
+
expect(mockNodeishFs[fn].mock.lastCall[0]).toBe(expectedPath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ValueError } from "@sinclair/typebox/errors";
|
|
2
|
+
export declare class LoadProjectInvalidArgument extends Error {
|
|
3
|
+
constructor(message: string, options: {
|
|
4
|
+
argument: string;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
2
7
|
export declare class ProjectSettingsInvalidError extends Error {
|
|
3
8
|
constructor(options: {
|
|
4
9
|
errors: ValueError[];
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;CAQ7C;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAChD,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAOnE;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAIpE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,0BAA2B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE;CAI1D;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;CAQ7C;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAChD,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAOnE;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAIpE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD"}
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export class LoadProjectInvalidArgument extends Error {
|
|
2
|
+
constructor(message, options) {
|
|
3
|
+
super(`The argument "${options.argument}" of loadProject() is invalid: ${message}`);
|
|
4
|
+
this.name = "LoadProjectInvalidArgument";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
1
7
|
export class ProjectSettingsInvalidError extends Error {
|
|
2
8
|
constructor(options) {
|
|
3
9
|
super(`The project settings are invalid:\n\n${options.errors
|
package/dist/loadProject.d.ts
CHANGED
|
@@ -4,8 +4,11 @@ import { type NodeishFilesystemSubset } from "./versionedInterfaces.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Creates an inlang instance.
|
|
6
6
|
*
|
|
7
|
-
* -
|
|
7
|
+
* @param settingsFilePath - Absolute path to the inlang settings file.
|
|
8
|
+
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
9
|
+
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
8
10
|
* and supporting legacy resolvedModules such as CJS.
|
|
11
|
+
* @param _capture - Use `_capture` to capture events for analytics.
|
|
9
12
|
*
|
|
10
13
|
*/
|
|
11
14
|
export declare const loadProject: (args: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;AAchF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAUjG;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAkMxB,CAAA;AAqGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/loadProject.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveModules } from "./resolve-modules/index.js";
|
|
2
2
|
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
3
|
-
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
|
|
3
|
+
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, LoadProjectInvalidArgument, } from "./errors.js";
|
|
4
4
|
import { createRoot, createSignal, createEffect } from "./reactivity/solid.js";
|
|
5
5
|
import { createMessagesQuery } from "./createMessagesQuery.js";
|
|
6
6
|
import { debounce } from "throttle-debounce";
|
|
@@ -8,21 +8,34 @@ import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.j
|
|
|
8
8
|
import { ProjectSettings, Message } from "./versionedInterfaces.js";
|
|
9
9
|
import { tryCatch } from "@inlang/result";
|
|
10
10
|
import { migrateIfOutdated } from "@inlang/project-settings/migration";
|
|
11
|
+
import { createNodeishFsWithAbsolutePaths, isAbsolutePath, } from "./createNodeishFsWithAbsolutePaths.js";
|
|
11
12
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
|
|
12
13
|
/**
|
|
13
14
|
* Creates an inlang instance.
|
|
14
15
|
*
|
|
15
|
-
* -
|
|
16
|
+
* @param settingsFilePath - Absolute path to the inlang settings file.
|
|
17
|
+
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
18
|
+
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
16
19
|
* and supporting legacy resolvedModules such as CJS.
|
|
20
|
+
* @param _capture - Use `_capture` to capture events for analytics.
|
|
17
21
|
*
|
|
18
22
|
*/
|
|
19
23
|
export const loadProject = async (args) => {
|
|
24
|
+
// -- validation --------------------------------------------------------
|
|
25
|
+
//! the only place where throwing is acceptable because the project
|
|
26
|
+
//! won't even be loaded. do not throw anywhere else. otherwise, apps
|
|
27
|
+
//! can't handle errors gracefully.
|
|
28
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
29
|
+
throw new LoadProjectInvalidArgument(`Expected an absolute path but received "${args.settingsFilePath}".`, { argument: "settingsFilePath" });
|
|
30
|
+
}
|
|
31
|
+
// -- load project ------------------------------------------------------
|
|
20
32
|
return await createRoot(async () => {
|
|
21
33
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable();
|
|
34
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths(args);
|
|
22
35
|
// -- settings ------------------------------------------------------------
|
|
23
36
|
const [settings, _setSettings] = createSignal();
|
|
24
37
|
createEffect(() => {
|
|
25
|
-
loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs
|
|
38
|
+
loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs })
|
|
26
39
|
.then((settings) => {
|
|
27
40
|
setSettings(settings);
|
|
28
41
|
// rename settings to get a convenient access to the data in Posthog
|
|
@@ -34,7 +47,7 @@ export const loadProject = async (args) => {
|
|
|
34
47
|
});
|
|
35
48
|
});
|
|
36
49
|
// TODO: create FS watcher and update settings on change
|
|
37
|
-
const writeSettingsToDisk = skipFirst((settings) => _writeSettingsToDisk({ nodeishFs
|
|
50
|
+
const writeSettingsToDisk = skipFirst((settings) => _writeSettingsToDisk({ nodeishFs, settings }));
|
|
38
51
|
const setSettings = (settings) => {
|
|
39
52
|
try {
|
|
40
53
|
const validatedSettings = parseSettings(settings);
|
|
@@ -55,7 +68,7 @@ export const loadProject = async (args) => {
|
|
|
55
68
|
const _settings = settings();
|
|
56
69
|
if (!_settings)
|
|
57
70
|
return;
|
|
58
|
-
resolveModules({ settings: _settings, nodeishFs
|
|
71
|
+
resolveModules({ settings: _settings, nodeishFs, _import: args._import })
|
|
59
72
|
.then((resolvedModules) => {
|
|
60
73
|
setResolvedModules(resolvedModules);
|
|
61
74
|
})
|
package/dist/loadProject.test.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
import { describe, it, expect, vi } from "vitest";
|
|
3
3
|
import { loadProject } from "./loadProject.js";
|
|
4
|
-
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, } from "./errors.js";
|
|
4
|
+
import { LoadProjectInvalidArgument, ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, } from "./errors.js";
|
|
5
5
|
import { createNodeishMemoryFs } from "@lix-js/fs";
|
|
6
|
+
import { createMessage } from "./test-utilities/createMessage.js";
|
|
7
|
+
import { tryCatch } from "@inlang/result";
|
|
6
8
|
// ------------------------------------------------------------------------------------------------
|
|
7
9
|
const getValue = (subscribable) => {
|
|
8
10
|
let value;
|
|
@@ -79,11 +81,22 @@ const _import = async (name) => ({
|
|
|
79
81
|
});
|
|
80
82
|
// ------------------------------------------------------------------------------------------------
|
|
81
83
|
describe("initialization", () => {
|
|
84
|
+
it("should throw if settingsFilePath is not an absolute path", async () => {
|
|
85
|
+
const fs = createNodeishMemoryFs();
|
|
86
|
+
const result = await tryCatch(() => loadProject({
|
|
87
|
+
settingsFilePath: "relative/path",
|
|
88
|
+
nodeishFs: fs,
|
|
89
|
+
_import,
|
|
90
|
+
}));
|
|
91
|
+
expect(result.error).toBeInstanceOf(LoadProjectInvalidArgument);
|
|
92
|
+
expect(result.data).toBeUndefined();
|
|
93
|
+
});
|
|
82
94
|
describe("settings", () => {
|
|
83
95
|
it("should return an error if settings file is not found", async () => {
|
|
84
96
|
const fs = createNodeishMemoryFs();
|
|
97
|
+
fs.mkdir("/user/project", { recursive: true });
|
|
85
98
|
const project = await loadProject({
|
|
86
|
-
settingsFilePath: "
|
|
99
|
+
settingsFilePath: "/user/project/test.json",
|
|
87
100
|
nodeishFs: fs,
|
|
88
101
|
_import,
|
|
89
102
|
});
|
|
@@ -91,9 +104,10 @@ describe("initialization", () => {
|
|
|
91
104
|
});
|
|
92
105
|
it("should return an error if settings file is not a valid JSON", async () => {
|
|
93
106
|
const fs = await createNodeishMemoryFs();
|
|
94
|
-
await fs.
|
|
107
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
108
|
+
await fs.writeFile("/user/project/project.inlang.json", "invalid json");
|
|
95
109
|
const project = await loadProject({
|
|
96
|
-
settingsFilePath: "
|
|
110
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
97
111
|
nodeishFs: fs,
|
|
98
112
|
_import,
|
|
99
113
|
});
|
|
@@ -101,9 +115,10 @@ describe("initialization", () => {
|
|
|
101
115
|
});
|
|
102
116
|
it("should return an error if settings file is does not match schema", async () => {
|
|
103
117
|
const fs = await createNodeishMemoryFs();
|
|
104
|
-
await fs.
|
|
118
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
119
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({}));
|
|
105
120
|
const project = await loadProject({
|
|
106
|
-
settingsFilePath: "
|
|
121
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
107
122
|
nodeishFs: fs,
|
|
108
123
|
_import,
|
|
109
124
|
});
|
|
@@ -111,9 +126,10 @@ describe("initialization", () => {
|
|
|
111
126
|
});
|
|
112
127
|
it("should return the parsed settings", async () => {
|
|
113
128
|
const fs = await createNodeishMemoryFs();
|
|
114
|
-
await fs.
|
|
129
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
130
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
115
131
|
const project = await loadProject({
|
|
116
|
-
settingsFilePath: "
|
|
132
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
117
133
|
nodeishFs: fs,
|
|
118
134
|
_import,
|
|
119
135
|
});
|
|
@@ -122,18 +138,23 @@ describe("initialization", () => {
|
|
|
122
138
|
it("should not re-write the settings to disk when initializing", async () => {
|
|
123
139
|
const fs = await createNodeishMemoryFs();
|
|
124
140
|
const settingsWithDeifferentFormatting = JSON.stringify(settings, undefined, 4);
|
|
125
|
-
await fs.
|
|
141
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
142
|
+
await fs.writeFile("/user/project/project.inlang.json", settingsWithDeifferentFormatting);
|
|
126
143
|
const project = await loadProject({
|
|
127
|
-
settingsFilePath: "
|
|
144
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
128
145
|
nodeishFs: fs,
|
|
129
146
|
_import,
|
|
130
147
|
});
|
|
131
|
-
const settingsOnDisk = await fs.readFile("
|
|
148
|
+
const settingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
|
|
149
|
+
encoding: "utf-8",
|
|
150
|
+
});
|
|
132
151
|
expect(settingsOnDisk).toBe(settingsWithDeifferentFormatting);
|
|
133
152
|
project.setSettings(project.settings());
|
|
134
153
|
// TODO: how can we await `setsettings` correctly
|
|
135
154
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
136
|
-
const newsettingsOnDisk = await fs.readFile("
|
|
155
|
+
const newsettingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
|
|
156
|
+
encoding: "utf-8",
|
|
157
|
+
});
|
|
137
158
|
expect(newsettingsOnDisk).not.toBe(settingsWithDeifferentFormatting);
|
|
138
159
|
});
|
|
139
160
|
});
|
|
@@ -143,9 +164,10 @@ describe("initialization", () => {
|
|
|
143
164
|
default: {},
|
|
144
165
|
});
|
|
145
166
|
const fs = createNodeishMemoryFs();
|
|
146
|
-
await fs.
|
|
167
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
168
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
147
169
|
const project = await loadProject({
|
|
148
|
-
settingsFilePath: "
|
|
170
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
149
171
|
nodeishFs: fs,
|
|
150
172
|
_import: $badImport,
|
|
151
173
|
});
|
|
@@ -170,9 +192,10 @@ describe("functionality", () => {
|
|
|
170
192
|
describe("settings", () => {
|
|
171
193
|
it("should return the settings", async () => {
|
|
172
194
|
const fs = await createNodeishMemoryFs();
|
|
173
|
-
await fs.
|
|
195
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
196
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
174
197
|
const project = await loadProject({
|
|
175
|
-
settingsFilePath: "
|
|
198
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
176
199
|
nodeishFs: fs,
|
|
177
200
|
_import,
|
|
178
201
|
});
|
|
@@ -180,9 +203,10 @@ describe("functionality", () => {
|
|
|
180
203
|
});
|
|
181
204
|
it("should set a new settings", async () => {
|
|
182
205
|
const fs = await createNodeishMemoryFs();
|
|
183
|
-
await fs.
|
|
206
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
207
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
184
208
|
const project = await loadProject({
|
|
185
|
-
settingsFilePath: "
|
|
209
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
186
210
|
nodeishFs: fs,
|
|
187
211
|
_import,
|
|
188
212
|
});
|
|
@@ -201,9 +225,10 @@ describe("functionality", () => {
|
|
|
201
225
|
describe("setSettings", () => {
|
|
202
226
|
it("should fail if settings is not valid", async () => {
|
|
203
227
|
const fs = await createNodeishMemoryFs();
|
|
204
|
-
await fs.
|
|
228
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
229
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
205
230
|
const project = await loadProject({
|
|
206
|
-
settingsFilePath: "
|
|
231
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
207
232
|
nodeishFs: fs,
|
|
208
233
|
_import,
|
|
209
234
|
});
|
|
@@ -213,20 +238,21 @@ describe("functionality", () => {
|
|
|
213
238
|
});
|
|
214
239
|
it("should write settings to disk", async () => {
|
|
215
240
|
const fs = await createNodeishMemoryFs();
|
|
216
|
-
await fs.
|
|
241
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
242
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
217
243
|
const project = await loadProject({
|
|
218
|
-
settingsFilePath: "
|
|
244
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
219
245
|
nodeishFs: fs,
|
|
220
246
|
_import,
|
|
221
247
|
});
|
|
222
|
-
const before = await fs.readFile("
|
|
248
|
+
const before = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" });
|
|
223
249
|
expect(before).toBeDefined();
|
|
224
250
|
const result = project.setSettings({ ...settings, languageTags: [] });
|
|
225
251
|
expect(result.data).toBeUndefined();
|
|
226
252
|
expect(result.error).toBeUndefined();
|
|
227
253
|
// TODO: how to wait for fs.writeFile to finish?
|
|
228
254
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
229
|
-
const after = await fs.readFile("
|
|
255
|
+
const after = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" });
|
|
230
256
|
expect(after).toBeDefined();
|
|
231
257
|
expect(after).not.toBe(before);
|
|
232
258
|
});
|
|
@@ -239,9 +265,10 @@ describe("functionality", () => {
|
|
|
239
265
|
languageTags: ["en"],
|
|
240
266
|
modules: ["plugin.js", "lintRule.js"],
|
|
241
267
|
};
|
|
242
|
-
await fs.
|
|
268
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
269
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
243
270
|
const project = await loadProject({
|
|
244
|
-
settingsFilePath: "
|
|
271
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
245
272
|
nodeishFs: fs,
|
|
246
273
|
_import,
|
|
247
274
|
});
|
|
@@ -266,9 +293,10 @@ describe("functionality", () => {
|
|
|
266
293
|
languageTags: ["en"],
|
|
267
294
|
modules: ["plugin.js", "lintRule.js"],
|
|
268
295
|
};
|
|
269
|
-
await fs.
|
|
296
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
297
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
270
298
|
const project = await loadProject({
|
|
271
|
-
settingsFilePath: "
|
|
299
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
272
300
|
nodeishFs: fs,
|
|
273
301
|
_import,
|
|
274
302
|
});
|
|
@@ -296,7 +324,8 @@ describe("functionality", () => {
|
|
|
296
324
|
saveMessages: () => undefined,
|
|
297
325
|
};
|
|
298
326
|
const fs = await createNodeishMemoryFs();
|
|
299
|
-
await fs.
|
|
327
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
328
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({
|
|
300
329
|
sourceLanguageTag: "en",
|
|
301
330
|
languageTags: ["en"],
|
|
302
331
|
modules: ["plugin.js", "lintRule.js"],
|
|
@@ -307,7 +336,7 @@ describe("functionality", () => {
|
|
|
307
336
|
};
|
|
308
337
|
};
|
|
309
338
|
const project = await loadProject({
|
|
310
|
-
settingsFilePath: "
|
|
339
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
311
340
|
nodeishFs: fs,
|
|
312
341
|
_import,
|
|
313
342
|
});
|
|
@@ -337,7 +366,8 @@ describe("functionality", () => {
|
|
|
337
366
|
saveMessages: () => undefined,
|
|
338
367
|
};
|
|
339
368
|
const fs = await createNodeishMemoryFs();
|
|
340
|
-
await fs.
|
|
369
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
370
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({
|
|
341
371
|
sourceLanguageTag: "en",
|
|
342
372
|
languageTags: ["en"],
|
|
343
373
|
modules: ["plugin.js", "lintRule.js"],
|
|
@@ -348,7 +378,7 @@ describe("functionality", () => {
|
|
|
348
378
|
};
|
|
349
379
|
};
|
|
350
380
|
const project = await loadProject({
|
|
351
|
-
settingsFilePath: "
|
|
381
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
352
382
|
nodeishFs: fs,
|
|
353
383
|
_import,
|
|
354
384
|
});
|
|
@@ -359,9 +389,10 @@ describe("functionality", () => {
|
|
|
359
389
|
describe("errors", () => {
|
|
360
390
|
it("should return the errors", async () => {
|
|
361
391
|
const fs = await createNodeishMemoryFs();
|
|
362
|
-
await fs.
|
|
392
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
393
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
363
394
|
const project = await loadProject({
|
|
364
|
-
settingsFilePath: "
|
|
395
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
365
396
|
nodeishFs: fs,
|
|
366
397
|
_import,
|
|
367
398
|
});
|
|
@@ -373,9 +404,10 @@ describe("functionality", () => {
|
|
|
373
404
|
describe("customApi", () => {
|
|
374
405
|
it("should return the app specific api", async () => {
|
|
375
406
|
const fs = await createNodeishMemoryFs();
|
|
376
|
-
await fs.
|
|
407
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
408
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
377
409
|
const project = await loadProject({
|
|
378
|
-
settingsFilePath: "
|
|
410
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
379
411
|
nodeishFs: fs,
|
|
380
412
|
_import,
|
|
381
413
|
});
|
|
@@ -387,9 +419,10 @@ describe("functionality", () => {
|
|
|
387
419
|
describe("messages", () => {
|
|
388
420
|
it("should return the messages", async () => {
|
|
389
421
|
const fs = await createNodeishMemoryFs();
|
|
390
|
-
await fs.
|
|
422
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
423
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
391
424
|
const project = await loadProject({
|
|
392
|
-
settingsFilePath: "
|
|
425
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
393
426
|
nodeishFs: fs,
|
|
394
427
|
_import,
|
|
395
428
|
});
|
|
@@ -407,7 +440,8 @@ describe("functionality", () => {
|
|
|
407
440
|
pathPattern: "./resources/{languageTag}.json",
|
|
408
441
|
},
|
|
409
442
|
};
|
|
410
|
-
await fs.
|
|
443
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
444
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
411
445
|
await fs.mkdir("./resources");
|
|
412
446
|
const mockSaveFn = vi.fn();
|
|
413
447
|
const _mockPlugin = {
|
|
@@ -423,7 +457,7 @@ describe("functionality", () => {
|
|
|
423
457
|
};
|
|
424
458
|
};
|
|
425
459
|
const project = await loadProject({
|
|
426
|
-
settingsFilePath: "
|
|
460
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
427
461
|
nodeishFs: fs,
|
|
428
462
|
_import,
|
|
429
463
|
});
|
|
@@ -543,13 +577,68 @@ describe("functionality", () => {
|
|
|
543
577
|
},
|
|
544
578
|
]);
|
|
545
579
|
});
|
|
580
|
+
/*
|
|
581
|
+
* Passing all messages to saveMessages() simplifies plugins by an order of magnitude.
|
|
582
|
+
*
|
|
583
|
+
* The alternative would be to pass only the messages that changed to saveMessages().
|
|
584
|
+
* But, this would require plugins to maintain a separate data structure of messages
|
|
585
|
+
* and create optimizations, leading to (unjustified) complexity for plugin authors.
|
|
586
|
+
*
|
|
587
|
+
* Pros:
|
|
588
|
+
* - plugins don't need to transform the data (time complexity).
|
|
589
|
+
* - plugins don't to maintain a separate data structure (space complexity).
|
|
590
|
+
* - plugin authors don't need to deal with optimizations (ecosystem complexity).
|
|
591
|
+
*
|
|
592
|
+
* Cons:
|
|
593
|
+
* - Might be slow for a large number of messages. The requirement hasn't popped up yet though.
|
|
594
|
+
*/
|
|
595
|
+
it("should pass all messages, regardless of which message changed, to saveMessages()", async () => {
|
|
596
|
+
const fs = createNodeishMemoryFs();
|
|
597
|
+
const settings = {
|
|
598
|
+
sourceLanguageTag: "en",
|
|
599
|
+
languageTags: ["en", "de"],
|
|
600
|
+
modules: ["plugin.js"],
|
|
601
|
+
"plugin.project.json": {
|
|
602
|
+
pathPattern: "./resources/{languageTag}.json",
|
|
603
|
+
},
|
|
604
|
+
};
|
|
605
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
606
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
607
|
+
const mockSaveFn = vi.fn();
|
|
608
|
+
const _mockPlugin = {
|
|
609
|
+
id: "plugin.project.json",
|
|
610
|
+
description: "Mock plugin description",
|
|
611
|
+
displayName: "Mock Plugin",
|
|
612
|
+
loadMessages: () => [
|
|
613
|
+
createMessage("first", { en: "first message" }),
|
|
614
|
+
createMessage("second", { en: "second message" }),
|
|
615
|
+
createMessage("third", { en: "third message" }),
|
|
616
|
+
],
|
|
617
|
+
saveMessages: mockSaveFn,
|
|
618
|
+
};
|
|
619
|
+
const _import = async () => {
|
|
620
|
+
return {
|
|
621
|
+
default: _mockPlugin,
|
|
622
|
+
};
|
|
623
|
+
};
|
|
624
|
+
const project = await loadProject({
|
|
625
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
626
|
+
nodeishFs: fs,
|
|
627
|
+
_import,
|
|
628
|
+
});
|
|
629
|
+
project.query.messages.create({ data: createMessage("fourth", { en: "fourth message" }) });
|
|
630
|
+
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
631
|
+
expect(mockSaveFn.mock.calls.length).toBe(1);
|
|
632
|
+
expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4);
|
|
633
|
+
});
|
|
546
634
|
});
|
|
547
635
|
describe("lint", () => {
|
|
548
636
|
it.todo("should throw if lint reports are not initialized yet", async () => {
|
|
549
637
|
const fs = await createNodeishMemoryFs();
|
|
550
|
-
await fs.
|
|
638
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
639
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
551
640
|
const project = await loadProject({
|
|
552
|
-
settingsFilePath: "
|
|
641
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
553
642
|
nodeishFs: fs,
|
|
554
643
|
_import,
|
|
555
644
|
});
|
|
@@ -569,9 +658,10 @@ describe("functionality", () => {
|
|
|
569
658
|
modules: ["lintRule.js"],
|
|
570
659
|
};
|
|
571
660
|
const fs = createNodeishMemoryFs();
|
|
572
|
-
await fs.
|
|
661
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
662
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
573
663
|
const project = await loadProject({
|
|
574
|
-
settingsFilePath: "
|
|
664
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
575
665
|
nodeishFs: fs,
|
|
576
666
|
_import: async () => ({
|
|
577
667
|
default: mockMessageLintRule,
|