@inlang/sdk 0.8.0 → 0.10.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 +1 -1
- package/dist/errors.d.ts +18 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -10
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +14 -10
- package/dist/loadProject.test.js +4 -5
- package/dist/resolve-modules/errors.d.ts +6 -5
- package/dist/resolve-modules/errors.d.ts.map +1 -1
- package/dist/resolve-modules/errors.js +10 -8
- package/dist/resolve-modules/import.d.ts.map +1 -1
- package/dist/resolve-modules/import.js +2 -3
- package/dist/resolve-modules/message-lint-rules/errors.d.ts +5 -4
- package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -1
- package/dist/resolve-modules/message-lint-rules/errors.js +2 -4
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -1
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +4 -2
- package/dist/resolve-modules/plugins/errors.d.ts +35 -28
- package/dist/resolve-modules/plugins/errors.d.ts.map +1 -1
- package/dist/resolve-modules/plugins/errors.js +23 -30
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -1
- package/dist/resolve-modules/plugins/resolvePlugins.js +17 -18
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +79 -49
- package/dist/resolve-modules/plugins/types.d.ts +4 -5
- package/dist/resolve-modules/plugins/types.d.ts.map +1 -1
- package/dist/resolve-modules/resolveModules.d.ts.map +1 -1
- package/dist/resolve-modules/resolveModules.js +5 -4
- package/dist/resolve-modules/resolveModules.test.js +2 -2
- package/dist/test-utilities/createMessage.d.ts +1 -1
- package/package.json +1 -1
- package/src/adapter/solidAdapter.test.ts +1 -1
- package/src/errors.ts +19 -10
- package/src/loadProject.test.ts +11 -14
- package/src/loadProject.ts +18 -17
- package/src/resolve-modules/errors.ts +14 -8
- package/src/resolve-modules/import.ts +2 -3
- package/src/resolve-modules/message-lint-rules/errors.ts +5 -5
- package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +4 -2
- package/src/resolve-modules/plugins/errors.ts +34 -36
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +94 -62
- package/src/resolve-modules/plugins/resolvePlugins.ts +19 -50
- package/src/resolve-modules/plugins/types.ts +4 -8
- package/src/resolve-modules/resolveModules.test.ts +2 -2
- package/src/resolve-modules/resolveModules.ts +5 -6
|
@@ -2,56 +2,87 @@
|
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { resolvePlugins } from "./resolvePlugins.js";
|
|
4
4
|
import { PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginUsesReservedNamespaceError, PluginReturnedInvalidCustomApiError, PluginHasInvalidSchemaError, PluginsDoNotProvideLoadOrSaveMessagesError, } from "./errors.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
nodeishFs: {},
|
|
19
|
-
});
|
|
20
|
-
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidIdError);
|
|
5
|
+
it("should return an error if a plugin uses an invalid id", async () => {
|
|
6
|
+
const mockPlugin = {
|
|
7
|
+
// @ts-expect-error - invalid id
|
|
8
|
+
id: "no-namespace",
|
|
9
|
+
description: { en: "My plugin description" },
|
|
10
|
+
displayName: { en: "My plugin" },
|
|
11
|
+
loadMessages: () => undefined,
|
|
12
|
+
saveMessages: () => undefined,
|
|
13
|
+
};
|
|
14
|
+
const resolved = await resolvePlugins({
|
|
15
|
+
plugins: [mockPlugin],
|
|
16
|
+
settings: {},
|
|
17
|
+
nodeishFs: {},
|
|
21
18
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
19
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidIdError);
|
|
20
|
+
});
|
|
21
|
+
it("should return an error if a plugin uses APIs that are not available", async () => {
|
|
22
|
+
const mockPlugin = {
|
|
23
|
+
id: "plugin.namespace.undefinedApi",
|
|
24
|
+
description: { en: "My plugin description" },
|
|
25
|
+
displayName: { en: "My plugin" },
|
|
26
|
+
// @ts-expect-error the key is not available in type
|
|
27
|
+
nonExistentKey: {
|
|
28
|
+
nonexistentOptions: "value",
|
|
29
|
+
},
|
|
30
|
+
loadMessages: () => undefined,
|
|
31
|
+
saveMessages: () => undefined,
|
|
32
|
+
};
|
|
33
|
+
const resolved = await resolvePlugins({
|
|
34
|
+
plugins: [mockPlugin],
|
|
35
|
+
settings: {},
|
|
36
|
+
nodeishFs: {},
|
|
40
37
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
38
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError);
|
|
39
|
+
});
|
|
40
|
+
it("should not initialize a plugin that uses the 'inlang' namespace except for inlang whitelisted plugins", async () => {
|
|
41
|
+
const mockPlugin = {
|
|
42
|
+
id: "plugin.inlang.notWhitelisted",
|
|
43
|
+
description: { en: "My plugin description" },
|
|
44
|
+
displayName: { en: "My plugin" },
|
|
45
|
+
loadMessages: () => undefined,
|
|
46
|
+
};
|
|
47
|
+
const resolved = await resolvePlugins({
|
|
48
|
+
plugins: [mockPlugin],
|
|
49
|
+
settings: {},
|
|
50
|
+
nodeishFs: {},
|
|
51
|
+
});
|
|
52
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginUsesReservedNamespaceError);
|
|
53
|
+
});
|
|
54
|
+
it("should expose the project settings including the plugin settings", async () => {
|
|
55
|
+
const settings = {
|
|
56
|
+
sourceLanguageTag: "en",
|
|
57
|
+
languageTags: ["en", "de"],
|
|
58
|
+
modules: [],
|
|
59
|
+
"plugin.namespace.placeholder": {
|
|
60
|
+
myPluginSetting: "value",
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const mockPlugin = {
|
|
64
|
+
id: "plugin.namespace.placeholder",
|
|
65
|
+
description: { en: "My plugin description" },
|
|
66
|
+
displayName: { en: "My plugin" },
|
|
67
|
+
saveMessages: async ({ settings }) => {
|
|
68
|
+
expect(settings).toStrictEqual(settings);
|
|
69
|
+
},
|
|
70
|
+
addCustomApi: ({ settings }) => {
|
|
71
|
+
expect(settings).toStrictEqual(settings);
|
|
72
|
+
return {};
|
|
73
|
+
},
|
|
74
|
+
loadMessages: async ({ settings }) => {
|
|
75
|
+
expect(settings).toStrictEqual(settings);
|
|
76
|
+
return [];
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
const resolved = await resolvePlugins({
|
|
80
|
+
plugins: [mockPlugin],
|
|
81
|
+
settings: settings,
|
|
82
|
+
nodeishFs: {},
|
|
54
83
|
});
|
|
84
|
+
await resolved.data.loadMessages({ settings });
|
|
85
|
+
await resolved.data.saveMessages({ settings, messages: [] });
|
|
55
86
|
});
|
|
56
87
|
describe("loadMessages", () => {
|
|
57
88
|
it("should load messages from a local source", async () => {
|
|
@@ -67,8 +98,7 @@ describe("loadMessages", () => {
|
|
|
67
98
|
nodeishFs: {},
|
|
68
99
|
});
|
|
69
100
|
expect(await resolved.data.loadMessages({
|
|
70
|
-
|
|
71
|
-
sourceLanguageTag: "en",
|
|
101
|
+
settings: {},
|
|
72
102
|
})).toEqual([{ id: "test", expressions: [], selectors: [], variants: [] }]);
|
|
73
103
|
});
|
|
74
104
|
it("should collect an error if function is defined twice in multiple plugins", async () => {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { LanguageTag } from "@inlang/language-tag";
|
|
2
1
|
import type { NodeishFilesystem as LisaNodeishFilesystem } from "@lix-js/fs";
|
|
3
2
|
import type { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginHasInvalidSchemaError, PluginUsesReservedNamespaceError, PluginsDoNotProvideLoadOrSaveMessagesError } from "./errors.js";
|
|
4
3
|
import type { Message } from "@inlang/message";
|
|
5
|
-
import type { JSONObject } from "@inlang/json-types";
|
|
6
4
|
import type { CustomApiInlangIdeExtension, Plugin } from "@inlang/plugin";
|
|
5
|
+
import type { ProjectSettings } from "@inlang/project-settings";
|
|
7
6
|
/**
|
|
8
7
|
* The filesystem is a subset of project lisa's nodeish filesystem.
|
|
9
8
|
*
|
|
@@ -15,7 +14,7 @@ export type NodeishFilesystemSubset = Pick<LisaNodeishFilesystem, "readFile" | "
|
|
|
15
14
|
*/
|
|
16
15
|
export type ResolvePluginsFunction = (args: {
|
|
17
16
|
plugins: Array<Plugin>;
|
|
18
|
-
settings:
|
|
17
|
+
settings: ProjectSettings;
|
|
19
18
|
nodeishFs: NodeishFilesystemSubset;
|
|
20
19
|
}) => Promise<{
|
|
21
20
|
data: ResolvedPluginApi;
|
|
@@ -26,10 +25,10 @@ export type ResolvePluginsFunction = (args: {
|
|
|
26
25
|
*/
|
|
27
26
|
export type ResolvedPluginApi = {
|
|
28
27
|
loadMessages: (args: {
|
|
29
|
-
|
|
30
|
-
sourceLanguageTag: LanguageTag;
|
|
28
|
+
settings: ProjectSettings;
|
|
31
29
|
}) => Promise<Message[]> | Message[];
|
|
32
30
|
saveMessages: (args: {
|
|
31
|
+
settings: ProjectSettings;
|
|
33
32
|
messages: Message[];
|
|
34
33
|
}) => Promise<void> | void;
|
|
35
34
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,IAAI,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAC5E,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,qBAAqB,EACrB,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveModules.d.ts","sourceRoot":"","sources":["../../src/resolve-modules/resolveModules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkBvD,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"resolveModules.d.ts","sourceRoot":"","sources":["../../src/resolve-modules/resolveModules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkBvD,eAAO,MAAM,cAAc,EAAE,qBA+E5B,CAAA"}
|
|
@@ -19,7 +19,7 @@ export const resolveModules = async (args) => {
|
|
|
19
19
|
const importedModule = await tryCatch(() => _import(module));
|
|
20
20
|
// -- IMPORT MODULE --
|
|
21
21
|
if (importedModule.error) {
|
|
22
|
-
moduleErrors.push(new ModuleImportError(
|
|
22
|
+
moduleErrors.push(new ModuleImportError({
|
|
23
23
|
module: module,
|
|
24
24
|
cause: importedModule.error,
|
|
25
25
|
}));
|
|
@@ -27,16 +27,17 @@ export const resolveModules = async (args) => {
|
|
|
27
27
|
}
|
|
28
28
|
// -- MODULE DOES NOT EXPORT ANYTHING --
|
|
29
29
|
if (importedModule.data?.default === undefined) {
|
|
30
|
-
moduleErrors.push(new ModuleHasNoExportsError(
|
|
30
|
+
moduleErrors.push(new ModuleHasNoExportsError({
|
|
31
31
|
module: module,
|
|
32
32
|
}));
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
35
|
const isValidModule = ModuleCompiler.Check(importedModule.data);
|
|
36
36
|
if (isValidModule === false) {
|
|
37
|
-
const errors = [...ModuleCompiler.Errors(importedModule.data)]
|
|
38
|
-
moduleErrors.push(new ModuleExportIsInvalidError(
|
|
37
|
+
const errors = [...ModuleCompiler.Errors(importedModule.data)];
|
|
38
|
+
moduleErrors.push(new ModuleExportIsInvalidError({
|
|
39
39
|
module: module,
|
|
40
|
+
errors,
|
|
40
41
|
}));
|
|
41
42
|
continue;
|
|
42
43
|
}
|
|
@@ -11,7 +11,7 @@ it("should return an error if a plugin cannot be imported", async () => {
|
|
|
11
11
|
settings,
|
|
12
12
|
nodeishFs: {},
|
|
13
13
|
_import: () => {
|
|
14
|
-
throw new ModuleImportError(
|
|
14
|
+
throw new ModuleImportError({
|
|
15
15
|
module: settings.modules[0],
|
|
16
16
|
cause: new Error("Could not import"),
|
|
17
17
|
});
|
|
@@ -74,7 +74,7 @@ it("should return an error if a module cannot be imported", async () => {
|
|
|
74
74
|
modules: ["https://myplugin.com/index.js"],
|
|
75
75
|
};
|
|
76
76
|
const _import = async () => {
|
|
77
|
-
throw new ModuleImportError(
|
|
77
|
+
throw new ModuleImportError({
|
|
78
78
|
module: settings.modules[0],
|
|
79
79
|
cause: new Error(),
|
|
80
80
|
});
|
package/package.json
CHANGED
|
@@ -175,7 +175,7 @@ describe("messages", () => {
|
|
|
175
175
|
description: {
|
|
176
176
|
en: "wo",
|
|
177
177
|
},
|
|
178
|
-
loadMessages: ({
|
|
178
|
+
loadMessages: ({ settings }) => (settings.languageTags.length ? exampleMessages : []),
|
|
179
179
|
saveMessages: () => undefined,
|
|
180
180
|
}
|
|
181
181
|
|
package/src/errors.ts
CHANGED
|
@@ -1,34 +1,43 @@
|
|
|
1
|
+
import type { ValueError } from "@sinclair/typebox/errors"
|
|
2
|
+
|
|
1
3
|
export class ProjectSettingsInvalidError extends Error {
|
|
2
|
-
constructor(
|
|
3
|
-
super(
|
|
4
|
+
constructor(options: { errors: ValueError[] }) {
|
|
5
|
+
super(
|
|
6
|
+
`The project settings are invalid:\n\n${options.errors
|
|
7
|
+
.map((error) => `The value of "${error.path}" is invalid:\n\n${error.message}`)
|
|
8
|
+
.join("\n")}`
|
|
9
|
+
)
|
|
4
10
|
this.name = "ProjectSettingsInvalidError"
|
|
5
11
|
}
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
export class ProjectSettingsFileJSONSyntaxError extends Error {
|
|
9
|
-
constructor(
|
|
10
|
-
super(
|
|
15
|
+
constructor(options: { cause: ErrorOptions["cause"]; path: string }) {
|
|
16
|
+
super(
|
|
17
|
+
`The settings file at "${options.path}" is not a valid JSON file:\n\n${options.cause}`,
|
|
18
|
+
options
|
|
19
|
+
)
|
|
11
20
|
this.name = "ProjectSettingsFileJSONSyntaxError"
|
|
12
21
|
}
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
export class ProjectSettingsFileNotFoundError extends Error {
|
|
16
|
-
constructor(
|
|
17
|
-
super(
|
|
25
|
+
constructor(options: { cause?: ErrorOptions["cause"]; path: string }) {
|
|
26
|
+
super(`The file at "${options.path}" could not be read. Does the file exists?`, options)
|
|
18
27
|
this.name = "ProjectSettingsFileNotFoundError"
|
|
19
28
|
}
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export class PluginSaveMessagesError extends Error {
|
|
23
|
-
constructor(
|
|
24
|
-
super(
|
|
32
|
+
constructor(options: { cause: ErrorOptions["cause"] }) {
|
|
33
|
+
super(`An error occured in saveMessages() caused by ${options.cause}.`, options)
|
|
25
34
|
this.name = "PluginSaveMessagesError"
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
export class PluginLoadMessagesError extends Error {
|
|
30
|
-
constructor(
|
|
31
|
-
super(
|
|
39
|
+
constructor(options: { cause: ErrorOptions["cause"] }) {
|
|
40
|
+
super(`An error occured in loadMessages() caused by ${options.cause}.`, options)
|
|
32
41
|
this.name = "PluginLoadMessagesError"
|
|
33
42
|
}
|
|
34
43
|
}
|
package/src/loadProject.test.ts
CHANGED
|
@@ -482,17 +482,16 @@ describe("functionality", () => {
|
|
|
482
482
|
it("should call saveMessages() on updates", async () => {
|
|
483
483
|
const fs = createNodeishMemoryFs()
|
|
484
484
|
|
|
485
|
-
|
|
486
|
-
"
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
)
|
|
485
|
+
const settings: ProjectSettings = {
|
|
486
|
+
sourceLanguageTag: "en",
|
|
487
|
+
languageTags: ["en", "de"],
|
|
488
|
+
modules: ["plugin.js"],
|
|
489
|
+
"plugin.project.json": {
|
|
490
|
+
pathPattern: "./resources/{languageTag}.json",
|
|
491
|
+
},
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
await fs.writeFile("./project.inlang.json", JSON.stringify(settings))
|
|
496
495
|
|
|
497
496
|
await fs.mkdir("./resources")
|
|
498
497
|
|
|
@@ -583,9 +582,7 @@ describe("functionality", () => {
|
|
|
583
582
|
|
|
584
583
|
expect(mockSaveFn.mock.calls.length).toBe(1)
|
|
585
584
|
|
|
586
|
-
expect(mockSaveFn.mock.calls[0][0].settings).toStrictEqual(
|
|
587
|
-
pathPattern: "./resources/{languageTag}.json",
|
|
588
|
-
})
|
|
585
|
+
expect(mockSaveFn.mock.calls[0][0].settings).toStrictEqual(settings)
|
|
589
586
|
|
|
590
587
|
expect(Object.values(mockSaveFn.mock.calls[0][0].messages)).toStrictEqual([
|
|
591
588
|
{
|
package/src/loadProject.ts
CHANGED
|
@@ -73,7 +73,9 @@ export const loadProject = async (args: {
|
|
|
73
73
|
return { error }
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
throw new Error(
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Unhandled error in setSettings. This is an internal bug. Please file an issue."
|
|
78
|
+
)
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -113,17 +115,14 @@ export const loadProject = async (args: {
|
|
|
113
115
|
|
|
114
116
|
makeTrulyAsync(
|
|
115
117
|
_resolvedModules.resolvedPluginApi.loadMessages({
|
|
116
|
-
|
|
117
|
-
sourceLanguageTag: settingsValue!.sourceLanguageTag,
|
|
118
|
+
settings: settingsValue,
|
|
118
119
|
})
|
|
119
120
|
)
|
|
120
121
|
.then((messages) => {
|
|
121
122
|
setMessages(messages)
|
|
122
123
|
markInitAsComplete()
|
|
123
124
|
})
|
|
124
|
-
.catch((err) =>
|
|
125
|
-
markInitAsFailed(new PluginLoadMessagesError("Error in load messages", { cause: err }))
|
|
126
|
-
)
|
|
125
|
+
.catch((err) => markInitAsFailed(new PluginLoadMessagesError({ cause: err })))
|
|
127
126
|
})
|
|
128
127
|
|
|
129
128
|
// -- installed items ----------------------------------------------------
|
|
@@ -174,9 +173,12 @@ export const loadProject = async (args: {
|
|
|
174
173
|
500,
|
|
175
174
|
async (newMessages) => {
|
|
176
175
|
try {
|
|
177
|
-
await resolvedModules()?.resolvedPluginApi.saveMessages({
|
|
176
|
+
await resolvedModules()?.resolvedPluginApi.saveMessages({
|
|
177
|
+
settings: settingsValue,
|
|
178
|
+
messages: newMessages,
|
|
179
|
+
})
|
|
178
180
|
} catch (err) {
|
|
179
|
-
throw new PluginSaveMessagesError(
|
|
181
|
+
throw new PluginSaveMessagesError({
|
|
180
182
|
cause: err,
|
|
181
183
|
})
|
|
182
184
|
}
|
|
@@ -229,18 +231,17 @@ const loadSettings = async (args: {
|
|
|
229
231
|
async () => await args.nodeishFs.readFile(args.settingsFilePath, { encoding: "utf-8" })
|
|
230
232
|
)
|
|
231
233
|
if (settingsFileError)
|
|
232
|
-
throw new ProjectSettingsFileNotFoundError(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
)
|
|
234
|
+
throw new ProjectSettingsFileNotFoundError({
|
|
235
|
+
cause: settingsFileError,
|
|
236
|
+
path: args.settingsFilePath,
|
|
237
|
+
})
|
|
238
238
|
|
|
239
239
|
const json = tryCatch(() => JSON.parse(settingsFile!))
|
|
240
240
|
|
|
241
241
|
if (json.error) {
|
|
242
|
-
throw new ProjectSettingsFileJSONSyntaxError(
|
|
242
|
+
throw new ProjectSettingsFileJSONSyntaxError({
|
|
243
243
|
cause: json.error,
|
|
244
|
+
path: args.settingsFilePath,
|
|
244
245
|
})
|
|
245
246
|
}
|
|
246
247
|
return parseSettings(json.data)
|
|
@@ -251,8 +252,8 @@ const parseSettings = (settings: unknown) => {
|
|
|
251
252
|
if (settingsCompiler.Check(withMigration) === false) {
|
|
252
253
|
const typeErrors = [...settingsCompiler.Errors(settings)]
|
|
253
254
|
if (typeErrors.length > 0) {
|
|
254
|
-
throw new ProjectSettingsInvalidError(
|
|
255
|
-
|
|
255
|
+
throw new ProjectSettingsInvalidError({
|
|
256
|
+
errors: typeErrors,
|
|
256
257
|
})
|
|
257
258
|
}
|
|
258
259
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { ValueError } from "@sinclair/typebox/errors"
|
|
1
2
|
export * from "./plugins/errors.js"
|
|
2
3
|
export * from "./message-lint-rules/errors.js"
|
|
3
4
|
|
|
4
5
|
export class ModuleError extends Error {
|
|
5
|
-
public readonly
|
|
6
|
+
public readonly module: string
|
|
6
7
|
constructor(message: string, options: { module: string; cause?: Error }) {
|
|
7
8
|
super(message)
|
|
8
9
|
this.name = "ModuleError"
|
|
9
|
-
this.
|
|
10
|
+
this.module = options.module
|
|
10
11
|
this.cause = options.cause
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -15,8 +16,8 @@ export class ModuleError extends Error {
|
|
|
15
16
|
* Error when a Module does not export any plugins or lint rules.
|
|
16
17
|
*/
|
|
17
18
|
export class ModuleHasNoExportsError extends ModuleError {
|
|
18
|
-
constructor(
|
|
19
|
-
super(
|
|
19
|
+
constructor(options: { module: string; cause?: Error }) {
|
|
20
|
+
super(`Module "${module}" has no exports. Every module must have an "export default".`, options)
|
|
20
21
|
this.name = "ModuleHasNoExportsError"
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -25,15 +26,20 @@ export class ModuleHasNoExportsError extends ModuleError {
|
|
|
25
26
|
* Error when a Module cannot be imported.
|
|
26
27
|
*/
|
|
27
28
|
export class ModuleImportError extends ModuleError {
|
|
28
|
-
constructor(
|
|
29
|
-
super(
|
|
29
|
+
constructor(options: { module: string; cause: Error }) {
|
|
30
|
+
super(`Couldn't import the plugin "${module}":\n\n${options.cause}`, options)
|
|
30
31
|
this.name = "ModuleImportError"
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export class ModuleExportIsInvalidError extends ModuleError {
|
|
35
|
-
constructor(
|
|
36
|
-
super(
|
|
36
|
+
constructor(options: { module: string; errors: ValueError[] }) {
|
|
37
|
+
super(
|
|
38
|
+
`The export(s) of "${module}" are invalid:\n\n${options.errors
|
|
39
|
+
.map((error) => `Path "${error.path}" with value "${error.value}": "${error.message}"`)
|
|
40
|
+
.join("\n")}`,
|
|
41
|
+
options
|
|
42
|
+
)
|
|
37
43
|
this.name = "ModuleExportIsInvalidError"
|
|
38
44
|
}
|
|
39
45
|
}
|
|
@@ -50,14 +50,13 @@ async function $import(
|
|
|
50
50
|
try {
|
|
51
51
|
return await import(/* @vite-ignore */ moduleWithMimeType)
|
|
52
52
|
} catch (error) {
|
|
53
|
-
let message = `Error while importing ${uri}: ${(error as Error)?.message ?? "Unknown error"}`
|
|
54
53
|
if (error instanceof SyntaxError && uri.includes("jsdelivr")) {
|
|
55
|
-
message += dedent`\n\n
|
|
54
|
+
error.message += dedent`\n\n
|
|
56
55
|
Are you sure that the file exists on JSDelivr?
|
|
57
56
|
|
|
58
57
|
The error indicates that the imported file does not exist on JSDelivr. For non-existent files, JSDelivr returns a 404 text that JS cannot parse as a module and throws a SyntaxError.
|
|
59
58
|
`
|
|
60
59
|
}
|
|
61
|
-
throw new ModuleImportError(
|
|
60
|
+
throw new ModuleImportError({ module: uri, cause: error as Error })
|
|
62
61
|
}
|
|
63
62
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { MessageLintRule } from "@inlang/message-lint-rule"
|
|
2
|
+
import type { ValueError } from "@sinclair/typebox/errors"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export class MessageLintRuleIsInvalidError extends Error {
|
|
5
|
+
constructor(options: { id: MessageLintRule["id"]; errors: ValueError[] }) {
|
|
6
|
+
super(`The message lint rule "${options.id}" is invalid:\n\n${options.errors.join("\n")}`)
|
|
7
7
|
this.name = "MessageLintRuleIsInvalidError"
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -9,9 +9,11 @@ export const resolveMessageLintRules = (args: { messageLintRules: Array<MessageL
|
|
|
9
9
|
}
|
|
10
10
|
for (const rule of args.messageLintRules) {
|
|
11
11
|
if (Value.Check(MessageLintRule, rule) === false) {
|
|
12
|
+
const errors = [...Value.Errors(MessageLintRule, rule)]
|
|
12
13
|
result.errors.push(
|
|
13
|
-
new MessageLintRuleIsInvalidError(
|
|
14
|
-
|
|
14
|
+
new MessageLintRuleIsInvalidError({
|
|
15
|
+
id: rule.id,
|
|
16
|
+
errors,
|
|
15
17
|
})
|
|
16
18
|
)
|
|
17
19
|
continue
|
|
@@ -1,65 +1,63 @@
|
|
|
1
1
|
import type { Plugin } from "@inlang/plugin"
|
|
2
|
+
import type { ValueError } from "@sinclair/typebox/errors"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
public readonly plugin: string
|
|
9
|
-
|
|
10
|
-
constructor(message: string, options: PluginErrorOptions) {
|
|
11
|
-
super(message)
|
|
12
|
-
this.name = "PluginError"
|
|
13
|
-
this.plugin = options.plugin ?? "unknown"
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class PluginHasInvalidIdError extends PluginError {
|
|
18
|
-
constructor(message: string, options: PluginErrorOptions) {
|
|
19
|
-
super(message, options)
|
|
4
|
+
export class PluginHasInvalidIdError extends Error {
|
|
5
|
+
constructor(options: { id: Plugin["id"] }) {
|
|
6
|
+
super(
|
|
7
|
+
`Plugin "${options.id}" has an invalid id. The id must:\n1) Start with "plugin."\n2) camelCase\n3) Contain a namespace.\nAn example would be "plugin.namespace.myPlugin".`
|
|
8
|
+
)
|
|
20
9
|
this.name = "PluginHasInvalidIdError"
|
|
21
10
|
}
|
|
22
11
|
}
|
|
23
12
|
|
|
24
|
-
export class PluginUsesReservedNamespaceError extends
|
|
25
|
-
constructor(
|
|
26
|
-
super(
|
|
13
|
+
export class PluginUsesReservedNamespaceError extends Error {
|
|
14
|
+
constructor(options: { id: Plugin["id"] }) {
|
|
15
|
+
super(`Plugin ${options.id} uses reserved namespace 'inlang'.`)
|
|
27
16
|
this.name = "PluginUsesReservedNamespaceError"
|
|
28
17
|
}
|
|
29
18
|
}
|
|
30
19
|
|
|
31
|
-
export class PluginHasInvalidSchemaError extends
|
|
32
|
-
constructor(
|
|
33
|
-
super(
|
|
20
|
+
export class PluginHasInvalidSchemaError extends Error {
|
|
21
|
+
constructor(options: { id: Plugin["id"]; errors: ValueError[] }) {
|
|
22
|
+
super(
|
|
23
|
+
`Plugin "${options.id}" has an invalid schema:\n\n${options.errors
|
|
24
|
+
.map((error) => `Path "${error.path}" with value "${error.value}": "${error.message}"`)
|
|
25
|
+
.join("\n")})}\n\nPlease refer to the documentation for the correct schema.`
|
|
26
|
+
)
|
|
34
27
|
this.name = "PluginHasInvalidSchemaError"
|
|
35
28
|
}
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
export class PluginLoadMessagesFunctionAlreadyDefinedError extends
|
|
39
|
-
constructor(
|
|
40
|
-
super(
|
|
31
|
+
export class PluginLoadMessagesFunctionAlreadyDefinedError extends Error {
|
|
32
|
+
constructor(options: { id: Plugin["id"] }) {
|
|
33
|
+
super(
|
|
34
|
+
`Plugin "${options.id}" defines the \`loadMessages()\` function, but it was already defined by another plugin.\n\nInlang only allows one plugin to define the \`loadMessages()\` function.`
|
|
35
|
+
)
|
|
41
36
|
this.name = "PluginLoadMessagesFunctionAlreadyDefinedError"
|
|
42
37
|
}
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
export class PluginSaveMessagesFunctionAlreadyDefinedError extends
|
|
46
|
-
constructor(
|
|
47
|
-
super(
|
|
40
|
+
export class PluginSaveMessagesFunctionAlreadyDefinedError extends Error {
|
|
41
|
+
constructor(options: { id: Plugin["id"] }) {
|
|
42
|
+
super(
|
|
43
|
+
`Plugin "${options.id}" defines the \`saveMessages()\` function, but it was already defined by another plugin.\n\nInlang only allows one plugin to define the \`saveMessages()\` function.`
|
|
44
|
+
)
|
|
48
45
|
this.name = "PluginSaveMessagesFunctionAlreadyDefinedError"
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
export class PluginReturnedInvalidCustomApiError extends
|
|
53
|
-
constructor(
|
|
54
|
-
super(
|
|
49
|
+
export class PluginReturnedInvalidCustomApiError extends Error {
|
|
50
|
+
constructor(options: { id: Plugin["id"]; cause: ErrorOptions["cause"] }) {
|
|
51
|
+
super(`Plugin "${options.id}" returned an invalid custom API:\n\n${options.cause}`, options)
|
|
55
52
|
this.name = "PluginReturnedInvalidCustomApiError"
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
export class PluginsDoNotProvideLoadOrSaveMessagesError extends
|
|
60
|
-
constructor(
|
|
61
|
-
super(
|
|
56
|
+
export class PluginsDoNotProvideLoadOrSaveMessagesError extends Error {
|
|
57
|
+
constructor() {
|
|
58
|
+
super(
|
|
59
|
+
`No plugin provides a \`loadMessages()\` or \`saveMessages()\` function\n\nIn case no plugin threw an error, you likely forgot to add a plugin that handles the loading and saving of messages. Refer to the marketplace for available plugins https://inlang.com/marketplace.`
|
|
60
|
+
)
|
|
62
61
|
this.name = "PluginsDoNotProvideLoadOrSaveMessagesError"
|
|
63
|
-
options.plugin = "plugin.inlang.missing"
|
|
64
62
|
}
|
|
65
63
|
}
|