diff --git a/web/eslint-plugins/README.md b/web/eslint-plugins/README.md
index 5b7fde1..0499615 100644
--- a/web/eslint-plugins/README.md
+++ b/web/eslint-plugins/README.md
@@ -132,10 +132,11 @@ hct-авторство.
фактическому пути, поэтому относительным импортом правило не обойти.
- Старое: `routes/v1/**`, `lib/v1/**`.
-- Новое: `routes/**` без `v1/**`, `lib/components/**`, `lib/registry/**`,
- `lib/catalog.ts`, `lib/categories.ts`, `lib/tool-icons.ts`,
- `lib/registry-schema.ts`, `lib/registry-schema.test.ts`.
-- Общее (разрешено обоим): `core/`, `i18n/`, `theme`, `assets/`, корневой `lib`.
+- Новое: `routes/**` без `v1/**`; корневой `lib` целиком (`lib/**`) без
+ `lib/v1/**` и без общих пакетов.
+- Общее (разрешено обоим) — только служебные пакеты: `core/`, `theme`.
+ v1 не высовывается: из `lib/v1/**` наружу разрешены импорты лишь в
+ общие пакеты и в собственный форк i18n (`lib/v1/i18n/**`).
- Плагин **конфигурируем** (опции `old`/`new` + `root`/`alias` в
`eslint.config.js`): перенос старых файлов в папки `old/` — это правка
glob-паттернов в настройке, а не код правила.
diff --git a/web/eslint-plugins/__fixtures__/src/lib/v1/i18n/t.ts b/web/eslint-plugins/__fixtures__/src/lib/v1/i18n/t.ts
new file mode 100644
index 0000000..3b5add1
--- /dev/null
+++ b/web/eslint-plugins/__fixtures__/src/lib/v1/i18n/t.ts
@@ -0,0 +1 @@
+// Fixture stub for lint-rule tests: content is ignored, must exist for import resolution.
\ No newline at end of file
diff --git a/web/eslint-plugins/__tests__/no-mixed-imports.test.ts b/web/eslint-plugins/__tests__/no-mixed-imports.test.ts
index ac8be68..1dc5571 100644
--- a/web/eslint-plugins/__tests__/no-mixed-imports.test.ts
+++ b/web/eslint-plugins/__tests__/no-mixed-imports.test.ts
@@ -7,8 +7,8 @@
//
// Side map (mirrors the defaults in no-mixed-imports.js):
// old -> routes/v1/**, lib/v1/**
-// new -> routes/** (minus v1), lib/components/**, lib/registry/** + a few
-// shared-> everything else: core/, i18n/, theme, ...
+// new -> routes/** (minus v1), lib/** (minus lib/v1/** and shared)
+// shared-> everything else: core/, theme
import { describe, expect, it } from "vitest";
import noMixedImports from "../isolation/no-mixed-imports.js";
import { asRuleModule, verifyInFixtures, type FlatConfig } from "./helpers.js";
@@ -80,15 +80,27 @@ describe("isolation/no-mixed-imports", () => {
expectClean("lib/v1/old.ts", 'import { e } from "../core/errors";');
});
- it("allows old -> shared (i18n and theme via $lib)", () => {
- expectClean("routes/v1/+layout.svelte", 'import { t } from "$lib/i18n/t";');
+ it("flags old -> root i18n ($lib/i18n is the new branch now)", () => {
+ expectViolation(
+ "routes/v1/+layout.svelte",
+ 'import { t } from "$lib/i18n/t";',
+ "old",
+ "new",
+ );
+ });
+
+ it("allows old -> own i18n copy and theme via $lib", () => {
+ expectClean(
+ "routes/v1/+layout.svelte",
+ 'import { t } from "$lib/v1/i18n/t";',
+ );
expectClean(
"routes/v1/+layout.svelte",
'import { g } from "$lib/theme.svelte";',
);
});
- it("allows new -> shared and new -> new", () => {
+ it("allows new -> new (root i18n and components)", () => {
expectClean("routes/+page.svelte", 'import { t } from "$lib/i18n/t";');
expectClean(
"routes/+page.svelte",
@@ -96,11 +108,26 @@ describe("isolation/no-mixed-imports", () => {
);
});
- it("leaves shared files unrestricted in both directions", () => {
- expectClean("lib/i18n/t.ts", 'import { o } from "$lib/v1/old";');
+ it("flags new i18n importing old and leaves core shared", () => {
+ expectViolation(
+ "lib/i18n/t.ts",
+ 'import { o } from "$lib/v1/old";',
+ "new",
+ "old",
+ );
expectClean(
"lib/core/errors.ts",
'import { c } from "$lib/components/CheckerCanvas.svelte";',
);
});
+
+ it("keeps the old i18n copy on the old side", () => {
+ expectClean("lib/v1/i18n/t.ts", 'import { o } from "$lib/v1/old";');
+ expectViolation(
+ "lib/v1/i18n/t.ts",
+ 'import { c } from "$lib/components/CheckerCanvas.svelte";',
+ "old",
+ "new",
+ );
+ });
});
diff --git a/web/eslint-plugins/isolation/no-mixed-imports.js b/web/eslint-plugins/isolation/no-mixed-imports.js
index 13d55b9..d6e48ff 100644
--- a/web/eslint-plugins/isolation/no-mixed-imports.js
+++ b/web/eslint-plugins/isolation/no-mixed-imports.js
@@ -5,7 +5,7 @@
* `$lib/...` alias and relative `./` / `../` paths), classifies the source
* file and each import target by their actual location, and reports whenever an
* "old" file imports a "new" one or vice versa. Files that match neither side
- * are "shared" (core/, i18n/, theme, assets, tests, ...) and can import freely.
+ * are "shared" (core/, theme, assets, tests, ...) and can import freely.
*
* Options (object, all optional):
* - root: path from lint cwd to the source dir (default "src");
@@ -17,21 +17,19 @@
import fs from "node:fs";
import path from "node:path";
-// NEW declared as an allowlist: `lib/**` descends into v1/ and the shared dirs
-// (core/, i18n/, assets/, theme), so matching by prefix would misclassify them.
-// Negations (`!`) carve old paths out of `routes/**`.
+// NEW declared as an allowlist: `lib/**` descends into v1/ and the shared
+// packages (core/, theme), so negations carve them back out; `routes/**`
+// needs `!routes/v1/**` for the old branch. Last-match-wins
+// (see matchesGlobList).
const DEFAULT_OLD = ["routes/v1/**", "lib/v1/**"];
const DEFAULT_NEW = [
"routes/**",
"!routes/v1/**",
- "lib/components/**",
- "lib/registry/**",
- "lib/catalog.ts",
- "lib/categories.ts",
- "lib/tool-icons.ts",
- "lib/registry-schema.ts",
- "lib/registry-schema.test.ts",
+ "lib/**",
+ "!lib/v1/**",
+ "!lib/core/**",
+ "!lib/theme.svelte.ts",
];
/** Escape everything except glob metacharacters, then handle **, *, ?. */
diff --git a/web/eslint.config.js b/web/eslint.config.js
index 9b46c31..450c3c9 100644
--- a/web/eslint.config.js
+++ b/web/eslint.config.js
@@ -82,6 +82,7 @@ export default tseslint.config(
"eslint-plugins/__tests__/helpers.ts",
"eslint-plugins/__tests__/no-mixed-imports.test.ts",
"eslint-plugins/__fixtures__/src/lib/v1/old.ts",
+ "eslint-plugins/__fixtures__/src/lib/v1/i18n/t.ts",
"eslint-plugins/__fixtures__/src/lib/core/errors.ts",
"eslint-plugins/__fixtures__/src/lib/i18n/t.ts",
"eslint-plugins/__fixtures__/src/lib/theme.svelte.ts",
@@ -197,7 +198,7 @@ export default tseslint.config(
// Полная взаимная изоляция веток (см. plan-composite-params, Фаза 5).
// Кастомный плагин isolation/no-mixed-imports резолвит импорты по реальному
// пути (и $lib, и относительные) и ругается на old→new / new→old.
- // Общее (core/, i18n/, theme) разрешено обоим.
+ // Общее (core/, theme) разрешено обоим.
{
files: ["**/*.{ts,svelte}"],
plugins: {
diff --git a/web/src/lib/i18n/dict.ts b/web/src/lib/i18n/dict.ts
index dda97f3..382ddc3 100644
--- a/web/src/lib/i18n/dict.ts
+++ b/web/src/lib/i18n/dict.ts
@@ -1,4 +1,4 @@
-import type { CategoryId } from "../v1/categories";
+import type { CategoryId } from "../categories";
export const LOCALES = ["ru", "en"] as const;
export type Locale = (typeof LOCALES)[number];
diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte
index fbd4b81..f0a3709 100644
--- a/web/src/routes/+layout.svelte
+++ b/web/src/routes/+layout.svelte
@@ -1,6 +1,5 @@
-
+
diff --git a/web/src/routes/v1/+layout.svelte b/web/src/routes/v1/+layout.svelte
index 0837a2a..eda0668 100644
--- a/web/src/routes/v1/+layout.svelte
+++ b/web/src/routes/v1/+layout.svelte
@@ -1,6 +1,5 @@
-
+
diff --git a/web/src/lib/assets/favicon.svg b/web/static/favicon.svg
similarity index 100%
rename from web/src/lib/assets/favicon.svg
rename to web/static/favicon.svg