mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 06:35:24 +00:00
feat: type the plugin host record, move plugin strings out of core and add lint checks
This commit is contained in:
1 parent
bba06f7ff6
commit
b8bbcd2196
626 files changed
+23884
-4590
No files matched your search
@@ -0,0 +1,127 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { createRequire } from "node:module";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { scan, keysInSource, hasKey } =
|
||||
require("./check-plugin-locales.cjs") as {
|
||||
scan: (options: {
|
||||
pluginsDir: string;
|
||||
coreLocale: string;
|
||||
}) => Record<string, Array<{ key: string; file: string; inCore: boolean }>>;
|
||||
keysInSource: (source: string) => string[];
|
||||
hasKey: (tree: unknown, key: string) => boolean;
|
||||
};
|
||||
|
||||
let dir: string | null = null;
|
||||
|
||||
afterEach(() => {
|
||||
if (dir) fs.rmSync(dir, { recursive: true, force: true });
|
||||
dir = null;
|
||||
});
|
||||
|
||||
function fixture(files: Record<string, unknown>) {
|
||||
dir = fs.mkdtempSync(path.join(os.tmpdir(), "plugin-locales-"));
|
||||
for (const [file, content] of Object.entries(files)) {
|
||||
const full = path.join(dir, file);
|
||||
fs.mkdirSync(path.dirname(full), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
full,
|
||||
typeof content === "string" ? content : JSON.stringify(content),
|
||||
);
|
||||
}
|
||||
return {
|
||||
pluginsDir: path.join(dir, "plugins"),
|
||||
coreLocale: path.join(dir, "core.json"),
|
||||
};
|
||||
}
|
||||
|
||||
describe("keysInSource", () => {
|
||||
it("reads t() calls, key props and run time prefixes, not comments", () => {
|
||||
const keys = keysInSource(
|
||||
[
|
||||
'const a = t("hosts.one");',
|
||||
"const b = i18next.t('docker:hosts.two', { x: 1 });",
|
||||
'app.registerTab("x", X, { titleKey: "nav.three" });',
|
||||
'<EmptyState messageKey="hosts.four" />',
|
||||
"t(`hosts.sharing.levels.${level}.label`);",
|
||||
"t(`hosts.${label}`);",
|
||||
'// t("hosts.commented")',
|
||||
'storageKey: "not.a.string.key"',
|
||||
].join("\n"),
|
||||
);
|
||||
expect(keys.sort()).toEqual(
|
||||
[
|
||||
"docker:hosts.two",
|
||||
"hosts.four",
|
||||
"hosts.one",
|
||||
"hosts.sharing.levels.*",
|
||||
"nav.three",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasKey", () => {
|
||||
it("finds nested keys, dotted names and plural forms", () => {
|
||||
const tree = {
|
||||
a: { b: "x" },
|
||||
permissions: { "services.use": { title: "t" } },
|
||||
items: { count_one: "1", count_other: "n" },
|
||||
};
|
||||
expect(hasKey(tree, "a.b")).toBe(true);
|
||||
expect(hasKey(tree, "a")).toBe(true);
|
||||
expect(hasKey(tree, "permissions.services.use.title")).toBe(true);
|
||||
expect(hasKey(tree, "items.count")).toBe(true);
|
||||
expect(hasKey(tree, "a.c")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("scan", () => {
|
||||
it("passes keys a plugin defines and the shared common ones", () => {
|
||||
const options = fixture({
|
||||
"core.json": { common: { save: "Save" }, hosts: { name: "Name" } },
|
||||
"plugins/demo/manifest.json": { id: "demo", titleKey: "title" },
|
||||
"plugins/demo/locales/en.json": { title: "Demo", hosts: { name: "N" } },
|
||||
"plugins/demo/src/frontend/index.tsx":
|
||||
't("common.save"); t("hosts.name"); t("title");',
|
||||
});
|
||||
expect(scan(options)).toEqual({});
|
||||
});
|
||||
|
||||
it("fails a key that only resolves through core's fallback", () => {
|
||||
const options = fixture({
|
||||
"core.json": { hosts: { name: "Name" } },
|
||||
"plugins/demo/manifest.json": { id: "demo" },
|
||||
"plugins/demo/locales/en.json": {},
|
||||
"plugins/demo/src/frontend/index.tsx": 't("hosts.name");',
|
||||
});
|
||||
expect(scan(options)).toEqual({
|
||||
demo: [
|
||||
{
|
||||
key: "hosts.name",
|
||||
file: expect.stringContaining("index.tsx"),
|
||||
inCore: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("checks a namespaced key against that plugin and a prefix as a subtree", () => {
|
||||
const options = fixture({
|
||||
"core.json": { hosts: { levels: { view: "View" } } },
|
||||
"plugins/demo/manifest.json": { id: "demo" },
|
||||
"plugins/demo/locales/en.json": {},
|
||||
"plugins/demo/src/frontend/index.tsx":
|
||||
't("other:known.key"); t("other:gone.key"); t(`hosts.levels.${l}`);',
|
||||
"plugins/other/manifest.json": { id: "other" },
|
||||
"plugins/other/locales/en.json": { known: { key: "k" } },
|
||||
});
|
||||
expect(scan(options).demo.map((entry) => entry.key)).toEqual([
|
||||
"hosts.levels.*",
|
||||
"other:gone.key",
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user