@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,337 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import type {
3
+ InlangProject,
4
+ InstalledMessageLintRule,
5
+ InstalledPlugin,
6
+ Subscribable,
7
+ } from "./api.js"
8
+ import { type ImportFunction, resolveModules } from "./resolve-modules/index.js"
9
+ import { TypeCompiler } from "@sinclair/typebox/compiler"
10
+ import { Value } from "@sinclair/typebox/value"
11
+ import {
12
+ ProjectFilePathNotFoundError,
13
+ ProjectFileJSONSyntaxError,
14
+ InvalidConfigError,
15
+ NoPluginProvidesLoadOrSaveMessagesError,
16
+ PluginLoadMessagesError,
17
+ PluginSaveMessagesError,
18
+ } from "./errors.js"
19
+ import { createRoot, createSignal, createEffect } from "./reactivity/solid.js"
20
+ import { createMessagesQuery } from "./createMessagesQuery.js"
21
+ import { debounce } from "throttle-debounce"
22
+ import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.js"
23
+ import { ProjectConfig, Message, type NodeishFilesystemSubset } from "./versionedInterfaces.js"
24
+ import { tryCatch, type Result } from "@inlang/result"
25
+
26
+ const ConfigCompiler = TypeCompiler.Compile(ProjectConfig)
27
+
28
+ /**
29
+ * Creates an inlang instance.
30
+ *
31
+ * - Use `_import` to pass a custom import function for testing,
32
+ * and supporting legacy resolvedModules such as CJS.
33
+ *
34
+ */
35
+ export const openInlangProject = async (args: {
36
+ projectFilePath: string
37
+ nodeishFs: NodeishFilesystemSubset
38
+ _import?: ImportFunction
39
+ _capture?: (id: string, props: Record<string, unknown>) => void
40
+ }): Promise<InlangProject> => {
41
+ return await createRoot(async () => {
42
+ const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable()
43
+
44
+ // -- config ------------------------------------------------------------
45
+
46
+ const [config, _setConfig] = createSignal<ProjectConfig>()
47
+ createEffect(() => {
48
+ loadConfig({ projectFilePath: args.projectFilePath, nodeishFs: args.nodeishFs })
49
+ .then((config) => {
50
+ setConfig(config)
51
+ args._capture?.("SDK used config", config)
52
+ })
53
+ .catch((err) => {
54
+ markInitAsFailed(err)
55
+ })
56
+ })
57
+ // TODO: create FS watcher and update config on change
58
+
59
+ const writeConfigToDisk = skipFirst((config: ProjectConfig) =>
60
+ _writeConfigToDisk({ nodeishFs: args.nodeishFs, config }),
61
+ )
62
+
63
+ const setConfig = (config: ProjectConfig): Result<void, InvalidConfigError> => {
64
+ try {
65
+ const validatedConfig = validateConfig(config)
66
+ _setConfig(validatedConfig)
67
+
68
+ writeConfigToDisk(validatedConfig)
69
+ return { data: undefined }
70
+ } catch (error: unknown) {
71
+ if (error instanceof InvalidConfigError) {
72
+ return { error }
73
+ }
74
+
75
+ throw new Error("unhandled")
76
+ }
77
+ }
78
+
79
+ // -- resolvedModules -----------------------------------------------------------
80
+
81
+ const [resolvedModules, setResolvedModules] =
82
+ createSignal<Awaited<ReturnType<typeof resolveModules>>>()
83
+
84
+ createEffect(() => {
85
+ const conf = config()
86
+ if (!conf) return
87
+
88
+ loadModules({ config: conf, nodeishFs: args.nodeishFs, _import: args._import })
89
+ .then((resolvedModules) => {
90
+ // TODO move to resolveModules
91
+ if (
92
+ !resolvedModules.resolvedPluginApi.loadMessages ||
93
+ !resolvedModules.resolvedPluginApi.saveMessages
94
+ ) {
95
+ throw new NoPluginProvidesLoadOrSaveMessagesError()
96
+ }
97
+ setResolvedModules(resolvedModules)
98
+
99
+ // TODO: handle `detectedLanguageTags`
100
+ })
101
+ .catch((err) => markInitAsFailed(err))
102
+ })
103
+
104
+ // -- messages ----------------------------------------------------------
105
+
106
+ let configValue: ProjectConfig
107
+ createEffect(() => (configValue = config()!)) // workaround to not run effects twice (e.g. config change + modules change) (I'm sure there exists a solid way of doing this, but I haven't found it yet)
108
+
109
+ const [messages, setMessages] = createSignal<Message[]>()
110
+ createEffect(() => {
111
+ const conf = config()
112
+ if (!conf) return
113
+
114
+ const _resolvedModules = resolvedModules()
115
+ if (!_resolvedModules) return
116
+
117
+ if (!_resolvedModules.resolvedPluginApi.loadMessages) {
118
+ markInitAsFailed(undefined)
119
+ return
120
+ }
121
+
122
+ makeTrulyAsync(
123
+ _resolvedModules.resolvedPluginApi.loadMessages({
124
+ languageTags: configValue!.languageTags,
125
+ sourceLanguageTag: configValue!.sourceLanguageTag,
126
+ }),
127
+ )
128
+ .then((messages) => {
129
+ setMessages(messages)
130
+ markInitAsComplete()
131
+ })
132
+ .catch((err) =>
133
+ markInitAsFailed(new PluginLoadMessagesError("Error in load messages", { cause: err })),
134
+ )
135
+ })
136
+
137
+ // -- installed items ----------------------------------------------------
138
+
139
+ const installedMessageLintRules = () => {
140
+ if (!resolvedModules()) return []
141
+ return resolvedModules()!.messageLintRules.map(
142
+ (rule) =>
143
+ ({
144
+ meta: rule.meta,
145
+ module:
146
+ resolvedModules()?.meta.find((m) => m.id.includes(rule.meta.id))?.module ??
147
+ "Unknown module. You stumbled on a bug in inlang's source code. Please open an issue.",
148
+
149
+ // default to warning, see https://github.com/inlang/inlang/issues/1254
150
+ lintLevel:
151
+ configValue.settings["project.messageLintRuleLevels"]?.[rule.meta.id] ?? "warning",
152
+ } satisfies InstalledMessageLintRule),
153
+ ) satisfies Array<InstalledMessageLintRule>
154
+ }
155
+
156
+ const installedPlugins = () => {
157
+ if (!resolvedModules()) return []
158
+ return resolvedModules()!.plugins.map((plugin) => ({
159
+ meta: plugin.meta,
160
+ module:
161
+ resolvedModules()?.meta.find((m) => m.id.includes(plugin.meta.id))?.module ??
162
+ "Unknown module. You stumbled on a bug in inlang's source code. Please open an issue.",
163
+ })) satisfies Array<InstalledPlugin>
164
+ }
165
+
166
+ // -- app ---------------------------------------------------------------
167
+
168
+ const initializeError: Error | undefined = await initialized.catch((error) => error)
169
+
170
+ const messagesQuery = createMessagesQuery(() => messages() || [])
171
+ const lintReportsQuery = createMessageLintReportsQuery(
172
+ messages,
173
+ config,
174
+ installedMessageLintRules,
175
+ resolvedModules,
176
+ )
177
+
178
+ const debouncedSave = skipFirst(
179
+ debounce(
180
+ 500,
181
+ async (newMessages) => {
182
+ try {
183
+ await resolvedModules()!.resolvedPluginApi.saveMessages({ messages: newMessages })
184
+ } catch (err) {
185
+ throw new PluginSaveMessagesError("Error in saving messages", {
186
+ cause: err,
187
+ })
188
+ }
189
+ if (
190
+ newMessages.length !== 0 &&
191
+ JSON.stringify(newMessages) !== JSON.stringify(messages())
192
+ ) {
193
+ setMessages(newMessages)
194
+ }
195
+ },
196
+ { atBegin: false },
197
+ ),
198
+ )
199
+
200
+ createEffect(() => {
201
+ debouncedSave(messagesQuery.getAll())
202
+ })
203
+
204
+ return {
205
+ installed: {
206
+ plugins: createSubscribable(() => installedPlugins()),
207
+ messageLintRules: createSubscribable(() => installedMessageLintRules()),
208
+ },
209
+ errors: createSubscribable(() => [
210
+ ...(initializeError ? [initializeError] : []),
211
+ ...(resolvedModules() ? resolvedModules()!.errors : []),
212
+ // have a query error exposed
213
+ //...(lintErrors() ?? []),
214
+ ]),
215
+ config: createSubscribable(() => config()),
216
+ setConfig,
217
+ customApi: createSubscribable(() => resolvedModules()?.resolvedPluginApi.customApi || {}),
218
+ query: {
219
+ messages: messagesQuery,
220
+ messageLintReports: lintReportsQuery,
221
+ },
222
+ } satisfies InlangProject
223
+ })
224
+ }
225
+
226
+ //const x = {} as InlangProject
227
+
228
+ // ------------------------------------------------------------------------------------------------
229
+
230
+ const loadConfig = async (args: {
231
+ projectFilePath: string
232
+ nodeishFs: NodeishFilesystemSubset
233
+ }) => {
234
+ const { data: configFile, error: configFileError } = await tryCatch(
235
+ async () => await args.nodeishFs.readFile(args.projectFilePath, { encoding: "utf-8" }),
236
+ )
237
+ if (configFileError)
238
+ throw new ProjectFilePathNotFoundError(
239
+ `Could not locate config file in (${args.projectFilePath}).`,
240
+ {
241
+ cause: configFileError,
242
+ },
243
+ )
244
+
245
+ const { data: parsedConfig, error: parseConfigError } = tryCatch(() => JSON.parse(configFile!))
246
+ if (parseConfigError)
247
+ throw new ProjectFileJSONSyntaxError(`The config is not a valid JSON file.`, {
248
+ cause: parseConfigError,
249
+ })
250
+ return validateConfig(parsedConfig)
251
+ }
252
+
253
+ const validateConfig = (config: unknown) => {
254
+ const typeErrors = [...ConfigCompiler.Errors(config)]
255
+ if (typeErrors.length > 0) {
256
+ throw new InvalidConfigError(`The config is invalid according to the schema.`, {
257
+ cause: typeErrors,
258
+ })
259
+ }
260
+
261
+ // @ts-ignore - fix after refactor
262
+ return Value.Cast(ProjectConfig, config)
263
+ }
264
+
265
+ const _writeConfigToDisk = async (args: {
266
+ nodeishFs: NodeishFilesystemSubset
267
+ config: ProjectConfig
268
+ }) => {
269
+ const { data: serializedConfig, error: serializeConfigError } = tryCatch(() =>
270
+ // TODO: this will probably not match the original formatting
271
+ JSON.stringify(args.config, undefined, 2),
272
+ )
273
+ if (serializeConfigError) throw serializeConfigError
274
+
275
+ const { error: writeConfigError } = await tryCatch(async () =>
276
+ args.nodeishFs.writeFile("./project.inlang.json", serializedConfig!),
277
+ )
278
+ if (writeConfigError) throw writeConfigError
279
+ }
280
+
281
+ // ------------------------------------------------------------------------------------------------
282
+
283
+ const loadModules = async (args: {
284
+ config: ProjectConfig
285
+ nodeishFs: NodeishFilesystemSubset
286
+ _import?: ImportFunction
287
+ }) =>
288
+ resolveModules({
289
+ config: args.config,
290
+ nodeishFs: args.nodeishFs,
291
+ _import: args._import,
292
+ })
293
+
294
+ const createAwaitable = () => {
295
+ let resolve: () => void
296
+ let reject: () => void
297
+
298
+ const promise = new Promise<void>((res, rej) => {
299
+ resolve = res
300
+ reject = rej
301
+ })
302
+
303
+ return [promise, resolve!, reject!] as [
304
+ awaitable: Promise<void>,
305
+ resolve: () => void,
306
+ reject: (e: unknown) => void,
307
+ ]
308
+ }
309
+
310
+ // ------------------------------------------------------------------------------------------------
311
+
312
+ // TODO: create global util type
313
+ type MaybePromise<T> = T | Promise<T>
314
+
315
+ const makeTrulyAsync = <T>(fn: MaybePromise<T>): Promise<T> => (async () => fn)()
316
+
317
+ // Skip initial call, eg. to skip setup of a createEffect
318
+ function skipFirst(func: (args: any) => any) {
319
+ let initial = false
320
+ return function (...args: any) {
321
+ if (initial) {
322
+ // @ts-ignore
323
+ return func.apply(this, args)
324
+ }
325
+ initial = true
326
+ }
327
+ }
328
+
329
+ export function createSubscribable<T>(signal: () => T): Subscribable<T> {
330
+ return Object.assign(signal, {
331
+ subscribe: (callback: (value: T) => void) => {
332
+ createEffect(() => {
333
+ callback(signal())
334
+ })
335
+ },
336
+ })
337
+ }
@@ -0,0 +1,33 @@
1
+ import { ProjectConfig } from "./versionedInterfaces.js"
2
+ import type { Result } from "@inlang/result"
3
+ import { TypeCompiler } from "@sinclair/typebox/compiler"
4
+
5
+ export class ParseConfigError extends Error {
6
+ readonly #id = "ParseConfigException"
7
+
8
+ constructor(message: string, cause?: string) {
9
+ super(message)
10
+ this.name = this.#id
11
+ this.cause = cause
12
+ }
13
+ }
14
+
15
+ // @ts-ignore - fix after refactor
16
+ const ConfigCompiler = TypeCompiler.Compile(ProjectConfig)
17
+
18
+ export const parseConfig = (config: ProjectConfig): Result<ProjectConfig, ParseConfigError> => {
19
+ if (ConfigCompiler.Check(config)) {
20
+ return {
21
+ data: config,
22
+ error: undefined as never,
23
+ }
24
+ } else {
25
+ return {
26
+ error: new ParseConfigError(
27
+ "The inlang config is not valid.",
28
+ [...ConfigCompiler.Errors(config)].toString(),
29
+ ),
30
+ data: undefined as never,
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,135 @@
1
+ // @ts-nocheck
2
+ /* eslint-disable */
3
+
4
+ import { batch } from "./solid.js"
5
+ import { TriggerCache } from "./trigger.js"
6
+
7
+ // src/index.ts
8
+ var $KEYS = Symbol("track-keys")
9
+ var ReactiveMap = class extends Map {
10
+ #keyTriggers = new TriggerCache()
11
+ #valueTriggers = new TriggerCache()
12
+ constructor(initial) {
13
+ super()
14
+ if (initial) for (const v of initial) super.set(v[0], v[1])
15
+ }
16
+ // reads
17
+ has(key) {
18
+ this.#keyTriggers.track(key)
19
+ return super.has(key)
20
+ }
21
+ get(key) {
22
+ this.#valueTriggers.track(key)
23
+ return super.get(key)
24
+ }
25
+ get size() {
26
+ this.#keyTriggers.track($KEYS)
27
+ return super.size
28
+ }
29
+ keys() {
30
+ this.#keyTriggers.track($KEYS)
31
+ return super.keys()
32
+ }
33
+ values() {
34
+ this.#keyTriggers.track($KEYS)
35
+ for (const v of super.keys()) this.#valueTriggers.track(v)
36
+ return super.values()
37
+ }
38
+ entries() {
39
+ this.#keyTriggers.track($KEYS)
40
+ for (const v of super.keys()) this.#valueTriggers.track(v)
41
+ return super.entries()
42
+ }
43
+ // writes
44
+ set(key, value) {
45
+ batch(() => {
46
+ if (super.has(key)) {
47
+ if (super.get(key) === value) return
48
+ } else {
49
+ this.#keyTriggers.dirty(key)
50
+ this.#keyTriggers.dirty($KEYS)
51
+ }
52
+ this.#valueTriggers.dirty(key)
53
+ super.set(key, value)
54
+ })
55
+ return this
56
+ }
57
+ delete(key) {
58
+ const r = super.delete(key)
59
+ if (r) {
60
+ batch(() => {
61
+ this.#keyTriggers.dirty(key)
62
+ this.#keyTriggers.dirty($KEYS)
63
+ this.#valueTriggers.dirty(key)
64
+ })
65
+ }
66
+ return r
67
+ }
68
+ clear() {
69
+ if (super.size) {
70
+ batch(() => {
71
+ for (const v of super.keys()) {
72
+ this.#keyTriggers.dirty(v)
73
+ this.#valueTriggers.dirty(v)
74
+ }
75
+ super.clear()
76
+ this.#keyTriggers.dirty($KEYS)
77
+ })
78
+ }
79
+ }
80
+ // callback
81
+ forEach(callbackfn) {
82
+ this.#keyTriggers.track($KEYS)
83
+ super.forEach((value, key) => callbackfn(value, key, this))
84
+ }
85
+ [Symbol.iterator]() {
86
+ return this.entries()
87
+ }
88
+ }
89
+ var ReactiveWeakMap = class extends WeakMap {
90
+ #keyTriggers = new TriggerCache(WeakMap)
91
+ #valueTriggers = new TriggerCache(WeakMap)
92
+ constructor(initial) {
93
+ super()
94
+ if (initial) for (const v of initial) super.set(v[0], v[1])
95
+ }
96
+ has(key) {
97
+ this.#keyTriggers.track(key)
98
+ return super.has(key)
99
+ }
100
+ get(key) {
101
+ this.#valueTriggers.track(key)
102
+ return super.get(key)
103
+ }
104
+ set(key, value) {
105
+ batch(() => {
106
+ if (super.has(key)) {
107
+ if (super.get(key) === value) return
108
+ } else this.#keyTriggers.dirty(key)
109
+ this.#valueTriggers.dirty(key)
110
+ super.set(key, value)
111
+ })
112
+ return this
113
+ }
114
+ delete(key) {
115
+ const r = super.delete(key)
116
+ if (r) {
117
+ batch(() => {
118
+ this.#keyTriggers.dirty(key)
119
+ this.#valueTriggers.dirty(key)
120
+ })
121
+ }
122
+ return r
123
+ }
124
+ }
125
+ function createMap(initial) {
126
+ const map = new ReactiveMap(initial)
127
+ return new Proxy(() => [...map], {
128
+ get: (_, b) => map[b],
129
+ })
130
+ }
131
+ function createWeakMap(initial) {
132
+ return new ReactiveWeakMap(initial)
133
+ }
134
+
135
+ export { ReactiveMap, ReactiveWeakMap, createMap, createWeakMap }
@@ -0,0 +1,36 @@
1
+ import {
2
+ createSignal as _createSignal,
3
+ createMemo as _createMemo,
4
+ createRoot as _createRoot,
5
+ createEffect as _createEffect,
6
+ observable as _observable,
7
+ batch as _batch,
8
+ from as _from,
9
+ getListener as _getListener,
10
+ onCleanup as _onCleanup,
11
+ DEV,
12
+ // @ts-ignore
13
+ } from "solid-js/dist/solid.js"
14
+
15
+ const createSignal = _createSignal as typeof import("solid-js")["createSignal"]
16
+ const createMemo = _createMemo as typeof import("solid-js")["createMemo"]
17
+ const createRoot = _createRoot as typeof import("solid-js")["createRoot"]
18
+ const createEffect = _createEffect as typeof import("solid-js")["createEffect"]
19
+ const observable = _observable as typeof import("solid-js")["observable"]
20
+ const from = _from as typeof import("solid-js")["from"]
21
+ const batch = _batch as typeof import("solid-js")["batch"]
22
+ const getListener = _getListener as typeof import("solid-js")["getListener"]
23
+ const onCleanup = _onCleanup as typeof import("solid-js")["onCleanup"]
24
+
25
+ export {
26
+ createSignal,
27
+ createMemo,
28
+ createRoot,
29
+ createEffect,
30
+ observable,
31
+ from,
32
+ batch,
33
+ getListener,
34
+ onCleanup,
35
+ DEV,
36
+ }
@@ -0,0 +1,46 @@
1
+ // @ts-nocheck
2
+ /* eslint-disable */
3
+
4
+ import { createSignal, getListener, onCleanup, DEV } from "./solid.js"
5
+
6
+ const noop = () => {}
7
+
8
+ const isServer = false
9
+
10
+ // src/index.ts
11
+ var triggerOptions = !isServer && DEV ? { equals: false, name: "trigger" } : { equals: false }
12
+ var triggerCacheOptions = !isServer && DEV ? { equals: false, internal: true } : triggerOptions
13
+ function createTrigger() {
14
+ if (isServer) {
15
+ return [noop, noop]
16
+ }
17
+ return createSignal(void 0, triggerOptions)
18
+ }
19
+ var TriggerCache = class {
20
+ #map
21
+ constructor(mapConstructor = Map) {
22
+ this.#map = new mapConstructor()
23
+ }
24
+ dirty(key) {
25
+ if (isServer) return
26
+ this.#map.get(key)?.$$()
27
+ }
28
+ track(key) {
29
+ if (!getListener()) return
30
+ let trigger = this.#map.get(key)
31
+ if (!trigger) {
32
+ const [$, $$] = createSignal(void 0, triggerCacheOptions)
33
+ this.#map.set(key, (trigger = { $, $$, n: 1 }))
34
+ } else trigger.n++
35
+ onCleanup(() => {
36
+ if (trigger.n-- === 1) queueMicrotask(() => trigger.n === 0 && this.#map.delete(key))
37
+ })
38
+ trigger.$()
39
+ }
40
+ }
41
+ function createTriggerCache(mapConstructor = Map) {
42
+ const map = new TriggerCache(mapConstructor)
43
+ return [map.track.bind(map), map.dirty.bind(map)]
44
+ }
45
+
46
+ export { TriggerCache, createTrigger, createTriggerCache }
@@ -0,0 +1,39 @@
1
+ export * from "./plugins/errors.js"
2
+ export * from "./message-lint-rules/errors.js"
3
+
4
+ export class ModuleError extends Error {
5
+ public readonly Module: string
6
+ constructor(message: string, options: { module: string; cause?: Error }) {
7
+ super(message)
8
+ this.name = "ModuleError"
9
+ this.Module = options.module
10
+ this.cause = options.cause
11
+ }
12
+ }
13
+
14
+ /**
15
+ * Error when a Module does not export any plugins or lint rules.
16
+ */
17
+ export class ModuleHasNoExportsError extends ModuleError {
18
+ constructor(message: string, options: { module: string; cause?: Error }) {
19
+ super(message, options)
20
+ this.name = "ModuleHasNoExportsError"
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Error when a Module cannot be imported.
26
+ */
27
+ export class ModuleImportError extends ModuleError {
28
+ constructor(message: string, options: { module: string; cause: Error }) {
29
+ super(message, options)
30
+ this.name = "ModuleImportError"
31
+ }
32
+ }
33
+
34
+ export class ModuleExportIsInvalidError extends ModuleError {
35
+ constructor(message: string, options: { module: string; cause?: Error }) {
36
+ super(message, options)
37
+ this.name = "ModuleExportIsInvalidError"
38
+ }
39
+ }
@@ -0,0 +1,58 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { createNodeishMemoryFs } from "@lix-js/fs"
4
+ import { describe, expect, it } from "vitest"
5
+ import { createImport } from "./import.js"
6
+
7
+ describe("$import", async () => {
8
+ const fs = createNodeishMemoryFs()
9
+ await fs.writeFile(
10
+ "./mock-module.js",
11
+ `
12
+ export function hello() {
13
+ return "hello";
14
+ }
15
+ `,
16
+ )
17
+
18
+ // mock module in a directory
19
+ await fs.mkdir("./nested")
20
+ await fs.writeFile(
21
+ "./nested/mock-module-two.js",
22
+ `
23
+ export function hello() {
24
+ return "world";
25
+ }
26
+ `,
27
+ )
28
+
29
+ const _import = createImport({
30
+ readFile: fs.readFile,
31
+ fetch,
32
+ })
33
+
34
+ it("should import a module from a local path", async () => {
35
+ const module = await _import("./mock-module.js")
36
+ expect(module.hello()).toBe("hello")
37
+ })
38
+
39
+ it("should import a module from a nested local path", async () => {
40
+ const module = await _import("./nested/mock-module-two.js")
41
+ expect(module.hello()).toBe("world")
42
+ })
43
+
44
+ it("should import an ES module from a url", async () => {
45
+ const module = await _import("https://cdn.jsdelivr.net/npm/normalize-url@7.2.0/index.js")
46
+ const normalizeUrl = module.default
47
+ expect(normalizeUrl("inlang.com")).toBe("http://inlang.com")
48
+ })
49
+
50
+ it("should throw if a module is loaded that is not an ES module", async () => {
51
+ try {
52
+ await _import("https://cdn.jsdelivr.net/npm/lodash@4.17.21")
53
+ throw "function did not throw"
54
+ } catch {
55
+ expect(true).toBe(true)
56
+ }
57
+ })
58
+ })