@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.
@@ -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, 0));
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, 0));
168
- expect(counter).toBe(2); // 2 times because effect creation + set
169
- expect(Object.values(project.query.messages.getAll()).length).toBe(0);
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 bathPath = args.settingsFilePath.split("/").slice(0, -1).join("/");
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(bathPath + "/" + path);
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;AAUjG;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAkMxB,CAAA;AAqGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
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"}
@@ -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(args);
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: args.settingsFilePath, nodeishFs })
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) => {
@@ -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.project.json": {
618
+ "plugin.placeholder.name": {
602
619
  pathPattern: "./resources/{languageTag}.json",
603
620
  },
604
621
  };
605
- await fs.mkdir("/user/project", { recursive: true });
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.project.json",
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: "/user/project/project.inlang.json",
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,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;KAAE;CAIzC;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"}
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;AAuBxD,eAAO,MAAM,cAAc,EAAE,sBAqH5B,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, PluginUsesReservedNamespaceError, } from "./errors.js";
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, PluginUsesReservedNamespaceError, PluginReturnedInvalidCustomApiError, PluginHasInvalidSchemaError, PluginsDoNotProvideLoadOrSaveMessagesError, } from "./errors.js";
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, PluginUsesReservedNamespaceError, PluginsDoNotProvideLoadOrSaveMessagesError } from "./errors.js";
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 | PluginUsesReservedNamespaceError | PluginsDoNotProvideLoadOrSaveMessagesError>;
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,gCAAgC,EAChC,0CAA0C,EAC1C,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE/D;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACzC,iBAAiB,EACjB,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAC9C,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACtB,QAAQ,EAAE,eAAe,CAAA;IACzB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAK,OAAO,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAA;IACvB,MAAM,EAAE,KAAK,CACV,mCAAmC,GACnC,6CAA6C,GAC7C,6CAA6C,GAC7C,uBAAuB,GACvB,2BAA2B,GAC3B,gCAAgC,GAChC,0CAA0C,CAC5C,CAAA;CACD,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAA;IACrF,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChG;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,MAAM,CAAC,OAAO,MAAM,IAAI,MAAM,EAAE,GAAG,WAAW,MAAM,IAAI,MAAM,EAAE,EAAE,OAAO,CAAC,GAAG;QACvF,yBAAyB,CAAC,EAAE,2BAA2B,CAAA;KACvD,CAAA;CACD,CAAA"}
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.12.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.0",
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, 0))
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, 0))
210
+ await new Promise((resolve) => setTimeout(resolve, 510))
209
211
 
210
- expect(counter).toBe(2) // 2 times because effect creation + set
211
- expect(Object.values(project.query.messages.getAll()).length).toBe(0)
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 bathPath = args.settingsFilePath.split("/").slice(0, -1).join("/")
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(bathPath + "/" + path)
29
+ return normalizePath(basePath + "/" + path)
30
30
  }
31
31
 
32
32
  return {
@@ -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.project.json": {
726
+ "plugin.placeholder.name": {
705
727
  pathPattern: "./resources/{languageTag}.json",
706
728
  },
707
729
  }
708
730
 
709
- await fs.mkdir("/user/project", { recursive: true })
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.project.json",
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: "/user/project/project.inlang.json",
754
+ settingsFilePath: "/project.inlang.json",
734
755
  nodeishFs: fs,
735
756
  _import,
736
757
  })
@@ -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(args)
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: args.settingsFilePath, nodeishFs })
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
  }>