chore: update isolation plugin

This commit is contained in:
2026-09-12 20:55:59 +05:00
parent dda5711a33
commit ce33201302
9 changed files with 54 additions and 28 deletions
+5 -4
View File
@@ -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-паттернов в настройке, а не код правила.
@@ -0,0 +1 @@
// Fixture stub for lint-rule tests: content is ignored, must exist for import resolution.
@@ -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",
);
});
});
@@ -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 **, *, ?. */
+2 -1
View File
@@ -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: {
+1 -1
View File
@@ -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];
+1 -2
View File
@@ -1,6 +1,5 @@
<script lang="ts">
import { page } from "$app/state";
import favicon from "$lib/assets/favicon.svg";
import Footer from "$lib/components/layout/Footer.svelte";
import TopBar from "$lib/components/layout/TopBar.svelte";
import { initLocale } from "$lib/i18n/locale.svelte";
@@ -45,7 +44,7 @@
</script>
<svelte:head>
<link rel="icon" href={favicon} />
<link rel="icon" href="/favicon.svg" />
</svelte:head>
<main class="preview-root" data-theme={theme}>
+1 -2
View File
@@ -1,6 +1,5 @@
<script lang="ts">
import { resolve } from "$app/paths";
import favicon from "$lib/assets/favicon.svg";
import { LOCALES, type Locale } from "$lib/v1/i18n/dict";
import { getLocale, initLocale, setLocale } from "$lib/v1/i18n/locale.svelte";
import { t } from "$lib/v1/i18n/t";
@@ -19,7 +18,7 @@
</script>
<svelte:head>
<link rel="icon" href={favicon} />
<link rel="icon" href="/favicon.svg" />
</svelte:head>
<div class="app">

Before

Width:  |  Height:  |  Size: 920 B

After

Width:  |  Height:  |  Size: 920 B