@inlang/sdk 0.8.0 → 0.9.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/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +5 -3
- package/dist/loadProject.test.js +4 -5
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -1
- package/dist/resolve-modules/plugins/resolvePlugins.js +3 -5
- 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/package.json +1 -1
- package/src/adapter/solidAdapter.test.ts +1 -1
- package/src/loadProject.test.ts +11 -14
- package/src/loadProject.ts +5 -3
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +94 -62
- package/src/resolve-modules/plugins/resolvePlugins.ts +3 -5
- package/src/resolve-modules/plugins/types.ts +4 -8
|
@@ -143,7 +143,7 @@ describe("messages", () => {
|
|
|
143
143
|
description: {
|
|
144
144
|
en: "wo",
|
|
145
145
|
},
|
|
146
|
-
loadMessages: ({
|
|
146
|
+
loadMessages: ({ settings }) => (settings.languageTags.length ? exampleMessages : []),
|
|
147
147
|
saveMessages: () => undefined,
|
|
148
148
|
};
|
|
149
149
|
const mockImport = async () => ({ default: mockPlugin });
|
|
@@ -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;AAahF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAMjG;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,
|
|
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;AAahF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAMjG;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAqLxB,CAAA;AAsGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/loadProject.js
CHANGED
|
@@ -77,8 +77,7 @@ export const loadProject = async (args) => {
|
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
79
|
makeTrulyAsync(_resolvedModules.resolvedPluginApi.loadMessages({
|
|
80
|
-
|
|
81
|
-
sourceLanguageTag: settingsValue.sourceLanguageTag,
|
|
80
|
+
settings: settingsValue,
|
|
82
81
|
}))
|
|
83
82
|
.then((messages) => {
|
|
84
83
|
setMessages(messages);
|
|
@@ -117,7 +116,10 @@ export const loadProject = async (args) => {
|
|
|
117
116
|
const lintReportsQuery = createMessageLintReportsQuery(messages, settings, installedMessageLintRules, resolvedModules);
|
|
118
117
|
const debouncedSave = skipFirst(debounce(500, async (newMessages) => {
|
|
119
118
|
try {
|
|
120
|
-
await resolvedModules()?.resolvedPluginApi.saveMessages({
|
|
119
|
+
await resolvedModules()?.resolvedPluginApi.saveMessages({
|
|
120
|
+
settings: settingsValue,
|
|
121
|
+
messages: newMessages,
|
|
122
|
+
});
|
|
121
123
|
}
|
|
122
124
|
catch (err) {
|
|
123
125
|
throw new PluginSaveMessagesError("Error in saving messages", {
|
package/dist/loadProject.test.js
CHANGED
|
@@ -399,14 +399,15 @@ describe("functionality", () => {
|
|
|
399
399
|
describe("query", () => {
|
|
400
400
|
it("should call saveMessages() on updates", async () => {
|
|
401
401
|
const fs = createNodeishMemoryFs();
|
|
402
|
-
|
|
402
|
+
const settings = {
|
|
403
403
|
sourceLanguageTag: "en",
|
|
404
404
|
languageTags: ["en", "de"],
|
|
405
405
|
modules: ["plugin.js"],
|
|
406
406
|
"plugin.project.json": {
|
|
407
407
|
pathPattern: "./resources/{languageTag}.json",
|
|
408
408
|
},
|
|
409
|
-
}
|
|
409
|
+
};
|
|
410
|
+
await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
|
|
410
411
|
await fs.mkdir("./resources");
|
|
411
412
|
const mockSaveFn = vi.fn();
|
|
412
413
|
const _mockPlugin = {
|
|
@@ -486,9 +487,7 @@ describe("functionality", () => {
|
|
|
486
487
|
});
|
|
487
488
|
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
488
489
|
expect(mockSaveFn.mock.calls.length).toBe(1);
|
|
489
|
-
expect(mockSaveFn.mock.calls[0][0].settings).toStrictEqual(
|
|
490
|
-
pathPattern: "./resources/{languageTag}.json",
|
|
491
|
-
});
|
|
490
|
+
expect(mockSaveFn.mock.calls[0][0].settings).toStrictEqual(settings);
|
|
492
491
|
expect(Object.values(mockSaveFn.mock.calls[0][0].messages)).toStrictEqual([
|
|
493
492
|
{
|
|
494
493
|
id: "a",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvePlugins.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/resolvePlugins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAuBxD,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"resolvePlugins.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/resolvePlugins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAuBxD,eAAO,MAAM,cAAc,EAAE,sBAkJ5B,CAAA"}
|
|
@@ -27,7 +27,7 @@ export const resolvePlugins = async (args) => {
|
|
|
27
27
|
// -- INVALID ID in META --
|
|
28
28
|
const hasInvalidId = errors.some((error) => error.path === "/id");
|
|
29
29
|
if (hasInvalidId) {
|
|
30
|
-
result.errors.push(new PluginHasInvalidIdError(`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be
|
|
30
|
+
result.errors.push(new PluginHasInvalidIdError(`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be camelCase and contain a namespace like plugin.namespace.myPlugin.`, { plugin: plugin.id }));
|
|
31
31
|
}
|
|
32
32
|
// -- USES RESERVED NAMESPACE --
|
|
33
33
|
if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
|
|
@@ -53,7 +53,7 @@ export const resolvePlugins = async (args) => {
|
|
|
53
53
|
if (typeof plugin.addCustomApi === "function") {
|
|
54
54
|
// TODO: why do we call this function 2 times (here for validation and later for retrieving the actual value)?
|
|
55
55
|
const { data: customApi, error } = tryCatch(() => plugin.addCustomApi({
|
|
56
|
-
settings: args.settings
|
|
56
|
+
settings: args.settings,
|
|
57
57
|
}));
|
|
58
58
|
if (error) {
|
|
59
59
|
// @ts-ignore
|
|
@@ -74,20 +74,18 @@ export const resolvePlugins = async (args) => {
|
|
|
74
74
|
if (typeof plugin.loadMessages === "function") {
|
|
75
75
|
result.data.loadMessages = (_args) => plugin.loadMessages({
|
|
76
76
|
..._args,
|
|
77
|
-
settings: args.settings?.[plugin.id] ?? {},
|
|
78
77
|
nodeishFs: args.nodeishFs,
|
|
79
78
|
});
|
|
80
79
|
}
|
|
81
80
|
if (typeof plugin.saveMessages === "function") {
|
|
82
81
|
result.data.saveMessages = (_args) => plugin.saveMessages({
|
|
83
82
|
..._args,
|
|
84
|
-
settings: args.settings?.[plugin.id] ?? {},
|
|
85
83
|
nodeishFs: args.nodeishFs,
|
|
86
84
|
});
|
|
87
85
|
}
|
|
88
86
|
if (typeof plugin.addCustomApi === "function") {
|
|
89
87
|
const { data: customApi } = tryCatch(() => plugin.addCustomApi({
|
|
90
|
-
settings: args.settings
|
|
88
|
+
settings: args.settings,
|
|
91
89
|
}));
|
|
92
90
|
if (customApi) {
|
|
93
91
|
result.data.customApi = deepmerge(result.data.customApi, customApi);
|
|
@@ -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"}
|
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/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
|
@@ -113,8 +113,7 @@ export const loadProject = async (args: {
|
|
|
113
113
|
|
|
114
114
|
makeTrulyAsync(
|
|
115
115
|
_resolvedModules.resolvedPluginApi.loadMessages({
|
|
116
|
-
|
|
117
|
-
sourceLanguageTag: settingsValue!.sourceLanguageTag,
|
|
116
|
+
settings: settingsValue,
|
|
118
117
|
})
|
|
119
118
|
)
|
|
120
119
|
.then((messages) => {
|
|
@@ -174,7 +173,10 @@ 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
181
|
throw new PluginSaveMessagesError("Error in saving messages", {
|
|
180
182
|
cause: err,
|
|
@@ -11,65 +11,98 @@ import {
|
|
|
11
11
|
PluginsDoNotProvideLoadOrSaveMessagesError,
|
|
12
12
|
} from "./errors.js"
|
|
13
13
|
import type { Plugin } from "@inlang/plugin"
|
|
14
|
+
import type { ProjectSettings } from "@inlang/project-settings"
|
|
15
|
+
|
|
16
|
+
it("should return an error if a plugin uses an invalid id", async () => {
|
|
17
|
+
const mockPlugin: Plugin = {
|
|
18
|
+
// @ts-expect-error - invalid id
|
|
19
|
+
id: "no-namespace",
|
|
20
|
+
description: { en: "My plugin description" },
|
|
21
|
+
displayName: { en: "My plugin" },
|
|
22
|
+
loadMessages: () => undefined as any,
|
|
23
|
+
saveMessages: () => undefined as any,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const resolved = await resolvePlugins({
|
|
27
|
+
plugins: [mockPlugin],
|
|
28
|
+
settings: {} as any as any,
|
|
29
|
+
nodeishFs: {} as any,
|
|
30
|
+
})
|
|
14
31
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const mockPlugin: Plugin = {
|
|
18
|
-
// @ts-expect-error - invalid id
|
|
19
|
-
id: "no-namespace",
|
|
20
|
-
description: { en: "My plugin description" },
|
|
21
|
-
displayName: { en: "My plugin" },
|
|
22
|
-
loadMessages: () => undefined as any,
|
|
23
|
-
saveMessages: () => undefined as any,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const resolved = await resolvePlugins({
|
|
27
|
-
plugins: [mockPlugin],
|
|
28
|
-
settings: {},
|
|
29
|
-
nodeishFs: {} as any,
|
|
30
|
-
})
|
|
32
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidIdError)
|
|
33
|
+
})
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
it("should return an error if a plugin uses APIs that are not available", async () => {
|
|
36
|
+
const mockPlugin: Plugin = {
|
|
37
|
+
id: "plugin.namespace.undefinedApi",
|
|
38
|
+
description: { en: "My plugin description" },
|
|
39
|
+
displayName: { en: "My plugin" },
|
|
40
|
+
// @ts-expect-error the key is not available in type
|
|
41
|
+
nonExistentKey: {
|
|
42
|
+
nonexistentOptions: "value",
|
|
43
|
+
},
|
|
44
|
+
loadMessages: () => undefined as any,
|
|
45
|
+
saveMessages: () => undefined as any,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resolved = await resolvePlugins({
|
|
49
|
+
plugins: [mockPlugin],
|
|
50
|
+
settings: {} as any,
|
|
51
|
+
nodeishFs: {} as any,
|
|
33
52
|
})
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
id: "plugin.namespace.undefinedApi",
|
|
38
|
-
description: { en: "My plugin description" },
|
|
39
|
-
displayName: { en: "My plugin" },
|
|
40
|
-
// @ts-expect-error the key is not available in type
|
|
41
|
-
nonExistentKey: {
|
|
42
|
-
nonexistentOptions: "value",
|
|
43
|
-
},
|
|
44
|
-
loadMessages: () => undefined as any,
|
|
45
|
-
saveMessages: () => undefined as any,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const resolved = await resolvePlugins({
|
|
49
|
-
plugins: [mockPlugin],
|
|
50
|
-
settings: {},
|
|
51
|
-
nodeishFs: {} as any,
|
|
52
|
-
})
|
|
54
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError)
|
|
55
|
+
})
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
it("should not initialize a plugin that uses the 'inlang' namespace except for inlang whitelisted plugins", async () => {
|
|
58
|
+
const mockPlugin: Plugin = {
|
|
59
|
+
id: "plugin.inlang.notWhitelisted",
|
|
60
|
+
description: { en: "My plugin description" },
|
|
61
|
+
displayName: { en: "My plugin" },
|
|
62
|
+
loadMessages: () => undefined as any,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const resolved = await resolvePlugins({
|
|
66
|
+
plugins: [mockPlugin],
|
|
67
|
+
settings: {} as any,
|
|
68
|
+
nodeishFs: {} as any,
|
|
55
69
|
})
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
id: "plugin.inlang.notWhitelisted",
|
|
60
|
-
description: { en: "My plugin description" },
|
|
61
|
-
displayName: { en: "My plugin" },
|
|
62
|
-
loadMessages: () => undefined as any,
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const resolved = await resolvePlugins({
|
|
66
|
-
plugins: [mockPlugin],
|
|
67
|
-
settings: {},
|
|
68
|
-
nodeishFs: {} as any,
|
|
69
|
-
})
|
|
71
|
+
expect(resolved.errors[0]).toBeInstanceOf(PluginUsesReservedNamespaceError)
|
|
72
|
+
})
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
it("should expose the project settings including the plugin settings", async () => {
|
|
75
|
+
const settings: ProjectSettings = {
|
|
76
|
+
sourceLanguageTag: "en",
|
|
77
|
+
languageTags: ["en", "de"],
|
|
78
|
+
modules: [],
|
|
79
|
+
"plugin.namespace.placeholder": {
|
|
80
|
+
myPluginSetting: "value",
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
const mockPlugin: Plugin = {
|
|
84
|
+
id: "plugin.namespace.placeholder",
|
|
85
|
+
description: { en: "My plugin description" },
|
|
86
|
+
displayName: { en: "My plugin" },
|
|
87
|
+
saveMessages: async ({ settings }) => {
|
|
88
|
+
expect(settings).toStrictEqual(settings)
|
|
89
|
+
},
|
|
90
|
+
addCustomApi: ({ settings }) => {
|
|
91
|
+
expect(settings).toStrictEqual(settings)
|
|
92
|
+
return {}
|
|
93
|
+
},
|
|
94
|
+
loadMessages: async ({ settings }) => {
|
|
95
|
+
expect(settings).toStrictEqual(settings)
|
|
96
|
+
return []
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
const resolved = await resolvePlugins({
|
|
100
|
+
plugins: [mockPlugin],
|
|
101
|
+
settings: settings,
|
|
102
|
+
nodeishFs: {} as any,
|
|
72
103
|
})
|
|
104
|
+
await resolved.data.loadMessages!({ settings })
|
|
105
|
+
await resolved.data.saveMessages!({ settings, messages: [] })
|
|
73
106
|
})
|
|
74
107
|
|
|
75
108
|
describe("loadMessages", () => {
|
|
@@ -83,14 +116,13 @@ describe("loadMessages", () => {
|
|
|
83
116
|
|
|
84
117
|
const resolved = await resolvePlugins({
|
|
85
118
|
plugins: [mockPlugin],
|
|
86
|
-
settings: {},
|
|
119
|
+
settings: {} as any,
|
|
87
120
|
nodeishFs: {} as any,
|
|
88
121
|
})
|
|
89
122
|
|
|
90
123
|
expect(
|
|
91
124
|
await resolved.data.loadMessages!({
|
|
92
|
-
|
|
93
|
-
sourceLanguageTag: "en",
|
|
125
|
+
settings: {} as any,
|
|
94
126
|
})
|
|
95
127
|
).toEqual([{ id: "test", expressions: [], selectors: [], variants: [] }])
|
|
96
128
|
})
|
|
@@ -112,7 +144,7 @@ describe("loadMessages", () => {
|
|
|
112
144
|
const resolved = await resolvePlugins({
|
|
113
145
|
plugins: [mockPlugin, mockPlugin2],
|
|
114
146
|
nodeishFs: {} as any,
|
|
115
|
-
settings: {},
|
|
147
|
+
settings: {} as any,
|
|
116
148
|
})
|
|
117
149
|
|
|
118
150
|
expect(resolved.errors[0]).toBeInstanceOf(PluginLoadMessagesFunctionAlreadyDefinedError)
|
|
@@ -129,7 +161,7 @@ describe("loadMessages", () => {
|
|
|
129
161
|
const resolved = await resolvePlugins({
|
|
130
162
|
plugins: [mockPlugin],
|
|
131
163
|
nodeishFs: {} as any,
|
|
132
|
-
settings: {},
|
|
164
|
+
settings: {} as any,
|
|
133
165
|
})
|
|
134
166
|
|
|
135
167
|
expect(resolved.errors).toHaveLength(1)
|
|
@@ -150,7 +182,7 @@ describe("saveMessages", () => {
|
|
|
150
182
|
const resolved = await resolvePlugins({
|
|
151
183
|
plugins: [mockPlugin],
|
|
152
184
|
nodeishFs: {} as any,
|
|
153
|
-
settings: {},
|
|
185
|
+
settings: {} as any,
|
|
154
186
|
})
|
|
155
187
|
|
|
156
188
|
expect(resolved.errors).toHaveLength(0)
|
|
@@ -173,7 +205,7 @@ describe("saveMessages", () => {
|
|
|
173
205
|
|
|
174
206
|
const resolved = await resolvePlugins({
|
|
175
207
|
plugins: [mockPlugin, mockPlugin2],
|
|
176
|
-
settings: {},
|
|
208
|
+
settings: {} as any,
|
|
177
209
|
nodeishFs: {} as any,
|
|
178
210
|
})
|
|
179
211
|
|
|
@@ -191,7 +223,7 @@ describe("saveMessages", () => {
|
|
|
191
223
|
const resolved = await resolvePlugins({
|
|
192
224
|
plugins: [mockPlugin],
|
|
193
225
|
nodeishFs: {} as any,
|
|
194
|
-
settings: {},
|
|
226
|
+
settings: {} as any,
|
|
195
227
|
})
|
|
196
228
|
expect(resolved.errors).toHaveLength(1)
|
|
197
229
|
expect(resolved.errors[0]).toBeInstanceOf(PluginsDoNotProvideLoadOrSaveMessagesError)
|
|
@@ -214,7 +246,7 @@ describe("addCustomApi", () => {
|
|
|
214
246
|
|
|
215
247
|
const resolved = await resolvePlugins({
|
|
216
248
|
plugins: [mockPlugin],
|
|
217
|
-
settings: {},
|
|
249
|
+
settings: {} as any,
|
|
218
250
|
nodeishFs: {} as any,
|
|
219
251
|
})
|
|
220
252
|
|
|
@@ -249,7 +281,7 @@ describe("addCustomApi", () => {
|
|
|
249
281
|
|
|
250
282
|
const resolved = await resolvePlugins({
|
|
251
283
|
plugins: [mockPlugin, mockPlugin2],
|
|
252
|
-
settings: {},
|
|
284
|
+
settings: {} as any,
|
|
253
285
|
nodeishFs: {} as any,
|
|
254
286
|
})
|
|
255
287
|
|
|
@@ -269,7 +301,7 @@ describe("addCustomApi", () => {
|
|
|
269
301
|
|
|
270
302
|
const resolved = await resolvePlugins({
|
|
271
303
|
plugins: [mockPlugin],
|
|
272
|
-
settings: {},
|
|
304
|
+
settings: {} as any,
|
|
273
305
|
nodeishFs: {} as any,
|
|
274
306
|
})
|
|
275
307
|
|
|
@@ -292,7 +324,7 @@ describe("addCustomApi", () => {
|
|
|
292
324
|
|
|
293
325
|
const resolved = await resolvePlugins({
|
|
294
326
|
plugins: [mockPlugin],
|
|
295
|
-
settings: {},
|
|
327
|
+
settings: {} as any,
|
|
296
328
|
nodeishFs: {} as any,
|
|
297
329
|
})
|
|
298
330
|
|
|
@@ -44,7 +44,7 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
44
44
|
if (hasInvalidId) {
|
|
45
45
|
result.errors.push(
|
|
46
46
|
new PluginHasInvalidIdError(
|
|
47
|
-
`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be
|
|
47
|
+
`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be camelCase and contain a namespace like plugin.namespace.myPlugin.`,
|
|
48
48
|
{ plugin: plugin.id }
|
|
49
49
|
)
|
|
50
50
|
)
|
|
@@ -99,7 +99,7 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
99
99
|
// TODO: why do we call this function 2 times (here for validation and later for retrieving the actual value)?
|
|
100
100
|
const { data: customApi, error } = tryCatch(() =>
|
|
101
101
|
plugin.addCustomApi!({
|
|
102
|
-
settings: args.settings
|
|
102
|
+
settings: args.settings,
|
|
103
103
|
})
|
|
104
104
|
)
|
|
105
105
|
if (error) {
|
|
@@ -130,7 +130,6 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
130
130
|
result.data.loadMessages = (_args) =>
|
|
131
131
|
plugin.loadMessages!({
|
|
132
132
|
..._args,
|
|
133
|
-
settings: args.settings?.[plugin.id] ?? {},
|
|
134
133
|
nodeishFs: args.nodeishFs,
|
|
135
134
|
})
|
|
136
135
|
}
|
|
@@ -139,7 +138,6 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
139
138
|
result.data.saveMessages = (_args) =>
|
|
140
139
|
plugin.saveMessages!({
|
|
141
140
|
..._args,
|
|
142
|
-
settings: args.settings?.[plugin.id] ?? {},
|
|
143
141
|
nodeishFs: args.nodeishFs,
|
|
144
142
|
})
|
|
145
143
|
}
|
|
@@ -147,7 +145,7 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
147
145
|
if (typeof plugin.addCustomApi === "function") {
|
|
148
146
|
const { data: customApi } = tryCatch(() =>
|
|
149
147
|
plugin.addCustomApi!({
|
|
150
|
-
settings: args.settings
|
|
148
|
+
settings: args.settings,
|
|
151
149
|
})
|
|
152
150
|
)
|
|
153
151
|
if (customApi) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { LanguageTag } from "@inlang/language-tag"
|
|
2
1
|
import type { NodeishFilesystem as LisaNodeishFilesystem } from "@lix-js/fs"
|
|
3
2
|
import type {
|
|
4
3
|
PluginReturnedInvalidCustomApiError,
|
|
@@ -10,8 +9,8 @@ import type {
|
|
|
10
9
|
PluginsDoNotProvideLoadOrSaveMessagesError,
|
|
11
10
|
} from "./errors.js"
|
|
12
11
|
import type { Message } from "@inlang/message"
|
|
13
|
-
import type { JSONObject } from "@inlang/json-types"
|
|
14
12
|
import type { CustomApiInlangIdeExtension, Plugin } from "@inlang/plugin"
|
|
13
|
+
import type { ProjectSettings } from "@inlang/project-settings"
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* The filesystem is a subset of project lisa's nodeish filesystem.
|
|
@@ -28,7 +27,7 @@ export type NodeishFilesystemSubset = Pick<
|
|
|
28
27
|
*/
|
|
29
28
|
export type ResolvePluginsFunction = (args: {
|
|
30
29
|
plugins: Array<Plugin>
|
|
31
|
-
settings:
|
|
30
|
+
settings: ProjectSettings
|
|
32
31
|
nodeishFs: NodeishFilesystemSubset
|
|
33
32
|
}) => Promise<{
|
|
34
33
|
data: ResolvedPluginApi
|
|
@@ -47,11 +46,8 @@ export type ResolvePluginsFunction = (args: {
|
|
|
47
46
|
* The API after resolving the plugins.
|
|
48
47
|
*/
|
|
49
48
|
export type ResolvedPluginApi = {
|
|
50
|
-
loadMessages: (args: {
|
|
51
|
-
|
|
52
|
-
sourceLanguageTag: LanguageTag
|
|
53
|
-
}) => Promise<Message[]> | Message[]
|
|
54
|
-
saveMessages: (args: { messages: Message[] }) => Promise<void> | void
|
|
49
|
+
loadMessages: (args: { settings: ProjectSettings }) => Promise<Message[]> | Message[]
|
|
50
|
+
saveMessages: (args: { settings: ProjectSettings; messages: Message[] }) => Promise<void> | void
|
|
55
51
|
/**
|
|
56
52
|
* App specific APIs.
|
|
57
53
|
*
|