@inlang/sdk 0.12.0 → 0.13.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 +5 -5
- package/dist/createNodeishFsWithAbsolutePaths.js +3 -3
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +22 -3
- package/dist/loadProject.test.js +22 -6
- package/dist/resolve-modules/plugins/errors.d.ts +0 -5
- package/dist/resolve-modules/plugins/errors.d.ts.map +1 -1
- package/dist/resolve-modules/plugins/errors.js +0 -6
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -1
- package/dist/resolve-modules/plugins/resolvePlugins.js +1 -12
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +1 -15
- package/dist/resolve-modules/plugins/types.d.ts +2 -2
- package/dist/resolve-modules/plugins/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/adapter/solidAdapter.test.ts +7 -5
- package/src/createNodeishFsWithAbsolutePaths.ts +3 -3
- package/src/loadProject.test.ts +27 -6
- package/src/loadProject.ts +27 -3
- package/src/resolve-modules/plugins/errors.ts +0 -7
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +0 -18
- package/src/resolve-modules/plugins/resolvePlugins.ts +0 -15
- package/src/resolve-modules/plugins/types.ts +0 -2
|
@@ -88,7 +88,7 @@ describe("config", () => {
|
|
|
88
88
|
const newConfig = { ...project.settings(), languageTags: ["en", "de"] };
|
|
89
89
|
project.setSettings(newConfig);
|
|
90
90
|
// TODO: how can we await `setConfig` correctly
|
|
91
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
92
92
|
expect(counter).toBe(2); // 2 times because effect creation + set
|
|
93
93
|
expect(project.settings()).toStrictEqual(newConfig);
|
|
94
94
|
});
|
|
@@ -162,11 +162,11 @@ describe("messages", () => {
|
|
|
162
162
|
counter += 1;
|
|
163
163
|
});
|
|
164
164
|
expect(Object.values(project.query.messages.getAll()).length).toBe(2);
|
|
165
|
-
project.setSettings({ ...project.settings(), languageTags: [] });
|
|
165
|
+
project.setSettings({ ...project.settings(), languageTags: ["en"] });
|
|
166
166
|
// TODO: how can we await `setConfig` correctly
|
|
167
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
168
|
-
expect(counter).toBe(
|
|
169
|
-
expect(Object.values(project.query.messages.getAll()).length).toBe(
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, 510));
|
|
168
|
+
expect(counter).toBe(1); // 2 times because effect creation + set
|
|
169
|
+
expect(Object.values(project.query.messages.getAll()).length).toBe(2);
|
|
170
170
|
});
|
|
171
171
|
it("should react to changes in messages", async () => {
|
|
172
172
|
const fs = createNodeishMemoryFs();
|
|
@@ -12,12 +12,12 @@ export const createNodeishFsWithAbsolutePaths = (args) => {
|
|
|
12
12
|
}
|
|
13
13
|
// get the base path of the settings file by
|
|
14
14
|
// removing the file name from the path
|
|
15
|
-
const
|
|
15
|
+
const basePath = args.settingsFilePath.split("/").slice(0, -1).join("/");
|
|
16
16
|
const makeAbsolute = (path) => {
|
|
17
17
|
if (isAbsolutePath(path)) {
|
|
18
|
-
return path;
|
|
18
|
+
return normalizePath(path);
|
|
19
19
|
}
|
|
20
|
-
return normalizePath(
|
|
20
|
+
return normalizePath(basePath + "/" + path);
|
|
21
21
|
};
|
|
22
22
|
return {
|
|
23
23
|
// @ts-expect-error
|
|
@@ -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;AAchF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,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;AAWjG;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAuMxB,CAAA;AAuHD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/loadProject.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveModules } from "./resolve-modules/index.js";
|
|
2
|
-
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
2
|
+
import { TypeCompiler, ValueErrorType } from "@sinclair/typebox/compiler";
|
|
3
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";
|
|
@@ -9,6 +9,7 @@ import { ProjectSettings, Message } from "./versionedInterfaces.js";
|
|
|
9
9
|
import { tryCatch } from "@inlang/result";
|
|
10
10
|
import { migrateIfOutdated } from "@inlang/project-settings/migration";
|
|
11
11
|
import { createNodeishFsWithAbsolutePaths, isAbsolutePath, } from "./createNodeishFsWithAbsolutePaths.js";
|
|
12
|
+
import { normalizePath } from "@lix-js/fs";
|
|
12
13
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
|
|
13
14
|
/**
|
|
14
15
|
* Creates an inlang instance.
|
|
@@ -28,14 +29,18 @@ export const loadProject = async (args) => {
|
|
|
28
29
|
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
29
30
|
throw new LoadProjectInvalidArgument(`Expected an absolute path but received "${args.settingsFilePath}".`, { argument: "settingsFilePath" });
|
|
30
31
|
}
|
|
32
|
+
const settingsFilePath = normalizePath(args.settingsFilePath);
|
|
31
33
|
// -- load project ------------------------------------------------------
|
|
32
34
|
return await createRoot(async () => {
|
|
33
35
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable();
|
|
34
|
-
const nodeishFs = createNodeishFsWithAbsolutePaths(
|
|
36
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
37
|
+
settingsFilePath,
|
|
38
|
+
nodeishFs: args.nodeishFs,
|
|
39
|
+
});
|
|
35
40
|
// -- settings ------------------------------------------------------------
|
|
36
41
|
const [settings, _setSettings] = createSignal();
|
|
37
42
|
createEffect(() => {
|
|
38
|
-
loadSettings({ settingsFilePath
|
|
43
|
+
loadSettings({ settingsFilePath, nodeishFs })
|
|
39
44
|
.then((settings) => {
|
|
40
45
|
setSettings(settings);
|
|
41
46
|
// rename settings to get a convenient access to the data in Posthog
|
|
@@ -196,6 +201,20 @@ const parseSettings = (settings) => {
|
|
|
196
201
|
});
|
|
197
202
|
}
|
|
198
203
|
}
|
|
204
|
+
const { sourceLanguageTag, languageTags } = settings;
|
|
205
|
+
if (!languageTags.includes(sourceLanguageTag)) {
|
|
206
|
+
throw new ProjectSettingsInvalidError({
|
|
207
|
+
errors: [
|
|
208
|
+
{
|
|
209
|
+
message: `The sourceLanguageTag "${sourceLanguageTag}" is not included in the languageTags "${languageTags.join('", "')}". Please add it to the languageTags.`,
|
|
210
|
+
type: ValueErrorType.String,
|
|
211
|
+
schema: ProjectSettings,
|
|
212
|
+
value: sourceLanguageTag,
|
|
213
|
+
path: "sourceLanguageTag",
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
});
|
|
217
|
+
}
|
|
199
218
|
return withMigration;
|
|
200
219
|
};
|
|
201
220
|
const _writeSettingsToDisk = async (args) => {
|
package/dist/loadProject.test.js
CHANGED
|
@@ -236,6 +236,23 @@ describe("functionality", () => {
|
|
|
236
236
|
expect(result.data).toBeUndefined();
|
|
237
237
|
expect(result.error).toBeInstanceOf(ProjectSettingsInvalidError);
|
|
238
238
|
});
|
|
239
|
+
it("should throw an error if sourceLanguageTag is not in languageTags", async () => {
|
|
240
|
+
const fs = await createNodeishMemoryFs();
|
|
241
|
+
await fs.mkdir("/user/project", { recursive: true });
|
|
242
|
+
const settings = {
|
|
243
|
+
sourceLanguageTag: "en",
|
|
244
|
+
languageTags: ["de"],
|
|
245
|
+
modules: [],
|
|
246
|
+
};
|
|
247
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
248
|
+
const project = await loadProject({
|
|
249
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
250
|
+
nodeishFs: fs,
|
|
251
|
+
_import,
|
|
252
|
+
});
|
|
253
|
+
expect(project.errors()).toHaveLength(1);
|
|
254
|
+
expect(project.errors()[0]).toBeInstanceOf(ProjectSettingsInvalidError);
|
|
255
|
+
});
|
|
239
256
|
it("should write settings to disk", async () => {
|
|
240
257
|
const fs = await createNodeishMemoryFs();
|
|
241
258
|
await fs.mkdir("/user/project", { recursive: true });
|
|
@@ -247,7 +264,7 @@ describe("functionality", () => {
|
|
|
247
264
|
});
|
|
248
265
|
const before = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" });
|
|
249
266
|
expect(before).toBeDefined();
|
|
250
|
-
const result = project.setSettings({ ...settings, languageTags: [] });
|
|
267
|
+
const result = project.setSettings({ ...settings, languageTags: ["en"] });
|
|
251
268
|
expect(result.data).toBeUndefined();
|
|
252
269
|
expect(result.error).toBeUndefined();
|
|
253
270
|
// TODO: how to wait for fs.writeFile to finish?
|
|
@@ -598,15 +615,14 @@ describe("functionality", () => {
|
|
|
598
615
|
sourceLanguageTag: "en",
|
|
599
616
|
languageTags: ["en", "de"],
|
|
600
617
|
modules: ["plugin.js"],
|
|
601
|
-
"plugin.
|
|
618
|
+
"plugin.placeholder.name": {
|
|
602
619
|
pathPattern: "./resources/{languageTag}.json",
|
|
603
620
|
},
|
|
604
621
|
};
|
|
605
|
-
await fs.
|
|
606
|
-
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
|
|
622
|
+
await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
|
|
607
623
|
const mockSaveFn = vi.fn();
|
|
608
624
|
const _mockPlugin = {
|
|
609
|
-
id: "plugin.
|
|
625
|
+
id: "plugin.placeholder.name",
|
|
610
626
|
description: "Mock plugin description",
|
|
611
627
|
displayName: "Mock Plugin",
|
|
612
628
|
loadMessages: () => [
|
|
@@ -622,7 +638,7 @@ describe("functionality", () => {
|
|
|
622
638
|
};
|
|
623
639
|
};
|
|
624
640
|
const project = await loadProject({
|
|
625
|
-
settingsFilePath: "/
|
|
641
|
+
settingsFilePath: "/project.inlang.json",
|
|
626
642
|
nodeishFs: fs,
|
|
627
643
|
_import,
|
|
628
644
|
});
|
|
@@ -5,11 +5,6 @@ export declare class PluginHasInvalidIdError extends Error {
|
|
|
5
5
|
id: Plugin["id"];
|
|
6
6
|
});
|
|
7
7
|
}
|
|
8
|
-
export declare class PluginUsesReservedNamespaceError extends Error {
|
|
9
|
-
constructor(options: {
|
|
10
|
-
id: Plugin["id"];
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
8
|
export declare class PluginHasInvalidSchemaError extends Error {
|
|
14
9
|
constructor(options: {
|
|
15
10
|
id: Plugin["id"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;KAAE;CAMzC;AAED,qBAAa,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;KAAE;CAMzC;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;CAQ/D;AAED,qBAAa,6CAA8C,SAAQ,KAAK;gBAC3D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;KAAE;CAMzC;AAED,qBAAa,6CAA8C,SAAQ,KAAK;gBAC3D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;KAAE;CAMzC;AAED,qBAAa,mCAAoC,SAAQ,KAAK;gBACjD,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIvE;AAED,qBAAa,0CAA2C,SAAQ,KAAK;;CAOpE"}
|
|
@@ -4,12 +4,6 @@ export class PluginHasInvalidIdError extends Error {
|
|
|
4
4
|
this.name = "PluginHasInvalidIdError";
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
-
export class PluginUsesReservedNamespaceError extends Error {
|
|
8
|
-
constructor(options) {
|
|
9
|
-
super(`Plugin ${options.id} uses reserved namespace 'inlang'.`);
|
|
10
|
-
this.name = "PluginUsesReservedNamespaceError";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
7
|
export class PluginHasInvalidSchemaError extends Error {
|
|
14
8
|
constructor(options) {
|
|
15
9
|
super(`Plugin "${options.id}" has an invalid schema:\n\n${options.errors
|
|
@@ -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;
|
|
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;AAiBxD,eAAO,MAAM,cAAc,EAAE,sBA4G5B,CAAA"}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { Plugin } from "@inlang/plugin";
|
|
2
|
-
import { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginsDoNotProvideLoadOrSaveMessagesError, PluginHasInvalidIdError, PluginHasInvalidSchemaError,
|
|
2
|
+
import { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginsDoNotProvideLoadOrSaveMessagesError, PluginHasInvalidIdError, PluginHasInvalidSchemaError, } from "./errors.js";
|
|
3
3
|
import { deepmerge } from "deepmerge-ts";
|
|
4
4
|
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
5
5
|
import { tryCatch } from "@inlang/result";
|
|
6
|
-
const whitelistedPlugins = [
|
|
7
|
-
"plugin.inlang.json",
|
|
8
|
-
"plugin.inlang.i18next",
|
|
9
|
-
"plugin.inlang.paraglideJs",
|
|
10
|
-
];
|
|
11
6
|
// @ts-ignore - type mismatch error
|
|
12
7
|
const PluginCompiler = TypeCompiler.Compile(Plugin);
|
|
13
8
|
export const resolvePlugins = async (args) => {
|
|
@@ -29,12 +24,6 @@ export const resolvePlugins = async (args) => {
|
|
|
29
24
|
if (hasInvalidId) {
|
|
30
25
|
result.errors.push(new PluginHasInvalidIdError({ id: plugin.id }));
|
|
31
26
|
}
|
|
32
|
-
// -- USES RESERVED NAMESPACE --
|
|
33
|
-
if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
|
|
34
|
-
result.errors.push(new PluginUsesReservedNamespaceError({
|
|
35
|
-
id: plugin.id,
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
27
|
// -- USES INVALID SCHEMA --
|
|
39
28
|
if (errors.length > 0) {
|
|
40
29
|
result.errors.push(new PluginHasInvalidSchemaError({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import { resolvePlugins } from "./resolvePlugins.js";
|
|
4
|
-
import { PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError,
|
|
4
|
+
import { PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginReturnedInvalidCustomApiError, PluginHasInvalidSchemaError, PluginsDoNotProvideLoadOrSaveMessagesError, } from "./errors.js";
|
|
5
5
|
it("should return an error if a plugin uses an invalid id", async () => {
|
|
6
6
|
const mockPlugin = {
|
|
7
7
|
// @ts-expect-error - invalid id
|
|
@@ -37,20 +37,6 @@ it("should return an error if a plugin uses APIs that are not available", async
|
|
|
37
37
|
});
|
|
38
38
|
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError);
|
|
39
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
40
|
it("should expose the project settings including the plugin settings", async () => {
|
|
55
41
|
const settings = {
|
|
56
42
|
sourceLanguageTag: "en",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NodeishFilesystem } from "@lix-js/fs";
|
|
2
|
-
import type { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginHasInvalidSchemaError,
|
|
2
|
+
import type { PluginReturnedInvalidCustomApiError, PluginLoadMessagesFunctionAlreadyDefinedError, PluginSaveMessagesFunctionAlreadyDefinedError, PluginHasInvalidIdError, PluginHasInvalidSchemaError, PluginsDoNotProvideLoadOrSaveMessagesError } from "./errors.js";
|
|
3
3
|
import type { Message } from "@inlang/message";
|
|
4
4
|
import type { CustomApiInlangIdeExtension, Plugin } from "@inlang/plugin";
|
|
5
5
|
import type { ProjectSettings } from "@inlang/project-settings";
|
|
@@ -18,7 +18,7 @@ export type ResolvePluginsFunction = (args: {
|
|
|
18
18
|
nodeishFs: NodeishFilesystemSubset;
|
|
19
19
|
}) => Promise<{
|
|
20
20
|
data: ResolvedPluginApi;
|
|
21
|
-
errors: Array<PluginReturnedInvalidCustomApiError | PluginLoadMessagesFunctionAlreadyDefinedError | PluginSaveMessagesFunctionAlreadyDefinedError | PluginHasInvalidIdError | PluginHasInvalidSchemaError |
|
|
21
|
+
errors: Array<PluginReturnedInvalidCustomApiError | PluginLoadMessagesFunctionAlreadyDefinedError | PluginSaveMessagesFunctionAlreadyDefinedError | PluginHasInvalidIdError | PluginHasInvalidSchemaError | PluginsDoNotProvideLoadOrSaveMessagesError>;
|
|
22
22
|
}>;
|
|
23
23
|
/**
|
|
24
24
|
* The API after resolving the plugins.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EACX,mCAAmC,EACnC,6CAA6C,EAC7C,6CAA6C,EAC7C,uBAAuB,EACvB,2BAA2B,EAC3B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/plugins/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EACX,mCAAmC,EACnC,6CAA6C,EAC7C,6CAA6C,EAC7C,uBAAuB,EACvB,2BAA2B,EAC3B,0CAA0C,EAC1C,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE/D;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACzC,iBAAiB,EACjB,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAC9C,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACtB,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAK,OAAO,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,EAAE,KAAK,CACV,mCAAmC,GACnC,6CAA6C,GAC7C,6CAA6C,GAC7C,uBAAuB,GACvB,2BAA2B,GAC3B,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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inlang/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@inlang/project-settings": "*",
|
|
39
39
|
"@inlang/result": "*",
|
|
40
40
|
"@lix-js/fs": "*",
|
|
41
|
-
"@sinclair/typebox": "^0.31.
|
|
41
|
+
"@sinclair/typebox": "^0.31.17",
|
|
42
42
|
"deepmerge-ts": "^5.1.0",
|
|
43
43
|
"solid-js": "1.6.12",
|
|
44
44
|
"throttle-debounce": "5.0.0",
|
|
@@ -109,8 +109,10 @@ describe("config", () => {
|
|
|
109
109
|
const newConfig = { ...project.settings()!, languageTags: ["en", "de"] }
|
|
110
110
|
|
|
111
111
|
project.setSettings(newConfig)
|
|
112
|
+
|
|
112
113
|
// TODO: how can we await `setConfig` correctly
|
|
113
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
115
|
+
|
|
114
116
|
expect(counter).toBe(2) // 2 times because effect creation + set
|
|
115
117
|
expect(project.settings()).toStrictEqual(newConfig)
|
|
116
118
|
})
|
|
@@ -202,13 +204,13 @@ describe("messages", () => {
|
|
|
202
204
|
|
|
203
205
|
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
|
|
204
206
|
|
|
205
|
-
project.setSettings({ ...project.settings()!, languageTags: [] })
|
|
207
|
+
project.setSettings({ ...project.settings()!, languageTags: ["en"] })
|
|
206
208
|
|
|
207
209
|
// TODO: how can we await `setConfig` correctly
|
|
208
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
210
|
+
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
209
211
|
|
|
210
|
-
expect(counter).toBe(
|
|
211
|
-
expect(Object.values(project.query.messages.getAll()).length).toBe(
|
|
212
|
+
expect(counter).toBe(1) // 2 times because effect creation + set
|
|
213
|
+
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
|
|
212
214
|
})
|
|
213
215
|
|
|
214
216
|
it("should react to changes in messages", async () => {
|
|
@@ -19,14 +19,14 @@ export const createNodeishFsWithAbsolutePaths = (args: {
|
|
|
19
19
|
|
|
20
20
|
// get the base path of the settings file by
|
|
21
21
|
// removing the file name from the path
|
|
22
|
-
const
|
|
22
|
+
const basePath = args.settingsFilePath.split("/").slice(0, -1).join("/")
|
|
23
23
|
|
|
24
24
|
const makeAbsolute = (path: string) => {
|
|
25
25
|
if (isAbsolutePath(path)) {
|
|
26
|
-
return path
|
|
26
|
+
return normalizePath(path)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
return normalizePath(
|
|
29
|
+
return normalizePath(basePath + "/" + path)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
return {
|
package/src/loadProject.test.ts
CHANGED
|
@@ -289,6 +289,28 @@ describe("functionality", () => {
|
|
|
289
289
|
expect(result.error).toBeInstanceOf(ProjectSettingsInvalidError)
|
|
290
290
|
})
|
|
291
291
|
|
|
292
|
+
it("should throw an error if sourceLanguageTag is not in languageTags", async () => {
|
|
293
|
+
const fs = await createNodeishMemoryFs()
|
|
294
|
+
await fs.mkdir("/user/project", { recursive: true })
|
|
295
|
+
|
|
296
|
+
const settings: ProjectSettings = {
|
|
297
|
+
sourceLanguageTag: "en",
|
|
298
|
+
languageTags: ["de"],
|
|
299
|
+
modules: [],
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
303
|
+
|
|
304
|
+
const project = await loadProject({
|
|
305
|
+
settingsFilePath: "/user/project/project.inlang.json",
|
|
306
|
+
nodeishFs: fs,
|
|
307
|
+
_import,
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
expect(project.errors()).toHaveLength(1)
|
|
311
|
+
expect(project.errors()![0]).toBeInstanceOf(ProjectSettingsInvalidError)
|
|
312
|
+
})
|
|
313
|
+
|
|
292
314
|
it("should write settings to disk", async () => {
|
|
293
315
|
const fs = await createNodeishMemoryFs()
|
|
294
316
|
await fs.mkdir("/user/project", { recursive: true })
|
|
@@ -302,7 +324,7 @@ describe("functionality", () => {
|
|
|
302
324
|
const before = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" })
|
|
303
325
|
expect(before).toBeDefined()
|
|
304
326
|
|
|
305
|
-
const result = project.setSettings({ ...settings, languageTags: [] })
|
|
327
|
+
const result = project.setSettings({ ...settings, languageTags: ["en"] })
|
|
306
328
|
expect(result.data).toBeUndefined()
|
|
307
329
|
expect(result.error).toBeUndefined()
|
|
308
330
|
|
|
@@ -701,18 +723,17 @@ describe("functionality", () => {
|
|
|
701
723
|
sourceLanguageTag: "en",
|
|
702
724
|
languageTags: ["en", "de"],
|
|
703
725
|
modules: ["plugin.js"],
|
|
704
|
-
"plugin.
|
|
726
|
+
"plugin.placeholder.name": {
|
|
705
727
|
pathPattern: "./resources/{languageTag}.json",
|
|
706
728
|
},
|
|
707
729
|
}
|
|
708
730
|
|
|
709
|
-
await fs.
|
|
710
|
-
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
731
|
+
await fs.writeFile("./project.inlang.json", JSON.stringify(settings))
|
|
711
732
|
|
|
712
733
|
const mockSaveFn = vi.fn()
|
|
713
734
|
|
|
714
735
|
const _mockPlugin: Plugin = {
|
|
715
|
-
id: "plugin.
|
|
736
|
+
id: "plugin.placeholder.name",
|
|
716
737
|
description: "Mock plugin description",
|
|
717
738
|
displayName: "Mock Plugin",
|
|
718
739
|
loadMessages: () => [
|
|
@@ -730,7 +751,7 @@ describe("functionality", () => {
|
|
|
730
751
|
}
|
|
731
752
|
|
|
732
753
|
const project = await loadProject({
|
|
733
|
-
settingsFilePath: "/
|
|
754
|
+
settingsFilePath: "/project.inlang.json",
|
|
734
755
|
nodeishFs: fs,
|
|
735
756
|
_import,
|
|
736
757
|
})
|
package/src/loadProject.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
Subscribable,
|
|
7
7
|
} from "./api.js"
|
|
8
8
|
import { type ImportFunction, resolveModules } from "./resolve-modules/index.js"
|
|
9
|
-
import { TypeCompiler } from "@sinclair/typebox/compiler"
|
|
9
|
+
import { TypeCompiler, ValueErrorType } from "@sinclair/typebox/compiler"
|
|
10
10
|
import {
|
|
11
11
|
ProjectSettingsFileJSONSyntaxError,
|
|
12
12
|
ProjectSettingsFileNotFoundError,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
createNodeishFsWithAbsolutePaths,
|
|
27
27
|
isAbsolutePath,
|
|
28
28
|
} from "./createNodeishFsWithAbsolutePaths.js"
|
|
29
|
+
import { normalizePath } from "@lix-js/fs"
|
|
29
30
|
|
|
30
31
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
|
|
31
32
|
|
|
@@ -56,16 +57,21 @@ export const loadProject = async (args: {
|
|
|
56
57
|
)
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
const settingsFilePath = normalizePath(args.settingsFilePath)
|
|
61
|
+
|
|
59
62
|
// -- load project ------------------------------------------------------
|
|
60
63
|
return await createRoot(async () => {
|
|
61
64
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable()
|
|
62
|
-
const nodeishFs = createNodeishFsWithAbsolutePaths(
|
|
65
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
66
|
+
settingsFilePath,
|
|
67
|
+
nodeishFs: args.nodeishFs,
|
|
68
|
+
})
|
|
63
69
|
|
|
64
70
|
// -- settings ------------------------------------------------------------
|
|
65
71
|
|
|
66
72
|
const [settings, _setSettings] = createSignal<ProjectSettings>()
|
|
67
73
|
createEffect(() => {
|
|
68
|
-
loadSettings({ settingsFilePath
|
|
74
|
+
loadSettings({ settingsFilePath, nodeishFs })
|
|
69
75
|
.then((settings) => {
|
|
70
76
|
setSettings(settings)
|
|
71
77
|
// rename settings to get a convenient access to the data in Posthog
|
|
@@ -278,6 +284,24 @@ const parseSettings = (settings: unknown) => {
|
|
|
278
284
|
})
|
|
279
285
|
}
|
|
280
286
|
}
|
|
287
|
+
|
|
288
|
+
const { sourceLanguageTag, languageTags } = settings as ProjectSettings
|
|
289
|
+
if (!languageTags.includes(sourceLanguageTag)) {
|
|
290
|
+
throw new ProjectSettingsInvalidError({
|
|
291
|
+
errors: [
|
|
292
|
+
{
|
|
293
|
+
message: `The sourceLanguageTag "${sourceLanguageTag}" is not included in the languageTags "${languageTags.join(
|
|
294
|
+
'", "'
|
|
295
|
+
)}". Please add it to the languageTags.`,
|
|
296
|
+
type: ValueErrorType.String,
|
|
297
|
+
schema: ProjectSettings,
|
|
298
|
+
value: sourceLanguageTag,
|
|
299
|
+
path: "sourceLanguageTag",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
|
|
281
305
|
return withMigration
|
|
282
306
|
}
|
|
283
307
|
|
|
@@ -10,13 +10,6 @@ export class PluginHasInvalidIdError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export class PluginUsesReservedNamespaceError extends Error {
|
|
14
|
-
constructor(options: { id: Plugin["id"] }) {
|
|
15
|
-
super(`Plugin ${options.id} uses reserved namespace 'inlang'.`)
|
|
16
|
-
this.name = "PluginUsesReservedNamespaceError"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
13
|
export class PluginHasInvalidSchemaError extends Error {
|
|
21
14
|
constructor(options: { id: Plugin["id"]; errors: ValueError[] }) {
|
|
22
15
|
super(
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
PluginLoadMessagesFunctionAlreadyDefinedError,
|
|
6
6
|
PluginSaveMessagesFunctionAlreadyDefinedError,
|
|
7
7
|
PluginHasInvalidIdError,
|
|
8
|
-
PluginUsesReservedNamespaceError,
|
|
9
8
|
PluginReturnedInvalidCustomApiError,
|
|
10
9
|
PluginHasInvalidSchemaError,
|
|
11
10
|
PluginsDoNotProvideLoadOrSaveMessagesError,
|
|
@@ -54,23 +53,6 @@ it("should return an error if a plugin uses APIs that are not available", async
|
|
|
54
53
|
expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError)
|
|
55
54
|
})
|
|
56
55
|
|
|
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,
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
expect(resolved.errors[0]).toBeInstanceOf(PluginUsesReservedNamespaceError)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
56
|
it("should expose the project settings including the plugin settings", async () => {
|
|
75
57
|
const settings: ProjectSettings = {
|
|
76
58
|
sourceLanguageTag: "en",
|
|
@@ -8,17 +8,11 @@ import {
|
|
|
8
8
|
PluginsDoNotProvideLoadOrSaveMessagesError,
|
|
9
9
|
PluginHasInvalidIdError,
|
|
10
10
|
PluginHasInvalidSchemaError,
|
|
11
|
-
PluginUsesReservedNamespaceError,
|
|
12
11
|
} from "./errors.js"
|
|
13
12
|
import { deepmerge } from "deepmerge-ts"
|
|
14
13
|
import { TypeCompiler } from "@sinclair/typebox/compiler"
|
|
15
14
|
import { tryCatch } from "@inlang/result"
|
|
16
15
|
|
|
17
|
-
const whitelistedPlugins = [
|
|
18
|
-
"plugin.inlang.json",
|
|
19
|
-
"plugin.inlang.i18next",
|
|
20
|
-
"plugin.inlang.paraglideJs",
|
|
21
|
-
]
|
|
22
16
|
// @ts-ignore - type mismatch error
|
|
23
17
|
const PluginCompiler = TypeCompiler.Compile(Plugin)
|
|
24
18
|
|
|
@@ -45,15 +39,6 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
|
|
|
45
39
|
result.errors.push(new PluginHasInvalidIdError({ id: plugin.id }))
|
|
46
40
|
}
|
|
47
41
|
|
|
48
|
-
// -- USES RESERVED NAMESPACE --
|
|
49
|
-
if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
|
|
50
|
-
result.errors.push(
|
|
51
|
-
new PluginUsesReservedNamespaceError({
|
|
52
|
-
id: plugin.id,
|
|
53
|
-
})
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
42
|
// -- USES INVALID SCHEMA --
|
|
58
43
|
if (errors.length > 0) {
|
|
59
44
|
result.errors.push(
|
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
PluginSaveMessagesFunctionAlreadyDefinedError,
|
|
6
6
|
PluginHasInvalidIdError,
|
|
7
7
|
PluginHasInvalidSchemaError,
|
|
8
|
-
PluginUsesReservedNamespaceError,
|
|
9
8
|
PluginsDoNotProvideLoadOrSaveMessagesError,
|
|
10
9
|
} from "./errors.js"
|
|
11
10
|
import type { Message } from "@inlang/message"
|
|
@@ -37,7 +36,6 @@ export type ResolvePluginsFunction = (args: {
|
|
|
37
36
|
| PluginSaveMessagesFunctionAlreadyDefinedError
|
|
38
37
|
| PluginHasInvalidIdError
|
|
39
38
|
| PluginHasInvalidSchemaError
|
|
40
|
-
| PluginUsesReservedNamespaceError
|
|
41
39
|
| PluginsDoNotProvideLoadOrSaveMessagesError
|
|
42
40
|
>
|
|
43
41
|
}>
|