@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.
- package/README.md +25 -0
- package/dist/adapter/solidAdapter.d.ts +32 -0
- package/dist/adapter/solidAdapter.d.ts.map +1 -0
- package/dist/adapter/solidAdapter.js +39 -0
- package/dist/adapter/solidAdapter.test.d.ts +2 -0
- package/dist/adapter/solidAdapter.test.d.ts.map +1 -0
- package/dist/adapter/solidAdapter.test.js +284 -0
- package/dist/api.d.ts +88 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +1 -0
- package/dist/createMessageLintReportsQuery.d.ts +9 -0
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -0
- package/dist/createMessageLintReportsQuery.js +48 -0
- package/dist/createMessagesQuery.d.ts +7 -0
- package/dist/createMessagesQuery.d.ts.map +1 -0
- package/dist/createMessagesQuery.js +57 -0
- package/dist/createMessagesQuery.test.d.ts +2 -0
- package/dist/createMessagesQuery.test.d.ts.map +1 -0
- package/dist/createMessagesQuery.test.js +304 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/lint/index.d.ts +3 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +2 -0
- package/dist/lint/message/errors.d.ts +7 -0
- package/dist/lint/message/errors.d.ts.map +1 -0
- package/dist/lint/message/errors.js +9 -0
- package/dist/lint/message/lintMessages.d.ts +17 -0
- package/dist/lint/message/lintMessages.d.ts.map +1 -0
- package/dist/lint/message/lintMessages.js +12 -0
- package/dist/lint/message/lintMessages.test.d.ts +2 -0
- package/dist/lint/message/lintMessages.test.d.ts.map +1 -0
- package/dist/lint/message/lintMessages.test.js +105 -0
- package/dist/lint/message/lintSingleMessage.d.ts +23 -0
- package/dist/lint/message/lintSingleMessage.d.ts.map +1 -0
- package/dist/lint/message/lintSingleMessage.js +36 -0
- package/dist/lint/message/lintSingleMessage.test.d.ts +2 -0
- package/dist/lint/message/lintSingleMessage.test.d.ts.map +1 -0
- package/dist/lint/message/lintSingleMessage.test.js +155 -0
- package/dist/messages/errors.d.ts +13 -0
- package/dist/messages/errors.d.ts.map +1 -0
- package/dist/messages/errors.js +18 -0
- package/dist/messages/index.d.ts +3 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +2 -0
- package/dist/messages/variant.d.ts +46 -0
- package/dist/messages/variant.d.ts.map +1 -0
- package/dist/messages/variant.js +177 -0
- package/dist/messages/variant.test.d.ts +2 -0
- package/dist/messages/variant.test.d.ts.map +1 -0
- package/dist/messages/variant.test.js +379 -0
- package/dist/openInlangProject.d.ts +18 -0
- package/dist/openInlangProject.d.ts.map +1 -0
- package/dist/openInlangProject.js +226 -0
- package/dist/openInlangProject.test.d.ts +2 -0
- package/dist/openInlangProject.test.d.ts.map +1 -0
- package/dist/openInlangProject.test.js +627 -0
- package/dist/parseConfig.d.ts +8 -0
- package/dist/parseConfig.d.ts.map +1 -0
- package/dist/parseConfig.js +26 -0
- package/dist/reactivity/map.d.ts +66 -0
- package/dist/reactivity/map.d.ts.map +1 -0
- package/dist/reactivity/map.js +143 -0
- package/dist/reactivity/solid.d.ts +12 -0
- package/dist/reactivity/solid.d.ts.map +1 -0
- package/dist/reactivity/solid.js +13 -0
- package/dist/reactivity/trigger.d.ts +11 -0
- package/dist/reactivity/trigger.d.ts.map +1 -0
- package/dist/reactivity/trigger.js +46 -0
- package/dist/resolve-modules/errors.d.ts +34 -0
- package/dist/resolve-modules/errors.d.ts.map +1 -0
- package/dist/resolve-modules/errors.js +35 -0
- package/dist/resolve-modules/import.d.ts +35 -0
- package/dist/resolve-modules/import.d.ts.map +1 -0
- package/dist/resolve-modules/import.js +40 -0
- package/dist/resolve-modules/import.test.d.ts +2 -0
- package/dist/resolve-modules/import.test.d.ts.map +1 -0
- package/dist/resolve-modules/import.test.js +45 -0
- package/dist/resolve-modules/index.d.ts +3 -0
- package/dist/resolve-modules/index.d.ts.map +1 -0
- package/dist/resolve-modules/index.js +2 -0
- package/dist/resolve-modules/message-lint-rules/errors.d.ts +8 -0
- package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -0
- package/dist/resolve-modules/message-lint-rules/errors.js +8 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts +9 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -0
- package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +21 -0
- package/dist/resolve-modules/plugins/errors.d.ts +28 -0
- package/dist/resolve-modules/plugins/errors.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/errors.js +44 -0
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts +3 -0
- package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/resolvePlugins.js +108 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts +2 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/resolvePlugins.test.js +289 -0
- package/dist/resolve-modules/plugins/types.d.ts +60 -0
- package/dist/resolve-modules/plugins/types.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/types.js +1 -0
- package/dist/resolve-modules/plugins/types.test.d.ts +2 -0
- package/dist/resolve-modules/plugins/types.test.d.ts.map +1 -0
- package/dist/resolve-modules/plugins/types.test.js +49 -0
- package/dist/resolve-modules/resolveModules.d.ts +3 -0
- package/dist/resolve-modules/resolveModules.d.ts.map +1 -0
- package/dist/resolve-modules/resolveModules.js +70 -0
- package/dist/resolve-modules/resolveModules.test.d.ts +2 -0
- package/dist/resolve-modules/resolveModules.test.d.ts.map +1 -0
- package/dist/resolve-modules/resolveModules.test.js +143 -0
- package/dist/resolve-modules/types.d.ts +62 -0
- package/dist/resolve-modules/types.d.ts.map +1 -0
- package/dist/resolve-modules/types.js +1 -0
- package/dist/test-utilities/createMessage.d.ts +17 -0
- package/dist/test-utilities/createMessage.d.ts.map +1 -0
- package/dist/test-utilities/createMessage.js +16 -0
- package/dist/test-utilities/createMessage.test.d.ts +2 -0
- package/dist/test-utilities/createMessage.test.d.ts.map +1 -0
- package/dist/test-utilities/createMessage.test.js +91 -0
- package/dist/test-utilities/index.d.ts +2 -0
- package/dist/test-utilities/index.d.ts.map +1 -0
- package/dist/test-utilities/index.js +1 -0
- package/dist/versionedInterfaces.d.ts +8 -0
- package/dist/versionedInterfaces.d.ts.map +1 -0
- package/dist/versionedInterfaces.js +8 -0
- package/package.json +58 -0
- package/src/adapter/solidAdapter.test.ts +363 -0
- package/src/adapter/solidAdapter.ts +77 -0
- package/src/api.ts +86 -0
- package/src/createMessageLintReportsQuery.ts +77 -0
- package/src/createMessagesQuery.test.ts +435 -0
- package/src/createMessagesQuery.ts +64 -0
- package/src/errors.ts +46 -0
- package/src/index.ts +29 -0
- package/src/lint/index.ts +2 -0
- package/src/lint/message/errors.ts +9 -0
- package/src/lint/message/lintMessages.test.ts +122 -0
- package/src/lint/message/lintMessages.ts +33 -0
- package/src/lint/message/lintSingleMessage.test.ts +183 -0
- package/src/lint/message/lintSingleMessage.ts +62 -0
- package/src/messages/errors.ts +25 -0
- package/src/messages/index.ts +2 -0
- package/src/messages/variant.test.ts +444 -0
- package/src/messages/variant.ts +242 -0
- package/src/openInlangProject.test.ts +734 -0
- package/src/openInlangProject.ts +337 -0
- package/src/parseConfig.ts +33 -0
- package/src/reactivity/map.ts +135 -0
- package/src/reactivity/solid.ts +36 -0
- package/src/reactivity/trigger.ts +46 -0
- package/src/resolve-modules/errors.ts +39 -0
- package/src/resolve-modules/import.test.ts +58 -0
- package/src/resolve-modules/import.ts +69 -0
- package/src/resolve-modules/index.ts +2 -0
- package/src/resolve-modules/message-lint-rules/errors.ts +9 -0
- package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +24 -0
- package/src/resolve-modules/plugins/errors.ts +57 -0
- package/src/resolve-modules/plugins/resolvePlugins.test.ts +340 -0
- package/src/resolve-modules/plugins/resolvePlugins.ts +170 -0
- package/src/resolve-modules/plugins/types.test.ts +57 -0
- package/src/resolve-modules/plugins/types.ts +77 -0
- package/src/resolve-modules/resolveModules.test.ts +176 -0
- package/src/resolve-modules/resolveModules.ts +97 -0
- package/src/resolve-modules/types.ts +71 -0
- package/src/test-utilities/createMessage.test.ts +100 -0
- package/src/test-utilities/createMessage.ts +20 -0
- package/src/test-utilities/index.ts +1 -0
- 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"
|