New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@inlang/sdk

Package Overview
Dependencies
Maintainers
2
Versions
86
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@inlang/sdk - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

dist/api.test-d.d.ts

4

dist/adapter/solidAdapter.d.ts

@@ -14,4 +14,4 @@ import type { InlangProject, MessageLintReportsQueryApi } from "../api.js";

errors: () => ReturnType<InlangProject["errors"]>;
config: () => ReturnType<InlangProject["config"]>;
setConfig: InlangProject["setConfig"];
settings: () => ReturnType<InlangProject["settings"]>;
setSettings: InlangProject["setSettings"];
query: {

@@ -18,0 +18,0 @@ messages: {

@@ -8,3 +8,3 @@ import { observable } from "../reactivity/solid.js";

customApi: convert(project.customApi),
config: convert(project.config),
settings: convert(project.settings),
errors: convert(project.errors),

@@ -15,3 +15,3 @@ installed: {

},
setConfig: project.setConfig,
setSettings: project.setSettings,
query: {

@@ -18,0 +18,0 @@ messages: {

@@ -5,3 +5,3 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

import { solidAdapter } from "./solidAdapter.js";
import { openInlangProject } from "../openInlangProject.js";
import { loadProject } from "../loadProject.js";
import { createNodeishMemoryFs } from "@lix-js/fs";

@@ -13,18 +13,14 @@ // ------------------------------------------------------------------------------------------------

modules: ["plugin.js", "plugin2.js"],
settings: {
"project.messageLintRuleLevels": {
"messageLintRule.inlang.missingTranslation": "error",
},
"plugin.inlang.i18next": {
pathPattern: "./examples/example01/{languageTag}.json",
variableReferencePattern: ["{", "}"],
},
messageLintRuleLevels: {
"messageLintRule.project.missingTranslation": "error",
},
"plugin.project.i18next": {
pathPattern: "./examples/example01/{languageTag}.json",
variableReferencePattern: ["{", "}"],
},
};
const mockPlugin = {
meta: {
id: "plugin.inlang.i18next",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
},
id: "plugin.project.i18next",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
loadMessages: () => exampleMessages,

@@ -69,8 +65,6 @@ saveMessages: () => undefined,

const mockLintRule = {
meta: {
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
},
message: () => undefined,
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
run: () => undefined,
};

@@ -85,4 +79,4 @@ const $import = async (name) => ({

await fs.writeFile("./project.inlang.json", JSON.stringify(config));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -93,12 +87,12 @@ _import: $import,

createEffect(() => {
inlang.config();
project.settings();
counter += 1;
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const newConfig = { ...inlang.config(), languageTags: ["en", "de"] };
inlang.setConfig(newConfig);
const newConfig = { ...project.settings(), languageTags: ["en", "de"] };
project.setSettings(newConfig);
// TODO: how can we await `setConfig` correctly
await new Promise((resolve) => setTimeout(resolve, 0));
expect(counter).toBe(2); // 2 times because effect creation + set
expect(inlang.config()).toStrictEqual(newConfig);
expect(project.settings()).toStrictEqual(newConfig);
});

@@ -110,4 +104,4 @@ });

await fs.writeFile("./project.inlang.json", JSON.stringify(config));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -119,10 +113,10 @@ _import: $import,

createEffect(() => {
inlang.installed.plugins();
project.installed.plugins();
counterPlugins += 1;
});
createEffect(() => {
inlang.installed.messageLintRules();
project.installed.messageLintRules();
counterLint += 1;
});
inlang.setConfig({ ...inlang.config(), languageTags: ["en", "fr"] });
project.setSettings({ ...project.settings(), languageTags: ["en", "fr"] });
// TODO: how can we await `setConfig` correctly

@@ -141,11 +135,9 @@ await new Promise((resolve) => setTimeout(resolve, 0));

modules: ["./plugin-a.js"],
settings: {
"library.inlang.paraglideJsSveltekit": {
languageNegotiation: {
strategies: [
{
type: "localStorage",
},
],
},
"library.project.paraglideJsSveltekit": {
languageNegotiation: {
strategies: [
{
type: "localStorage",
},
],
},

@@ -155,11 +147,9 @@ },

const mockPlugin = {
meta: {
id: "plugin.mock.id",
displayName: {
en: "hello",
},
description: {
en: "wo",
},
id: "plugin.mock.id",
displayName: {
en: "hello",
},
description: {
en: "wo",
},
loadMessages: ({ languageTags }) => (languageTags.length ? exampleMessages : []),

@@ -170,4 +160,4 @@ saveMessages: () => undefined,

await fs.writeFile("./project.inlang.json", JSON.stringify(mockConfig));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -178,11 +168,11 @@ _import: mockImport,

createEffect(() => {
inlang.query.messages.getAll();
project.query.messages.getAll();
counter += 1;
});
expect(Object.values(inlang.query.messages.getAll()).length).toBe(2);
inlang.setConfig({ ...inlang.config(), languageTags: [] });
expect(Object.values(project.query.messages.getAll()).length).toBe(2);
project.setSettings({ ...project.settings(), languageTags: [] });
// TODO: how can we await `setConfig` correctly
await new Promise((resolve) => setTimeout(resolve, 0));
expect(counter).toBe(2); // 2 times because effect creation + set
expect(Object.values(inlang.query.messages.getAll()).length).toBe(0);
expect(Object.values(project.query.messages.getAll()).length).toBe(0);
});

@@ -192,4 +182,4 @@ it("should react to changes in messages", async () => {

await fs.writeFile("./project.inlang.json", JSON.stringify(config));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -200,10 +190,10 @@ _import: $import,

createEffect(() => {
inlang.query.messages.getAll();
project.query.messages.getAll();
counter += 1;
});
const messagesBefore = inlang.query.messages.getAll;
const messagesBefore = project.query.messages.getAll;
expect(Object.values(messagesBefore()).length).toBe(2);
expect((Object.values(messagesBefore())[0]?.variants.find((variant) => variant.languageTag === "en")
?.pattern[0]).value).toBe("test");
inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -228,3 +218,3 @@ // TODO: use `createMessage` utility

expect(counter).toBe(2); // 2 times because effect creation + set
const messagesAfter = inlang.query.messages.getAll;
const messagesAfter = project.query.messages.getAll;
expect(Object.values(messagesAfter()).length).toBe(2);

@@ -239,5 +229,5 @@ expect((Object.values(messagesAfter())[0]?.variants.find((variant) => variant.languageTag === "en")

const fs = createNodeishMemoryFs();
await fs.writeFile("./inlang.config.json", JSON.stringify(config));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./inlang.config.json",
await fs.writeFile("./project.config.json", JSON.stringify(config));
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.config.json",
nodeishFs: fs,

@@ -248,14 +238,14 @@ _import: $import,

createEffect(() => {
inlang.query.messageLintReports.getAll();
project.query.messageLintReports.getAll();
counter += 1;
});
const newConfig = { ...inlang.config(), languageTags: ["en", "de"] };
inlang.setConfig(newConfig);
const newConfig = { ...project.settings(), languageTags: ["en", "de"] };
project.setSettings(newConfig);
expect(counter).toBe(1);
expect(inlang.query.messageLintReports.getAll()).toEqual([]);
expect(project.query.messageLintReports.getAll()).toEqual([]);
await new Promise((resolve) => setTimeout(resolve, 510));
const newConfig2 = { ...inlang.config(), languageTags: ["en", "de", "fr"] };
inlang.setConfig(newConfig2);
const newConfig2 = { ...project.settings(), languageTags: ["en", "de", "fr"] };
project.setSettings(newConfig2);
expect(counter).toBe(9);
expect(inlang.query.messageLintReports.getAll()).toEqual([]);
expect(project.query.messageLintReports.getAll()).toEqual([]);
});

@@ -268,5 +258,5 @@ });

const fs = createNodeishMemoryFs();
await fs.writeFile("./inlang.config.json", JSON.stringify(config));
const inlang = solidAdapter(await openInlangProject({
projectFilePath: "./inlang.config.json",
await fs.writeFile("./project.config.json", JSON.stringify(config));
const project = solidAdapter(await loadProject({
settingsFilePath: "./project.config.json",
nodeishFs: fs,

@@ -277,6 +267,6 @@ _import: $import,

createEffect(() => {
inlang.query.messageLintReports.getAll();
project.query.messageLintReports.getAll();
counter += 1;
});
inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -289,5 +279,5 @@ data: {

expect(counter).toBe(1);
expect(inlang.query.messageLintReports.getAll()).toEqual([]);
expect(project.query.messageLintReports.getAll()).toEqual([]);
await new Promise((resolve) => setTimeout(resolve, 510));
inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -300,5 +290,5 @@ data: {

expect(counter).toBe(6);
expect(inlang.query.messageLintReports.getAll()).toEqual([]);
expect(project.query.messageLintReports.getAll()).toEqual([]);
});
});
});
import type { Result } from "@inlang/result";
import type * as RuntimeError from "./errors.js";
import type * as ModuleResolutionError from "./resolve-modules/errors.js";
import type { MessageLintLevel, MessageLintRule, Message, Plugin, ProjectConfig, MessageLintReport } from "./versionedInterfaces.js";
import type { MessageLintLevel, MessageLintRule, Message, Plugin, ProjectSettings, MessageLintReport } from "./versionedInterfaces.js";
import type { ResolvedPluginApi } from "./resolve-modules/plugins/types.js";
export type InstalledPlugin = {
meta: Plugin["meta"];
id: Plugin["id"];
displayName: Plugin["displayName"];
description: Plugin["description"];
/**

@@ -14,3 +16,5 @@ * The module which the plugin is installed from.

export type InstalledMessageLintRule = {
meta: MessageLintRule["meta"];
id: MessageLintRule["id"];
displayName: MessageLintRule["displayName"];
description: MessageLintRule["description"];
/**

@@ -20,3 +24,3 @@ * The module which the lint rule is installed from.

module: string;
lintLevel: MessageLintLevel;
level: MessageLintLevel;
};

@@ -30,4 +34,4 @@ export type InlangProject = {

customApi: Subscribable<ResolvedPluginApi["customApi"]>;
config: Subscribable<ProjectConfig | undefined>;
setConfig: (config: ProjectConfig) => Result<void, RuntimeError.InvalidConfigError>;
settings: Subscribable<ProjectSettings>;
setSettings: (config: ProjectSettings) => Result<void, RuntimeError.ProjectSettingsInvalidError>;
query: {

@@ -34,0 +38,0 @@ messages: MessageQueryApi;

import type { InlangProject, InstalledMessageLintRule } from "./api.js";
import type { ProjectConfig } from "@inlang/project-config";
import type { ProjectSettings } from "@inlang/project-settings";
import type { resolveModules } from "./resolve-modules/index.js";

@@ -8,3 +8,3 @@ import type { Message } from "./versionedInterfaces.js";

*/
export declare function createMessageLintReportsQuery(messages: () => Array<Message> | undefined, config: () => ProjectConfig | undefined, installedMessageLintRules: () => Array<InstalledMessageLintRule>, resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined): InlangProject["query"]["messageLintReports"];
export declare function createMessageLintReportsQuery(messages: () => Array<Message> | undefined, settings: () => ProjectSettings, installedMessageLintRules: () => Array<InstalledMessageLintRule>, resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined): InlangProject["query"]["messageLintReports"];
//# sourceMappingURL=createMessageLintReportsQuery.d.ts.map
import { createEffect } from "./reactivity/solid.js";
import { createSubscribable } from "./openInlangProject.js";
import { createSubscribable } from "./loadProject.js";
import { lintSingleMessage } from "./lint/index.js";

@@ -8,21 +8,21 @@ import { ReactiveMap } from "./reactivity/map.js";

*/
export function createMessageLintReportsQuery(messages, config, installedMessageLintRules, resolvedModules) {
export function createMessageLintReportsQuery(messages, settings, installedMessageLintRules, resolvedModules) {
// @ts-expect-error
const index = new ReactiveMap();
createEffect(() => {
const msgs = messages();
const conf = config();
const modules = resolvedModules();
if (msgs && conf && modules) {
const _messages = messages();
const _settings = settings();
if (_messages && _settings && modules) {
// console.log("new calculation")
// index.clear()
for (const message of msgs) {
for (const message of _messages) {
// TODO: only lint changed messages and update arrays selectively
lintSingleMessage({
rules: modules.messageLintRules,
ruleSettings: conf.settings,
ruleLevels: Object.fromEntries(installedMessageLintRules().map((rule) => [rule.meta.id, rule.lintLevel])),
sourceLanguageTag: conf.sourceLanguageTag,
languageTags: conf.languageTags,
messages: msgs,
settings: {
..._settings,
messageLintRuleLevels: Object.fromEntries(installedMessageLintRules().map((rule) => [rule.id, rule.level])),
},
messages: _messages,
message: message,

@@ -29,0 +29,0 @@ }).then((report) => {

import { ReactiveMap } from "./reactivity/map.js";
import { createEffect } from "./reactivity/solid.js";
import { createSubscribable } from "./openInlangProject.js";
import { createSubscribable } from "./loadProject.js";
/**

@@ -5,0 +5,0 @@ * Creates a reactive query API for messages.

@@ -1,8 +0,8 @@

export declare class InvalidConfigError extends Error {
export declare class ProjectSettingsInvalidError extends Error {
constructor(message: string, options: ErrorOptions);
}
export declare class ProjectFileJSONSyntaxError extends Error {
export declare class ProjectSettingsFileJSONSyntaxError extends Error {
constructor(message: string, options: ErrorOptions);
}
export declare class ProjectFilePathNotFoundError extends Error {
export declare class ProjectSettingsFileNotFoundError extends Error {
constructor(message: string, options: ErrorOptions);

@@ -9,0 +9,0 @@ }

@@ -1,17 +0,17 @@

export class InvalidConfigError extends Error {
export class ProjectSettingsInvalidError extends Error {
constructor(message, options) {
super(message, options);
this.name = "InvalidConfigError";
this.name = "ProjectSettingsInvalidError";
}
}
export class ProjectFileJSONSyntaxError extends Error {
export class ProjectSettingsFileJSONSyntaxError extends Error {
constructor(message, options) {
super(message, options);
this.name = "ProjectFileJSONSyntaxError";
this.name = "ProjectSettingsFileJSONSyntaxError";
}
}
export class ProjectFilePathNotFoundError extends Error {
export class ProjectSettingsFileNotFoundError extends Error {
constructor(message, options) {
super(message, options);
this.name = "ProjectFilePathNotFoundError";
this.name = "ProjectSettingsFileNotFoundError";
}

@@ -18,0 +18,0 @@ }

@@ -8,6 +8,6 @@ /**

export { type ImportFunction, createImport } from "./resolve-modules/index.js";
export { openInlangProject } from "./openInlangProject.js";
export { loadProject } from "./loadProject.js";
export { solidAdapter, type InlangProjectWithSolidAdapter } from "./adapter/solidAdapter.js";
export { createMessagesQuery } from "./createMessagesQuery.js";
export { ProjectFilePathNotFoundError, ProjectFileJSONSyntaxError, InvalidConfigError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
export { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
export * from "./messages/variant.js";

@@ -14,0 +14,0 @@ export * from "./versionedInterfaces.js";

@@ -7,8 +7,8 @@ /**

export { createImport } from "./resolve-modules/index.js";
export { openInlangProject } from "./openInlangProject.js";
export { loadProject } from "./loadProject.js";
export { solidAdapter } from "./adapter/solidAdapter.js";
export { createMessagesQuery } from "./createMessagesQuery.js";
export { ProjectFilePathNotFoundError, ProjectFileJSONSyntaxError, InvalidConfigError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
export { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
export * from "./messages/variant.js";
export * from "./versionedInterfaces.js";
export { InlangModule } from "@inlang/module";
import type { Message } from "@inlang/message";
import type { MessagedLintRuleThrowedError } from "./errors.js";
import type { LanguageTag } from "@inlang/language-tag";
import type { JSONObject } from "@inlang/json-types";
import type { MessageLintLevel, MessageLintReport, MessageLintRule } from "@inlang/message-lint-rule";
import type { MessageLintReport, MessageLintRule } from "@inlang/message-lint-rule";
import type { ProjectSettings } from "@inlang/project-settings";
export declare const lintMessages: (args: {
sourceLanguageTag: LanguageTag;
languageTags: LanguageTag[];
ruleSettings: Record<MessageLintRule["meta"]["id"], JSONObject>;
ruleLevels: Record<MessageLintRule["meta"]["id"], MessageLintLevel>;
settings: ProjectSettings & Required<Pick<ProjectSettings, "messageLintRuleLevels">>;
rules: MessageLintRule[];

@@ -12,0 +8,0 @@ messages: Message[];

import { beforeEach, describe, expect, test, vi } from "vitest";
import { lintMessages } from "./lintMessages.js";
const lintRule1 = {
meta: {
id: "messageLintRule.x.1",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.x.1",
displayName: { en: "" },
description: { en: "" },
run: vi.fn(),
};
const lintRule2 = {
meta: {
id: "messageLintRule.x.2",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.x.2",
displayName: { en: "" },
description: { en: "" },
run: vi.fn(),
};

@@ -29,3 +25,3 @@ const message1 = { id: "m1" };

let called = 0;
lintRule2.message.mockImplementation(async () => {
lintRule2.run.mockImplementation(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));

@@ -35,13 +31,15 @@ called++;

await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: [],
messages,
rules: [lintRule1, lintRule2],
});
expect(lintRule1.message).toHaveBeenCalledTimes(3);
expect(lintRule1.run).toHaveBeenCalledTimes(3);
expect(called).toBe(3);

@@ -51,3 +49,3 @@ });

const fn = vi.fn();
lintRule1.message.mockImplementation(async ({ message }) => {
lintRule1.run.mockImplementation(async ({ message }) => {
fn("r1", "before", message.id);

@@ -57,3 +55,3 @@ await new Promise((resolve) => setTimeout(resolve, 0));

});
lintRule2.message.mockImplementation(async ({ message }) => {
lintRule2.run.mockImplementation(async ({ message }) => {
fn("r2", "before", message.id);

@@ -64,10 +62,12 @@ await new Promise((resolve) => setTimeout(resolve, 0));

await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
rules: [lintRule1, lintRule2],
sourceLanguageTag: "en",
languageTags: [],
messages,

@@ -90,16 +90,18 @@ });

test("it should not abort the linting process when errors occur", async () => {
lintRule1.message.mockImplementation(({ report }) => {
lintRule1.run.mockImplementation(({ report }) => {
report({});
});
lintRule2.message.mockImplementation(() => {
lintRule2.run.mockImplementation(() => {
throw new Error("error");
});
const { data, errors } = await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: [],
messages,

@@ -106,0 +108,0 @@ rules: [lintRule1, lintRule2],

@@ -1,6 +0,5 @@

import type { MessageLintLevel, MessageLintRule, MessageLintReport } from "@inlang/message-lint-rule";
import type { MessageLintRule, MessageLintReport } from "@inlang/message-lint-rule";
import type { Message } from "@inlang/message";
import { MessagedLintRuleThrowedError } from "./errors.js";
import type { LanguageTag } from "@inlang/language-tag";
import type { JSONObject } from "@inlang/json-types";
import type { ProjectSettings } from "@inlang/project-settings";
/**

@@ -12,6 +11,3 @@ * Lint a single message.

export declare const lintSingleMessage: (args: {
sourceLanguageTag: LanguageTag;
languageTags: LanguageTag[];
ruleSettings: Record<MessageLintRule["meta"]["id"], JSONObject>;
ruleLevels: Record<MessageLintRule["meta"]["id"], MessageLintLevel>;
settings: ProjectSettings & Required<Pick<ProjectSettings, "messageLintRuleLevels">>;
rules: MessageLintRule[];

@@ -18,0 +14,0 @@ messages: Message[];

@@ -11,17 +11,17 @@ import { MessagedLintRuleThrowedError } from "./errors.js";

const promises = args.rules.map(async (rule) => {
const ruleId = rule.meta.id;
const settings = args.ruleSettings?.[ruleId] ?? {};
const level = args.ruleLevels?.[ruleId];
const level = args.settings.messageLintRuleLevels?.[rule.id];
if (level === undefined) {
throw Error("No lint level provided for lint rule: " + ruleId);
throw Error("No lint level provided for lint rule: " + rule.id);
}
try {
await rule.message({
...args,
settings,
await rule.run({
message: args.message,
settings: args.settings,
report: (reportArgs) => {
reports.push({
ruleId,
ruleId: rule.id,
level,
...reportArgs,
messageId: reportArgs.messageId,
languageTag: reportArgs.languageTag,
body: reportArgs.body,
});

@@ -32,3 +32,3 @@ },

catch (error) {
errors.push(new MessagedLintRuleThrowedError(`Lint rule '${ruleId}' throwed while linting message "${args.message.id}".`, { cause: error }));
errors.push(new MessagedLintRuleThrowedError(`Lint rule '${rule.id}' throwed while linting message "${args.message.id}".`, { cause: error }));
}

@@ -35,0 +35,0 @@ });

@@ -5,16 +5,12 @@ import { beforeEach, describe, expect, test, vi } from "vitest";

const lintRule1 = {
meta: {
id: "messageLintRule.r.1",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.r.1",
displayName: "",
description: "",
run: vi.fn(),
};
const lintRule2 = {
meta: {
id: "messageLintRule.r.2",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.r.2",
displayName: "",
description: "",
run: vi.fn(),
};

@@ -31,8 +27,10 @@ const message1 = {};

test("it should throw if a lint level is not provided for a given lint rule", async () => {
lintRule1.message.mockImplementation(({ report }) => report({}));
lintRule1.run.mockImplementation(({ report }) => report({}));
const result = await tryCatch(() => lintSingleMessage({
ruleLevels: {},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
messageLintRuleLevels: {},
modules: [],
},
messages,

@@ -46,10 +44,12 @@ message: message1,

test("it should override the default lint level", async () => {
lintRule1.message.mockImplementation(({ report }) => report({}));
lintRule1.run.mockImplementation(({ report }) => report({}));
const reports = await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "error",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "error",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -62,14 +62,14 @@ message: message1,

test("it should pass the correct settings", async () => {
const settings = {};
const settings = {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
},
};
const fn = vi.fn();
lintRule1.message.mockImplementation(({ settings }) => fn(settings));
lintRule1.run.mockImplementation(({ settings }) => fn(settings));
await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
},
ruleSettings: {
[lintRule1.meta.id]: settings,
},
sourceLanguageTag: "en",
languageTags: ["en"],
settings,
messages,

@@ -85,6 +85,6 @@ message: message1,

let m2Called = false;
lintRule1.message.mockImplementation(() => {
lintRule1.run.mockImplementation(() => {
m1Called = true;
});
lintRule2.message.mockImplementation(async () => {
lintRule2.run.mockImplementation(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));

@@ -94,9 +94,11 @@ m2Called = true;

await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -111,20 +113,22 @@ message: message1,

const fn = vi.fn();
lintRule1.message.mockImplementation(async () => {
fn(lintRule1.meta.id, "before");
lintRule1.run.mockImplementation(async () => {
fn(lintRule1.id, "before");
await new Promise((resolve) => setTimeout(resolve, 0));
fn(lintRule1.meta.id, "after");
fn(lintRule1.id, "after");
});
lintRule2.message.mockImplementation(async () => {
fn(lintRule2.meta.id, "before");
lintRule2.run.mockImplementation(async () => {
fn(lintRule2.id, "before");
await new Promise((resolve) => setTimeout(resolve, 0));
fn(lintRule2.meta.id, "after");
fn(lintRule2.id, "after");
});
await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -135,22 +139,24 @@ message: message1,

expect(fn).toHaveBeenCalledTimes(4);
expect(fn).toHaveBeenNthCalledWith(1, lintRule1.meta.id, "before");
expect(fn).toHaveBeenNthCalledWith(2, lintRule2.meta.id, "before");
expect(fn).toHaveBeenNthCalledWith(3, lintRule1.meta.id, "after");
expect(fn).toHaveBeenNthCalledWith(4, lintRule2.meta.id, "after");
expect(fn).toHaveBeenNthCalledWith(1, lintRule1.id, "before");
expect(fn).toHaveBeenNthCalledWith(2, lintRule2.id, "before");
expect(fn).toHaveBeenNthCalledWith(3, lintRule1.id, "after");
expect(fn).toHaveBeenNthCalledWith(4, lintRule2.id, "after");
});
test("it should not abort the linting process when errors occur", async () => {
lintRule1.message.mockImplementation(() => {
lintRule1.run.mockImplementation(() => {
throw new Error("error");
});
lintRule2.message.mockImplementation(({ report }) => {
lintRule2.run.mockImplementation(({ report }) => {
report({});
});
const result = await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -157,0 +163,0 @@ message: message1,

@@ -1,2 +0,2 @@

import { ProjectConfig } from "./versionedInterfaces.js";
import { ProjectSettings } from "./versionedInterfaces.js";
import type { Result } from "@inlang/result";

@@ -7,3 +7,3 @@ export declare class ParseConfigError extends Error {

}
export declare const parseConfig: (config: ProjectConfig) => Result<ProjectConfig, ParseConfigError>;
export declare const parseSettings: (config: ProjectSettings) => Result<ProjectSettings, ParseConfigError>;
//# sourceMappingURL=parseConfig.d.ts.map

@@ -1,2 +0,2 @@

import { ProjectConfig } from "./versionedInterfaces.js";
import { ProjectSettings } from "./versionedInterfaces.js";
import { TypeCompiler } from "@sinclair/typebox/compiler";

@@ -12,4 +12,4 @@ export class ParseConfigError extends Error {

// @ts-ignore - fix after refactor
const ConfigCompiler = TypeCompiler.Compile(ProjectConfig);
export const parseConfig = (config) => {
const ConfigCompiler = TypeCompiler.Compile(ProjectSettings);
export const parseSettings = (config) => {
if (ConfigCompiler.Check(config)) {

@@ -16,0 +16,0 @@ return {

@@ -11,3 +11,3 @@ import { Value } from "@sinclair/typebox/value";

if (Value.Check(MessageLintRule, rule) === false) {
result.errors.push(new MessageLintRuleIsInvalidError(`Couldn't parse lint rule "${rule.meta.id}"`, {
result.errors.push(new MessageLintRuleIsInvalidError(`Couldn't parse lint rule "${rule.id}"`, {
module: "not implemented",

@@ -14,0 +14,0 @@ }));

import type { Plugin } from "@inlang/plugin";
type PluginErrorOptions = {
plugin: Plugin["meta"]["id"];
plugin: Plugin["id"];
} & Partial<Error>;

@@ -5,0 +5,0 @@ declare class PluginError extends Error {

@@ -29,10 +29,10 @@ import { Plugin } from "@inlang/plugin";

// -- INVALID ID in META --
const hasInvalidId = errors.some((error) => error.path === "/meta/id");
const hasInvalidId = errors.some((error) => error.path === "/id");
if (hasInvalidId) {
result.errors.push(new PluginHasInvalidIdError(`Plugin ${plugin.meta.id} has an invalid id "${plugin.meta.id}". It must be kebap-case and contain a namespace like project.my-plugin.`, { plugin: plugin.meta.id }));
result.errors.push(new PluginHasInvalidIdError(`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be kebap-case and contain a namespace like project.my-plugin.`, { plugin: plugin.id }));
}
// -- USES RESERVED NAMESPACE --
if (plugin.meta.id.includes("inlang") && !whitelistedPlugins.includes(plugin.meta.id)) {
result.errors.push(new PluginUsesReservedNamespaceError(`Plugin ${plugin.meta.id} uses reserved namespace 'inlang'.`, {
plugin: plugin.meta.id,
if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
result.errors.push(new PluginUsesReservedNamespaceError(`Plugin ${plugin.id} uses reserved namespace 'inlang'.`, {
plugin: plugin.id,
}));

@@ -42,4 +42,4 @@ }

if (errors.length > 0) {
result.errors.push(new PluginHasInvalidSchemaError(`Plugin ${plugin.meta.id} uses an invalid schema. Please check the documentation for the correct Plugin type.`, {
plugin: plugin.meta.id,
result.errors.push(new PluginHasInvalidSchemaError(`Plugin ${plugin.id} uses an invalid schema. Please check the documentation for the correct Plugin type.`, {
plugin: plugin.id,
cause: errors,

@@ -50,6 +50,6 @@ }));

if (typeof plugin.loadMessages === "function" && result.data.loadMessages !== undefined) {
result.errors.push(new PluginLoadMessagesFunctionAlreadyDefinedError(`Plugin ${plugin.meta.id} defines the loadMessages function, but it was already defined by another plugin.`, { plugin: plugin.meta.id }));
result.errors.push(new PluginLoadMessagesFunctionAlreadyDefinedError(`Plugin ${plugin.id} defines the loadMessages function, but it was already defined by another plugin.`, { plugin: plugin.id }));
}
if (typeof plugin.saveMessages === "function" && result.data.saveMessages !== undefined) {
result.errors.push(new PluginSaveMessagesFunctionAlreadyDefinedError(`Plugin ${plugin.meta.id} defines the saveMessages function, but it was already defined by another plugin.`, { plugin: plugin.meta.id }));
result.errors.push(new PluginSaveMessagesFunctionAlreadyDefinedError(`Plugin ${plugin.id} defines the saveMessages function, but it was already defined by another plugin.`, { plugin: plugin.id }));
}

@@ -60,3 +60,3 @@ // --- ADD APP SPECIFIC API ---

const { data: customApi, error } = tryCatch(() => plugin.addCustomApi({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
}));

@@ -69,3 +69,3 @@ if (error) {

if (typeof customApi !== "object") {
result.errors.push(new PluginReturnedInvalidCustomApiError(`Plugin ${plugin.meta.id} defines the addCustomApi function, but it does not return an object.`, { plugin: plugin.meta.id, cause: error }));
result.errors.push(new PluginReturnedInvalidCustomApiError(`Plugin ${plugin.id} defines the addCustomApi function, but it does not return an object.`, { plugin: plugin.id, cause: error }));
}

@@ -83,3 +83,3 @@ }

..._args,
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -91,3 +91,3 @@ });

..._args,
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -98,3 +98,3 @@ });

const detectedLangugeTags = await plugin.detectedLanguageTags({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -108,3 +108,3 @@ });

const { data: customApi } = tryCatch(() => plugin.addCustomApi({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
}));

@@ -111,0 +111,0 @@ if (customApi) {

@@ -8,13 +8,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

const mockPlugin = {
meta: {
// @ts-expect-error - invalid id
id: "no-namespace",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
// @ts-expect-error - invalid id
id: "no-namespace",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: () => undefined,
saveMessages: () => undefined,
addCustomApi() {
return {};
},
};

@@ -30,7 +25,5 @@ const resolved = await resolvePlugins({

const mockPlugin = {
meta: {
id: "plugin.namespace.undefinedApi",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.undefinedApi",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
// @ts-expect-error the key is not available in type

@@ -52,7 +45,5 @@ nonExistentKey: {

const mockPlugin = {
meta: {
id: "plugin.inlang.notWhitelisted",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.inlang.notWhitelisted",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: () => undefined,

@@ -71,7 +62,5 @@ };

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => [{ id: "test", expressions: [], selectors: [], variants: [] }],

@@ -91,15 +80,11 @@ };

const mockPlugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined,
};
const mockPlugin2 = {
meta: {
id: "plugin.namepsace.loadMessagesSecond",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesSecond",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined,

@@ -116,7 +101,5 @@ };

const mockPlugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
saveMessages: async () => undefined,

@@ -136,7 +119,5 @@ };

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined,

@@ -154,15 +135,11 @@ saveMessages: async () => undefined,

const mockPlugin = {
meta: {
id: "plugin.namepsace.saveMessages",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.saveMessages",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
saveMessages: async () => undefined,
};
const mockPlugin2 = {
meta: {
id: "plugin.namepsace.saveMessages2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.saveMessages2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
saveMessages: async () => undefined,

@@ -179,7 +156,5 @@ };

const mockPlugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined,

@@ -199,7 +174,5 @@ };

const mockPlugin = {
meta: {
id: "plugin.namepsace.detectedLanguageTags",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.detectedLanguageTags",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
detectedLanguageTags: async () => ["de", "en"],

@@ -211,7 +184,5 @@ addCustomApi: () => {

const mockPlugin2 = {
meta: {
id: "plugin.namepsace.detectedLanguageTags2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.detectedLanguageTags2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => {

@@ -233,7 +204,5 @@ return {};

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -254,7 +223,5 @@ "my-app": {

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -270,7 +237,5 @@ "my-app-1": {

const mockPlugin2 = {
meta: {
id: "plugin.namespace.placeholder2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -293,7 +258,5 @@ "my-app-3": {

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
// @ts-expect-error - invalid return type

@@ -311,7 +274,5 @@ addCustomApi: () => undefined,

const mockPlugin = {
meta: {
id: "plugin.namepsace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -318,0 +279,0 @@ "app.inlang.placeholder": {

@@ -18,3 +18,3 @@ import type { LanguageTag } from "@inlang/language-tag";

plugins: Array<Plugin>;
settings: Record<Plugin["meta"]["id"], JSONObject>;
settings: Record<Plugin["id"], JSONObject>;
nodeishFs: NodeishFilesystemSubset;

@@ -21,0 +21,0 @@ }) => Promise<{

@@ -1,2 +0,2 @@

import { ProjectConfig } from "@inlang/project-config";
import { ProjectSettings } from "@inlang/project-settings";
import { Value } from "@sinclair/typebox/value";

@@ -10,7 +10,5 @@ import { describe, test, expect } from "vitest";

const mockPlugin = {
meta: {
id: "plugin.namespace.placeholder",
displayName: { en: "" },
description: { en: "" },
},
id: "plugin.namespace.placeholder",
displayName: { en: "" },
description: { en: "" },
};

@@ -24,3 +22,3 @@ const passCases = ["plugin.namespace.helloWorld", "plugin.namespace.i18n"];

for (const pass of passCases) {
mockPlugin.meta.id = pass;
mockPlugin.id = pass;
// @ts-ignore - type mismatch error. fix after refactor

@@ -30,3 +28,3 @@ expect(Value.Check(Plugin, mockPlugin)).toBe(true);

for (const fail of failCases) {
mockPlugin.meta.id = fail;
mockPlugin.id = fail;
// @ts-ignore - type mismatch error. fix after refactor

@@ -37,17 +35,15 @@ expect(Value.Check(Plugin, mockPlugin)).toBe(false);

test("meta.id should be a valid inlang.config.setting key", () => {
const mockConfig = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["en", "de"],
modules: [],
settings: {},
};
const cases = ["plugin.namespace.helloWorld", "plugin.namespace.i18n"];
for (const _case of cases) {
const config = { ...mockConfig, settings: { [_case]: {} } };
const mergedSettings = { ...settings, [_case]: {} };
expect(Value.Check(ProjectSettings, mergedSettings)).toBe(true);
// @ts-ignore - type mismatch error. fix after refactor
expect(Value.Check(ProjectConfig, config)).toBe(true);
// @ts-ignore - type mismatch error. fix after refactor
expect(Value.Check(Plugin["properties"]["meta"]["properties"]["id"], _case)).toBe(true);
expect(Value.Check(Plugin["properties"]["id"], _case)).toBe(true);
}
});
});

@@ -15,3 +15,3 @@ import { InlangModule } from "@inlang/module";

const meta = [];
for (const module of args.config.modules) {
for (const module of args.settings.modules) {
/**

@@ -29,4 +29,4 @@ * -------------- BEGIN SETUP --------------

}
// -- MODULE DOES NOT EXPORT PLUGINS OR LINT RULES --
if (importedModule.data?.default?.meta?.id === undefined) {
// -- MODULE DOES NOT EXPORT ANYTHING --
if (importedModule.data?.default === undefined) {
moduleErrors.push(new ModuleHasNoExportsError(`Module "${module}" has no exports.`, {

@@ -39,4 +39,4 @@ module: module,

if (isValidModule === false) {
const errors = [...ModuleCompiler.Errors(importedModule.data)];
moduleErrors.push(new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors, {
const errors = [...ModuleCompiler.Errors(importedModule.data)].map((e) => `${e.path} ${e.message}`);
moduleErrors.push(new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors.join("\n"), {
module: module,

@@ -48,8 +48,8 @@ }));

module: module,
id: importedModule.data.default.meta.id,
id: importedModule.data.default.id,
});
if (importedModule.data.default.meta.id.startsWith("plugin.")) {
if (importedModule.data.default.id.startsWith("plugin.")) {
allPlugins.push(importedModule.data.default);
}
else if (importedModule.data.default.meta.id.startsWith("messageLintRule.")) {
else if (importedModule.data.default.id.startsWith("messageLintRule.")) {
allMessageLintRules.push(importedModule.data.default);

@@ -63,3 +63,3 @@ }

plugins: allPlugins,
settings: args.config.settings,
settings: args.settings,
nodeishFs: args.nodeishFs,

@@ -66,0 +66,0 @@ });

@@ -5,14 +5,13 @@ import { expect, it } from "vitest";

it("should return an error if a plugin cannot be imported", async () => {
const config = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
};
const resolved = await resolveModules({
config,
settings,
nodeishFs: {},
_import: () => {
throw new ModuleImportError("Could not import", {
module: config.modules[0],
module: settings.modules[0],
cause: new Error("Could not import"),

@@ -27,7 +26,5 @@ });

const mockPlugin = {
meta: {
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
},
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
loadMessages: () => undefined,

@@ -42,14 +39,11 @@ saveMessages: () => undefined,

const mockMessageLintRule = {
meta: {
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
},
message: () => undefined,
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
run: () => undefined,
};
const config = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["lint-rule.js", "plugin.js"],
settings: {},
};

@@ -69,22 +63,21 @@ const _import = async (name) => {

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} });
const resolved = await resolveModules({ settings, _import, nodeishFs: {} });
// Assert results
expect(resolved.errors).toHaveLength(0);
// Check for the meta data of the plugin
expect(resolved.plugins.some((module) => module.meta.id === mockPlugin.meta.id)).toBeDefined();
expect(resolved.plugins.some((module) => module.id === mockPlugin.id)).toBeDefined();
// Check for the app specific api
expect(resolved.resolvedPluginApi["customApi"]?.["app.inlang.ideExtension"]).toBeDefined();
// Check for the lint rule
expect(resolved.messageLintRules[0]?.meta.id).toBe(mockMessageLintRule.meta.id);
expect(resolved.messageLintRules[0]?.id).toBe(mockMessageLintRule.id);
});
it("should return an error if a module cannot be imported", async () => {
const config = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
};
const _import = async () => {
throw new ModuleImportError("Could not import", {
module: config.modules[0],
module: settings.modules[0],
cause: new Error(),

@@ -94,18 +87,15 @@ });

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} });
const resolved = await resolveModules({ settings, _import, nodeishFs: {} });
// Assert results
expect(resolved.errors[0]).toBeInstanceOf(ModuleImportError);
});
it("should return an error if a module does not export any plugins or lint rules", async () => {
const config = {
it("should return an error if a module does not export anything", async () => {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
};
const _import = async () => ({
default: {},
});
const _import = async () => ({});
// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} });
const resolved = await resolveModules({ settings, _import, nodeishFs: {} });
// Assert results

@@ -115,18 +105,15 @@ expect(resolved.errors[0]).toBeInstanceOf(ModuleHasNoExportsError);

it("should return an error if a module exports an invalid plugin or lint rule", async () => {
const config = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
};
const _import = async () => ({
// @ts-expect-error - invalid meta of a plugin
default: {
// @ts-expect-error - invalid meta of a plugin
meta: {
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
},
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
},
});
const resolved = await resolveModules({ config, _import, nodeishFs: {} });
const resolved = await resolveModules({ settings, _import, nodeishFs: {} });
expect(resolved.errors[0]).toBeInstanceOf(ModuleExportIsInvalidError);

@@ -136,7 +123,6 @@ });

const errorMessage = "Unhandled error during plugin resolution";
const config = {
const settings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
};

@@ -147,5 +133,5 @@ const _import = async () => {

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} });
const resolved = await resolveModules({ settings, _import, nodeishFs: {} });
// Assert results
expect(resolved.errors[0]).toBeInstanceOf(ModuleError);
});

@@ -1,2 +0,2 @@

import type { ProjectConfig } from "@inlang/project-config";
import type { ProjectSettings } from "@inlang/project-settings";
import type { MessageLintRule } from "@inlang/message-lint-rule";

@@ -14,3 +14,3 @@ import type { Plugin } from "@inlang/plugin";

export type ResolveModuleFunction = (args: {
config: ProjectConfig;
settings: ProjectSettings;
nodeishFs: NodeishFilesystemSubset;

@@ -38,3 +38,3 @@ _import?: ImportFunction;

*/
id: Plugin["meta"]["id"] | MessageLintRule["meta"]["id"];
id: Plugin["id"] | MessageLintRule["id"];
}>;

@@ -41,0 +41,0 @@ /**

@@ -6,4 +6,4 @@ export * from "@inlang/json-types";

export * from "@inlang/plugin";
export * from "@inlang/project-config";
export * from "@inlang/project-settings";
export * from "@inlang/translatable";
//# sourceMappingURL=versionedInterfaces.d.ts.map

@@ -7,3 +7,3 @@ // Re-exporting for easier importing

export * from "@inlang/plugin";
export * from "@inlang/project-config";
export * from "@inlang/project-settings";
export * from "@inlang/translatable";
{
"name": "@inlang/sdk",
"type": "module",
"version": "0.4.0",
"version": "0.5.0",
"license": "Apache-2.0",

@@ -38,3 +38,3 @@ "publishConfig": {

"@inlang/plugin": "*",
"@inlang/project-config": "*",
"@inlang/project-settings": "*",
"@inlang/result": "*",

@@ -41,0 +41,0 @@ "@lix-js/fs": "*",

@@ -6,7 +6,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

import { solidAdapter } from "./solidAdapter.js"
import { openInlangProject } from "../openInlangProject.js"
import { loadProject } from "../loadProject.js"
import { createNodeishMemoryFs } from "@lix-js/fs"
import type {
Message,
ProjectConfig,
ProjectSettings,
Plugin,

@@ -19,23 +19,19 @@ MessageLintRule,

const config: ProjectConfig = {
const config: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: ["plugin.js", "plugin2.js"],
settings: {
"project.messageLintRuleLevels": {
"messageLintRule.inlang.missingTranslation": "error",
},
"plugin.inlang.i18next": {
pathPattern: "./examples/example01/{languageTag}.json",
variableReferencePattern: ["{", "}"],
},
messageLintRuleLevels: {
"messageLintRule.project.missingTranslation": "error",
},
"plugin.project.i18next": {
pathPattern: "./examples/example01/{languageTag}.json",
variableReferencePattern: ["{", "}"],
},
}
const mockPlugin: Plugin = {
meta: {
id: "plugin.inlang.i18next",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
},
id: "plugin.project.i18next",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
loadMessages: () => exampleMessages,

@@ -82,8 +78,6 @@ saveMessages: () => undefined,

const mockLintRule: MessageLintRule = {
meta: {
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
},
message: () => undefined,
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
run: () => undefined,
}

@@ -101,5 +95,5 @@

await fs.writeFile("./project.inlang.json", JSON.stringify(config))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -113,3 +107,3 @@ _import: $import,

createEffect(() => {
inlang.config()
project.settings()
counter += 1

@@ -119,9 +113,9 @@ })

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const newConfig = { ...inlang.config()!, languageTags: ["en", "de"] }
const newConfig = { ...project.settings()!, languageTags: ["en", "de"] }
inlang.setConfig(newConfig)
project.setSettings(newConfig)
// TODO: how can we await `setConfig` correctly
await new Promise((resolve) => setTimeout(resolve, 0))
expect(counter).toBe(2) // 2 times because effect creation + set
expect(inlang.config()).toStrictEqual(newConfig)
expect(project.settings()).toStrictEqual(newConfig)
})

@@ -134,5 +128,5 @@ })

await fs.writeFile("./project.inlang.json", JSON.stringify(config))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -147,3 +141,3 @@ _import: $import,

createEffect(() => {
inlang.installed.plugins()
project.installed.plugins()
counterPlugins += 1

@@ -153,7 +147,7 @@ })

createEffect(() => {
inlang.installed.messageLintRules()
project.installed.messageLintRules()
counterLint += 1
})
inlang.setConfig({ ...inlang.config()!, languageTags: ["en", "fr"] })
project.setSettings({ ...project.settings()!, languageTags: ["en", "fr"] })

@@ -171,15 +165,13 @@ // TODO: how can we await `setConfig` correctly

const fs = createNodeishMemoryFs()
const mockConfig: ProjectConfig = {
const mockConfig: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["en", "de"],
modules: ["./plugin-a.js"],
settings: {
"library.inlang.paraglideJsSveltekit": {
languageNegotiation: {
strategies: [
{
type: "localStorage",
} as any,
],
},
"library.project.paraglideJsSveltekit": {
languageNegotiation: {
strategies: [
{
type: "localStorage",
} as any,
],
},

@@ -189,11 +181,9 @@ },

const mockPlugin: Plugin = {
meta: {
id: "plugin.mock.id",
displayName: {
en: "hello",
},
description: {
en: "wo",
},
id: "plugin.mock.id",
displayName: {
en: "hello",
},
description: {
en: "wo",
},
loadMessages: ({ languageTags }) => (languageTags.length ? exampleMessages : []),

@@ -206,5 +196,5 @@ saveMessages: () => undefined,

await fs.writeFile("./project.inlang.json", JSON.stringify(mockConfig))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -218,9 +208,9 @@ _import: mockImport,

createEffect(() => {
inlang.query.messages.getAll()
project.query.messages.getAll()
counter += 1
})
expect(Object.values(inlang.query.messages.getAll()).length).toBe(2)
expect(Object.values(project.query.messages.getAll()).length).toBe(2)
inlang.setConfig({ ...inlang.config()!, languageTags: [] })
project.setSettings({ ...project.settings()!, languageTags: [] })

@@ -231,3 +221,3 @@ // TODO: how can we await `setConfig` correctly

expect(counter).toBe(2) // 2 times because effect creation + set
expect(Object.values(inlang.query.messages.getAll()).length).toBe(0)
expect(Object.values(project.query.messages.getAll()).length).toBe(0)
})

@@ -238,5 +228,5 @@

await fs.writeFile("./project.inlang.json", JSON.stringify(config))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./project.inlang.json",
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.inlang.json",
nodeishFs: fs,

@@ -250,7 +240,7 @@ _import: $import,

createEffect(() => {
inlang.query.messages.getAll()
project.query.messages.getAll()
counter += 1
})
const messagesBefore = inlang.query.messages.getAll
const messagesBefore = project.query.messages.getAll
expect(Object.values(messagesBefore()).length).toBe(2)

@@ -264,3 +254,3 @@ expect(

inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -286,3 +276,3 @@ // TODO: use `createMessage` utility

expect(counter).toBe(2) // 2 times because effect creation + set
const messagesAfter = inlang.query.messages.getAll
const messagesAfter = project.query.messages.getAll
expect(Object.values(messagesAfter()).length).toBe(2)

@@ -302,6 +292,6 @@ expect(

const fs = createNodeishMemoryFs()
await fs.writeFile("./inlang.config.json", JSON.stringify(config))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./inlang.config.json",
await fs.writeFile("./project.config.json", JSON.stringify(config))
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.config.json",
nodeishFs: fs,

@@ -315,19 +305,19 @@ _import: $import,

createEffect(() => {
inlang.query.messageLintReports.getAll()
project.query.messageLintReports.getAll()
counter += 1
})
const newConfig = { ...inlang.config()!, languageTags: ["en", "de"] }
inlang.setConfig(newConfig)
const newConfig = { ...project.settings()!, languageTags: ["en", "de"] }
project.setSettings(newConfig)
expect(counter).toBe(1)
expect(inlang.query.messageLintReports.getAll()).toEqual([])
expect(project.query.messageLintReports.getAll()).toEqual([])
await new Promise((resolve) => setTimeout(resolve, 510))
const newConfig2 = { ...inlang.config()!, languageTags: ["en", "de", "fr"] }
inlang.setConfig(newConfig2)
const newConfig2 = { ...project.settings()!, languageTags: ["en", "de", "fr"] }
project.setSettings(newConfig2)
expect(counter).toBe(9)
expect(inlang.query.messageLintReports.getAll()).toEqual([])
expect(project.query.messageLintReports.getAll()).toEqual([])
})

@@ -342,6 +332,6 @@ })

const fs = createNodeishMemoryFs()
await fs.writeFile("./inlang.config.json", JSON.stringify(config))
const inlang = solidAdapter(
await openInlangProject({
projectFilePath: "./inlang.config.json",
await fs.writeFile("./project.config.json", JSON.stringify(config))
const project = solidAdapter(
await loadProject({
settingsFilePath: "./project.config.json",
nodeishFs: fs,

@@ -355,7 +345,7 @@ _import: $import,

createEffect(() => {
inlang.query.messageLintReports.getAll()
project.query.messageLintReports.getAll()
counter += 1
})
inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -369,7 +359,7 @@ data: {

expect(counter).toBe(1)
expect(inlang.query.messageLintReports.getAll()).toEqual([])
expect(project.query.messageLintReports.getAll()).toEqual([])
await new Promise((resolve) => setTimeout(resolve, 510))
inlang.query.messages.update({
project.query.messages.update({
where: { id: "a" },

@@ -383,5 +373,5 @@ data: {

expect(counter).toBe(6)
expect(inlang.query.messageLintReports.getAll()).toEqual([])
expect(project.query.messageLintReports.getAll()).toEqual([])
})
})
})

@@ -17,3 +17,3 @@ import type { InlangProject, MessageLintReportsQueryApi } from "../api.js"

customApi: convert(project.customApi),
config: convert(project.config),
settings: convert(project.settings),
errors: convert(project.errors),

@@ -24,3 +24,3 @@ installed: {

},
setConfig: project.setConfig,
setSettings: project.setSettings,
query: {

@@ -56,4 +56,4 @@ messages: {

errors: () => ReturnType<InlangProject["errors"]>
config: () => ReturnType<InlangProject["config"]>
setConfig: InlangProject["setConfig"]
settings: () => ReturnType<InlangProject["settings"]>
setSettings: InlangProject["setSettings"]
query: {

@@ -60,0 +60,0 @@ messages: {

@@ -9,3 +9,3 @@ import type { Result } from "@inlang/result"

Plugin,
ProjectConfig,
ProjectSettings,
MessageLintReport,

@@ -16,3 +16,5 @@ } from "./versionedInterfaces.js"

export type InstalledPlugin = {
meta: Plugin["meta"]
id: Plugin["id"]
displayName: Plugin["displayName"]
description: Plugin["description"]
/**

@@ -26,3 +28,5 @@ * The module which the plugin is installed from.

export type InstalledMessageLintRule = {
meta: MessageLintRule["meta"]
id: MessageLintRule["id"]
displayName: MessageLintRule["displayName"]
description: MessageLintRule["description"]
/**

@@ -32,3 +36,3 @@ * The module which the lint rule is installed from.

module: string
lintLevel: MessageLintLevel
level: MessageLintLevel
}

@@ -45,4 +49,4 @@

customApi: Subscribable<ResolvedPluginApi["customApi"]>
config: Subscribable<ProjectConfig | undefined>
setConfig: (config: ProjectConfig) => Result<void, RuntimeError.InvalidConfigError>
settings: Subscribable<ProjectSettings>
setSettings: (config: ProjectSettings) => Result<void, RuntimeError.ProjectSettingsInvalidError>
query: {

@@ -49,0 +53,0 @@ messages: MessageQueryApi

import { createEffect } from "./reactivity/solid.js"
import { createSubscribable } from "./openInlangProject.js"
import { createSubscribable } from "./loadProject.js"
import type { InlangProject, InstalledMessageLintRule, MessageLintReportsQueryApi } from "./api.js"
import type { ProjectConfig } from "@inlang/project-config"
import type { ProjectSettings } from "@inlang/project-settings"
import type { resolveModules } from "./resolve-modules/index.js"
import type {
JSONObject,
MessageLintReport,
MessageLintRule,
Message,
} from "./versionedInterfaces.js"
import type { MessageLintReport, Message } from "./versionedInterfaces.js"
import { lintSingleMessage } from "./lint/index.js"

@@ -20,3 +15,3 @@ import { ReactiveMap } from "./reactivity/map.js"

messages: () => Array<Message> | undefined,
config: () => ProjectConfig | undefined,
settings: () => ProjectSettings,
installedMessageLintRules: () => Array<InstalledMessageLintRule>,

@@ -29,10 +24,10 @@ resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined,

createEffect(() => {
const msgs = messages()
const conf = config()
const modules = resolvedModules()
const _messages = messages()
const _settings = settings()
if (msgs && conf && modules) {
if (_messages && _settings && modules) {
// console.log("new calculation")
// index.clear()
for (const message of msgs) {
for (const message of _messages) {
// TODO: only lint changed messages and update arrays selectively

@@ -42,9 +37,9 @@

rules: modules.messageLintRules,
ruleSettings: conf.settings as Record<MessageLintRule["meta"]["id"], JSONObject>,
ruleLevels: Object.fromEntries(
installedMessageLintRules().map((rule) => [rule.meta.id, rule.lintLevel]),
),
sourceLanguageTag: conf.sourceLanguageTag,
languageTags: conf.languageTags,
messages: msgs,
settings: {
..._settings,
messageLintRuleLevels: Object.fromEntries(
installedMessageLintRules().map((rule) => [rule.id, rule.level]),
),
},
messages: _messages,
message: message,

@@ -51,0 +46,0 @@ }).then((report) => {

import type { Message } from "@inlang/message"
import { ReactiveMap } from "./reactivity/map.js"
import { createEffect } from "./reactivity/solid.js"
import { createSubscribable } from "./openInlangProject.js"
import { createSubscribable } from "./loadProject.js"
import type { InlangProject, MessageQueryApi } from "./api.js"

@@ -6,0 +6,0 @@

@@ -1,19 +0,19 @@

export class InvalidConfigError extends Error {
export class ProjectSettingsInvalidError extends Error {
constructor(message: string, options: ErrorOptions) {
super(message, options)
this.name = "InvalidConfigError"
this.name = "ProjectSettingsInvalidError"
}
}
export class ProjectFileJSONSyntaxError extends Error {
export class ProjectSettingsFileJSONSyntaxError extends Error {
constructor(message: string, options: ErrorOptions) {
super(message, options)
this.name = "ProjectFileJSONSyntaxError"
this.name = "ProjectSettingsFileJSONSyntaxError"
}
}
export class ProjectFilePathNotFoundError extends Error {
export class ProjectSettingsFileNotFoundError extends Error {
constructor(message: string, options: ErrorOptions) {
super(message, options)
this.name = "ProjectFilePathNotFoundError"
this.name = "ProjectSettingsFileNotFoundError"
}

@@ -20,0 +20,0 @@ }

@@ -15,9 +15,9 @@ /**

export { type ImportFunction, createImport } from "./resolve-modules/index.js"
export { openInlangProject } from "./openInlangProject.js"
export { loadProject } from "./loadProject.js"
export { solidAdapter, type InlangProjectWithSolidAdapter } from "./adapter/solidAdapter.js"
export { createMessagesQuery } from "./createMessagesQuery.js"
export {
ProjectFilePathNotFoundError,
ProjectFileJSONSyntaxError,
InvalidConfigError,
ProjectSettingsFileJSONSyntaxError,
ProjectSettingsFileNotFoundError,
ProjectSettingsInvalidError,
PluginLoadMessagesError,

@@ -24,0 +24,0 @@ PluginSaveMessagesError,

@@ -7,17 +7,13 @@ import { beforeEach, describe, expect, test, vi } from "vitest"

const lintRule1 = {
meta: {
id: "messageLintRule.x.1",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.x.1",
displayName: { en: "" },
description: { en: "" },
run: vi.fn(),
} satisfies MessageLintRule
const lintRule2 = {
meta: {
id: "messageLintRule.x.2",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.x.2",
displayName: { en: "" },
description: { en: "" },
run: vi.fn(),
} satisfies MessageLintRule

@@ -38,3 +34,3 @@

let called = 0
lintRule2.message.mockImplementation(async () => {
lintRule2.run.mockImplementation(async () => {
await new Promise((resolve) => setTimeout(resolve, 0))

@@ -45,9 +41,11 @@ called++

await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: [],
messages,

@@ -57,3 +55,3 @@ rules: [lintRule1, lintRule2],

expect(lintRule1.message).toHaveBeenCalledTimes(3)
expect(lintRule1.run).toHaveBeenCalledTimes(3)
expect(called).toBe(3)

@@ -65,3 +63,3 @@ })

lintRule1.message.mockImplementation(async ({ message }) => {
lintRule1.run.mockImplementation(async ({ message }) => {
fn("r1", "before", message.id)

@@ -71,3 +69,3 @@ await new Promise((resolve) => setTimeout(resolve, 0))

})
lintRule2.message.mockImplementation(async ({ message }) => {
lintRule2.run.mockImplementation(async ({ message }) => {
fn("r2", "before", message.id)

@@ -79,10 +77,12 @@ await new Promise((resolve) => setTimeout(resolve, 0))

await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
rules: [lintRule1, lintRule2],
sourceLanguageTag: "en",
languageTags: [],
messages,

@@ -107,6 +107,6 @@ })

test("it should not abort the linting process when errors occur", async () => {
lintRule1.message.mockImplementation(({ report }) => {
lintRule1.run.mockImplementation(({ report }) => {
report({} as MessageLintReport)
})
lintRule2.message.mockImplementation(() => {
lintRule2.run.mockImplementation(() => {
throw new Error("error")

@@ -116,9 +116,11 @@ })

const { data, errors } = await lintMessages({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: [],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: [],
messages,

@@ -125,0 +127,0 @@ rules: [lintRule1, lintRule2],

import type { Message } from "@inlang/message"
import { lintSingleMessage } from "./lintSingleMessage.js"
import type { MessagedLintRuleThrowedError } from "./errors.js"
import type { LanguageTag } from "@inlang/language-tag"
import type { JSONObject } from "@inlang/json-types"
import type {
MessageLintLevel,
MessageLintReport,
MessageLintRule,
} from "@inlang/message-lint-rule"
import type { MessageLintReport, MessageLintRule } from "@inlang/message-lint-rule"
import type { ProjectSettings } from "@inlang/project-settings"
export const lintMessages = async (args: {
sourceLanguageTag: LanguageTag
languageTags: LanguageTag[]
ruleSettings: Record<MessageLintRule["meta"]["id"], JSONObject>
ruleLevels: Record<MessageLintRule["meta"]["id"], MessageLintLevel>
settings: ProjectSettings & Required<Pick<ProjectSettings, "messageLintRuleLevels">>
rules: MessageLintRule[]

@@ -18,0 +10,0 @@ messages: Message[]

@@ -8,17 +8,13 @@ import { beforeEach, describe, expect, test, vi } from "vitest"

const lintRule1 = {
meta: {
id: "messageLintRule.r.1",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.r.1",
displayName: "",
description: "",
run: vi.fn(),
} satisfies MessageLintRule
const lintRule2 = {
meta: {
id: "messageLintRule.r.2",
displayName: { en: "" },
description: { en: "" },
},
message: vi.fn(),
id: "messageLintRule.r.2",
displayName: "",
description: "",
run: vi.fn(),
} satisfies MessageLintRule

@@ -39,10 +35,12 @@

test("it should throw if a lint level is not provided for a given lint rule", async () => {
lintRule1.message.mockImplementation(({ report }) => report({} as MessageLintReport))
lintRule1.run.mockImplementation(({ report }) => report({} as MessageLintReport))
const result = await tryCatch(() =>
lintSingleMessage({
ruleLevels: {},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
messageLintRuleLevels: {},
modules: [],
},
messages,

@@ -58,11 +56,13 @@ message: message1,

test("it should override the default lint level", async () => {
lintRule1.message.mockImplementation(({ report }) => report({} as MessageLintReport))
lintRule1.run.mockImplementation(({ report }) => report({} as MessageLintReport))
const reports = await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "error",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "error",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -76,16 +76,16 @@ message: message1,

test("it should pass the correct settings", async () => {
const settings = {}
const settings = {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id as any]: "warning",
},
}
const fn = vi.fn()
lintRule1.message.mockImplementation(({ settings }) => fn(settings))
lintRule1.run.mockImplementation(({ settings }) => fn(settings))
await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
},
ruleSettings: {
[lintRule1.meta.id]: settings,
},
sourceLanguageTag: "en",
languageTags: ["en"],
settings,
messages,

@@ -103,6 +103,6 @@ message: message1,

let m2Called = false
lintRule1.message.mockImplementation(() => {
lintRule1.run.mockImplementation(() => {
m1Called = true
})
lintRule2.message.mockImplementation(async () => {
lintRule2.run.mockImplementation(async () => {
await new Promise((resolve) => setTimeout(resolve, 0))

@@ -113,9 +113,11 @@ m2Called = true

await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -133,21 +135,23 @@ message: message1,

lintRule1.message.mockImplementation(async () => {
fn(lintRule1.meta.id, "before")
lintRule1.run.mockImplementation(async () => {
fn(lintRule1.id, "before")
await new Promise((resolve) => setTimeout(resolve, 0))
fn(lintRule1.meta.id, "after")
fn(lintRule1.id, "after")
})
lintRule2.message.mockImplementation(async () => {
fn(lintRule2.meta.id, "before")
lintRule2.run.mockImplementation(async () => {
fn(lintRule2.id, "before")
await new Promise((resolve) => setTimeout(resolve, 0))
fn(lintRule2.meta.id, "after")
fn(lintRule2.id, "after")
})
await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -159,14 +163,14 @@ message: message1,

expect(fn).toHaveBeenCalledTimes(4)
expect(fn).toHaveBeenNthCalledWith(1, lintRule1.meta.id, "before")
expect(fn).toHaveBeenNthCalledWith(2, lintRule2.meta.id, "before")
expect(fn).toHaveBeenNthCalledWith(3, lintRule1.meta.id, "after")
expect(fn).toHaveBeenNthCalledWith(4, lintRule2.meta.id, "after")
expect(fn).toHaveBeenNthCalledWith(1, lintRule1.id, "before")
expect(fn).toHaveBeenNthCalledWith(2, lintRule2.id, "before")
expect(fn).toHaveBeenNthCalledWith(3, lintRule1.id, "after")
expect(fn).toHaveBeenNthCalledWith(4, lintRule2.id, "after")
})
test("it should not abort the linting process when errors occur", async () => {
lintRule1.message.mockImplementation(() => {
lintRule1.run.mockImplementation(() => {
throw new Error("error")
})
lintRule2.message.mockImplementation(({ report }) => {
lintRule2.run.mockImplementation(({ report }) => {
report({} as MessageLintReport)

@@ -176,9 +180,11 @@ })

const result = await lintSingleMessage({
ruleLevels: {
[lintRule1.meta.id]: "warning",
[lintRule2.meta.id]: "warning",
settings: {
sourceLanguageTag: "en",
languageTags: ["en"],
modules: [],
messageLintRuleLevels: {
[lintRule1.id]: "warning",
[lintRule2.id]: "warning",
},
},
ruleSettings: {},
sourceLanguageTag: "en",
languageTags: ["en"],
messages,

@@ -185,0 +191,0 @@ message: message1,

@@ -1,10 +0,5 @@

import type {
MessageLintLevel,
MessageLintRule,
MessageLintReport,
} from "@inlang/message-lint-rule"
import type { MessageLintRule, MessageLintReport } from "@inlang/message-lint-rule"
import type { Message } from "@inlang/message"
import { MessagedLintRuleThrowedError } from "./errors.js"
import type { LanguageTag } from "@inlang/language-tag"
import type { JSONObject } from "@inlang/json-types"
import type { ProjectSettings } from "@inlang/project-settings"

@@ -17,6 +12,3 @@ /**

export const lintSingleMessage = async (args: {
sourceLanguageTag: LanguageTag
languageTags: LanguageTag[]
ruleSettings: Record<MessageLintRule["meta"]["id"], JSONObject>
ruleLevels: Record<MessageLintRule["meta"]["id"], MessageLintLevel>
settings: ProjectSettings & Required<Pick<ProjectSettings, "messageLintRuleLevels">>
rules: MessageLintRule[]

@@ -30,19 +22,19 @@ messages: Message[]

const promises = args.rules.map(async (rule) => {
const ruleId = rule.meta.id
const settings = args.ruleSettings?.[ruleId] ?? {}
const level = args.ruleLevels?.[ruleId]
const level = args.settings.messageLintRuleLevels?.[rule.id]
if (level === undefined) {
throw Error("No lint level provided for lint rule: " + ruleId)
throw Error("No lint level provided for lint rule: " + rule.id)
}
try {
await rule.message({
...args,
settings,
await rule.run({
message: args.message,
settings: args.settings,
report: (reportArgs) => {
reports.push({
ruleId,
ruleId: rule.id,
level,
...reportArgs,
messageId: reportArgs.messageId,
languageTag: reportArgs.languageTag,
body: reportArgs.body,
})

@@ -54,3 +46,3 @@ },

new MessagedLintRuleThrowedError(
`Lint rule '${ruleId}' throwed while linting message "${args.message.id}".`,
`Lint rule '${rule.id}' throwed while linting message "${args.message.id}".`,
{ cause: error },

@@ -57,0 +49,0 @@ ),

@@ -1,2 +0,2 @@

import { ProjectConfig } from "./versionedInterfaces.js"
import { ProjectSettings } from "./versionedInterfaces.js"
import type { Result } from "@inlang/result"

@@ -16,5 +16,7 @@ import { TypeCompiler } from "@sinclair/typebox/compiler"

// @ts-ignore - fix after refactor
const ConfigCompiler = TypeCompiler.Compile(ProjectConfig)
const ConfigCompiler = TypeCompiler.Compile(ProjectSettings)
export const parseConfig = (config: ProjectConfig): Result<ProjectConfig, ParseConfigError> => {
export const parseSettings = (
config: ProjectSettings,
): Result<ProjectSettings, ParseConfigError> => {
if (ConfigCompiler.Check(config)) {

@@ -21,0 +23,0 @@ return {

@@ -13,3 +13,3 @@ import { Value } from "@sinclair/typebox/value"

result.errors.push(
new MessageLintRuleIsInvalidError(`Couldn't parse lint rule "${rule.meta.id}"`, {
new MessageLintRuleIsInvalidError(`Couldn't parse lint rule "${rule.id}"`, {
module: "not implemented",

@@ -16,0 +16,0 @@ }),

import type { Plugin } from "@inlang/plugin"
type PluginErrorOptions = {
plugin: Plugin["meta"]["id"]
plugin: Plugin["id"]
} & Partial<Error>

@@ -6,0 +6,0 @@

@@ -18,13 +18,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

const mockPlugin: Plugin = {
meta: {
// @ts-expect-error - invalid id
id: "no-namespace",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
// @ts-expect-error - invalid id
id: "no-namespace",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: () => undefined as any,
saveMessages: () => undefined as any,
addCustomApi() {
return {}
},
}

@@ -43,7 +38,5 @@

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.undefinedApi",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.undefinedApi",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
// @ts-expect-error the key is not available in type

@@ -68,7 +61,5 @@ nonExistentKey: {

const mockPlugin: Plugin = {
meta: {
id: "plugin.inlang.notWhitelisted",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.inlang.notWhitelisted",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: () => undefined as any,

@@ -90,7 +81,5 @@ }

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => [{ id: "test", expressions: [], selectors: [], variants: [] }],

@@ -115,15 +104,11 @@ }

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined as any,
}
const mockPlugin2: Plugin = {
meta: {
id: "plugin.namepsace.loadMessagesSecond",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesSecond",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined as any,

@@ -143,7 +128,5 @@ }

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
saveMessages: async () => undefined as any,

@@ -166,7 +149,5 @@ }

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined as any,

@@ -187,15 +168,11 @@ saveMessages: async () => undefined as any,

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.saveMessages",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.saveMessages",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
saveMessages: async () => undefined as any,
}
const mockPlugin2: Plugin = {
meta: {
id: "plugin.namepsace.saveMessages2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.saveMessages2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },

@@ -216,7 +193,5 @@ saveMessages: async () => undefined as any,

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.loadMessagesFirst",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
loadMessages: async () => undefined as any,

@@ -238,7 +213,5 @@ }

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.detectedLanguageTags",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.detectedLanguageTags",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
detectedLanguageTags: async () => ["de", "en"],

@@ -250,7 +223,5 @@ addCustomApi: () => {

const mockPlugin2: Plugin = {
meta: {
id: "plugin.namepsace.detectedLanguageTags2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.detectedLanguageTags2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => {

@@ -275,7 +246,5 @@ return {}

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },

@@ -300,7 +269,5 @@ addCustomApi: () => ({

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -316,7 +283,5 @@ "my-app-1": {

const mockPlugin2: Plugin = {
meta: {
id: "plugin.namespace.placeholder2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder2",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },

@@ -343,7 +308,5 @@ addCustomApi: () => ({

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namespace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
// @ts-expect-error - invalid return type

@@ -364,7 +327,5 @@ addCustomApi: () => undefined,

const mockPlugin: Plugin = {
meta: {
id: "plugin.namepsace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
},
id: "plugin.namepsace.placeholder",
description: { en: "My plugin description" },
displayName: { en: "My plugin" },
addCustomApi: () => ({

@@ -371,0 +332,0 @@ "app.inlang.placeholder": {

@@ -44,8 +44,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

// -- INVALID ID in META --
const hasInvalidId = errors.some((error) => error.path === "/meta/id")
const hasInvalidId = errors.some((error) => error.path === "/id")
if (hasInvalidId) {
result.errors.push(
new PluginHasInvalidIdError(
`Plugin ${plugin.meta.id} has an invalid id "${plugin.meta.id}". It must be kebap-case and contain a namespace like project.my-plugin.`,
{ plugin: plugin.meta.id },
`Plugin ${plugin.id} has an invalid id "${plugin.id}". It must be kebap-case and contain a namespace like project.my-plugin.`,
{ plugin: plugin.id },
),

@@ -56,8 +56,8 @@ )

// -- USES RESERVED NAMESPACE --
if (plugin.meta.id.includes("inlang") && !whitelistedPlugins.includes(plugin.meta.id)) {
if (plugin.id.includes("inlang") && !whitelistedPlugins.includes(plugin.id)) {
result.errors.push(
new PluginUsesReservedNamespaceError(
`Plugin ${plugin.meta.id} uses reserved namespace 'inlang'.`,
`Plugin ${plugin.id} uses reserved namespace 'inlang'.`,
{
plugin: plugin.meta.id,
plugin: plugin.id,
},

@@ -72,5 +72,5 @@ ),

new PluginHasInvalidSchemaError(
`Plugin ${plugin.meta.id} uses an invalid schema. Please check the documentation for the correct Plugin type.`,
`Plugin ${plugin.id} uses an invalid schema. Please check the documentation for the correct Plugin type.`,
{
plugin: plugin.meta.id,
plugin: plugin.id,
cause: errors,

@@ -86,4 +86,4 @@ },

new PluginLoadMessagesFunctionAlreadyDefinedError(
`Plugin ${plugin.meta.id} defines the loadMessages function, but it was already defined by another plugin.`,
{ plugin: plugin.meta.id },
`Plugin ${plugin.id} defines the loadMessages function, but it was already defined by another plugin.`,
{ plugin: plugin.id },
),

@@ -96,4 +96,4 @@ )

new PluginSaveMessagesFunctionAlreadyDefinedError(
`Plugin ${plugin.meta.id} defines the saveMessages function, but it was already defined by another plugin.`,
{ plugin: plugin.meta.id },
`Plugin ${plugin.id} defines the saveMessages function, but it was already defined by another plugin.`,
{ plugin: plugin.id },
),

@@ -108,3 +108,3 @@ )

plugin.addCustomApi!({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
}),

@@ -120,4 +120,4 @@ )

new PluginReturnedInvalidCustomApiError(
`Plugin ${plugin.meta.id} defines the addCustomApi function, but it does not return an object.`,
{ plugin: plugin.meta.id, cause: error },
`Plugin ${plugin.id} defines the addCustomApi function, but it does not return an object.`,
{ plugin: plugin.id, cause: error },
),

@@ -141,3 +141,3 @@ )

..._args,
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -151,3 +151,3 @@ })

..._args,
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -159,3 +159,3 @@ })

const detectedLangugeTags = await plugin.detectedLanguageTags!({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
nodeishFs: args.nodeishFs,

@@ -171,3 +171,3 @@ })

plugin.addCustomApi!({
settings: args.settings?.[plugin.meta.id] ?? {},
settings: args.settings?.[plugin.id] ?? {},
}),

@@ -174,0 +174,0 @@ )

@@ -1,2 +0,2 @@

import { ProjectConfig } from "@inlang/project-config"
import { ProjectSettings } from "@inlang/project-settings"
import { Value } from "@sinclair/typebox/value"

@@ -9,10 +9,8 @@ import { describe, test, expect } from "vitest"

test("meta.id should enforce plugin.namespace.* patterns", () => {
expectType<`plugin.${string}.${string}`>("" as Plugin["meta"]["id"])
expectType<`plugin.${string}.${string}`>("" as Plugin["id"])
const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.placeholder",
displayName: { en: "" },
description: { en: "" },
},
id: "plugin.namespace.placeholder",
displayName: { en: "" },
description: { en: "" },
}

@@ -28,3 +26,3 @@

for (const pass of passCases) {
mockPlugin.meta.id = pass as any
mockPlugin.id = pass as any

@@ -36,3 +34,3 @@ // @ts-ignore - type mismatch error. fix after refactor

for (const fail of failCases) {
mockPlugin.meta.id = fail as any
mockPlugin.id = fail as any
// @ts-ignore - type mismatch error. fix after refactor

@@ -44,7 +42,6 @@ expect(Value.Check(Plugin, mockPlugin)).toBe(false)

test("meta.id should be a valid inlang.config.setting key", () => {
const mockConfig: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["en", "de"],
modules: [],
settings: {},
}

@@ -54,9 +51,8 @@ const cases = ["plugin.namespace.helloWorld", "plugin.namespace.i18n"]

for (const _case of cases) {
const config = { ...mockConfig, settings: { [_case]: {} } }
const mergedSettings = { ...settings, [_case]: {} }
expect(Value.Check(ProjectSettings, mergedSettings)).toBe(true)
// @ts-ignore - type mismatch error. fix after refactor
expect(Value.Check(ProjectConfig, config)).toBe(true)
// @ts-ignore - type mismatch error. fix after refactor
expect(Value.Check(Plugin["properties"]["meta"]["properties"]["id"], _case)).toBe(true)
expect(Value.Check(Plugin["properties"]["id"], _case)).toBe(true)
}
})
})

@@ -31,3 +31,3 @@ import type { LanguageTag } from "@inlang/language-tag"

plugins: Array<Plugin>
settings: Record<Plugin["meta"]["id"], JSONObject>
settings: Record<Plugin["id"], JSONObject>
nodeishFs: NodeishFilesystemSubset

@@ -34,0 +34,0 @@ }) => Promise<{

@@ -12,19 +12,18 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */

import { resolveModules } from "./resolveModules.js"
import type { ProjectConfig } from "@inlang/project-config"
import type { ProjectSettings } from "@inlang/project-settings"
import type { InlangModule } from "@inlang/module"
it("should return an error if a plugin cannot be imported", async () => {
const config: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
}
const resolved = await resolveModules({
config,
settings,
nodeishFs: {} as any,
_import: () => {
throw new ModuleImportError("Could not import", {
module: config.modules[0]!,
module: settings.modules[0]!,
cause: new Error("Could not import"),

@@ -41,7 +40,5 @@ })

const mockPlugin: Plugin = {
meta: {
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
},
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
displayName: { en: "Mock Plugin" },
loadMessages: () => undefined as any,

@@ -57,15 +54,12 @@ saveMessages: () => undefined as any,

const mockMessageLintRule: MessageLintRule = {
meta: {
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
},
message: () => undefined,
id: "messageLintRule.namespace.mock",
description: { en: "Mock lint rule description" },
displayName: { en: "Mock Lint Rule" },
run: () => undefined,
}
const config: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["lint-rule.js", "plugin.js"],
settings: {},
}

@@ -86,3 +80,3 @@

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
const resolved = await resolveModules({ settings, _import, nodeishFs: {} as any })

@@ -92,15 +86,14 @@ // Assert results

// Check for the meta data of the plugin
expect(resolved.plugins.some((module) => module.meta.id === mockPlugin.meta.id)).toBeDefined()
expect(resolved.plugins.some((module) => module.id === mockPlugin.id)).toBeDefined()
// Check for the app specific api
expect(resolved.resolvedPluginApi["customApi"]?.["app.inlang.ideExtension"]).toBeDefined()
// Check for the lint rule
expect(resolved.messageLintRules[0]?.meta.id).toBe(mockMessageLintRule.meta.id)
expect(resolved.messageLintRules[0]?.id).toBe(mockMessageLintRule.id)
})
it("should return an error if a module cannot be imported", async () => {
const config: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
}

@@ -110,3 +103,3 @@

throw new ModuleImportError("Could not import", {
module: config.modules[0]!,
module: settings.modules[0]!,
cause: new Error(),

@@ -117,3 +110,3 @@ })

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
const resolved = await resolveModules({ settings, _import, nodeishFs: {} as any })

@@ -124,16 +117,13 @@ // Assert results

it("should return an error if a module does not export any plugins or lint rules", async () => {
const config: ProjectConfig = {
it("should return an error if a module does not export anything", async () => {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
}
const _import = async () => ({
default: {},
})
const _import = async () => ({})
// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
const resolved = await resolveModules({ settings, _import, nodeishFs: {} as any })

@@ -145,20 +135,17 @@ // Assert results

it("should return an error if a module exports an invalid plugin or lint rule", async () => {
const config: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
}
const _import = async () =>
({
// @ts-expect-error - invalid meta of a plugin
default: {
// @ts-expect-error - invalid meta of a plugin
meta: {
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
},
id: "plugin.namespace.mock",
description: { en: "Mock plugin description" },
},
} satisfies InlangModule)
const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
const resolved = await resolveModules({ settings, _import, nodeishFs: {} as any })
expect(resolved.errors[0]).toBeInstanceOf(ModuleExportIsInvalidError)

@@ -169,7 +156,6 @@ })

const errorMessage = "Unhandled error during plugin resolution"
const config: ProjectConfig = {
const settings: ProjectSettings = {
sourceLanguageTag: "en",
languageTags: ["de", "en"],
modules: ["https://myplugin.com/index.js"],
settings: {},
}

@@ -182,3 +168,3 @@

// Call the function
const resolved = await resolveModules({ config, _import, nodeishFs: {} as any })
const resolved = await resolveModules({ settings, _import, nodeishFs: {} as any })

@@ -185,0 +171,0 @@ // Assert results

@@ -28,3 +28,3 @@ import type { ResolveModuleFunction } from "./types.js"

for (const module of args.config.modules) {
for (const module of args.settings.modules) {
/**

@@ -47,4 +47,4 @@ * -------------- BEGIN SETUP --------------

// -- MODULE DOES NOT EXPORT PLUGINS OR LINT RULES --
if (importedModule.data?.default?.meta?.id === undefined) {
// -- MODULE DOES NOT EXPORT ANYTHING --
if (importedModule.data?.default === undefined) {
moduleErrors.push(

@@ -61,5 +61,7 @@ new ModuleHasNoExportsError(`Module "${module}" has no exports.`, {

if (isValidModule === false) {
const errors = [...ModuleCompiler.Errors(importedModule.data)]
const errors = [...ModuleCompiler.Errors(importedModule.data)].map(
(e) => `${e.path} ${e.message}`,
)
moduleErrors.push(
new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors, {
new ModuleExportIsInvalidError(`Module "${module}" is invalid: ` + errors.join("\n"), {
module: module,

@@ -73,8 +75,8 @@ }),

module: module,
id: importedModule.data.default.meta.id,
id: importedModule.data.default.id,
})
if (importedModule.data.default.meta.id.startsWith("plugin.")) {
if (importedModule.data.default.id.startsWith("plugin.")) {
allPlugins.push(importedModule.data.default as Plugin)
} else if (importedModule.data.default.meta.id.startsWith("messageLintRule.")) {
} else if (importedModule.data.default.id.startsWith("messageLintRule.")) {
allMessageLintRules.push(importedModule.data.default as MessageLintRule)

@@ -88,3 +90,3 @@ } else {

plugins: allPlugins,
settings: args.config.settings,
settings: args.settings,
nodeishFs: args.nodeishFs,

@@ -91,0 +93,0 @@ })

@@ -1,2 +0,2 @@

import type { ProjectConfig } from "@inlang/project-config"
import type { ProjectSettings } from "@inlang/project-settings"
import type { MessageLintRule } from "@inlang/message-lint-rule"

@@ -19,3 +19,3 @@ import type { Plugin } from "@inlang/plugin"

export type ResolveModuleFunction = (args: {
config: ProjectConfig
settings: ProjectSettings
nodeishFs: NodeishFilesystemSubset

@@ -43,3 +43,3 @@ _import?: ImportFunction

*/
id: Plugin["meta"]["id"] | MessageLintRule["meta"]["id"]
id: Plugin["id"] | MessageLintRule["id"]
}>

@@ -46,0 +46,0 @@ /**

@@ -8,3 +8,3 @@ // Re-exporting for easier importing

export * from "@inlang/plugin"
export * from "@inlang/project-config"
export * from "@inlang/project-settings"
export * from "@inlang/translatable"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc