@inlang/sdk 0.1.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 (170) hide show
  1. package/README.md +25 -0
  2. package/dist/adapter/solidAdapter.d.ts +32 -0
  3. package/dist/adapter/solidAdapter.d.ts.map +1 -0
  4. package/dist/adapter/solidAdapter.js +39 -0
  5. package/dist/adapter/solidAdapter.test.d.ts +2 -0
  6. package/dist/adapter/solidAdapter.test.d.ts.map +1 -0
  7. package/dist/adapter/solidAdapter.test.js +284 -0
  8. package/dist/api.d.ts +88 -0
  9. package/dist/api.d.ts.map +1 -0
  10. package/dist/api.js +1 -0
  11. package/dist/createMessageLintReportsQuery.d.ts +9 -0
  12. package/dist/createMessageLintReportsQuery.d.ts.map +1 -0
  13. package/dist/createMessageLintReportsQuery.js +48 -0
  14. package/dist/createMessagesQuery.d.ts +7 -0
  15. package/dist/createMessagesQuery.d.ts.map +1 -0
  16. package/dist/createMessagesQuery.js +57 -0
  17. package/dist/createMessagesQuery.test.d.ts +2 -0
  18. package/dist/createMessagesQuery.test.d.ts.map +1 -0
  19. package/dist/createMessagesQuery.test.js +304 -0
  20. package/dist/errors.d.ts +22 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +39 -0
  23. package/dist/index.d.ts +15 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +13 -0
  26. package/dist/lint/index.d.ts +3 -0
  27. package/dist/lint/index.d.ts.map +1 -0
  28. package/dist/lint/index.js +2 -0
  29. package/dist/lint/message/errors.d.ts +7 -0
  30. package/dist/lint/message/errors.d.ts.map +1 -0
  31. package/dist/lint/message/errors.js +9 -0
  32. package/dist/lint/message/lintMessages.d.ts +17 -0
  33. package/dist/lint/message/lintMessages.d.ts.map +1 -0
  34. package/dist/lint/message/lintMessages.js +12 -0
  35. package/dist/lint/message/lintMessages.test.d.ts +2 -0
  36. package/dist/lint/message/lintMessages.test.d.ts.map +1 -0
  37. package/dist/lint/message/lintMessages.test.js +105 -0
  38. package/dist/lint/message/lintSingleMessage.d.ts +23 -0
  39. package/dist/lint/message/lintSingleMessage.d.ts.map +1 -0
  40. package/dist/lint/message/lintSingleMessage.js +36 -0
  41. package/dist/lint/message/lintSingleMessage.test.d.ts +2 -0
  42. package/dist/lint/message/lintSingleMessage.test.d.ts.map +1 -0
  43. package/dist/lint/message/lintSingleMessage.test.js +155 -0
  44. package/dist/messages/errors.d.ts +13 -0
  45. package/dist/messages/errors.d.ts.map +1 -0
  46. package/dist/messages/errors.js +18 -0
  47. package/dist/messages/index.d.ts +3 -0
  48. package/dist/messages/index.d.ts.map +1 -0
  49. package/dist/messages/index.js +2 -0
  50. package/dist/messages/variant.d.ts +46 -0
  51. package/dist/messages/variant.d.ts.map +1 -0
  52. package/dist/messages/variant.js +177 -0
  53. package/dist/messages/variant.test.d.ts +2 -0
  54. package/dist/messages/variant.test.d.ts.map +1 -0
  55. package/dist/messages/variant.test.js +379 -0
  56. package/dist/openInlangProject.d.ts +18 -0
  57. package/dist/openInlangProject.d.ts.map +1 -0
  58. package/dist/openInlangProject.js +226 -0
  59. package/dist/openInlangProject.test.d.ts +2 -0
  60. package/dist/openInlangProject.test.d.ts.map +1 -0
  61. package/dist/openInlangProject.test.js +627 -0
  62. package/dist/parseConfig.d.ts +8 -0
  63. package/dist/parseConfig.d.ts.map +1 -0
  64. package/dist/parseConfig.js +26 -0
  65. package/dist/reactivity/map.d.ts +66 -0
  66. package/dist/reactivity/map.d.ts.map +1 -0
  67. package/dist/reactivity/map.js +143 -0
  68. package/dist/reactivity/solid.d.ts +12 -0
  69. package/dist/reactivity/solid.d.ts.map +1 -0
  70. package/dist/reactivity/solid.js +13 -0
  71. package/dist/reactivity/trigger.d.ts +11 -0
  72. package/dist/reactivity/trigger.d.ts.map +1 -0
  73. package/dist/reactivity/trigger.js +46 -0
  74. package/dist/resolve-modules/errors.d.ts +34 -0
  75. package/dist/resolve-modules/errors.d.ts.map +1 -0
  76. package/dist/resolve-modules/errors.js +35 -0
  77. package/dist/resolve-modules/import.d.ts +35 -0
  78. package/dist/resolve-modules/import.d.ts.map +1 -0
  79. package/dist/resolve-modules/import.js +40 -0
  80. package/dist/resolve-modules/import.test.d.ts +2 -0
  81. package/dist/resolve-modules/import.test.d.ts.map +1 -0
  82. package/dist/resolve-modules/import.test.js +45 -0
  83. package/dist/resolve-modules/index.d.ts +3 -0
  84. package/dist/resolve-modules/index.d.ts.map +1 -0
  85. package/dist/resolve-modules/index.js +2 -0
  86. package/dist/resolve-modules/message-lint-rules/errors.d.ts +8 -0
  87. package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -0
  88. package/dist/resolve-modules/message-lint-rules/errors.js +8 -0
  89. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts +9 -0
  90. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -0
  91. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +21 -0
  92. package/dist/resolve-modules/plugins/errors.d.ts +28 -0
  93. package/dist/resolve-modules/plugins/errors.d.ts.map +1 -0
  94. package/dist/resolve-modules/plugins/errors.js +44 -0
  95. package/dist/resolve-modules/plugins/resolvePlugins.d.ts +3 -0
  96. package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -0
  97. package/dist/resolve-modules/plugins/resolvePlugins.js +108 -0
  98. package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts +2 -0
  99. package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts.map +1 -0
  100. package/dist/resolve-modules/plugins/resolvePlugins.test.js +289 -0
  101. package/dist/resolve-modules/plugins/types.d.ts +60 -0
  102. package/dist/resolve-modules/plugins/types.d.ts.map +1 -0
  103. package/dist/resolve-modules/plugins/types.js +1 -0
  104. package/dist/resolve-modules/plugins/types.test.d.ts +2 -0
  105. package/dist/resolve-modules/plugins/types.test.d.ts.map +1 -0
  106. package/dist/resolve-modules/plugins/types.test.js +49 -0
  107. package/dist/resolve-modules/resolveModules.d.ts +3 -0
  108. package/dist/resolve-modules/resolveModules.d.ts.map +1 -0
  109. package/dist/resolve-modules/resolveModules.js +70 -0
  110. package/dist/resolve-modules/resolveModules.test.d.ts +2 -0
  111. package/dist/resolve-modules/resolveModules.test.d.ts.map +1 -0
  112. package/dist/resolve-modules/resolveModules.test.js +143 -0
  113. package/dist/resolve-modules/types.d.ts +62 -0
  114. package/dist/resolve-modules/types.d.ts.map +1 -0
  115. package/dist/resolve-modules/types.js +1 -0
  116. package/dist/test-utilities/createMessage.d.ts +17 -0
  117. package/dist/test-utilities/createMessage.d.ts.map +1 -0
  118. package/dist/test-utilities/createMessage.js +16 -0
  119. package/dist/test-utilities/createMessage.test.d.ts +2 -0
  120. package/dist/test-utilities/createMessage.test.d.ts.map +1 -0
  121. package/dist/test-utilities/createMessage.test.js +91 -0
  122. package/dist/test-utilities/index.d.ts +2 -0
  123. package/dist/test-utilities/index.d.ts.map +1 -0
  124. package/dist/test-utilities/index.js +1 -0
  125. package/dist/versionedInterfaces.d.ts +8 -0
  126. package/dist/versionedInterfaces.d.ts.map +1 -0
  127. package/dist/versionedInterfaces.js +8 -0
  128. package/package.json +58 -0
  129. package/src/adapter/solidAdapter.test.ts +363 -0
  130. package/src/adapter/solidAdapter.ts +77 -0
  131. package/src/api.ts +86 -0
  132. package/src/createMessageLintReportsQuery.ts +77 -0
  133. package/src/createMessagesQuery.test.ts +435 -0
  134. package/src/createMessagesQuery.ts +64 -0
  135. package/src/errors.ts +46 -0
  136. package/src/index.ts +29 -0
  137. package/src/lint/index.ts +2 -0
  138. package/src/lint/message/errors.ts +9 -0
  139. package/src/lint/message/lintMessages.test.ts +122 -0
  140. package/src/lint/message/lintMessages.ts +33 -0
  141. package/src/lint/message/lintSingleMessage.test.ts +183 -0
  142. package/src/lint/message/lintSingleMessage.ts +62 -0
  143. package/src/messages/errors.ts +25 -0
  144. package/src/messages/index.ts +2 -0
  145. package/src/messages/variant.test.ts +444 -0
  146. package/src/messages/variant.ts +242 -0
  147. package/src/openInlangProject.test.ts +734 -0
  148. package/src/openInlangProject.ts +337 -0
  149. package/src/parseConfig.ts +33 -0
  150. package/src/reactivity/map.ts +135 -0
  151. package/src/reactivity/solid.ts +36 -0
  152. package/src/reactivity/trigger.ts +46 -0
  153. package/src/resolve-modules/errors.ts +39 -0
  154. package/src/resolve-modules/import.test.ts +58 -0
  155. package/src/resolve-modules/import.ts +69 -0
  156. package/src/resolve-modules/index.ts +2 -0
  157. package/src/resolve-modules/message-lint-rules/errors.ts +9 -0
  158. package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +24 -0
  159. package/src/resolve-modules/plugins/errors.ts +57 -0
  160. package/src/resolve-modules/plugins/resolvePlugins.test.ts +340 -0
  161. package/src/resolve-modules/plugins/resolvePlugins.ts +170 -0
  162. package/src/resolve-modules/plugins/types.test.ts +57 -0
  163. package/src/resolve-modules/plugins/types.ts +77 -0
  164. package/src/resolve-modules/resolveModules.test.ts +176 -0
  165. package/src/resolve-modules/resolveModules.ts +97 -0
  166. package/src/resolve-modules/types.ts +71 -0
  167. package/src/test-utilities/createMessage.test.ts +100 -0
  168. package/src/test-utilities/createMessage.ts +20 -0
  169. package/src/test-utilities/index.ts +1 -0
  170. package/src/versionedInterfaces.ts +9 -0
@@ -0,0 +1,57 @@
1
+ import { ProjectConfig } from "@inlang/project-config"
2
+ import { Value } from "@sinclair/typebox/value"
3
+ import { describe, test, expect } from "vitest"
4
+ import { expectType } from "tsd"
5
+ import { Plugin } from "@inlang/plugin"
6
+
7
+ describe("Plugin", () => {
8
+ test("meta.id should enforce plugin.namespace.* patterns", () => {
9
+ expectType<`plugin.${string}.${string}`>("" as Plugin["meta"]["id"])
10
+
11
+ const mockPlugin: Plugin = {
12
+ meta: {
13
+ id: "plugin.namespace.placeholder",
14
+ displayName: { en: "" },
15
+ description: { en: "" },
16
+ },
17
+ }
18
+
19
+ const passCases = ["plugin.namespace.helloWorld", "plugin.namespace.i18n"]
20
+ const failCases = [
21
+ "namespace.hello_World",
22
+ "plugin.namespace-HelloWorld",
23
+ "lintRule.namespace.coolPlugin",
24
+ ]
25
+
26
+ for (const pass of passCases) {
27
+ mockPlugin.meta.id = pass as any
28
+
29
+ // @ts-ignore - type mismatch error. fix after refactor
30
+ expect(Value.Check(Plugin, mockPlugin)).toBe(true)
31
+ }
32
+
33
+ for (const fail of failCases) {
34
+ mockPlugin.meta.id = fail as any
35
+ // @ts-ignore - type mismatch error. fix after refactor
36
+ expect(Value.Check(Plugin, mockPlugin)).toBe(false)
37
+ }
38
+ })
39
+
40
+ test("meta.id should be a valid inlang.config.setting key", () => {
41
+ const mockConfig: ProjectConfig = {
42
+ sourceLanguageTag: "en",
43
+ languageTags: ["en", "de"],
44
+ modules: [],
45
+ settings: {},
46
+ }
47
+ const cases = ["plugin.namespace.helloWorld", "plugin.namespace.i18n"]
48
+
49
+ for (const _case of cases) {
50
+ const config = { ...mockConfig, settings: { [_case]: {} } }
51
+ // @ts-ignore - type mismatch error. fix after refactor
52
+ expect(Value.Check(ProjectConfig, config)).toBe(true)
53
+ // @ts-ignore - type mismatch error. fix after refactor
54
+ expect(Value.Check(Plugin["properties"]["meta"]["properties"]["id"], _case)).toBe(true)
55
+ }
56
+ })
57
+ })
@@ -0,0 +1,77 @@
1
+ import type { LanguageTag } from "@inlang/language-tag"
2
+ import type { NodeishFilesystem as LisaNodeishFilesystem } from "@lix-js/fs"
3
+ import type {
4
+ PluginReturnedInvalidCustomApiError,
5
+ PluginLoadMessagesFunctionAlreadyDefinedError,
6
+ PluginSaveMessagesFunctionAlreadyDefinedError,
7
+ PluginHasInvalidIdError,
8
+ PluginHasInvalidSchemaError,
9
+ PluginUsesReservedNamespaceError,
10
+ } from "./errors.js"
11
+ import type { Message } from "@inlang/message"
12
+ import type { JSONObject } from "@inlang/json-types"
13
+ import type { CustomApiInlangIdeExtension, Plugin } from "@inlang/plugin"
14
+
15
+ /**
16
+ * The filesystem is a subset of project lisa's nodeish filesystem.
17
+ *
18
+ * - only uses minimally required functions to decrease the API footprint on the ecosystem.
19
+ */
20
+ export type NodeishFilesystemSubset = Pick<
21
+ LisaNodeishFilesystem,
22
+ "readFile" | "readdir" | "mkdir" | "writeFile"
23
+ >
24
+
25
+ /**
26
+ * Function that resolves (imports and initializes) the plugins.
27
+ */
28
+ export type ResolvePluginsFunction = (args: {
29
+ plugins: Array<Plugin>
30
+ settings: Record<Plugin["meta"]["id"], JSONObject>
31
+ nodeishFs: NodeishFilesystemSubset
32
+ }) => Promise<{
33
+ data: ResolvedPluginApi
34
+ errors: Array<
35
+ | PluginReturnedInvalidCustomApiError
36
+ | PluginLoadMessagesFunctionAlreadyDefinedError
37
+ | PluginSaveMessagesFunctionAlreadyDefinedError
38
+ | PluginHasInvalidIdError
39
+ | PluginHasInvalidSchemaError
40
+ | PluginUsesReservedNamespaceError
41
+ >
42
+ }>
43
+
44
+ /**
45
+ * The API after resolving the plugins.
46
+ */
47
+ export type ResolvedPluginApi = {
48
+ loadMessages: (args: {
49
+ languageTags: LanguageTag[]
50
+ sourceLanguageTag: LanguageTag
51
+ }) => Promise<Message[]> | Message[]
52
+ saveMessages: (args: { messages: Message[] }) => Promise<void> | void
53
+ /**
54
+ * Detect language tags in the project provided plugins.
55
+ */
56
+ detectedLanguageTags: LanguageTag[]
57
+ /**
58
+ * App specific APIs.
59
+ *
60
+ * @example
61
+ * // define
62
+ * customApi: ({ settings }) => ({
63
+ * "app.inlang.ide-extension": {
64
+ * messageReferenceMatcher: () => {
65
+ * // use settings
66
+ * settings.pathPattern
67
+ * return
68
+ * }
69
+ * }
70
+ * })
71
+ * // use
72
+ * customApi['app.inlang.ide-extension'].messageReferenceMatcher()
73
+ */
74
+ customApi: Record<`app.${string}.${string}` | `library.${string}.${string}`, unknown> & {
75
+ "app.inlang.ideExtension"?: CustomApiInlangIdeExtension
76
+ }
77
+ }
@@ -0,0 +1,176 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import type { MessageLintRule } from "@inlang/message-lint-rule"
3
+ import type { Plugin } from "@inlang/plugin"
4
+ import { expect, it } from "vitest"
5
+ import {
6
+ ModuleError,
7
+ ModuleExportIsInvalidError,
8
+ ModuleHasNoExportsError,
9
+ ModuleImportError,
10
+ } from "./errors.js"
11
+ import { resolveModules } from "./resolveModules.js"
12
+ import type { ProjectConfig } from "@inlang/project-config"
13
+ import type { InlangModule } from "@inlang/module"
14
+
15
+ it("should return an error if a plugin cannot be imported", async () => {
16
+ const config: ProjectConfig = {
17
+ sourceLanguageTag: "en",
18
+ languageTags: ["de", "en"],
19
+ modules: ["https://myplugin.com/index.js"],
20
+ settings: {},
21
+ }
22
+
23
+ const resolved = await resolveModules({
24
+ config,
25
+ nodeishFs: {} as any,
26
+ _import: () => {
27
+ throw new ModuleImportError("Could not import", {
28
+ module: config.modules[0]!,
29
+ cause: new Error("Could not import"),
30
+ })
31
+ },
32
+ })
33
+
34
+ expect(resolved.errors[0]).toBeInstanceOf(ModuleImportError)
35
+ })
36
+
37
+ it("should resolve plugins and message lint rules successfully", async () => {
38
+ // Define mock data
39
+ const mockPlugin: Plugin = {
40
+ meta: {
41
+ id: "plugin.namespace.mock",
42
+ description: { en: "Mock plugin description" },
43
+ displayName: { en: "Mock Plugin" },
44
+ },
45
+ loadMessages: () => undefined as any,
46
+ saveMessages: () => undefined as any,
47
+ addCustomApi: () => ({
48
+ "app.inlang.ideExtension": {
49
+ messageReferenceMatcher: () => undefined as any,
50
+ },
51
+ }),
52
+ }
53
+
54
+ const mockMessageLintRule: MessageLintRule = {
55
+ meta: {
56
+ id: "messageLintRule.namespace.mock",
57
+ description: { en: "Mock lint rule description" },
58
+ displayName: { en: "Mock Lint Rule" },
59
+ },
60
+ message: () => undefined,
61
+ }
62
+
63
+ const config: ProjectConfig = {
64
+ sourceLanguageTag: "en",
65
+ languageTags: ["de", "en"],
66
+ modules: ["lint-rule.js", "plugin.js"],
67
+ settings: {},
68
+ }
69
+
70
+ const _import = async (name: string) => {
71
+ if (name === "lint-rule.js") {
72
+ return {
73
+ default: mockMessageLintRule,
74
+ }
75
+ } else {
76
+ return {
77
+ default: mockPlugin,
78
+ }
79
+ }
80
+ }
81
+
82
+ // Call the function
83
+ const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
84
+
85
+ // Assert results
86
+ expect(resolved.errors).toHaveLength(0)
87
+ // Check for the meta data of the plugin
88
+ expect(resolved.plugins.some((module) => module.meta.id === mockPlugin.meta.id)).toBeDefined()
89
+ // Check for the app specific api
90
+ expect(resolved.resolvedPluginApi["customApi"]?.["app.inlang.ideExtension"]).toBeDefined()
91
+ // Check for the lint rule
92
+ expect(resolved.messageLintRules[0]?.meta.id).toBe(mockMessageLintRule.meta.id)
93
+ })
94
+
95
+ it("should return an error if a module cannot be imported", async () => {
96
+ const config: ProjectConfig = {
97
+ sourceLanguageTag: "en",
98
+ languageTags: ["de", "en"],
99
+ modules: ["https://myplugin.com/index.js"],
100
+ settings: {},
101
+ }
102
+
103
+ const _import = async () => {
104
+ throw new ModuleImportError("Could not import", {
105
+ module: config.modules[0]!,
106
+ cause: new Error(),
107
+ })
108
+ }
109
+
110
+ // Call the function
111
+ const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
112
+
113
+ // Assert results
114
+ expect(resolved.errors[0]).toBeInstanceOf(ModuleImportError)
115
+ })
116
+
117
+ it("should return an error if a module does not export any plugins or lint rules", async () => {
118
+ const config: ProjectConfig = {
119
+ sourceLanguageTag: "en",
120
+ languageTags: ["de", "en"],
121
+ modules: ["https://myplugin.com/index.js"],
122
+ settings: {},
123
+ }
124
+
125
+ const _import = async () => ({
126
+ default: {},
127
+ })
128
+
129
+ // Call the function
130
+ const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
131
+
132
+ // Assert results
133
+ expect(resolved.errors[0]).toBeInstanceOf(ModuleHasNoExportsError)
134
+ })
135
+
136
+ it("should return an error if a module exports an invalid plugin or lint rule", async () => {
137
+ const config: ProjectConfig = {
138
+ sourceLanguageTag: "en",
139
+ languageTags: ["de", "en"],
140
+ modules: ["https://myplugin.com/index.js"],
141
+ settings: {},
142
+ }
143
+ const _import = async () =>
144
+ ({
145
+ default: {
146
+ // @ts-expect-error - invalid meta of a plugin
147
+ meta: {
148
+ id: "plugin.namespace.mock",
149
+ description: { en: "Mock plugin description" },
150
+ },
151
+ },
152
+ } satisfies InlangModule)
153
+
154
+ const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
155
+ expect(resolved.errors[0]).toBeInstanceOf(ModuleExportIsInvalidError)
156
+ })
157
+
158
+ it("should handle other unhandled errors during plugin resolution", async () => {
159
+ const errorMessage = "Unhandled error during plugin resolution"
160
+ const config: ProjectConfig = {
161
+ sourceLanguageTag: "en",
162
+ languageTags: ["de", "en"],
163
+ modules: ["https://myplugin.com/index.js"],
164
+ settings: {},
165
+ }
166
+
167
+ const _import = async () => {
168
+ throw new Error(errorMessage)
169
+ }
170
+
171
+ // Call the function
172
+ const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
173
+
174
+ // Assert results
175
+ expect(resolved.errors[0]).toBeInstanceOf(ModuleError)
176
+ })
@@ -0,0 +1,97 @@
1
+ import type { ResolveModuleFunction } from "./types.js"
2
+ import { InlangModule } from "@inlang/module"
3
+ import {
4
+ ModuleError,
5
+ ModuleImportError,
6
+ ModuleHasNoExportsError,
7
+ ModuleExportIsInvalidError,
8
+ } from "./errors.js"
9
+ import { tryCatch } from "@inlang/result"
10
+ import { resolveMessageLintRules } from "./message-lint-rules/resolveMessageLintRules.js"
11
+ import type { Plugin } from "@inlang/plugin"
12
+ import { createImport } from "./import.js"
13
+ import type { MessageLintRule } from "@inlang/message-lint-rule"
14
+ import { resolvePlugins } from "./plugins/resolvePlugins.js"
15
+ import { TypeCompiler } from "@sinclair/typebox/compiler"
16
+
17
+ const ModuleCompiler = TypeCompiler.Compile(InlangModule)
18
+
19
+ export const resolveModules: ResolveModuleFunction = async (args) => {
20
+ const _import = args._import ?? createImport({ readFile: args.nodeishFs.readFile, fetch })
21
+ const moduleErrors: Array<ModuleError> = []
22
+
23
+ const allPlugins: Array<Plugin> = []
24
+ const allMessageLintRules: Array<MessageLintRule> = []
25
+
26
+ const meta: Awaited<ReturnType<ResolveModuleFunction>>["meta"] = []
27
+
28
+ for (const module of args.config.modules) {
29
+ /**
30
+ * -------------- BEGIN SETUP --------------
31
+ */
32
+
33
+ const importedModule = await tryCatch<InlangModule>(() => _import(module))
34
+
35
+ // -- IMPORT MODULE --
36
+ if (importedModule.error) {
37
+ moduleErrors.push(
38
+ new ModuleImportError(`Couldn't import the plugin "${module}"`, {
39
+ module: module,
40
+ cause: importedModule.error as Error,
41
+ }),
42
+ )
43
+ continue
44
+ }
45
+
46
+ // -- MODULE DOES NOT EXPORT PLUGINS OR LINT RULES --
47
+ if (importedModule.data?.default?.meta?.id === undefined) {
48
+ moduleErrors.push(
49
+ new ModuleHasNoExportsError(`Module "${module}" has no exports.`, {
50
+ module: module,
51
+ }),
52
+ )
53
+ continue
54
+ }
55
+
56
+ const isValidModule = ModuleCompiler.Check(importedModule.data)
57
+
58
+ if (isValidModule === false) {
59
+ const errors = [...ModuleCompiler.Errors(importedModule.data)]
60
+ moduleErrors.push(
61
+ new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors, {
62
+ module: module,
63
+ }),
64
+ )
65
+ continue
66
+ }
67
+
68
+ meta.push({
69
+ module: module,
70
+ id: importedModule.data.default.meta.id,
71
+ })
72
+
73
+ if (importedModule.data.default.meta.id.startsWith("plugin.")) {
74
+ allPlugins.push(importedModule.data.default as Plugin)
75
+ } else if (importedModule.data.default.meta.id.startsWith("messageLintRule.")) {
76
+ allMessageLintRules.push(importedModule.data.default as MessageLintRule)
77
+ } else {
78
+ throw new Error(`Unimplemented module type. Must start with "plugin." or "messageLintRule.`)
79
+ }
80
+ }
81
+
82
+ const resolvedPlugins = await resolvePlugins({
83
+ plugins: allPlugins,
84
+ settings: args.config.settings as any, // TODO: fix type
85
+ nodeishFs: args.nodeishFs,
86
+ })
87
+
88
+ const resolvedLintRules = resolveMessageLintRules({ messageLintRules: allMessageLintRules })
89
+
90
+ return {
91
+ meta,
92
+ messageLintRules: allMessageLintRules,
93
+ plugins: allPlugins,
94
+ resolvedPluginApi: resolvedPlugins.data,
95
+ errors: [...moduleErrors, ...resolvedLintRules.errors, ...resolvedPlugins.errors],
96
+ }
97
+ }
@@ -0,0 +1,71 @@
1
+ import type { ProjectConfig } from "@inlang/project-config"
2
+ import type { MessageLintRule } from "@inlang/message-lint-rule"
3
+ import type { Plugin } from "@inlang/plugin"
4
+ import type {
5
+ NodeishFilesystemSubset,
6
+ ResolvePluginsFunction,
7
+ ResolvedPluginApi,
8
+ } from "./plugins/types.js"
9
+ import type { ModuleHasNoExportsError, ModuleImportError } from "./errors.js"
10
+ import type { ImportFunction } from "./import.js"
11
+ import type { resolveMessageLintRules } from "./message-lint-rules/resolveMessageLintRules.js"
12
+
13
+ /**
14
+ * Function that resolves modules from the config.
15
+ *
16
+ * Pass a custom `_import` function to override the default import function.
17
+ */
18
+ export type ResolveModuleFunction = (args: {
19
+ config: ProjectConfig
20
+ nodeishFs: NodeishFilesystemSubset
21
+ _import?: ImportFunction
22
+ }) => Promise<{
23
+ /**
24
+ * Metadata about the resolved module.
25
+ *
26
+ * @example
27
+ * [{
28
+ * id: "plugin.inlang.json",
29
+ * module: "https://myplugin.com/index.js"
30
+ * }]
31
+ */
32
+ meta: Array<{
33
+ /**
34
+ * The module link.
35
+ *
36
+ * @example "https://myplugin.com/index.js"
37
+ */
38
+ module: string
39
+ /**
40
+ * The resolved item id of the module.
41
+ */
42
+ id: Plugin["meta"]["id"] | MessageLintRule["meta"]["id"]
43
+ }>
44
+ /**
45
+ * The resolved plugins.
46
+ */
47
+ plugins: Array<Plugin>
48
+ /**
49
+ * The resolved message lint rules.
50
+ */
51
+ messageLintRules: Array<MessageLintRule>
52
+ /**
53
+ * The resolved api provided by plugins.
54
+ */
55
+ resolvedPluginApi: ResolvedPluginApi
56
+ /**
57
+ * Errors during the resolution process.
58
+ *
59
+ * This includes errors from:
60
+ * - importing module
61
+ * - resolving plugins
62
+ * - resolving lint rules
63
+ * - resolving the runtime plugin api
64
+ */
65
+ errors: Array<
66
+ | ModuleHasNoExportsError
67
+ | ModuleImportError
68
+ | Awaited<ReturnType<ResolvePluginsFunction>>["errors"][number]
69
+ | Awaited<ReturnType<typeof resolveMessageLintRules>>["errors"][number]
70
+ >
71
+ }>
@@ -0,0 +1,100 @@
1
+ import { expect, test } from "vitest"
2
+ import { createMessage } from "./createMessage.js"
3
+
4
+ test("should create a simple message", () => {
5
+ expect(
6
+ createMessage("welcome", {
7
+ de: "Hallo inlang",
8
+ }),
9
+ ).toMatchInlineSnapshot(`
10
+ {
11
+ "id": "welcome",
12
+ "selectors": [],
13
+ "variants": [
14
+ {
15
+ "languageTag": "de",
16
+ "match": {},
17
+ "pattern": [
18
+ {
19
+ "type": "Text",
20
+ "value": "Hallo inlang",
21
+ },
22
+ ],
23
+ },
24
+ ],
25
+ }
26
+ `)
27
+ })
28
+
29
+ test("should create a message with pattern", () => {
30
+ expect(
31
+ createMessage("greeting", {
32
+ en: [
33
+ { type: "Text", value: "Hi " },
34
+ { type: "VariableReference", name: "name" },
35
+ { type: "Text", value: '"' },
36
+ ],
37
+ }),
38
+ ).toMatchInlineSnapshot(`
39
+ {
40
+ "id": "greeting",
41
+ "selectors": [],
42
+ "variants": [
43
+ {
44
+ "languageTag": "en",
45
+ "match": {},
46
+ "pattern": [
47
+ {
48
+ "type": "Text",
49
+ "value": "Hi ",
50
+ },
51
+ {
52
+ "name": "name",
53
+ "type": "VariableReference",
54
+ },
55
+ {
56
+ "type": "Text",
57
+ "value": "\\"",
58
+ },
59
+ ],
60
+ },
61
+ ],
62
+ }
63
+ `)
64
+ })
65
+
66
+ test("should create a message with a pattern", () => {
67
+ expect(
68
+ createMessage("welcome", {
69
+ en: "hello inlang",
70
+ de: [{ type: "Text", value: "Hallo inlang" }],
71
+ }),
72
+ ).toMatchInlineSnapshot(`
73
+ {
74
+ "id": "welcome",
75
+ "selectors": [],
76
+ "variants": [
77
+ {
78
+ "languageTag": "en",
79
+ "match": {},
80
+ "pattern": [
81
+ {
82
+ "type": "Text",
83
+ "value": "hello inlang",
84
+ },
85
+ ],
86
+ },
87
+ {
88
+ "languageTag": "de",
89
+ "match": {},
90
+ "pattern": [
91
+ {
92
+ "type": "Text",
93
+ "value": "Hallo inlang",
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ }
99
+ `)
100
+ })
@@ -0,0 +1,20 @@
1
+ import type { Message, Pattern } from "@inlang/message"
2
+
3
+ export const createMessage = (id: string, patterns: Record<string, Pattern | string>) =>
4
+ ({
5
+ id,
6
+ selectors: [],
7
+ variants: Object.entries(patterns).map(([languageTag, patterns]) => ({
8
+ languageTag,
9
+ match: {},
10
+ pattern:
11
+ typeof patterns === "string"
12
+ ? [
13
+ {
14
+ type: "Text",
15
+ value: patterns,
16
+ },
17
+ ]
18
+ : patterns,
19
+ })),
20
+ } satisfies Message)
@@ -0,0 +1 @@
1
+ export { createMessage } from "./createMessage.js"
@@ -0,0 +1,9 @@
1
+ // Re-exporting for easier importing
2
+
3
+ export * from "@inlang/json-types"
4
+ export * from "@inlang/language-tag"
5
+ export * from "@inlang/message-lint-rule"
6
+ export * from "@inlang/message"
7
+ export * from "@inlang/plugin"
8
+ export * from "@inlang/project-config"
9
+ export * from "@inlang/translatable"