@inlang/sdk 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/adapter/solidAdapter.test.js +1 -1
  2. package/dist/errors.d.ts +18 -5
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +12 -10
  5. package/dist/loadProject.d.ts.map +1 -1
  6. package/dist/loadProject.js +14 -10
  7. package/dist/loadProject.test.js +4 -5
  8. package/dist/resolve-modules/errors.d.ts +6 -5
  9. package/dist/resolve-modules/errors.d.ts.map +1 -1
  10. package/dist/resolve-modules/errors.js +10 -8
  11. package/dist/resolve-modules/import.d.ts.map +1 -1
  12. package/dist/resolve-modules/import.js +2 -3
  13. package/dist/resolve-modules/message-lint-rules/errors.d.ts +5 -4
  14. package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -1
  15. package/dist/resolve-modules/message-lint-rules/errors.js +2 -4
  16. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -1
  17. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +4 -2
  18. package/dist/resolve-modules/plugins/errors.d.ts +35 -28
  19. package/dist/resolve-modules/plugins/errors.d.ts.map +1 -1
  20. package/dist/resolve-modules/plugins/errors.js +23 -30
  21. package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -1
  22. package/dist/resolve-modules/plugins/resolvePlugins.js +17 -18
  23. package/dist/resolve-modules/plugins/resolvePlugins.test.js +79 -49
  24. package/dist/resolve-modules/plugins/types.d.ts +4 -5
  25. package/dist/resolve-modules/plugins/types.d.ts.map +1 -1
  26. package/dist/resolve-modules/resolveModules.d.ts.map +1 -1
  27. package/dist/resolve-modules/resolveModules.js +5 -4
  28. package/dist/resolve-modules/resolveModules.test.js +2 -2
  29. package/dist/test-utilities/createMessage.d.ts +1 -1
  30. package/package.json +1 -1
  31. package/src/adapter/solidAdapter.test.ts +1 -1
  32. package/src/errors.ts +19 -10
  33. package/src/loadProject.test.ts +11 -14
  34. package/src/loadProject.ts +18 -17
  35. package/src/resolve-modules/errors.ts +14 -8
  36. package/src/resolve-modules/import.ts +2 -3
  37. package/src/resolve-modules/message-lint-rules/errors.ts +5 -5
  38. package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +4 -2
  39. package/src/resolve-modules/plugins/errors.ts +34 -36
  40. package/src/resolve-modules/plugins/resolvePlugins.test.ts +94 -62
  41. package/src/resolve-modules/plugins/resolvePlugins.ts +19 -50
  42. package/src/resolve-modules/plugins/types.ts +4 -8
  43. package/src/resolve-modules/resolveModules.test.ts +2 -2
  44. package/src/resolve-modules/resolveModules.ts +5 -6
@@ -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
- describe("generally", () => {
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: {},
29
- nodeishFs: {} as any,
30
- })
32
+ expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidIdError)
33
+ })
31
34
 
32
- expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidIdError)
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
- 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: {},
51
- nodeishFs: {} as any,
52
- })
54
+ expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError)
55
+ })
53
56
 
54
- expect(resolved.errors[0]).toBeInstanceOf(PluginHasInvalidSchemaError)
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
- 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: {},
68
- nodeishFs: {} as any,
69
- })
71
+ expect(resolved.errors[0]).toBeInstanceOf(PluginUsesReservedNamespaceError)
72
+ })
70
73
 
71
- expect(resolved.errors[0]).toBeInstanceOf(PluginUsesReservedNamespaceError)
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
- languageTags: ["en"],
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
 
@@ -42,56 +42,35 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
42
42
  // -- INVALID ID in META --
43
43
  const hasInvalidId = errors.some((error) => error.path === "/id")
44
44
  if (hasInvalidId) {
45
- result.errors.push(
46
- new PluginHasInvalidIdError(
47
- `Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be kebap-case and contain a namespace like project.my-plugin.`,
48
- { plugin: plugin.id }
49
- )
50
- )
45
+ result.errors.push(new PluginHasInvalidIdError({ id: plugin.id }))
51
46
  }
52
47
 
53
48
  // -- USES RESERVED NAMESPACE --
54
49
  if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
55
50
  result.errors.push(
56
- new PluginUsesReservedNamespaceError(
57
- `Plugin ${plugin.id} uses reserved namespace 'inlang'.`,
58
- {
59
- plugin: plugin.id,
60
- }
61
- )
51
+ new PluginUsesReservedNamespaceError({
52
+ id: plugin.id,
53
+ })
62
54
  )
63
55
  }
64
56
 
65
57
  // -- USES INVALID SCHEMA --
66
58
  if (errors.length > 0) {
67
59
  result.errors.push(
68
- new PluginHasInvalidSchemaError(
69
- `Plugin ${plugin.id} uses an invalid schema. Please check the documentation for the correct Plugin type.`,
70
- {
71
- plugin: plugin.id,
72
- cause: errors,
73
- }
74
- )
60
+ new PluginHasInvalidSchemaError({
61
+ id: plugin.id,
62
+ errors: errors,
63
+ })
75
64
  )
76
65
  }
77
66
 
78
67
  // -- ALREADY DEFINED LOADMESSAGES / SAVEMESSAGES / DETECTEDLANGUAGETAGS --
79
68
  if (typeof plugin.loadMessages === "function" && result.data.loadMessages !== undefined) {
80
- result.errors.push(
81
- new PluginLoadMessagesFunctionAlreadyDefinedError(
82
- `Plugin ${plugin.id} defines the loadMessages function, but it was already defined by another plugin.`,
83
- { plugin: plugin.id }
84
- )
85
- )
69
+ result.errors.push(new PluginLoadMessagesFunctionAlreadyDefinedError({ id: plugin.id }))
86
70
  }
87
71
 
88
72
  if (typeof plugin.saveMessages === "function" && result.data.saveMessages !== undefined) {
89
- result.errors.push(
90
- new PluginSaveMessagesFunctionAlreadyDefinedError(
91
- `Plugin ${plugin.id} defines the saveMessages function, but it was already defined by another plugin.`,
92
- { plugin: plugin.id }
93
- )
94
- )
73
+ result.errors.push(new PluginSaveMessagesFunctionAlreadyDefinedError({ id: plugin.id }))
95
74
  }
96
75
 
97
76
  // --- ADD APP SPECIFIC API ---
@@ -99,20 +78,17 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
99
78
  // TODO: why do we call this function 2 times (here for validation and later for retrieving the actual value)?
100
79
  const { data: customApi, error } = tryCatch(() =>
101
80
  plugin.addCustomApi!({
102
- settings: args.settings?.[plugin.id] ?? {},
81
+ settings: args.settings,
103
82
  })
104
83
  )
105
84
  if (error) {
106
- // @ts-ignore
107
- delete error.stack
108
- result.errors.push(error as any) // TODO: add correct error type
109
- }
110
- if (typeof customApi !== "object") {
85
+ result.errors.push(new PluginReturnedInvalidCustomApiError({ id: plugin.id, cause: error }))
86
+ } else if (typeof customApi !== "object") {
111
87
  result.errors.push(
112
- new PluginReturnedInvalidCustomApiError(
113
- `Plugin ${plugin.id} defines the addCustomApi function, but it does not return an object.`,
114
- { plugin: plugin.id, cause: error }
115
- )
88
+ new PluginReturnedInvalidCustomApiError({
89
+ id: plugin.id,
90
+ cause: new Error(`The return value must be an object. Received "${typeof customApi}".`),
91
+ })
116
92
  )
117
93
  }
118
94
  }
@@ -130,7 +106,6 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
130
106
  result.data.loadMessages = (_args) =>
131
107
  plugin.loadMessages!({
132
108
  ..._args,
133
- settings: args.settings?.[plugin.id] ?? {},
134
109
  nodeishFs: args.nodeishFs,
135
110
  })
136
111
  }
@@ -139,7 +114,6 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
139
114
  result.data.saveMessages = (_args) =>
140
115
  plugin.saveMessages!({
141
116
  ..._args,
142
- settings: args.settings?.[plugin.id] ?? {},
143
117
  nodeishFs: args.nodeishFs,
144
118
  })
145
119
  }
@@ -147,7 +121,7 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
147
121
  if (typeof plugin.addCustomApi === "function") {
148
122
  const { data: customApi } = tryCatch(() =>
149
123
  plugin.addCustomApi!({
150
- settings: args.settings?.[plugin.id] ?? {},
124
+ settings: args.settings,
151
125
  })
152
126
  )
153
127
  if (customApi) {
@@ -161,12 +135,7 @@ export const resolvePlugins: ResolvePluginsFunction = async (args) => {
161
135
  typeof result.data.loadMessages !== "function" ||
162
136
  typeof result.data.saveMessages !== "function"
163
137
  ) {
164
- result.errors.push(
165
- new PluginsDoNotProvideLoadOrSaveMessagesError(
166
- "It seems you did not install any plugin that handles messages. Please add one to make inlang work. See https://inlang.com/documentation/plugins/registry.",
167
- { plugin: undefined }
168
- )
169
- )
138
+ result.errors.push(new PluginsDoNotProvideLoadOrSaveMessagesError())
170
139
  }
171
140
 
172
141
  return result
@@ -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: Record<Plugin["id"], JSONObject>
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
- languageTags: LanguageTag[]
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
  *
@@ -23,7 +23,7 @@ it("should return an error if a plugin cannot be imported", async () => {
23
23
  settings,
24
24
  nodeishFs: {} as any,
25
25
  _import: () => {
26
- throw new ModuleImportError("Could not import", {
26
+ throw new ModuleImportError({
27
27
  module: settings.modules[0]!,
28
28
  cause: new Error("Could not import"),
29
29
  })
@@ -94,7 +94,7 @@ it("should return an error if a module cannot be imported", async () => {
94
94
  }
95
95
 
96
96
  const _import = async () => {
97
- throw new ModuleImportError("Could not import", {
97
+ throw new ModuleImportError({
98
98
  module: settings.modules[0]!,
99
99
  cause: new Error(),
100
100
  })
@@ -35,7 +35,7 @@ export const resolveModules: ResolveModuleFunction = async (args) => {
35
35
  // -- IMPORT MODULE --
36
36
  if (importedModule.error) {
37
37
  moduleErrors.push(
38
- new ModuleImportError(`Couldn't import the plugin "${module}"`, {
38
+ new ModuleImportError({
39
39
  module: module,
40
40
  cause: importedModule.error as Error,
41
41
  })
@@ -46,7 +46,7 @@ export const resolveModules: ResolveModuleFunction = async (args) => {
46
46
  // -- MODULE DOES NOT EXPORT ANYTHING --
47
47
  if (importedModule.data?.default === undefined) {
48
48
  moduleErrors.push(
49
- new ModuleHasNoExportsError(`Module "${module}" has no exports.`, {
49
+ new ModuleHasNoExportsError({
50
50
  module: module,
51
51
  })
52
52
  )
@@ -56,12 +56,11 @@ export const resolveModules: ResolveModuleFunction = async (args) => {
56
56
  const isValidModule = ModuleCompiler.Check(importedModule.data)
57
57
 
58
58
  if (isValidModule === false) {
59
- const errors = [...ModuleCompiler.Errors(importedModule.data)].map(
60
- (e) => `${e.path} ${e.message}`
61
- )
59
+ const errors = [...ModuleCompiler.Errors(importedModule.data)]
62
60
  moduleErrors.push(
63
- new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors.join("\n"), {
61
+ new ModuleExportIsInvalidError({
64
62
  module: module,
63
+ errors,
65
64
  })
66
65
  )
67
66
  continue