eslint-plugin-weld 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -74,7 +74,9 @@ export default [
74
74
  - [`weld/no-barrel-bypass`](docs/rules/no-barrel-bypass.md) — запрещает импорты, которые входят
75
75
  внутрь модуля мимо его точки входа (`index.*`), минуя баррель.
76
76
 
77
- Правило учитывает алиасы путей и корень репозитория через `settings.weld`:
77
+ Правило учитывает алиасы путей и корень репозитория. Если алиасы не заданы в конфиге, они
78
+ автоматически подхватываются из `compilerOptions.paths` ближайшего `tsconfig.json` (с резолвом
79
+ `extends`); задать их явно можно через `settings.weld`:
78
80
 
79
81
  ```js
80
82
  export default [
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createRequire } from 'module';
2
2
  import createDebug from 'debug';
3
3
  import { existsSync } from 'fs';
4
- import path from 'path';
4
+ import path2 from 'path';
5
+ import { getTsconfig } from 'get-tsconfig';
5
6
 
6
7
  // src/index.ts
7
8
  var PACKAGE_NAME = "eslint-plugin-weld";
@@ -60,11 +61,11 @@ function isModuleExtension(ext) {
60
61
  }
61
62
 
62
63
  // src/path/posix.ts
63
- function toPosix(path2) {
64
- return path2.replace(/\\/g, "/");
64
+ function toPosix(path3) {
65
+ return path3.replace(/\\/g, "/");
65
66
  }
66
- function segments(path2) {
67
- return path2.split("/").filter(Boolean);
67
+ function segments(path3) {
68
+ return path3.split("/").filter(Boolean);
68
69
  }
69
70
  function joinSegments(segs) {
70
71
  return segs.length === 0 ? "/" : `/${segs.join("/")}`;
@@ -72,8 +73,8 @@ function joinSegments(segs) {
72
73
  function joinPath(dir, segment) {
73
74
  return dir === "/" ? `/${segment}` : `${dir}/${segment}`;
74
75
  }
75
- function basename(path2) {
76
- return path2.slice(path2.lastIndexOf("/") + 1);
76
+ function basename(path3) {
77
+ return path3.slice(path3.lastIndexOf("/") + 1);
77
78
  }
78
79
  function splitExtension(base) {
79
80
  const dot = base.lastIndexOf(".");
@@ -82,16 +83,16 @@ function splitExtension(base) {
82
83
  }
83
84
  return { name: base.slice(0, dot), ext: base.slice(dot + 1) };
84
85
  }
85
- function dirname(path2) {
86
- const segs = segments(path2);
86
+ function dirname(path3) {
87
+ const segs = segments(path3);
87
88
  if (segs.length === 0) {
88
89
  return "/";
89
90
  }
90
91
  return joinSegments(segs.slice(0, -1));
91
92
  }
92
- function resolvePath(base, path2) {
93
- const segs = path2.startsWith("/") ? [] : segments(base);
94
- for (const part of path2.split("/")) {
93
+ function resolvePath(base, path3) {
94
+ const segs = path3.startsWith("/") ? [] : segments(base);
95
+ for (const part of path3.split("/")) {
95
96
  if (part === "" || part === ".") {
96
97
  continue;
97
98
  }
@@ -159,15 +160,15 @@ function pickLongest(aliases, key) {
159
160
  function matchAlias(specifier, aliases) {
160
161
  return pickLongest(aliases, (alias) => covers(alias.prefix, specifier) ? alias.prefix : null);
161
162
  }
162
- function finalize(path2, form) {
163
- if (path2 === null) {
163
+ function finalize(path3, form) {
164
+ if (path3 === null) {
164
165
  return null;
165
166
  }
166
- const { ext } = splitExtension(basename(path2));
167
+ const { ext } = splitExtension(basename(path3));
167
168
  if (ext !== "" && !isModuleExtension(ext)) {
168
169
  return null;
169
170
  }
170
- return { path: path2, form, extension: ext === "" ? null : ext };
171
+ return { path: path3, form, extension: ext === "" ? null : ext };
171
172
  }
172
173
  function renderSpecifier(form, fromDir, barrier, aliases, extension = null) {
173
174
  const dir = renderDirectory(form, fromDir, barrier, aliases);
@@ -353,33 +354,41 @@ function readAliases(settings, override, baseUrlOverride) {
353
354
  }
354
355
  var TTL_MS = 6e4;
355
356
  var aliasesCache = [];
356
- function peekAliases(settings, override, baseUrlOverride, time) {
357
+ function sameKey(left, right) {
358
+ return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
359
+ }
360
+ function cachedAliases(key, parse) {
361
+ const time = Date.now();
357
362
  for (const entry of aliasesCache) {
358
- if (entry.expiresAt > time && entry.settings === settings && entry.override === override && entry.baseUrlOverride === baseUrlOverride) {
363
+ if (entry.expiresAt > time && sameKey(entry.key, key)) {
359
364
  return entry.result;
360
365
  }
361
366
  }
362
- return void 0;
363
- }
364
- function rememberAliases(entry, time) {
367
+ const result = parse();
365
368
  aliasesCache = aliasesCache.filter(
366
- (existing) => existing.expiresAt > time && !(existing.settings === entry.settings && existing.override === entry.override && existing.baseUrlOverride === entry.baseUrlOverride)
369
+ (entry) => entry.expiresAt > time && !sameKey(entry.key, key)
367
370
  );
368
- aliasesCache.push(entry);
369
- return entry.result;
371
+ aliasesCache.push({ key, result, expiresAt: time + TTL_MS });
372
+ return result;
370
373
  }
371
374
  function getAliases(settings, override, baseUrlOverride) {
372
- const time = Date.now();
373
- const cached = peekAliases(settings, override, baseUrlOverride, time);
374
- if (cached !== void 0) {
375
- return cached;
376
- }
377
- const result = readAliases(settings, override, baseUrlOverride);
378
- return rememberAliases(
379
- { settings, override, baseUrlOverride, result, expiresAt: time + TTL_MS },
380
- time
375
+ return cachedAliases(
376
+ [settings, override, baseUrlOverride],
377
+ () => readAliases(settings, override, baseUrlOverride)
381
378
  );
382
379
  }
380
+ function getAliasesFromPaths(paths, virtualBase, source) {
381
+ return cachedAliases(
382
+ [paths, virtualBase, source],
383
+ () => parseAliases(paths, virtualBase, source)
384
+ );
385
+ }
386
+ function hasAliases(settings, override) {
387
+ if (override !== void 0) {
388
+ return true;
389
+ }
390
+ return getWeldSettings(settings)?.aliases !== void 0;
391
+ }
383
392
  var DRIVE_PREFIX = /^[A-Za-z]:/;
384
393
  function isDriveRoot(realPath) {
385
394
  return realPath.length === 2 && DRIVE_PREFIX.test(realPath);
@@ -417,6 +426,17 @@ function sameSegment(left, right, index) {
417
426
  }
418
427
  return left === right;
419
428
  }
429
+ function commonRealDirectory(left, right) {
430
+ const prefixOf = (candidate) => DRIVE_PREFIX.exec(candidate)?.[0] ?? "";
431
+ const prefix = prefixOf(left);
432
+ const rightPrefix = prefixOf(right);
433
+ if (!sameSegment(prefix, rightPrefix, 0)) {
434
+ return null;
435
+ }
436
+ const leftRest = left.slice(prefix.length);
437
+ const depth = commonDepth(leftRest, right.slice(rightPrefix.length));
438
+ return `${prefix}${joinSegments(segments(leftRest).slice(0, depth))}`;
439
+ }
420
440
  function resolveRealPath(base, target) {
421
441
  const posixTarget = toPosix(target);
422
442
  if (isAbsoluteRealPath(posixTarget)) {
@@ -424,7 +444,7 @@ function resolveRealPath(base, target) {
424
444
  }
425
445
  const posixBase = toPosix(base);
426
446
  const resolveBase = isDriveRoot(posixBase) ? `${posixBase}/` : posixBase;
427
- const resolve = DRIVE_PREFIX.test(resolveBase) ? path.win32.resolve : path.posix.resolve;
447
+ const resolve = DRIVE_PREFIX.test(resolveBase) ? path2.win32.resolve : path2.posix.resolve;
428
448
  return toPosix(resolve(resolveBase, posixTarget));
429
449
  }
430
450
 
@@ -458,12 +478,12 @@ function findRepoRoot(startDir, exists) {
458
478
  var debug3 = createLogger("fs");
459
479
  var TTL_MS2 = 6e5;
460
480
  var NEGATIVE_TTL_MS = 5e3;
461
- function peek(cache, key, time) {
462
- const entry = cache.get(key);
481
+ function peek(cache2, key, time) {
482
+ const entry = cache2.get(key);
463
483
  return entry !== void 0 && entry.expiresAt > time ? entry.value : void 0;
464
484
  }
465
- function remember(cache, key, time, value) {
466
- cache.set(key, { value, expiresAt: time + (value ? TTL_MS2 : NEGATIVE_TTL_MS) });
485
+ function remember(cache2, key, time, value) {
486
+ cache2.set(key, { value, expiresAt: time + (value ? TTL_MS2 : NEGATIVE_TTL_MS) });
467
487
  return value;
468
488
  }
469
489
  function createFsHost(root, options = {}) {
@@ -542,15 +562,32 @@ function findRepoRootCached(cwd) {
542
562
  repoRootCache.set(cwd, found);
543
563
  return found;
544
564
  }
545
- function resolveRoot(settings, cwd, repoRootOverride) {
565
+ function resolveRoot(settings, cwd, repoRootOverride, coverDirs) {
546
566
  const explicitRoot = getRepoRoot(settings, repoRootOverride);
547
567
  if (explicitRoot !== void 0) {
548
568
  return resolveRealPath(cwd, explicitRoot);
549
569
  }
550
- return findRepoRootCached(cwd) ?? cwd;
570
+ const base = toPosix(findRepoRootCached(cwd) ?? cwd);
571
+ let root = base;
572
+ for (const coverDir of coverDirs) {
573
+ const combined = commonRealDirectory(root, toPosix(coverDir));
574
+ if (combined === null) {
575
+ debug3(
576
+ "resolveRoot: cover dir %s shares no prefix with root %s, ignoring",
577
+ coverDir,
578
+ root
579
+ );
580
+ continue;
581
+ }
582
+ root = combined;
583
+ }
584
+ if (root !== base) {
585
+ debug3("resolveRoot: auto root raised from %s to %s to cover requested dirs", base, root);
586
+ }
587
+ return root;
551
588
  }
552
- function getFsHost(settings, cwd, repoRootOverride) {
553
- const root = resolveRoot(settings, cwd, repoRootOverride);
589
+ function getFsHost(settings, cwd, repoRootOverride, coverDirs = []) {
590
+ const root = resolveRoot(settings, cwd, repoRootOverride, coverDirs);
554
591
  const cached = instanceCache.get(root);
555
592
  if (cached) {
556
593
  return cached;
@@ -560,8 +597,107 @@ function getFsHost(settings, cwd, repoRootOverride) {
560
597
  instanceCache.set(root, created);
561
598
  return created;
562
599
  }
600
+ var debug4 = createLogger("tsconfig");
601
+ var TTL_MS3 = 6e5;
602
+ var NEGATIVE_TTL_MS2 = 5e3;
603
+ var cache = /* @__PURE__ */ new Map();
604
+ function loadTsconfigPaths(realFilePath, options = {}) {
605
+ if (!path2.isAbsolute(realFilePath)) {
606
+ debug4("skip tsconfig lookup: file path is not absolute: %s", realFilePath);
607
+ return null;
608
+ }
609
+ const dir = path2.dirname(realFilePath);
610
+ const now = options.now ?? Date.now;
611
+ const time = now();
612
+ const entry = cache.get(dir);
613
+ if (entry && entry.expiresAt > time) {
614
+ return entry.value;
615
+ }
616
+ const readCache = /* @__PURE__ */ new Map();
617
+ const read = options.read ?? ((searchDir) => getTsconfig(searchDir, "tsconfig.json", readCache));
618
+ const value = readTsconfigPaths(dir, read);
619
+ cache.set(dir, { value, expiresAt: time + (value ? TTL_MS3 : NEGATIVE_TTL_MS2) });
620
+ return value;
621
+ }
622
+ function readTsconfigPaths(dir, read) {
623
+ let found;
624
+ try {
625
+ found = read(dir);
626
+ } catch (error) {
627
+ debug4("failed to read tsconfig near %s: %s", dir, error);
628
+ return null;
629
+ }
630
+ if (!found) {
631
+ debug4("no tsconfig found upwards from %s", dir);
632
+ return null;
633
+ }
634
+ const configPath = found.path;
635
+ const compilerOptions = found.config.compilerOptions ?? {};
636
+ const paths = compilerOptions["paths"];
637
+ if (typeof paths !== "object" || paths === null || Object.keys(paths).length === 0) {
638
+ debug4("tsconfig %s has no compilerOptions.paths", configPath);
639
+ return null;
640
+ }
641
+ const baseUrl = typeof compilerOptions["baseUrl"] === "string" ? compilerOptions["baseUrl"] : void 0;
642
+ const realBaseDir = baseUrl !== void 0 ? path2.resolve(path2.dirname(configPath), baseUrl) : getImplicitBaseUrl(compilerOptions) ?? path2.dirname(configPath);
643
+ const record = relativizePathsValues(paths, realBaseDir);
644
+ debug4("found paths in %s, base dir %s", configPath, realBaseDir);
645
+ return {
646
+ paths: record,
647
+ realBaseDir,
648
+ configPath,
649
+ realAnchors: collectRealAnchors(record, realBaseDir)
650
+ };
651
+ }
652
+ function getImplicitBaseUrl(compilerOptions) {
653
+ const symbol = Object.getOwnPropertySymbols(compilerOptions).find(
654
+ (candidate) => candidate.description === "implicitBaseUrl"
655
+ );
656
+ const value = symbol === void 0 ? void 0 : compilerOptions[symbol];
657
+ return typeof value === "string" ? value : void 0;
658
+ }
659
+ var ABSOLUTE_REAL_PATH = /^(?:\/|[A-Za-z]:)/;
660
+ function relativizePathsValues(paths, realBaseDir) {
661
+ const relativize = (item) => {
662
+ if (typeof item !== "string" || !ABSOLUTE_REAL_PATH.test(item)) {
663
+ return item;
664
+ }
665
+ const relative = toPosix(path2.relative(realBaseDir, item));
666
+ if (relative === "") {
667
+ return ".";
668
+ }
669
+ return relative.startsWith("*") ? `./${relative}` : relative;
670
+ };
671
+ const result = {};
672
+ for (const [key, value] of Object.entries(paths)) {
673
+ result[key] = Array.isArray(value) ? value.map(relativize) : relativize(value);
674
+ }
675
+ return result;
676
+ }
677
+ function collectRealAnchors(paths, realBaseDir) {
678
+ const anchors = /* @__PURE__ */ new Set();
679
+ for (const [key, value] of Object.entries(paths)) {
680
+ if (!hasValidStarShape(key)) {
681
+ continue;
682
+ }
683
+ const items = Array.isArray(value) ? value : [value];
684
+ for (const item of items) {
685
+ if (typeof item !== "string" || !hasValidStarShape(item)) {
686
+ continue;
687
+ }
688
+ const anchor = path2.resolve(realBaseDir, stripStarSuffix(item));
689
+ if (segments(toPosix(anchor)).includes("node_modules")) {
690
+ debug4("anchor %s is inside node_modules, excluded from root computation", anchor);
691
+ continue;
692
+ }
693
+ anchors.add(anchor);
694
+ }
695
+ }
696
+ return [...anchors];
697
+ }
563
698
 
564
699
  // src/rules/context.ts
700
+ var debug5 = createLogger("rules");
565
701
  var WELD_OPTION_PROPERTIES = {
566
702
  repoRoot: { type: "string" },
567
703
  aliasesBaseUrl: { type: "string" },
@@ -569,17 +705,53 @@ var WELD_OPTION_PROPERTIES = {
569
705
  };
570
706
  function resolveWeldContext(context, fsHostOverride) {
571
707
  const options = context.options[0] ?? {};
572
- const fsHost = getFsHost(context.settings, context.cwd, options.repoRoot);
708
+ const { fsHost, aliases } = resolveHostAndAliases(context, options, fsHostOverride);
573
709
  const fromFile = fsHost.toVirtual(context.filename);
574
710
  if (fromFile === null) {
575
711
  return null;
576
712
  }
577
- return {
578
- fromFile,
579
- aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl),
580
- fsHost,
581
- options
582
- };
713
+ return { fromFile, aliases, fsHost, options };
714
+ }
715
+ function resolveHostAndAliases(context, options, fsHostOverride) {
716
+ if (hasAliases(context.settings, options.aliases)) {
717
+ return {
718
+ fsHost: fsHostOverride ?? getFsHost(context.settings, context.cwd, options.repoRoot),
719
+ aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl)
720
+ };
721
+ }
722
+ const found = loadTsconfigPaths(context.filename);
723
+ const hasExplicitRoot = getRepoRoot(context.settings, options.repoRoot) !== void 0;
724
+ const fsHost = getFsHost(
725
+ context.settings,
726
+ context.cwd,
727
+ options.repoRoot,
728
+ found?.realAnchors ?? []
729
+ );
730
+ return { fsHost, aliases: aliasesFromTsconfig(found, fsHost, hasExplicitRoot) };
731
+ }
732
+ function aliasesFromTsconfig(found, fsHost, hasExplicitRoot) {
733
+ if (found === null) {
734
+ return [];
735
+ }
736
+ if (hasExplicitRoot && fsHost.toVirtual(found.configPath) === null) {
737
+ debug5("discarding tsconfig %s: it is outside the explicit root", found.configPath);
738
+ return [];
739
+ }
740
+ const virtualBase = fsHost.toVirtual(found.realBaseDir);
741
+ if (virtualBase === null) {
742
+ debug5(
743
+ "discarding tsconfig %s: base dir %s is outside the root",
744
+ found.configPath,
745
+ found.realBaseDir
746
+ );
747
+ return [];
748
+ }
749
+ try {
750
+ return getAliasesFromPaths(found.paths, virtualBase, found.configPath);
751
+ } catch (error) {
752
+ debug5("discarding tsconfig %s: %s", found.configPath, error);
753
+ return [];
754
+ }
583
755
  }
584
756
 
585
757
  // src/rules/no-barrel-bypass/core/barrier.ts
@@ -602,18 +774,18 @@ function findBarrier(fromDir, targetPath, hasEntryPoint) {
602
774
  }
603
775
 
604
776
  // src/rules/no-barrel-bypass/pipeline.ts
605
- var debug4 = createLogger("no-barrel-bypass");
777
+ var debug6 = createLogger("no-barrel-bypass");
606
778
  function createChecker({ fromFile, aliases }, host) {
607
779
  const fromDir = dirname(fromFile);
608
780
  return function checkImport(specifier) {
609
781
  const target = parseSpecifier(specifier, fromDir, aliases);
610
782
  if (target === null) {
611
- debug4("%s: %s external dependency", fromFile, specifier);
783
+ debug6("%s: %s external dependency", fromFile, specifier);
612
784
  return null;
613
785
  }
614
786
  const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);
615
787
  if (barrier === null) {
616
- debug4("%s: %s -> %s, no barrier on the way", fromFile, specifier, target.path);
788
+ debug6("%s: %s -> %s, no barrier on the way", fromFile, specifier, target.path);
617
789
  return null;
618
790
  }
619
791
  return renderSpecifier(target.form, fromDir, barrier, aliases, target.extension);
@@ -621,7 +793,7 @@ function createChecker({ fromFile, aliases }, host) {
621
793
  }
622
794
 
623
795
  // src/rules/no-barrel-bypass/index.ts
624
- var debug5 = createLogger("no-barrel-bypass");
796
+ var debug7 = createLogger("no-barrel-bypass");
625
797
  var messages = {
626
798
  bypass: "Import bypasses the module barrel. Use '{{suggestion}}' instead of '{{original}}'.",
627
799
  useBarrel: "Import through '{{suggestion}}'."
@@ -649,7 +821,7 @@ function createRule(fsHost) {
649
821
  messages
650
822
  },
651
823
  create(context) {
652
- const weld = resolveWeldContext(context);
824
+ const weld = resolveWeldContext(context, fsHost);
653
825
  if (weld === null) {
654
826
  return {};
655
827
  }
@@ -662,7 +834,7 @@ function createRule(fsHost) {
662
834
  if (suggestion === null) {
663
835
  return;
664
836
  }
665
- debug5(
837
+ debug7(
666
838
  "%s: crosses barrier, %s -> %s (fix=%o)",
667
839
  fromFile,
668
840
  original,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/debug.ts","../src/imports/nodes.ts","../src/extensions.ts","../src/path/posix.ts","../src/imports/specifier.ts","../src/settings/aliases.ts","../src/settings/weld.ts","../src/host/real-path.ts","../src/host/root.ts","../src/host/fs.ts","../src/rules/context.ts","../src/rules/no-barrel-bypass/core/barrier.ts","../src/rules/no-barrel-bypass/pipeline.ts","../src/rules/no-barrel-bypass/index.ts","../src/rules/index.ts","../src/index.ts"],"names":["path","debug","TTL_MS"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Debug-логирование плагина. Namespace строится здесь и только здесь: README объявляет\n * `DEBUG=eslint-plugin-weld:*` контрактом, а три независимых литерала по модулям этот контракт\n * ничем не удерживали.\n */\n\nimport createDebug from 'debug';\nimport type { Debugger } from 'debug';\n\n/** Имя пакета — оно же корень debug-namespace и `plugin.meta.name`. */\nexport const PACKAGE_NAME = 'eslint-plugin-weld';\n\n/** Логгер модуля: `scope` — короткое имя вроде `fs`, `root`, `aliases`. */\nexport function createLogger(scope: string): Debugger {\n return createDebug(`${PACKAGE_NAME}:${scope}`);\n}\n","/**\n * Обход импортов в AST: где в дереве лежит строка-специфаер и как её заменить. Про баррели,\n * границы модулей и любое конкретное правило слой не знает — это общая инфраструктура импортов,\n * как и `specifier.ts`.\n *\n * Здесь же зафиксированы решения, которые иначе каждое правило принимало бы заново: репортим на\n * самом литерале пути, а не на всём операторе; кавычку для правки берём из исходника; `require`\n * считается импортом, а `export ... from` — нет.\n */\n\nimport type { Rule } from 'eslint';\nimport type { Literal } from 'estree';\n\n/**\n * Строковый литерал специфаера — он же узел отчёта: сообщение указывается на самом пути, а не на\n * всём операторе импорта. `Rule.Node` в пересечении — ради `context.report`, `range` обязателен.\n */\nexport type SpecifierNode = Rule.Node & {\n type: 'Literal';\n value: string;\n raw: string;\n range: [number, number];\n};\n\nfunction isSpecifierNode(node: { type: string } | null | undefined): node is SpecifierNode {\n return (\n node != null &&\n node.type === 'Literal' &&\n typeof (node as unknown as Literal).value === 'string'\n );\n}\n\n/**\n * Слушатели для `create()`, вызывающие `onSpecifier` на каждом строковом специфаере импорта:\n * `import ... from`, динамический `import()` и `require()`.\n *\n * `export ... from` намеренно не обходится: у реэкспорта своя семантика границ, и правило,\n * которому он нужен, должно включить его осознанно, а не получить молча.\n */\nexport function createSpecifierVisitor(\n onSpecifier: (node: SpecifierNode) => void,\n): Rule.RuleListener {\n return {\n 'ImportDeclaration, ImportExpression'(\n node: Rule.Node & { source?: { type: string } | null },\n ) {\n if (isSpecifierNode(node.source)) {\n onSpecifier(node.source);\n }\n },\n CallExpression(node) {\n if (node.callee.type !== 'Identifier' || node.callee.name !== 'require') {\n return;\n }\n const arg = node.arguments[0];\n if (isSpecifierNode(arg)) {\n onSpecifier(arg);\n }\n },\n };\n}\n\n/**\n * Правка, подставляющая `specifier` вместо содержимого узла. Кавычка берётся из исходного текста —\n * правило не навязывает файлу свой стиль кавычек.\n */\nexport function replaceSpecifier(node: SpecifierNode, specifier: string): Rule.ReportFixer {\n const quote = node.raw[0] ?? \"'\";\n return (fixer) => fixer.replaceTextRange(node.range, `${quote}${specifier}${quote}`);\n}\n","/**\n * Словарь расширений файлов — общий для всех слоёв, как и `src/path/posix.ts`.\n *\n * Списка два, потому что понятия разные, хотя сегодня их состав совпадает. Точка входа — это файл,\n * который делает директорию модулем; модуль — это то, что вообще можно импортировать как код.\n * `.vue`/`.svelte` были бы модулями, но не обязательно точками входа; `.json` импортируется, но\n * баррелем не бывает. Одним списком на оба вопроса расширение любого из них молча меняло бы ответ\n * на второй.\n */\n\n/**\n * Имя файла точки входа без расширения. Ответ на тот же вопрос, что и {@link ENTRY_EXTENSIONS} —\n * какой файл делает директорию модулем, — поэтому живёт рядом с ним, а не литералом по слоям.\n */\nconst ENTRY_BASENAME = 'index';\n\n/** Расширения файла точки входа (`index.<ext>`) — по ним ищутся границы модулей. */\nexport const ENTRY_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mts', 'cts', 'mjs', 'cjs'] as const;\n\n/**\n * Расширения, при которых специфаер считается импортом кода внутрь репозитория. Специфаер с чужим\n * расширением (`./styles.css`, `./logo.svg`) правила не проверяют.\n */\nconst MODULE_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mts', 'cts', 'mjs', 'cjs'] as const;\n\nconst ENTRY_EXTENSION_SET: ReadonlySet<string> = new Set(ENTRY_EXTENSIONS);\nconst MODULE_EXTENSION_SET: ReadonlySet<string> = new Set(MODULE_EXTENSIONS);\n\n/** Входит ли расширение (без точки) в {@link ENTRY_EXTENSIONS}. */\nfunction isEntryExtension(ext: string): boolean {\n return ENTRY_EXTENSION_SET.has(ext);\n}\n\n/** Имя файла точки входа с расширением: `index.ts`, `index.mjs`. */\nexport function entryFileName(ext: string): string {\n return `${ENTRY_BASENAME}.${ext}`;\n}\n\n/** Все имена файлов точки входа — перебор при поиске границы модуля на диске. */\nexport const ENTRY_FILE_NAMES: readonly string[] = ENTRY_EXTENSIONS.map(entryFileName);\n\n/** Имя файла (без директории) — точка входа? Расширение обязано входить в {@link ENTRY_EXTENSIONS}. */\nexport function isEntryFileName(name: string, ext: string): boolean {\n return isEntryBasename(name) && isEntryExtension(ext);\n}\n\n/**\n * Имя без расширения — точка входа? В отличие от {@link isEntryFileName} расширение не\n * спрашивается: специфаер может быть записан и без него (`./other/index`), а вопрос «пробивает ли\n * импорт границу» от расширения не зависит. Ответ живёт здесь, а не у спрашивающего: иначе «что\n * делает директорию модулем» знали бы два слоя и разъехались бы при первой же правке.\n */\nexport function isEntryBasename(name: string): boolean {\n return name === ENTRY_BASENAME;\n}\n\n/** Входит ли расширение (без точки) в {@link MODULE_EXTENSIONS}. */\nexport function isModuleExtension(ext: string): boolean {\n return MODULE_EXTENSION_SET.has(ext);\n}\n","/**\n * Сегментные операции над виртуальными путями, без `node:path`.\n *\n * Виртуальный путь — posix-строка от корня репозитория (`/src/feature/x.ts`), корень — `/`.\n * `toPosix` — исключение: работает с реальными путями на границе (`createFsHost`), до перевода в\n * виртуальные; остальные функции здесь принимают только виртуальные пути.\n */\n\n/** Заменяет `\\` на `/`. Не резолвит `.`/`..` и ничего не знает о виртуальных путях. */\nexport function toPosix(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/** Непустые сегменты пути. Корень (`/`) и пустая строка дают пустой массив. */\nexport function segments(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\n/** Собирает виртуальный путь из сегментов. Пустой список — корень (`/`). */\nexport function joinSegments(segs: string[]): string {\n return segs.length === 0 ? '/' : `/${segs.join('/')}`;\n}\n\n/**\n * Дописывает сегмент к виртуальной директории, не удваивая `/` у корня. Спуск по дереву наращивает\n * путь этой функцией вместо `joinSegments` от среза — знание «у корня разделителя не нужно» живёт\n * здесь, а не у каждого, кто идёт вниз.\n */\nexport function joinPath(dir: string, segment: string): string {\n return dir === '/' ? `/${segment}` : `${dir}/${segment}`;\n}\n\n/** Последний сегмент виртуального пути после последнего `/`. Корень даёт пустую строку. */\nexport function basename(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1);\n}\n\n/** Разбивает basename на имя и расширение (без точки). Расширения нет — `ext` пустой. */\nexport function splitExtension(base: string): { name: string; ext: string } {\n // Точка в начале — не расширение (`.storybook`), точка в конце — пустое расширение (`x.`).\n const dot = base.lastIndexOf('.');\n if (dot <= 0 || dot === base.length - 1) {\n return { name: base, ext: '' };\n }\n\n return { name: base.slice(0, dot), ext: base.slice(dot + 1) };\n}\n\n/** Директория выше `path`. У корня родителя нет — возвращает сам корень. */\nexport function dirname(path: string): string {\n const segs = segments(path);\n if (segs.length === 0) {\n return '/';\n }\n\n return joinSegments(segs.slice(0, -1));\n}\n\n/**\n * Резолвит `path` (относительный, с `.`/`..`, или абсолютный) от виртуальной директории `base`.\n * Выход `..` выше `/` — за пределами репозитория, невыразимо виртуальным путём — возвращает `null`.\n */\nexport function resolvePath(base: string, path: string): string | null {\n const segs = path.startsWith('/') ? [] : segments(base);\n\n for (const part of path.split('/')) {\n if (part === '' || part === '.') {\n continue;\n }\n if (part === '..') {\n if (segs.length === 0) {\n return null;\n }\n segs.pop();\n continue;\n }\n segs.push(part);\n }\n\n return joinSegments(segs);\n}\n\n/** Длина общего префикса двух списков сегментов. */\nfunction commonPrefixLength(segsA: string[], segsB: string[]): number {\n let i = 0;\n while (i < segsA.length && i < segsB.length && segsA[i] === segsB[i]) {\n i += 1;\n }\n\n return i;\n}\n\n/**\n * Глубина самой глубокой общей директории в сегментах. У виртуальных путей общий корень `/` есть\n * всегда, поэтому глубина — от `0`.\n *\n * Отдаётся числом, а не путём: спрашивают об общей директории только чтобы отсчитать от неё\n * сегменты, и собранная строка тут же разбиралась бы обратно.\n */\nexport function commonDepth(a: string, b: string): number {\n return commonPrefixLength(segments(a), segments(b));\n}\n\n/**\n * Путь от директории `from` до `to`, без префикса `./` (его добавляет `renderSpecifier`).\n * Совпадающие пути дают пустую строку.\n */\nexport function relativePath(from: string, to: string): string {\n const segsFrom = segments(from);\n const segsTo = segments(to);\n\n const i = commonPrefixLength(segsFrom, segsTo);\n\n const ups = Array<string>(segsFrom.length - i).fill('..');\n const downs = segsTo.slice(i);\n\n return [...ups, ...downs].join('/');\n}\n","/**\n * Соответствие «строка импорта ↔ виртуальный путь»: `parseSpecifier` разбирает специфаер в цель,\n * `renderSpecifier` собирает специфаер обратно в той же форме записи. Про барьеры, баррели и любое\n * другое правило слой не знает — это общая инфраструктура импортов.\n *\n * Строка импорта сама и есть путь: относительная резолвится от директории файла, алиасная —\n * подстановкой якоря. Резолвер не нужен — существование цели проверяет `import/no-unresolved`.\n */\n\nimport { entryFileName, isModuleExtension } from '@/extensions.js';\nimport { basename, relativePath, resolvePath, splitExtension } from '@/path/index.js';\nimport type { Alias } from '@/settings/index.js';\n\nexport type Form = { kind: 'relative' } | { kind: 'alias'; alias: Alias };\n\n/**\n * `extension` — расширение, явно записанное в специфаере (без точки), либо `null`. Это факт про\n * форму записи, а не про диск: в TS-проекте под Node-ESM пишут `./x.js`, хотя на диске `x.ts`.\n */\nexport type Target = { path: string; form: Form; extension: string | null };\n\n/**\n * `null`, если специфаер не выражает путь внутрь репозитория: голый пакет, `@scope/pkg`,\n * абсолютный специфаер, специфаер с `?`/`!`, последний сегмент с расширением вне\n * `MODULE_EXTENSIONS`, либо относительный подъём `..` выше виртуального корня.\n */\nexport function parseSpecifier(\n specifier: string,\n fromDir: string,\n aliases: Alias[],\n): Target | null {\n if (specifier.includes('?') || specifier.includes('!')) {\n return null;\n }\n\n if (isRelativeSpecifier(specifier)) {\n return finalize(resolvePath(fromDir, specifier), { kind: 'relative' });\n }\n\n const alias = matchAlias(specifier, aliases);\n if (alias !== null) {\n // `matchAlias` уже проверил границу по сегменту, поэтому хвост — либо пустой, либо `/…`;\n // ведущие слэши снимаются, чтобы `resolvePath` не принял хвост за абсолютный путь.\n const suffix = specifier.slice(alias.prefix.length).replace(/^\\/+/, '');\n return finalize(resolvePath(alias.anchor, suffix), { kind: 'alias', alias });\n }\n\n return null;\n}\n\nfunction isRelativeSpecifier(specifier: string): boolean {\n return (\n specifier === '.' ||\n specifier === '..' ||\n specifier.startsWith('./') ||\n specifier.startsWith('../')\n );\n}\n\n/**\n * Самый длинный алиас, подходящий по `key`; `key` возвращает `null`, если алиас не подходит. При\n * равной длине побеждает первая запись по порядку объявления.\n */\nfunction pickLongest(aliases: Alias[], key: (alias: Alias) => string | null): Alias | null {\n let best: Alias | null = null;\n let bestLength = -1;\n\n for (const alias of aliases) {\n const matched = key(alias);\n if (matched === null || matched.length <= bestLength) {\n continue;\n }\n best = alias;\n bestLength = matched.length;\n }\n\n return best;\n}\n\n/** Самый длинный подходящий префикс; при равных префиксах — первая запись по порядку. */\nfunction matchAlias(specifier: string, aliases: Alias[]): Alias | null {\n return pickLongest(aliases, (alias) => (covers(alias.prefix, specifier) ? alias.prefix : null));\n}\n\nfunction finalize(path: string | null, form: Form): Target | null {\n if (path === null) {\n return null;\n }\n\n const { ext } = splitExtension(basename(path));\n if (ext !== '' && !isModuleExtension(ext)) {\n return null;\n }\n\n return { path, form, extension: ext === '' ? null : ext };\n}\n\n/**\n * Обратный рендер: директория `barrier` (граница) в специфаер в форме исходного импорта.\n * Форма выигрывает у пути цели — путь про форму записи ничего не знает.\n *\n * `extension` (расширение исходного специфаера) продолжает ту же логику: импорт с явным\n * расширением рендерится в `<barrier>/index.<ext>`, потому что там, где расширения обязательны\n * (Node-ESM), импорт голой директории не резолвится. Расширение берётся из исходного импорта, а не\n * с диска: в TS под Node-ESM на диске лежит `index.ts`, а писать полагается `index.js`.\n */\nexport function renderSpecifier(\n form: Form,\n fromDir: string,\n barrier: string,\n aliases: Alias[],\n extension: string | null = null,\n): string {\n const dir = renderDirectory(form, fromDir, barrier, aliases);\n return extension === null ? dir : withEntry(dir, extension);\n}\n\nfunction renderDirectory(form: Form, fromDir: string, barrier: string, aliases: Alias[]): string {\n if (form.kind === 'relative') {\n return renderRelative(fromDir, barrier);\n }\n\n if (covers(form.alias.anchor, barrier)) {\n return renderAlias(form.alias, barrier);\n }\n\n const longest = longestCoveringAlias(aliases, barrier);\n return longest === null ? renderRelative(fromDir, barrier) : renderAlias(longest, barrier);\n}\n\n/** Дописывает точку входа к специфаеру директории, не удваивая `/`. */\nfunction withEntry(dir: string, extension: string): string {\n const separator = dir.endsWith('/') ? '' : '/';\n return `${dir}${separator}${entryFileName(extension)}`;\n}\n\n/**\n * `value` равен `prefix` или лежит под ним — граница проверяется по сегменту, а не по символам\n * (`@/features` не покрывает `@/features-old`). Один вопрос на обе стороны алиаса: слева от него\n * так сопоставляется префикс со специфаером, справа — якорь с директорией.\n */\nfunction covers(prefix: string, value: string): boolean {\n return value === prefix || value.startsWith(`${prefix}/`);\n}\n\n/** Самый длинный якорь, покрывающий `dir`; `null`, если ни один не покрывает. */\nfunction longestCoveringAlias(aliases: Alias[], dir: string): Alias | null {\n return pickLongest(aliases, (alias) => (covers(alias.anchor, dir) ? alias.anchor : null));\n}\n\nfunction renderAlias(alias: Alias, barrier: string): string {\n if (barrier === alias.anchor) {\n return alias.prefix;\n }\n return `${alias.prefix}/${relativePath(alias.anchor, barrier)}`;\n}\n\nfunction renderRelative(fromDir: string, barrier: string): string {\n const rel = relativePath(fromDir, barrier);\n return rel.startsWith('./') || rel.startsWith('../') ? rel : `./${rel}`;\n}\n","import { createLogger } from '@/debug.js';\nimport { isEntryFileName } from '@/extensions.js';\nimport { basename, resolvePath, splitExtension } from '@/path/index.js';\n\nconst debug = createLogger('aliases');\n\nexport type Alias = { prefix: string; anchor: string };\n\n/**\n * Разбирает значение `settings.weld.aliases` (формат `paths` из tsconfig) в список алиасов, готовых\n * для `parseSpecifier`/`renderSpecifier`. Обе настройки приходят уже добытыми — читает их\n * `src/settings/weld.ts`; он же решает, что делать с отсутствующей настройкой, поэтому `undefined`\n * сюда не доходит. `<base>` — виртуальный путь от корня репозитория, не от `cwd`: `aliasesBaseUrl`\n * отсчитывается от корня репозитория. `source` называет место значения в конфиге в сообщениях об\n * ошибках.\n */\nexport function parseAliases(rawAliases: unknown, aliasesBaseUrl: string, source: string): Alias[] {\n if (typeof rawAliases !== 'object' || rawAliases === null || Array.isArray(rawAliases)) {\n throw new Error(`${source} must be an object`);\n }\n\n const base = resolvePath('/', aliasesBaseUrl) ?? '/';\n\n const result: Alias[] = [];\n const seen = new Set<string>();\n\n for (const [key, rawValue] of Object.entries(rawAliases)) {\n if (!hasValidStarShape(key)) {\n debug('%s: dropped %s — key must have no \"*\" or end with \"/*\"', source, key);\n continue;\n }\n const anchorsRaw = toAnchorList(key, rawValue, source);\n const prefix = stripStarSuffix(key);\n\n for (const anchorRaw of anchorsRaw) {\n if (!hasValidStarShape(anchorRaw)) {\n debug(\n '%s: dropped %s -> %s — target must have no \"*\" or end with \"/*\"',\n source,\n key,\n anchorRaw,\n );\n continue;\n }\n const anchor = normalizeAnchor(anchorRaw, base);\n if (anchor === null) {\n debug(\n '%s: dropped %s -> %s — not a directory (only index.<ext> stands for one) or above the repository root',\n source,\n key,\n anchorRaw,\n );\n continue;\n }\n\n const dedupeKey = `${prefix}\\0${anchor}`;\n if (seen.has(dedupeKey)) {\n continue;\n }\n seen.add(dedupeKey);\n result.push({ prefix, anchor });\n }\n }\n\n if (debug.enabled) {\n debug(\n `${source} (aliasesBaseUrl ${aliasesBaseUrl}) resolved to:\\n${formatAliases(result)}`,\n );\n }\n return result;\n}\n\n/** Для debug: по строке на алиас, `<префикс> - <якорь>`. Пустой список — явным словом. */\nfunction formatAliases(aliases: Alias[]): string {\n if (aliases.length === 0) {\n return '(none)';\n }\n\n return aliases.map(({ prefix, anchor }) => `${prefix} - ${anchor}`).join('\\n');\n}\n\n/** Значение записи `paths`: строка (частая опечатка) или массив строк. */\nfunction toAnchorList(key: string, value: unknown, source: string): string[] {\n if (typeof value === 'string') {\n return [value];\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(`${source}['${key}'] must contain only strings`);\n }\n }\n return value as string[];\n }\n\n throw new Error(`${source}['${key}'] must be a string or an array of strings`);\n}\n\n/**\n * Принимаются только записи без `*` или оканчивающиеся на `/*`; звёздочка в середине —\n * отбрасывается. `/*` тоже отбрасывается: `stripStarSuffix` дал бы пустой префикс/якорь, а пустая\n * строка — префикс любого абсолютного специфайера (`''` + `/` — начало любого `/foo`), из-за чего\n * запись стала бы вести себя как алиас на абсолютные пути, которые правило не обрабатывает.\n */\nfunction hasValidStarShape(value: string): boolean {\n const starCount = value.split('*').length - 1;\n if (starCount > 1 || (starCount === 1 && !value.endsWith('/*'))) {\n return false;\n }\n\n return stripStarSuffix(value) !== '';\n}\n\nfunction stripStarSuffix(value: string): string {\n return value.endsWith('/*') ? value.slice(0, -2) : value;\n}\n\n/**\n * Директория, которую обозначает запись `paths`. `null`, если запись — обычный файл\n * (не `index.<известное расширение>`) и директорией не выражается, либо якорь уходит `..`-подъёмом\n * выше виртуального корня.\n */\nfunction normalizeAnchor(anchorRaw: string, base: string): string | null {\n if (anchorRaw.endsWith('/*')) {\n return resolvePath(base, anchorRaw.slice(0, -2));\n }\n\n const lastSegment = basename(anchorRaw);\n const { name, ext } = splitExtension(lastSegment);\n\n if (ext === '') {\n return resolvePath(base, anchorRaw);\n }\n\n if (!isEntryFileName(name, ext)) {\n return null;\n }\n\n // Директория записи — сам `anchorRaw` без последнего сегмента; хвостовой `/` `resolvePath` съедает.\n return resolvePath(base, anchorRaw.slice(0, -lastSegment.length));\n}\n","/**\n * Единственная точка чтения `settings.weld`. Наружу слой отдаёт не сырую секцию, а геттер на каждую\n * настройку — `getRepoRoot` / `getAliasesBaseUrl` / `getAliases`; формат конфига за пределами\n * `src/settings/` не знает никто.\n *\n * Каждый геттер принимает необязательный `override` — значение этой настройки, подставляемое вместо\n * конфига. Оно тоже приходит из пользовательского конфига (опции правила), поэтому проверяется теми\n * же правилами, что и `settings.weld`; в сообщении об ошибке называется источник — `options.<имя>`\n * вместо `settings.weld.<имя>`.\n */\n\nimport type { Alias } from '@/settings/aliases.js';\nimport { parseAliases } from '@/settings/aliases.js';\n\n/** `aliasesBaseUrl` по умолчанию — сам корень репозитория. */\nconst DEFAULT_ALIASES_BASE_URL = '.';\n\n/** `settings.weld`, если задан. `undefined` без ошибки — секция необязательна. */\nfunction getWeldSettings(settings: unknown): Record<string, unknown> | undefined {\n if (typeof settings !== 'object' || settings === null) {\n return undefined;\n }\n\n const weld = (settings as Record<string, unknown>).weld;\n if (weld === undefined) {\n return undefined;\n }\n\n if (typeof weld !== 'object' || weld === null) {\n throw new Error('settings.weld must be an object');\n }\n\n return weld as Record<string, unknown>;\n}\n\nfunction requireString(value: unknown, source: string): string {\n if (typeof value !== 'string') {\n throw new Error(`${source} must be a string, got ${typeof value}`);\n }\n\n return value;\n}\n\n/**\n * `settings.weld.repoRoot` как он записан в конфиге — реальный путь, не виртуальный. Резолв\n * относительного значения и всё прочее знание о реальной ФС — за границей `src/host/`.\n * `undefined` — корень не задан, вызывающий ищет его сам.\n */\nexport function getRepoRoot(settings: unknown, override?: unknown): string | undefined {\n if (override !== undefined) {\n return requireString(override, 'options.repoRoot');\n }\n\n const repoRoot = getWeldSettings(settings)?.repoRoot;\n if (repoRoot === undefined) {\n return undefined;\n }\n\n return requireString(repoRoot, 'settings.weld.repoRoot');\n}\n\n/** `settings.weld.aliasesBaseUrl`; не задан — `'.'` (сам корень репозитория). */\nexport function getAliasesBaseUrl(settings: unknown, override?: unknown): string {\n if (override !== undefined) {\n return requireString(override, 'options.aliasesBaseUrl');\n }\n\n const aliasesBaseUrl = getWeldSettings(settings)?.aliasesBaseUrl;\n if (aliasesBaseUrl === undefined) {\n return DEFAULT_ALIASES_BASE_URL;\n }\n\n return requireString(aliasesBaseUrl, 'settings.weld.aliasesBaseUrl');\n}\n\nfunction readAliases(settings: unknown, override: unknown, baseUrlOverride: unknown): Alias[] {\n const fromOverride = override !== undefined;\n const rawAliases = fromOverride ? override : getWeldSettings(settings)?.aliases;\n if (rawAliases === undefined) {\n return [];\n }\n\n const source = fromOverride ? 'options.aliases' : 'settings.weld.aliases';\n return parseAliases(rawAliases, getAliasesBaseUrl(settings, baseUrlOverride), source);\n}\n\n/**\n * TTL записи кэша алиасов. Минута: сравнение по ссылке само по себе корректно (конфиг ESLint в\n * рамках прогона неизменен), поэтому TTL здесь не про свежесть ответа, а про то, чтобы записи не\n * жили вечно — иначе перечитанный конфиг языкового сервера навсегда оставлял бы в кэше и старый\n * объект `settings`, и разобранные по нему алиасы. Протухание посреди прогона ничего не стоит:\n * цена промаха — один разбор `paths`, десятки микросекунд.\n */\nconst TTL_MS = 60_000;\n\ntype AliasesCacheEntry = {\n settings: unknown;\n override: unknown;\n baseUrlOverride: unknown;\n result: Alias[];\n expiresAt: number;\n};\n\n/**\n * Кэш разобранных алиасов: правило спрашивает их на каждый линтуемый файл, а аргументы приходят те\n * же — в flat config объект `settings` у всех файлов одного конфиг-блока один и тот же, и опции\n * правила тоже. Попадание избавляет от повторного разбора `paths` и от повторного `debug`-лога о\n * нём.\n *\n * Список с линейным поиском, а не `Map`: ключ здесь — тройка ссылок, строкового ключа у неё нет, а\n * собирать его пришлось бы обходом тех же `paths`. Записей при этом единицы — по одной на\n * конфиг-блок ESLint.\n */\nlet aliasesCache: AliasesCacheEntry[] = [];\n\nfunction peekAliases(\n settings: unknown,\n override: unknown,\n baseUrlOverride: unknown,\n time: number,\n): Alias[] | undefined {\n for (const entry of aliasesCache) {\n if (\n entry.expiresAt > time &&\n entry.settings === settings &&\n entry.override === override &&\n entry.baseUrlOverride === baseUrlOverride\n ) {\n return entry.result;\n }\n }\n\n return undefined;\n}\n\n/**\n * Протухшие записи выбрасываются здесь, а не по таймеру: кэш растёт только на промахах, на промахе\n * же и чистится.\n */\nfunction rememberAliases(entry: AliasesCacheEntry, time: number): Alias[] {\n aliasesCache = aliasesCache.filter(\n (existing) =>\n existing.expiresAt > time &&\n !(\n existing.settings === entry.settings &&\n existing.override === entry.override &&\n existing.baseUrlOverride === entry.baseUrlOverride\n ),\n );\n aliasesCache.push(entry);\n return entry.result;\n}\n\n/**\n * Алиасы из `settings.weld`. Нет `aliases` — `[]`.\n *\n * `baseUrlOverride` — override той настройки, от которой отсчитываются якоря: алиасы без\n * `aliasesBaseUrl` переопределить нельзя, эти два значения имеют смысл только в паре.\n *\n * Результат берётся из кэша (см. {@link aliasesCache}), поэтому при попадании возвращается тот же\n * массив, что и в прошлый раз — считается он неизменяемым, потребители его только читают.\n */\nexport function getAliases(\n settings: unknown,\n override?: unknown,\n baseUrlOverride?: unknown,\n): Alias[] {\n const time = Date.now();\n\n const cached = peekAliases(settings, override, baseUrlOverride, time);\n if (cached !== undefined) {\n return cached;\n }\n\n // Запоминается только успешный разбор: на сломанном конфиге ESLint должен ругаться на каждом\n // файле, а не на первом.\n const result = readAliases(settings, override, baseUrlOverride);\n return rememberAliases(\n { settings, override, baseUrlOverride, result, expiresAt: time + TTL_MS },\n time,\n );\n}\n","/**\n * Арифметика реальных путей — внутренний словарь слоя `src/host/`, наружу не выходит.\n *\n * Здесь и только здесь живёт знание про формы реального пути: буквы дисков, корень ФС, склейка и\n * подъём вверх. `fs.ts` (перевод реального пути в виртуальный) и `root.ts` (поиск корня репозитория)\n * задают одни и те же вопросы; пока каждый отвечал на них у себя, «что считается диском» было\n * записано тремя литералами, и следующая правка кросс-платформенного поведения нашла бы не все.\n */\n\nimport path from 'node:path';\n\nimport { toPosix } from '@/path/index.js';\n\n/** Ведущий Windows-диск (`C:`) реального пути после `toPosix`. */\nconst DRIVE_PREFIX = /^[A-Za-z]:/;\n\n/** Путь — ровно диск без слэша (`C:`): для `win32` это не корень, а текущая директория диска. */\nfunction isDriveRoot(realPath: string): boolean {\n return realPath.length === 2 && DRIVE_PREFIX.test(realPath);\n}\n\n/**\n * Абсолютность реального пути: unix (`/…`) или Windows-диск (`C:…`) — уже после `toPosix`.\n *\n * `path.win32.isAbsolute` здесь не подходит: он считает неабсолютными drive-relative пути `C:` и\n * `C:foo`, а нам нужен признак «путь несёт диск или корень» — иначе `resolveRealPath` пытался бы\n * дорезолвить `C:` от `cwd`, и root `C:` (то, во что `normalizeRoot` превращает корень\n * Windows-ФС `C:/`) уехал бы в директорию запуска ESLint.\n */\nexport function isAbsoluteRealPath(realPath: string): boolean {\n return realPath.startsWith('/') || DRIVE_PREFIX.test(realPath);\n}\n\n/**\n * Нормализует root: `toPosix`, без завершающего слэша. Корень ФС (`/`, `C:/`) — как `''` / `C:`,\n * чтобы `root + vpath` не удваивал `/`.\n *\n * `node:path` тут не помощник: `normalize`/`resolve` сохраняют корень как `/` и `C:\\`, а нужна\n * именно строка-префикс для склейки `root + vpath`, для корня ФС пустая.\n */\nexport function normalizeRoot(root: string): string {\n const posix = toPosix(root);\n if (posix === '/') {\n return '';\n }\n\n return posix.endsWith('/') ? posix.slice(0, -1) : posix;\n}\n\n/** Соединяет директорию (в т.ч. корень `/` или `C:/`) с именем файла, не удваивая `/`. */\nexport function joinReal(dir: string, name: string): string {\n return dir.endsWith('/') ? `${dir}${name}` : `${dir}/${name}`;\n}\n\n/**\n * Директория выше `dir`. У корня ФС (`/`, `C:/`) родителя нет — возвращает сам корень, по чему\n * вызывающий код и определяет достижение верха.\n */\nexport function parentOf(dir: string): string {\n const idx = dir.lastIndexOf('/');\n if (idx === -1) {\n return dir;\n }\n\n const head = dir.slice(0, idx);\n if (head === '') {\n return '/';\n }\n if (isDriveRoot(head)) {\n return `${head}/`;\n }\n\n return head;\n}\n\n/**\n * Сравнение сегментов реального пути. Первый сегмент может быть Windows-диском — тогда буква\n * сравнивается без учёта регистра (`C:` и `c:` — один диск); остальные сегменты — строго:\n * регистрозависимость реальных ФС различается, и строгое сравнение — единственный ответ, не\n * зависящий от платформы запуска.\n */\nexport function sameSegment(left: string, right: string, index: number): boolean {\n if (index === 0 && DRIVE_PREFIX.test(left) && DRIVE_PREFIX.test(right)) {\n return left.toUpperCase() === right.toUpperCase();\n }\n\n return left === right;\n}\n\n/**\n * Резолвит `target` от `base` — оба реальные пути. `target` абсолютный (unix или Windows-диск) —\n * возвращается как есть; иначе схлопывается с `base` силами `node:path`.\n *\n * Ветка `node:path` выбирается по форме `base`, а не по текущей платформе: `win32.resolve` знает\n * про диски, `posix.resolve` — про unix-пути, и на абсолютном `base` обе чистые (к `process.cwd()`\n * они обращаются, только когда ни один аргумент не абсолютен). Один `win32.resolve` на оба случая\n * не годится: unix-путь без диска он достраивает диском из `process.cwd()`, и под Windows `/repo`\n * стал бы `C:/repo` — результат зависел бы от платформы, на которой запущен ESLint.\n *\n * Голый диск `C:` для `win32` — не корень, а «текущая директория диска C», поэтому перед резолвом\n * он дополняется до `C:/` (в таком виде root и приходит из `normalizeRoot`).\n */\nexport function resolveRealPath(base: string, target: string): string {\n const posixTarget = toPosix(target);\n if (isAbsoluteRealPath(posixTarget)) {\n return posixTarget;\n }\n\n const posixBase = toPosix(base);\n const resolveBase = isDriveRoot(posixBase) ? `${posixBase}/` : posixBase;\n const resolve = DRIVE_PREFIX.test(resolveBase) ? path.win32.resolve : path.posix.resolve;\n\n return toPosix(resolve(resolveBase, posixTarget));\n}\n","import { createLogger } from '@/debug.js';\nimport { toPosix } from '@/path/index.js';\n\nimport { joinReal, parentOf } from '@/host/real-path.js';\n\nconst debug = createLogger('root');\n\n/**\n * Подъём от `startDir` вверх, запоминает последнюю (самую верхнюю) директорию с `package.json`.\n * Останавливается на первой директории с `.git` (включительно — её `package.json` ещё учитывается)\n * или, если `.git` не встретился, на корне файловой системы. Ничего не нашли → `null`.\n *\n * Принимает реальный путь и реальный `exists` — работает до создания `FsHost`.\n */\nexport function findRepoRoot(startDir: string, exists: (path: string) => boolean): string | null {\n let current = toPosix(startDir);\n let found: string | null = null;\n\n debug('searching repo root from %s', current);\n\n for (;;) {\n if (exists(joinReal(current, 'package.json'))) {\n found = current;\n debug('found package.json at %s', current);\n }\n\n if (exists(joinReal(current, '.git'))) {\n debug('found .git at %s, stopping', current);\n break;\n }\n\n const parent = parentOf(current);\n if (parent === current) {\n debug('reached filesystem root at %s', current);\n break;\n }\n current = parent;\n }\n\n debug('resolved repo root: %o', found);\n return found;\n}\n","import { existsSync } from 'node:fs';\n\nimport { createLogger } from '@/debug.js';\nimport { ENTRY_FILE_NAMES } from '@/extensions.js';\nimport { dirname, joinSegments, segments, toPosix } from '@/path/index.js';\nimport { getRepoRoot } from '@/settings/index.js';\n\nimport {\n joinReal,\n normalizeRoot,\n resolveRealPath,\n sameSegment,\n isAbsoluteRealPath,\n} from '@/host/real-path.js';\nimport { findRepoRoot } from '@/host/root.js';\n\nconst debug = createLogger('fs');\n\n/** Граница ФС для правил: только виртуальные пути от корня репозитория (`/src/feature/x.ts`). */\nexport type FsHost = {\n /**\n * Есть ли в директории `dir` (виртуальный путь) точка входа — файл `index.<ext>` с одним из\n * `ENTRY_EXTENSIONS`. Именно этот вопрос, а не сырое «существует ли файл»: ответ на\n * директорию один, и кэшируется он одной записью вместо записи на каждое расширение.\n *\n * Границу намеренно держим узкой — примитив добавляется, когда его просит правило, а не\n * заранее: каждый метод здесь обязаны реализовать все фейки.\n */\n hasEntryPoint(dir: string): boolean;\n /** `null` — `realPath` вне root или не абсолютный. */\n toVirtual(realPath: string): string | null;\n};\n\ntype CacheEntry = { value: boolean; expiresAt: number };\ntype Cache = Map<string, CacheEntry>;\n\ntype CreateFsHostOptions = {\n now?: () => number;\n exists?: (realPath: string) => boolean;\n};\n\n/**\n * TTL положительного ответа. Десять минут: линт монорепы идёт минутами, а найденный `index.<ext>`\n * в рамках одного прогона никуда не девается — под работающим ESLint файлы не удаляют. Меньший TTL\n * просто заставлял бы перепроверять диск посреди прогона.\n */\nconst TTL_MS = 600_000;\n\n/**\n * TTL отрицательного ответа — заметно короче. Асимметрия отвечает асимметрии самих ответов:\n * «баррель есть» опровергается удалением файла, «барреля нет» — тем, что его дописали, а именно это\n * и делают в редакторе через секунды после того, как правило отрепортило нарушение. Долгий TTL\n * заставлял бы плагин в языковом сервере репортить уже созданный `index.<ext>` ещё десять минут.\n *\n * Кэш от этого не обесценивается: он нужен, чтобы отсутствующая ветка стоила одного обращения к ФС\n * вместо обращения на каждый сегмент, а весь спуск укладывается в линт одного файла — то есть в\n * миллисекунды. Пяти секунд хватает на пачку файлов, ссылающихся в одну и ту же мёртвую ветку.\n */\nconst NEGATIVE_TTL_MS = 5_000;\n\n/** Живое (непротухшее) значение кэша или `undefined`. */\nfunction peek(cache: Cache, key: string, time: number): boolean | undefined {\n const entry = cache.get(key);\n return entry !== undefined && entry.expiresAt > time ? entry.value : undefined;\n}\n\nfunction remember(cache: Cache, key: string, time: number, value: boolean): boolean {\n cache.set(key, { value, expiresAt: time + (value ? TTL_MS : NEGATIVE_TTL_MS) });\n return value;\n}\n\n/**\n * `createFsHost` — единственное место, знающее про реальные пути. Всё, что уходит вглубь правил, —\n * виртуальные пути; реальный root в публичный тип не входит, живёт в замыкании.\n */\nexport function createFsHost(root: string, options: CreateFsHostOptions = {}): FsHost {\n const normalizedRoot = normalizeRoot(root);\n const rootSegments = segments(normalizedRoot);\n const now = options.now ?? Date.now;\n const nativeExists = options.exists ?? existsSync;\n const entryPointCache: Cache = new Map();\n const directoryCache: Cache = new Map();\n\n /**\n * Реальный путь директории по виртуальному. Виртуальный путь всегда начинается с `/`, поэтому\n * для root-как-корня-ФС (`normalizedRoot === ''`) склейка сама даёт корректный путь от `/`.\n */\n function toRealDir(dir: string): string {\n return `${normalizedRoot}${dir}`;\n }\n\n function toVirtual(realPath: string): string | null {\n const posixPath = toPosix(realPath);\n if (!isAbsoluteRealPath(posixPath)) {\n return null;\n }\n\n const pathSegments = segments(posixPath);\n if (pathSegments.length < rootSegments.length) {\n return null;\n }\n for (let i = 0; i < rootSegments.length; i += 1) {\n if (!sameSegment(pathSegments[i] as string, rootSegments[i] as string, i)) {\n return null;\n }\n }\n\n return joinSegments(pathSegments.slice(rootSegments.length));\n }\n\n /**\n * Существует ли сама директория. Отдельный кэш и отдельный вопрос: несуществующие пути приходят\n * не поодиночке, а целыми ветками (промах алиаса, опечатка в специфаере, чужой корень) — и\n * каждая такая директория стоила бы перебора всех точек входа вместо одного обращения к ФС.\n */\n function directoryExists(dir: string): boolean {\n const time = now();\n const cached = peek(directoryCache, dir, time);\n if (cached !== undefined) {\n return cached;\n }\n\n if (missingParent(dir, time)) {\n // Родителя нет — значит нет и потомка, спрашивать диск не о чем.\n return remember(directoryCache, dir, time, false);\n }\n\n const found = nativeExists(toRealDir(dir));\n debug('check directory %s: %o', dir, found);\n return remember(directoryCache, dir, time, found);\n }\n\n /**\n * Известно ли уже, что родителя `dir` нет. Только по кэшу, без обращения к диску: спуск к цели\n * идёт сверху вниз, поэтому про родителя ответ к этому моменту есть — а несуществующие пути\n * приходят ветками, и без этого каждый сегмент отсутствующей ветки стоил бы своего обращения к\n * ФС. Отвечать на промах кэша рекурсивной проверкой предков не годится: она стоила бы обращения\n * на каждого предка там, где хватало одного.\n */\n function missingParent(dir: string, time: number): boolean {\n const parent = dirname(dir);\n if (parent === dir) {\n return false;\n }\n\n return peek(directoryCache, parent, time) === false;\n }\n\n /** Кэш по директории, с TTL: диск опрашивается на промахе или после истечения записи. */\n function hasEntryPoint(dir: string): boolean {\n if (!dir.startsWith('/')) {\n return false;\n }\n\n const time = now();\n const cached = peek(entryPointCache, dir, time);\n if (cached !== undefined) {\n return cached;\n }\n\n if (!directoryExists(dir)) {\n // Ответ запоминается и здесь: иначе каждый повторный вопрос про ту же отсутствующую\n // директорию заново проходил бы промах кэша точек входа, хотя ответ уже известен.\n return remember(entryPointCache, dir, time, false);\n }\n\n const realDir = toRealDir(dir);\n const found = ENTRY_FILE_NAMES.some((name) => nativeExists(joinReal(realDir, name)));\n debug('check entry point %s: %o', dir, found);\n return remember(entryPointCache, dir, time, found);\n }\n\n return { hasEntryPoint, toVirtual };\n}\n\nconst instanceCache = new Map<string, FsHost>();\nconst repoRootCache = new Map<string, string | null>();\n\nfunction findRepoRootCached(cwd: string): string | null {\n // `findRepoRoot` возвращает `string | null`, поэтому `undefined` однозначно значит «не кэшировано».\n const cached = repoRootCache.get(cwd);\n if (cached !== undefined) {\n return cached;\n }\n\n const found = findRepoRoot(cwd, existsSync);\n repoRootCache.set(cwd, found);\n return found;\n}\n\nfunction resolveRoot(settings: unknown, cwd: string, repoRootOverride?: unknown): string {\n const explicitRoot = getRepoRoot(settings, repoRootOverride);\n if (explicitRoot !== undefined) {\n return resolveRealPath(cwd, explicitRoot);\n }\n\n return findRepoRootCached(cwd) ?? cwd;\n}\n\n/**\n * `resolveRoot` + кэш инстансов по root — иначе TTL-кэш обращений к диску обнулялся бы на каждом\n * файле.\n *\n * `repoRootOverride` — значение repoRoot из опций правила; разбирает и проверяет его всё тот же\n * `getRepoRoot`, `src/host/` про формат конфига по-прежнему ничего не знает. Кэш инстансов ключуется\n * уже резолвнутым root, поэтому файлы с разным override не делят инстанс.\n */\nexport function getFsHost(settings: unknown, cwd: string, repoRootOverride?: unknown): FsHost {\n const root = resolveRoot(settings, cwd, repoRootOverride);\n\n const cached = instanceCache.get(root);\n if (cached) {\n return cached;\n }\n\n debug('getFsHost: creating instance for root %s', root);\n const created = createFsHost(root);\n instanceCache.set(root, created);\n return created;\n}\n\n/** Сбрасывает кэш инстансов `getFsHost` и мемоизацию `findRepoRoot` — для изоляции тестов. */\nexport function resetFsHostCaches(): void {\n instanceCache.clear();\n repoRootCache.clear();\n}\n","/**\n * Общий каркас правил: настройки WELD, объявленные один раз, и связка «ESLint-контекст → всё, что\n * правилу нужно до первого узла».\n *\n * Живёт выше `src/host/` и `src/settings/`: `getRepoRoot`/`getAliases` знают формат конфига,\n * `getFsHost` знает про диск, а решение «настройки правила перекрывают `settings.weld`» не\n * принадлежит ни тому, ни другому. Сами слои при этом не изолированы полностью: `host` берёт корень\n * репозитория через `getRepoRoot` — осознанная односторонняя зависимость `host → settings`\n * (см. `getFsHost` в `src/host/fs.ts`).\n *\n * Правило не перечисляет `repoRoot`/`aliasesBaseUrl`/`aliases` ни у себя в `meta.schema`, ни в типе\n * своих опций и не собирает `FsHost` руками — иначе четвёртая общая настройка потребовала бы правки\n * каждого правила по отдельности.\n */\n\nimport type { Rule } from 'eslint';\n\nimport type { FsHost } from '@/host/index.js';\nimport { getFsHost } from '@/host/index.js';\nimport type { Alias } from '@/settings/index.js';\nimport { getAliases } from '@/settings/index.js';\n\n/**\n * Свойства `meta.schema`, общие для всех правил: те же настройки, что и в `settings.weld`, но\n * заданные на самом правиле. Правило подмешивает их к своим спредом.\n */\nexport const WELD_OPTION_PROPERTIES = {\n repoRoot: { type: 'string' },\n aliasesBaseUrl: { type: 'string' },\n aliases: { type: 'object' },\n} as const;\n\n/**\n * Значения общих опций правила. Типы уже отсеяны `meta.schema` — в геттеры они идут как `unknown`,\n * а здесь нужны, чтобы подмешать общие опции к собственным опциям правила. Наружу тип не выходит:\n * правилу незачем его называть, пересечение делается здесь.\n */\ntype WeldOptions = {\n repoRoot?: string;\n aliasesBaseUrl?: string;\n aliases?: Record<string, unknown>;\n};\n\n/** Всё, что правило берёт из контекста до обхода AST. */\nexport type WeldContext<TOwnOptions = unknown> = {\n /** Файл, который сейчас линтуется, — виртуальный путь от корня репозитория. */\n fromFile: string;\n aliases: Alias[];\n fsHost: FsHost;\n /**\n * Опции правила, уже разобранные: собственные опции правила плюс общие. Правило читает их\n * отсюда, а не из `context.options[0]`: иначе каждое повторяло бы каст и дефолт, и форма опций\n * знала бы о себе в двух местах.\n */\n options: TOwnOptions & WeldOptions;\n};\n\n/**\n * `null` — линтуемый файл вне корня репозитория, и правилу нечего проверять: виртуального пути у\n * него нет.\n *\n * `fsHostOverride` — шов для тестов, общий на все правила: подменяет файловую систему целиком, и\n * тогда опция `repoRoot` ни на что не влияет. Шов именно параметром, а не модульным состоянием,\n * чтобы тесты не зависели от порядка запуска.\n *\n * `TOwnOptions` — только собственные опции правила: общие известны здесь и подмешиваются сами.\n */\nexport function resolveWeldContext<TOwnOptions = unknown>(\n context: Rule.RuleContext,\n fsHostOverride?: FsHost,\n): WeldContext<TOwnOptions> | null {\n const options = (context.options[0] ?? {}) as TOwnOptions & WeldOptions;\n const fsHost = fsHostOverride ?? getFsHost(context.settings, context.cwd, options.repoRoot);\n\n const fromFile = fsHost.toVirtual(context.filename);\n if (fromFile === null) {\n return null;\n }\n\n return {\n fromFile,\n aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl),\n fsHost,\n options,\n };\n}\n","/**\n * Арифметика границ модулей: находит первую точку входа (`index`), которую пробивает импорт из\n * `fromDir` в `targetPath`, поднимаясь от их общей директории вниз к цели.\n */\n\nimport { isEntryBasename } from '@/extensions.js';\nimport {\n basename,\n commonDepth,\n dirname,\n joinPath,\n joinSegments,\n segments,\n splitExtension,\n} from '@/path/index.js';\n\nfunction isIndexFile(targetPath: string): boolean {\n return isEntryBasename(splitExtension(basename(targetPath)).name);\n}\n\n/**\n * Возвращает виртуальный путь границы, которую пробивает импорт, либо `null`, если нарушения нет.\n * `hasEntryPoint` вызывается только для директорий строго между общим предком `fromDir`/`targetPath`\n * и `targetPath` — границы, внутри которых уже находится `fromDir`, не проверяются.\n */\nexport function findBarrier(\n fromDir: string,\n targetPath: string,\n hasEntryPoint: (dir: string) => boolean,\n): string | null {\n const targetDir = dirname(targetPath);\n const targetDirSegments = segments(targetDir);\n\n // Глубина общей директории — она же индекс первого сегмента ниже неё.\n const depth = commonDepth(fromDir, targetDir);\n\n // Спуск от общей директории вниз к цели: путь наращивается посегментно, а не пересобирается.\n let dir = joinSegments(targetDirSegments.slice(0, depth));\n for (let i = depth; i < targetDirSegments.length; i += 1) {\n dir = joinPath(dir, targetDirSegments[i] as string);\n if (hasEntryPoint(dir)) {\n const atTargetDir = i === targetDirSegments.length - 1;\n return atTargetDir && isIndexFile(targetPath) ? null : dir;\n }\n }\n\n return null;\n}\n","/**\n * Вход в ядро: собирает `parseSpecifier` → `findBarrier` → `renderSpecifier` в одну проверку.\n */\n\nimport { createLogger } from '@/debug.js';\nimport type { FsHost } from '@/host/index.js';\nimport { parseSpecifier, renderSpecifier } from '@/imports/index.js';\nimport { dirname } from '@/path/index.js';\nimport type { Alias } from '@/settings/index.js';\n\nimport { findBarrier } from '@/rules/no-barrel-bypass/core/barrier.js';\n\nconst debug = createLogger('no-barrel-bypass');\n\ntype CheckerInput = { fromFile: string; aliases: Alias[] };\n\nexport type Host = Pick<FsHost, 'hasEntryPoint'>;\n\n/**\n * Проверка одного специфаера: возвращает исправленный специфаер при нарушении границы, иначе `null`\n * — нарушения нет. Найденная граница наружу не отдаётся: она нужна только для рендера пути.\n *\n * Проверка собирается на файл, а не на импорт: `fromDir` от линтуемого файла не зависит от того,\n * какой специфаер сейчас разбирается.\n */\nexport function createChecker(\n { fromFile, aliases }: CheckerInput,\n host: Host,\n): (specifier: string) => string | null {\n const fromDir = dirname(fromFile);\n\n return function checkImport(specifier: string): string | null {\n const target = parseSpecifier(specifier, fromDir, aliases);\n if (target === null) {\n debug('%s: %s external dependency', fromFile, specifier);\n return null;\n }\n\n const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);\n if (barrier === null) {\n debug('%s: %s -> %s, no barrier on the way', fromFile, specifier, target.path);\n return null;\n }\n\n return renderSpecifier(target.form, fromDir, barrier, aliases, target.extension);\n };\n}\n","/**\n * Правило `weld/no-barrel-bypass`: ловит импорты, входящие внутрь модуля мимо его точки входа\n * (`index.<ext>`), и предлагает исправленный путь через `checkImport`.\n *\n * Всё, что не про баррели, — общее: настройки и `FsHost` даёт `resolveWeldContext`, обход и правку\n * специфаеров — `src/imports/nodes.ts`. Здесь остаётся вердикт и текст сообщения.\n */\n\nimport type { Rule } from 'eslint';\n\nimport { createLogger } from '@/debug.js';\nimport type { FsHost } from '@/host/index.js';\nimport type { SpecifierNode } from '@/imports/index.js';\nimport { createSpecifierVisitor, replaceSpecifier } from '@/imports/index.js';\nimport { WELD_OPTION_PROPERTIES, resolveWeldContext } from '@/rules/context.js';\n\nimport { createChecker } from '@/rules/no-barrel-bypass/pipeline.js';\n\nconst debug = createLogger('no-barrel-bypass');\n\nconst messages = {\n bypass: \"Import bypasses the module barrel. Use '{{suggestion}}' instead of '{{original}}'.\",\n useBarrel: \"Import through '{{suggestion}}'.\",\n};\n\n/**\n * Своя опция у правила одна; общие к ней подмешивает `resolveWeldContext`, см.\n * {@link WELD_OPTION_PROPERTIES}.\n */\ntype RuleOptions = { fix?: boolean };\n\n/**\n * `fsHost` — для инъекции в тестах; без него файловую систему собирает `resolveWeldContext` из\n * `context.settings`/`context.cwd` и опции `repoRoot`. При инъекции опция `repoRoot` ни на что не\n * влияет.\n */\nexport function createRule(fsHost?: FsHost): Rule.RuleModule {\n return {\n meta: {\n type: 'problem',\n docs: {\n description: 'disallow imports that reach into a module past its barrel',\n url: 'https://github.com/jt4d-lab/weld/blob/master/docs/rules/no-barrel-bypass.md',\n },\n fixable: 'code',\n hasSuggestions: true,\n schema: [\n {\n type: 'object',\n properties: {\n fix: { type: 'boolean' },\n ...WELD_OPTION_PROPERTIES,\n },\n additionalProperties: false,\n },\n ],\n messages,\n },\n create(context) {\n const weld = resolveWeldContext<RuleOptions>(context, fsHost);\n if (weld === null) {\n return {};\n }\n\n const { fromFile } = weld;\n const applyFix = weld.options.fix ?? true;\n const checkImport = createChecker(weld, weld.fsHost);\n\n return createSpecifierVisitor((sourceNode: SpecifierNode) => {\n const original = sourceNode.value;\n const suggestion = checkImport(original);\n\n if (suggestion === null) {\n return;\n }\n\n debug(\n '%s: crosses barrier, %s -> %s (fix=%o)',\n fromFile,\n original,\n suggestion,\n applyFix,\n );\n const fix = replaceSpecifier(sourceNode, suggestion);\n\n context.report({\n node: sourceNode,\n messageId: 'bypass',\n data: { suggestion, original },\n ...(applyFix\n ? { fix }\n : { suggest: [{ messageId: 'useBarrel', data: { suggestion }, fix }] }),\n });\n });\n },\n };\n}\n","import type { Rule } from 'eslint';\n\nimport { createRule as createNoBarrelBypassRule } from '@/rules/no-barrel-bypass/index.js';\n\nexport const rules = {\n 'no-barrel-bypass': createNoBarrelBypassRule(),\n} satisfies Record<string, Rule.RuleModule>;\n","import { createRequire } from 'node:module';\n\nimport type { ESLint, Linter } from 'eslint';\n\nimport { PACKAGE_NAME } from '@/debug.js';\nimport { rules } from '@/rules/index.js';\n\n/** Версию проставляет release-workflow перед упаковкой, поэтому она читается из `package.json`. */\nconst { version } = createRequire(import.meta.url)('../package.json') as { version: string };\n\nconst recommended: Linter.Config = {\n name: 'weld/recommended',\n rules: {\n 'weld/no-barrel-bypass': 'error',\n },\n};\n\nconst strict: Linter.Config = {\n name: 'weld/strict',\n rules: {\n 'weld/no-barrel-bypass': 'error',\n },\n};\n\nconst plugin = {\n meta: {\n name: PACKAGE_NAME,\n version,\n },\n rules,\n configs: {\n recommended,\n strict,\n },\n} satisfies ESLint.Plugin;\n\nrecommended.plugins = { weld: plugin };\nstrict.plugins = { weld: plugin };\n\nexport default plugin;\n"]}
1
+ {"version":3,"sources":["../src/debug.ts","../src/imports/nodes.ts","../src/extensions.ts","../src/path/posix.ts","../src/imports/specifier.ts","../src/settings/aliases.ts","../src/settings/weld.ts","../src/host/real-path.ts","../src/host/root.ts","../src/host/fs.ts","../src/tsconfig/load.ts","../src/rules/context.ts","../src/rules/no-barrel-bypass/core/barrier.ts","../src/rules/no-barrel-bypass/pipeline.ts","../src/rules/no-barrel-bypass/index.ts","../src/rules/index.ts","../src/index.ts"],"names":["path","debug","TTL_MS","cache","NEGATIVE_TTL_MS"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Debug-логирование плагина. Namespace строится здесь и только здесь: README объявляет\n * `DEBUG=eslint-plugin-weld:*` контрактом, а три независимых литерала по модулям этот контракт\n * ничем не удерживали.\n */\n\nimport createDebug from 'debug';\nimport type { Debugger } from 'debug';\n\n/** Имя пакета — оно же корень debug-namespace и `plugin.meta.name`. */\nexport const PACKAGE_NAME = 'eslint-plugin-weld';\n\n/** Логгер модуля: `scope` — короткое имя вроде `fs`, `root`, `aliases`. */\nexport function createLogger(scope: string): Debugger {\n return createDebug(`${PACKAGE_NAME}:${scope}`);\n}\n","/**\n * Обход импортов в AST: где в дереве лежит строка-специфаер и как её заменить. Про баррели,\n * границы модулей и любое конкретное правило слой не знает — это общая инфраструктура импортов,\n * как и `specifier.ts`.\n *\n * Здесь же зафиксированы решения, которые иначе каждое правило принимало бы заново: репортим на\n * самом литерале пути, а не на всём операторе; кавычку для правки берём из исходника; `require`\n * считается импортом, а `export ... from` — нет.\n */\n\nimport type { Rule } from 'eslint';\nimport type { Literal } from 'estree';\n\n/**\n * Строковый литерал специфаера — он же узел отчёта: сообщение указывается на самом пути, а не на\n * всём операторе импорта. `Rule.Node` в пересечении — ради `context.report`, `range` обязателен.\n */\nexport type SpecifierNode = Rule.Node & {\n type: 'Literal';\n value: string;\n raw: string;\n range: [number, number];\n};\n\nfunction isSpecifierNode(node: { type: string } | null | undefined): node is SpecifierNode {\n return (\n node != null &&\n node.type === 'Literal' &&\n typeof (node as unknown as Literal).value === 'string'\n );\n}\n\n/**\n * Слушатели для `create()`, вызывающие `onSpecifier` на каждом строковом специфаере импорта:\n * `import ... from`, динамический `import()` и `require()`.\n *\n * `export ... from` намеренно не обходится: у реэкспорта своя семантика границ, и правило,\n * которому он нужен, должно включить его осознанно, а не получить молча.\n */\nexport function createSpecifierVisitor(\n onSpecifier: (node: SpecifierNode) => void,\n): Rule.RuleListener {\n return {\n 'ImportDeclaration, ImportExpression'(\n node: Rule.Node & { source?: { type: string } | null },\n ) {\n if (isSpecifierNode(node.source)) {\n onSpecifier(node.source);\n }\n },\n CallExpression(node) {\n if (node.callee.type !== 'Identifier' || node.callee.name !== 'require') {\n return;\n }\n const arg = node.arguments[0];\n if (isSpecifierNode(arg)) {\n onSpecifier(arg);\n }\n },\n };\n}\n\n/**\n * Правка, подставляющая `specifier` вместо содержимого узла. Кавычка берётся из исходного текста —\n * правило не навязывает файлу свой стиль кавычек.\n */\nexport function replaceSpecifier(node: SpecifierNode, specifier: string): Rule.ReportFixer {\n const quote = node.raw[0] ?? \"'\";\n return (fixer) => fixer.replaceTextRange(node.range, `${quote}${specifier}${quote}`);\n}\n","/**\n * Словарь расширений файлов — общий для всех слоёв, как и `src/path/posix.ts`.\n *\n * Списка два, потому что понятия разные, хотя сегодня их состав совпадает. Точка входа — это файл,\n * который делает директорию модулем; модуль — это то, что вообще можно импортировать как код.\n * `.vue`/`.svelte` были бы модулями, но не обязательно точками входа; `.json` импортируется, но\n * баррелем не бывает. Одним списком на оба вопроса расширение любого из них молча меняло бы ответ\n * на второй.\n */\n\n/**\n * Имя файла точки входа без расширения. Ответ на тот же вопрос, что и {@link ENTRY_EXTENSIONS} —\n * какой файл делает директорию модулем, — поэтому живёт рядом с ним, а не литералом по слоям.\n */\nconst ENTRY_BASENAME = 'index';\n\n/** Расширения файла точки входа (`index.<ext>`) — по ним ищутся границы модулей. */\nexport const ENTRY_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mts', 'cts', 'mjs', 'cjs'] as const;\n\n/**\n * Расширения, при которых специфаер считается импортом кода внутрь репозитория. Специфаер с чужим\n * расширением (`./styles.css`, `./logo.svg`) правила не проверяют.\n */\nconst MODULE_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'mts', 'cts', 'mjs', 'cjs'] as const;\n\nconst ENTRY_EXTENSION_SET: ReadonlySet<string> = new Set(ENTRY_EXTENSIONS);\nconst MODULE_EXTENSION_SET: ReadonlySet<string> = new Set(MODULE_EXTENSIONS);\n\n/** Входит ли расширение (без точки) в {@link ENTRY_EXTENSIONS}. */\nfunction isEntryExtension(ext: string): boolean {\n return ENTRY_EXTENSION_SET.has(ext);\n}\n\n/** Имя файла точки входа с расширением: `index.ts`, `index.mjs`. */\nexport function entryFileName(ext: string): string {\n return `${ENTRY_BASENAME}.${ext}`;\n}\n\n/** Все имена файлов точки входа — перебор при поиске границы модуля на диске. */\nexport const ENTRY_FILE_NAMES: readonly string[] = ENTRY_EXTENSIONS.map(entryFileName);\n\n/** Имя файла (без директории) — точка входа? Расширение обязано входить в {@link ENTRY_EXTENSIONS}. */\nexport function isEntryFileName(name: string, ext: string): boolean {\n return isEntryBasename(name) && isEntryExtension(ext);\n}\n\n/**\n * Имя без расширения — точка входа? В отличие от {@link isEntryFileName} расширение не\n * спрашивается: специфаер может быть записан и без него (`./other/index`), а вопрос «пробивает ли\n * импорт границу» от расширения не зависит. Ответ живёт здесь, а не у спрашивающего: иначе «что\n * делает директорию модулем» знали бы два слоя и разъехались бы при первой же правке.\n */\nexport function isEntryBasename(name: string): boolean {\n return name === ENTRY_BASENAME;\n}\n\n/** Входит ли расширение (без точки) в {@link MODULE_EXTENSIONS}. */\nexport function isModuleExtension(ext: string): boolean {\n return MODULE_EXTENSION_SET.has(ext);\n}\n","/**\n * Сегментные операции над виртуальными путями, без `node:path`.\n *\n * Виртуальный путь — posix-строка от корня репозитория (`/src/feature/x.ts`), корень — `/`.\n * `toPosix` — исключение: работает с реальными путями на границе (`createFsHost`), до перевода в\n * виртуальные; остальные функции здесь принимают только виртуальные пути.\n */\n\n/** Заменяет `\\` на `/`. Не резолвит `.`/`..` и ничего не знает о виртуальных путях. */\nexport function toPosix(path: string): string {\n return path.replace(/\\\\/g, '/');\n}\n\n/** Непустые сегменты пути. Корень (`/`) и пустая строка дают пустой массив. */\nexport function segments(path: string): string[] {\n return path.split('/').filter(Boolean);\n}\n\n/** Собирает виртуальный путь из сегментов. Пустой список — корень (`/`). */\nexport function joinSegments(segs: string[]): string {\n return segs.length === 0 ? '/' : `/${segs.join('/')}`;\n}\n\n/**\n * Дописывает сегмент к виртуальной директории, не удваивая `/` у корня. Спуск по дереву наращивает\n * путь этой функцией вместо `joinSegments` от среза — знание «у корня разделителя не нужно» живёт\n * здесь, а не у каждого, кто идёт вниз.\n */\nexport function joinPath(dir: string, segment: string): string {\n return dir === '/' ? `/${segment}` : `${dir}/${segment}`;\n}\n\n/** Последний сегмент виртуального пути после последнего `/`. Корень даёт пустую строку. */\nexport function basename(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1);\n}\n\n/** Разбивает basename на имя и расширение (без точки). Расширения нет — `ext` пустой. */\nexport function splitExtension(base: string): { name: string; ext: string } {\n // Точка в начале — не расширение (`.storybook`), точка в конце — пустое расширение (`x.`).\n const dot = base.lastIndexOf('.');\n if (dot <= 0 || dot === base.length - 1) {\n return { name: base, ext: '' };\n }\n\n return { name: base.slice(0, dot), ext: base.slice(dot + 1) };\n}\n\n/** Директория выше `path`. У корня родителя нет — возвращает сам корень. */\nexport function dirname(path: string): string {\n const segs = segments(path);\n if (segs.length === 0) {\n return '/';\n }\n\n return joinSegments(segs.slice(0, -1));\n}\n\n/**\n * Резолвит `path` (относительный, с `.`/`..`, или абсолютный) от виртуальной директории `base`.\n * Выход `..` выше `/` — за пределами репозитория, невыразимо виртуальным путём — возвращает `null`.\n */\nexport function resolvePath(base: string, path: string): string | null {\n const segs = path.startsWith('/') ? [] : segments(base);\n\n for (const part of path.split('/')) {\n if (part === '' || part === '.') {\n continue;\n }\n if (part === '..') {\n if (segs.length === 0) {\n return null;\n }\n segs.pop();\n continue;\n }\n segs.push(part);\n }\n\n return joinSegments(segs);\n}\n\n/** Длина общего префикса двух списков сегментов. */\nfunction commonPrefixLength(segsA: string[], segsB: string[]): number {\n let i = 0;\n while (i < segsA.length && i < segsB.length && segsA[i] === segsB[i]) {\n i += 1;\n }\n\n return i;\n}\n\n/**\n * Глубина самой глубокой общей директории в сегментах. У виртуальных путей общий корень `/` есть\n * всегда, поэтому глубина — от `0`.\n *\n * Отдаётся числом, а не путём: спрашивают об общей директории только чтобы отсчитать от неё\n * сегменты, и собранная строка тут же разбиралась бы обратно.\n */\nexport function commonDepth(a: string, b: string): number {\n return commonPrefixLength(segments(a), segments(b));\n}\n\n/**\n * Путь от директории `from` до `to`, без префикса `./` (его добавляет `renderSpecifier`).\n * Совпадающие пути дают пустую строку.\n */\nexport function relativePath(from: string, to: string): string {\n const segsFrom = segments(from);\n const segsTo = segments(to);\n\n const i = commonPrefixLength(segsFrom, segsTo);\n\n const ups = Array<string>(segsFrom.length - i).fill('..');\n const downs = segsTo.slice(i);\n\n return [...ups, ...downs].join('/');\n}\n","/**\n * Соответствие «строка импорта ↔ виртуальный путь»: `parseSpecifier` разбирает специфаер в цель,\n * `renderSpecifier` собирает специфаер обратно в той же форме записи. Про барьеры, баррели и любое\n * другое правило слой не знает — это общая инфраструктура импортов.\n *\n * Строка импорта сама и есть путь: относительная резолвится от директории файла, алиасная —\n * подстановкой якоря. Резолвер не нужен — существование цели проверяет `import/no-unresolved`.\n */\n\nimport { entryFileName, isModuleExtension } from '@/extensions.js';\nimport { basename, relativePath, resolvePath, splitExtension } from '@/path/index.js';\nimport type { Alias } from '@/settings/index.js';\n\nexport type Form = { kind: 'relative' } | { kind: 'alias'; alias: Alias };\n\n/**\n * `extension` — расширение, явно записанное в специфаере (без точки), либо `null`. Это факт про\n * форму записи, а не про диск: в TS-проекте под Node-ESM пишут `./x.js`, хотя на диске `x.ts`.\n */\nexport type Target = { path: string; form: Form; extension: string | null };\n\n/**\n * `null`, если специфаер не выражает путь внутрь репозитория: голый пакет, `@scope/pkg`,\n * абсолютный специфаер, специфаер с `?`/`!`, последний сегмент с расширением вне\n * `MODULE_EXTENSIONS`, либо относительный подъём `..` выше виртуального корня.\n */\nexport function parseSpecifier(\n specifier: string,\n fromDir: string,\n aliases: Alias[],\n): Target | null {\n if (specifier.includes('?') || specifier.includes('!')) {\n return null;\n }\n\n if (isRelativeSpecifier(specifier)) {\n return finalize(resolvePath(fromDir, specifier), { kind: 'relative' });\n }\n\n const alias = matchAlias(specifier, aliases);\n if (alias !== null) {\n // `matchAlias` уже проверил границу по сегменту, поэтому хвост — либо пустой, либо `/…`;\n // ведущие слэши снимаются, чтобы `resolvePath` не принял хвост за абсолютный путь.\n const suffix = specifier.slice(alias.prefix.length).replace(/^\\/+/, '');\n return finalize(resolvePath(alias.anchor, suffix), { kind: 'alias', alias });\n }\n\n return null;\n}\n\nfunction isRelativeSpecifier(specifier: string): boolean {\n return (\n specifier === '.' ||\n specifier === '..' ||\n specifier.startsWith('./') ||\n specifier.startsWith('../')\n );\n}\n\n/**\n * Самый длинный алиас, подходящий по `key`; `key` возвращает `null`, если алиас не подходит. При\n * равной длине побеждает первая запись по порядку объявления.\n */\nfunction pickLongest(aliases: Alias[], key: (alias: Alias) => string | null): Alias | null {\n let best: Alias | null = null;\n let bestLength = -1;\n\n for (const alias of aliases) {\n const matched = key(alias);\n if (matched === null || matched.length <= bestLength) {\n continue;\n }\n best = alias;\n bestLength = matched.length;\n }\n\n return best;\n}\n\n/** Самый длинный подходящий префикс; при равных префиксах — первая запись по порядку. */\nfunction matchAlias(specifier: string, aliases: Alias[]): Alias | null {\n return pickLongest(aliases, (alias) => (covers(alias.prefix, specifier) ? alias.prefix : null));\n}\n\nfunction finalize(path: string | null, form: Form): Target | null {\n if (path === null) {\n return null;\n }\n\n const { ext } = splitExtension(basename(path));\n if (ext !== '' && !isModuleExtension(ext)) {\n return null;\n }\n\n return { path, form, extension: ext === '' ? null : ext };\n}\n\n/**\n * Обратный рендер: директория `barrier` (граница) в специфаер в форме исходного импорта.\n * Форма выигрывает у пути цели — путь про форму записи ничего не знает.\n *\n * `extension` (расширение исходного специфаера) продолжает ту же логику: импорт с явным\n * расширением рендерится в `<barrier>/index.<ext>`, потому что там, где расширения обязательны\n * (Node-ESM), импорт голой директории не резолвится. Расширение берётся из исходного импорта, а не\n * с диска: в TS под Node-ESM на диске лежит `index.ts`, а писать полагается `index.js`.\n */\nexport function renderSpecifier(\n form: Form,\n fromDir: string,\n barrier: string,\n aliases: Alias[],\n extension: string | null = null,\n): string {\n const dir = renderDirectory(form, fromDir, barrier, aliases);\n return extension === null ? dir : withEntry(dir, extension);\n}\n\nfunction renderDirectory(form: Form, fromDir: string, barrier: string, aliases: Alias[]): string {\n if (form.kind === 'relative') {\n return renderRelative(fromDir, barrier);\n }\n\n if (covers(form.alias.anchor, barrier)) {\n return renderAlias(form.alias, barrier);\n }\n\n const longest = longestCoveringAlias(aliases, barrier);\n return longest === null ? renderRelative(fromDir, barrier) : renderAlias(longest, barrier);\n}\n\n/** Дописывает точку входа к специфаеру директории, не удваивая `/`. */\nfunction withEntry(dir: string, extension: string): string {\n const separator = dir.endsWith('/') ? '' : '/';\n return `${dir}${separator}${entryFileName(extension)}`;\n}\n\n/**\n * `value` равен `prefix` или лежит под ним — граница проверяется по сегменту, а не по символам\n * (`@/features` не покрывает `@/features-old`). Один вопрос на обе стороны алиаса: слева от него\n * так сопоставляется префикс со специфаером, справа — якорь с директорией.\n */\nfunction covers(prefix: string, value: string): boolean {\n return value === prefix || value.startsWith(`${prefix}/`);\n}\n\n/** Самый длинный якорь, покрывающий `dir`; `null`, если ни один не покрывает. */\nfunction longestCoveringAlias(aliases: Alias[], dir: string): Alias | null {\n return pickLongest(aliases, (alias) => (covers(alias.anchor, dir) ? alias.anchor : null));\n}\n\nfunction renderAlias(alias: Alias, barrier: string): string {\n if (barrier === alias.anchor) {\n return alias.prefix;\n }\n return `${alias.prefix}/${relativePath(alias.anchor, barrier)}`;\n}\n\nfunction renderRelative(fromDir: string, barrier: string): string {\n const rel = relativePath(fromDir, barrier);\n return rel.startsWith('./') || rel.startsWith('../') ? rel : `./${rel}`;\n}\n","import { createLogger } from '@/debug.js';\nimport { isEntryFileName } from '@/extensions.js';\nimport { basename, resolvePath, splitExtension } from '@/path/index.js';\n\nconst debug = createLogger('aliases');\n\nexport type Alias = { prefix: string; anchor: string };\n\n/**\n * Разбирает значение формата `paths` из tsconfig — `settings.weld.aliases` или сами `paths` — в\n * список алиасов, готовых для `parseSpecifier`/`renderSpecifier`. Значение и база приходят уже\n * добытыми: их находит и кэширует `src/settings/weld.ts` (`getAliases` / `getAliasesFromPaths`), он\n * же решает, что делать с отсутствующей настройкой, поэтому `undefined` сюда не доходит.\n * `aliasesBaseUrl` — виртуальный путь от корня репозитория, не от `cwd`. `source` называет место\n * значения в сообщениях об ошибках.\n */\nexport function parseAliases(rawAliases: unknown, aliasesBaseUrl: string, source: string): Alias[] {\n if (typeof rawAliases !== 'object' || rawAliases === null || Array.isArray(rawAliases)) {\n throw new Error(`${source} must be an object`);\n }\n\n const base = resolvePath('/', aliasesBaseUrl) ?? '/';\n\n const result: Alias[] = [];\n const seen = new Set<string>();\n\n for (const [key, rawValue] of Object.entries(rawAliases)) {\n if (!hasValidStarShape(key)) {\n debug('%s: dropped %s — key must have no \"*\" or end with \"/*\"', source, key);\n continue;\n }\n const anchorsRaw = toAnchorList(key, rawValue, source);\n const prefix = stripStarSuffix(key);\n\n for (const anchorRaw of anchorsRaw) {\n if (!hasValidStarShape(anchorRaw)) {\n debug(\n '%s: dropped %s -> %s — target must have no \"*\" or end with \"/*\"',\n source,\n key,\n anchorRaw,\n );\n continue;\n }\n const anchor = normalizeAnchor(anchorRaw, base);\n if (anchor === null) {\n debug(\n '%s: dropped %s -> %s — not a directory (only index.<ext> stands for one) or above the repository root',\n source,\n key,\n anchorRaw,\n );\n continue;\n }\n\n const dedupeKey = `${prefix}\\0${anchor}`;\n if (seen.has(dedupeKey)) {\n continue;\n }\n seen.add(dedupeKey);\n result.push({ prefix, anchor });\n }\n }\n\n if (debug.enabled) {\n debug(\n `${source} (aliasesBaseUrl ${aliasesBaseUrl}) resolved to:\\n${formatAliases(result)}`,\n );\n }\n return result;\n}\n\n/** Для debug: по строке на алиас, `<префикс> - <якорь>`. Пустой список — явным словом. */\nfunction formatAliases(aliases: Alias[]): string {\n if (aliases.length === 0) {\n return '(none)';\n }\n\n return aliases.map(({ prefix, anchor }) => `${prefix} - ${anchor}`).join('\\n');\n}\n\n/** Значение записи `paths`: строка (частая опечатка) или массив строк. */\nfunction toAnchorList(key: string, value: unknown, source: string): string[] {\n if (typeof value === 'string') {\n return [value];\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(`${source}['${key}'] must contain only strings`);\n }\n }\n return value as string[];\n }\n\n throw new Error(`${source}['${key}'] must be a string or an array of strings`);\n}\n\n/**\n * Принимаются только записи без `*` или оканчивающиеся на `/*`; звёздочка в середине —\n * отбрасывается. `/*` тоже отбрасывается: `stripStarSuffix` дал бы пустой префикс/якорь, а пустая\n * строка — префикс любого абсолютного специфайера (`''` + `/` — начало любого `/foo`), из-за чего\n * запись стала бы вести себя как алиас на абсолютные пути, которые правило не обрабатывает.\n */\nexport function hasValidStarShape(value: string): boolean {\n const starCount = value.split('*').length - 1;\n if (starCount > 1 || (starCount === 1 && !value.endsWith('/*'))) {\n return false;\n }\n\n return stripStarSuffix(value) !== '';\n}\n\nexport function stripStarSuffix(value: string): string {\n return value.endsWith('/*') ? value.slice(0, -2) : value;\n}\n\n/**\n * Директория, которую обозначает запись `paths`. `null`, если запись — обычный файл\n * (не `index.<известное расширение>`) и директорией не выражается, либо якорь уходит `..`-подъёмом\n * выше виртуального корня.\n */\nfunction normalizeAnchor(anchorRaw: string, base: string): string | null {\n if (anchorRaw.endsWith('/*')) {\n return resolvePath(base, anchorRaw.slice(0, -2));\n }\n\n const lastSegment = basename(anchorRaw);\n const { name, ext } = splitExtension(lastSegment);\n\n if (ext === '') {\n return resolvePath(base, anchorRaw);\n }\n\n if (!isEntryFileName(name, ext)) {\n return null;\n }\n\n // Директория записи — сам `anchorRaw` без последнего сегмента; хвостовой `/` `resolvePath` съедает.\n return resolvePath(base, anchorRaw.slice(0, -lastSegment.length));\n}\n","/**\n * Единственная точка чтения `settings.weld`. Наружу слой отдаёт не сырую секцию, а геттер на каждую\n * настройку — `getRepoRoot` / `getAliasesBaseUrl` / `getAliases`; формат конфига за пределами\n * `src/settings/` не знает никто.\n *\n * Каждый геттер принимает необязательный `override` — значение этой настройки, подставляемое вместо\n * конфига. Оно тоже приходит из пользовательского конфига (опции правила), поэтому проверяется теми\n * же правилами, что и `settings.weld`; в сообщении об ошибке называется источник — `options.<имя>`\n * вместо `settings.weld.<имя>`.\n *\n * Здесь же живёт кэш разобранных алиасов, поэтому и второй вход в тот же разбор — `paths` из\n * tsconfig (`getAliasesFromPaths`) — идёт отсюда: решение «алиасы разбираются один раз на конфиг»\n * должно быть одно на оба источника.\n */\n\nimport type { Alias } from '@/settings/aliases.js';\nimport { parseAliases } from '@/settings/aliases.js';\n\n/** `aliasesBaseUrl` по умолчанию — сам корень репозитория. */\nconst DEFAULT_ALIASES_BASE_URL = '.';\n\n/** `settings.weld`, если задан. `undefined` без ошибки — секция необязательна. */\nfunction getWeldSettings(settings: unknown): Record<string, unknown> | undefined {\n if (typeof settings !== 'object' || settings === null) {\n return undefined;\n }\n\n const weld = (settings as Record<string, unknown>).weld;\n if (weld === undefined) {\n return undefined;\n }\n\n if (typeof weld !== 'object' || weld === null) {\n throw new Error('settings.weld must be an object');\n }\n\n return weld as Record<string, unknown>;\n}\n\nfunction requireString(value: unknown, source: string): string {\n if (typeof value !== 'string') {\n throw new Error(`${source} must be a string, got ${typeof value}`);\n }\n\n return value;\n}\n\n/**\n * `settings.weld.repoRoot` как он записан в конфиге — реальный путь, не виртуальный. Резолв\n * относительного значения и всё прочее знание о реальной ФС — за границей `src/host/`.\n * `undefined` — корень не задан, вызывающий ищет его сам.\n */\nexport function getRepoRoot(settings: unknown, override?: unknown): string | undefined {\n if (override !== undefined) {\n return requireString(override, 'options.repoRoot');\n }\n\n const repoRoot = getWeldSettings(settings)?.repoRoot;\n if (repoRoot === undefined) {\n return undefined;\n }\n\n return requireString(repoRoot, 'settings.weld.repoRoot');\n}\n\n/** `settings.weld.aliasesBaseUrl`; не задан — `'.'` (сам корень репозитория). */\nexport function getAliasesBaseUrl(settings: unknown, override?: unknown): string {\n if (override !== undefined) {\n return requireString(override, 'options.aliasesBaseUrl');\n }\n\n const aliasesBaseUrl = getWeldSettings(settings)?.aliasesBaseUrl;\n if (aliasesBaseUrl === undefined) {\n return DEFAULT_ALIASES_BASE_URL;\n }\n\n return requireString(aliasesBaseUrl, 'settings.weld.aliasesBaseUrl');\n}\n\nfunction readAliases(settings: unknown, override: unknown, baseUrlOverride: unknown): Alias[] {\n const fromOverride = override !== undefined;\n const rawAliases = fromOverride ? override : getWeldSettings(settings)?.aliases;\n if (rawAliases === undefined) {\n return [];\n }\n\n const source = fromOverride ? 'options.aliases' : 'settings.weld.aliases';\n return parseAliases(rawAliases, getAliasesBaseUrl(settings, baseUrlOverride), source);\n}\n\n/**\n * TTL записи кэша алиасов. Минута: сравнение по ссылке само по себе корректно (конфиг ESLint в\n * рамках прогона неизменен), поэтому TTL здесь не про свежесть ответа, а про то, чтобы записи не\n * жили вечно — иначе перечитанный конфиг языкового сервера навсегда оставлял бы в кэше и старый\n * объект `settings`, и разобранные по нему алиасы. Протухание посреди прогона ничего не стоит:\n * цена промаха — один разбор `paths`, десятки микросекунд.\n */\nconst TTL_MS = 60_000;\n\n/** Ключ записи — тройка аргументов разбора, сравниваемых по ссылке. */\ntype AliasesCacheKey = readonly [unknown, unknown, unknown];\n\ntype AliasesCacheEntry = { key: AliasesCacheKey; result: Alias[]; expiresAt: number };\n\n/**\n * Кэш разобранных алиасов, общий на оба источника (`settings.weld.aliases` и `paths` tsconfig):\n * правило спрашивает алиасы на каждый линтуемый файл, а аргументы приходят те же — в flat config\n * объект `settings` у всех файлов одного конфиг-блока один и тот же, опции правила тоже, а `paths`\n * отдаёт по ссылке кэш `src/tsconfig/`. Попадание избавляет от повторного разбора `paths` и от\n * повторного `debug`-лога о нём.\n *\n * Список с линейным поиском, а не `Map`: ключ здесь — тройка ссылок, строкового ключа у неё нет, а\n * собирать его пришлось бы обходом тех же `paths`. Записей при этом единицы — по одной на\n * конфиг-блок ESLint.\n */\nlet aliasesCache: AliasesCacheEntry[] = [];\n\nfunction sameKey(left: AliasesCacheKey, right: AliasesCacheKey): boolean {\n return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];\n}\n\n/**\n * Разбор через кэш. Запоминается только успешный разбор: на сломанном конфиге ESLint должен\n * ругаться на каждом файле, а не на первом. Протухшие записи выбрасываются здесь, а не по таймеру:\n * кэш растёт только на промахах, на промахе же и чистится.\n */\nfunction cachedAliases(key: AliasesCacheKey, parse: () => Alias[]): Alias[] {\n const time = Date.now();\n for (const entry of aliasesCache) {\n if (entry.expiresAt > time && sameKey(entry.key, key)) {\n return entry.result;\n }\n }\n\n const result = parse();\n aliasesCache = aliasesCache.filter(\n (entry) => entry.expiresAt > time && !sameKey(entry.key, key),\n );\n aliasesCache.push({ key, result, expiresAt: time + TTL_MS });\n return result;\n}\n\n/**\n * Алиасы из `settings.weld`. Нет `aliases` — `[]`.\n *\n * `baseUrlOverride` — override той настройки, от которой отсчитываются якоря: алиасы без\n * `aliasesBaseUrl` переопределить нельзя, эти два значения имеют смысл только в паре.\n *\n * Результат берётся из кэша (см. {@link aliasesCache}), поэтому при попадании возвращается тот же\n * массив, что и в прошлый раз — считается он неизменяемым, потребители его только читают.\n */\nexport function getAliases(\n settings: unknown,\n override?: unknown,\n baseUrlOverride?: unknown,\n): Alias[] {\n return cachedAliases([settings, override, baseUrlOverride], () =>\n readAliases(settings, override, baseUrlOverride),\n );\n}\n\n/**\n * Разбор внешних `paths` (формат `compilerOptions.paths` из tsconfig) в `Alias[]` — те же правила и\n * тот же кэш, что у `settings.weld.aliases`. Параметры: сами `paths`, виртуальная директория, от\n * которой отсчитываются записи, и `source` — имя источника значения в сообщениях об ошибках\n * (реальный путь tsconfig). На кривых значениях бросает так же, как разбор `settings.weld.aliases`,\n * — решение «глотать или падать» принадлежит вызывающему.\n */\nexport function getAliasesFromPaths(paths: unknown, virtualBase: string, source: string): Alias[] {\n return cachedAliases([paths, virtualBase, source], () =>\n parseAliases(paths, virtualBase, source),\n );\n}\n\n/**\n * Заданы ли алиасы явно — в `options.aliases` (`override`) или в `settings.weld.aliases`.\n * «Заданы» — это присутствие ключа: пустой `{}` тоже считается (и выключает автопоиск tsconfig).\n * При заданном `override` секция `settings` не читается — как в геттерах.\n */\nexport function hasAliases(settings: unknown, override?: unknown): boolean {\n if (override !== undefined) {\n return true;\n }\n\n return getWeldSettings(settings)?.aliases !== undefined;\n}\n","/**\n * Арифметика реальных путей — внутренний словарь слоя `src/host/`, наружу не выходит.\n *\n * Здесь и только здесь живёт знание про формы реального пути: буквы дисков, корень ФС, склейка и\n * подъём вверх. `fs.ts` (перевод реального пути в виртуальный) и `root.ts` (поиск корня репозитория)\n * задают одни и те же вопросы; пока каждый отвечал на них у себя, «что считается диском» было\n * записано тремя литералами, и следующая правка кросс-платформенного поведения нашла бы не все.\n */\n\nimport path from 'node:path';\n\nimport { commonDepth, joinSegments, segments, toPosix } from '@/path/index.js';\n\n/** Ведущий Windows-диск (`C:`) реального пути после `toPosix`. */\nconst DRIVE_PREFIX = /^[A-Za-z]:/;\n\n/** Путь — ровно диск без слэша (`C:`): для `win32` это не корень, а текущая директория диска. */\nfunction isDriveRoot(realPath: string): boolean {\n return realPath.length === 2 && DRIVE_PREFIX.test(realPath);\n}\n\n/**\n * Абсолютность реального пути: unix (`/…`) или Windows-диск (`C:…`) — уже после `toPosix`.\n *\n * `path.win32.isAbsolute` здесь не подходит: он считает неабсолютными drive-relative пути `C:` и\n * `C:foo`, а нам нужен признак «путь несёт диск или корень» — иначе `resolveRealPath` пытался бы\n * дорезолвить `C:` от `cwd`, и root `C:` (то, во что `normalizeRoot` превращает корень\n * Windows-ФС `C:/`) уехал бы в директорию запуска ESLint.\n */\nexport function isAbsoluteRealPath(realPath: string): boolean {\n return realPath.startsWith('/') || DRIVE_PREFIX.test(realPath);\n}\n\n/**\n * Нормализует root: `toPosix`, без завершающего слэша. Корень ФС (`/`, `C:/`) — как `''` / `C:`,\n * чтобы `root + vpath` не удваивал `/`.\n *\n * `node:path` тут не помощник: `normalize`/`resolve` сохраняют корень как `/` и `C:\\`, а нужна\n * именно строка-префикс для склейки `root + vpath`, для корня ФС пустая.\n */\nexport function normalizeRoot(root: string): string {\n const posix = toPosix(root);\n if (posix === '/') {\n return '';\n }\n\n return posix.endsWith('/') ? posix.slice(0, -1) : posix;\n}\n\n/** Соединяет директорию (в т.ч. корень `/` или `C:/`) с именем файла, не удваивая `/`. */\nexport function joinReal(dir: string, name: string): string {\n return dir.endsWith('/') ? `${dir}${name}` : `${dir}/${name}`;\n}\n\n/**\n * Директория выше `dir`. У корня ФС (`/`, `C:/`) родителя нет — возвращает сам корень, по чему\n * вызывающий код и определяет достижение верха.\n */\nexport function parentOf(dir: string): string {\n const idx = dir.lastIndexOf('/');\n if (idx === -1) {\n return dir;\n }\n\n const head = dir.slice(0, idx);\n if (head === '') {\n return '/';\n }\n if (isDriveRoot(head)) {\n return `${head}/`;\n }\n\n return head;\n}\n\n/**\n * Сравнение сегментов реального пути. Первый сегмент может быть Windows-диском — тогда буква\n * сравнивается без учёта регистра (`C:` и `c:` — один диск); остальные сегменты — строго:\n * регистрозависимость реальных ФС различается, и строгое сравнение — единственный ответ, не\n * зависящий от платформы запуска.\n */\nexport function sameSegment(left: string, right: string, index: number): boolean {\n if (index === 0 && DRIVE_PREFIX.test(left) && DRIVE_PREFIX.test(right)) {\n return left.toUpperCase() === right.toUpperCase();\n }\n\n return left === right;\n}\n\n/**\n * Самая глубокая общая директория двух реальных posix-путей. Первым сегментом может быть\n * Windows-диск (буква сравнивается без учёта регистра — тот же `sameSegment`): пути с разными\n * префиксами (разные диски, диск против unix-корня) общей директории не имеют — `null`.\n *\n * Диск снимается до сравнения сегментов, поэтому остаток — обычный posix-путь, и общую глубину\n * считает `commonDepth` из `src/path/`; про диски знает только код выше.\n */\nexport function commonRealDirectory(left: string, right: string): string | null {\n const prefixOf = (candidate: string): string => DRIVE_PREFIX.exec(candidate)?.[0] ?? '';\n const prefix = prefixOf(left);\n const rightPrefix = prefixOf(right);\n if (!sameSegment(prefix, rightPrefix, 0)) {\n return null;\n }\n\n const leftRest = left.slice(prefix.length);\n const depth = commonDepth(leftRest, right.slice(rightPrefix.length));\n\n return `${prefix}${joinSegments(segments(leftRest).slice(0, depth))}`;\n}\n\n/**\n * Резолвит `target` от `base` — оба реальные пути. `target` абсолютный (unix или Windows-диск) —\n * возвращается как есть; иначе схлопывается с `base` силами `node:path`.\n *\n * Ветка `node:path` выбирается по форме `base`, а не по текущей платформе: `win32.resolve` знает\n * про диски, `posix.resolve` — про unix-пути, и на абсолютном `base` обе чистые (к `process.cwd()`\n * они обращаются, только когда ни один аргумент не абсолютен). Один `win32.resolve` на оба случая\n * не годится: unix-путь без диска он достраивает диском из `process.cwd()`, и под Windows `/repo`\n * стал бы `C:/repo` — результат зависел бы от платформы, на которой запущен ESLint.\n *\n * Голый диск `C:` для `win32` — не корень, а «текущая директория диска C», поэтому перед резолвом\n * он дополняется до `C:/` (в таком виде root и приходит из `normalizeRoot`).\n */\nexport function resolveRealPath(base: string, target: string): string {\n const posixTarget = toPosix(target);\n if (isAbsoluteRealPath(posixTarget)) {\n return posixTarget;\n }\n\n const posixBase = toPosix(base);\n const resolveBase = isDriveRoot(posixBase) ? `${posixBase}/` : posixBase;\n const resolve = DRIVE_PREFIX.test(resolveBase) ? path.win32.resolve : path.posix.resolve;\n\n return toPosix(resolve(resolveBase, posixTarget));\n}\n","import { createLogger } from '@/debug.js';\nimport { toPosix } from '@/path/index.js';\n\nimport { joinReal, parentOf } from '@/host/real-path.js';\n\nconst debug = createLogger('root');\n\n/**\n * Подъём от `startDir` вверх, запоминает последнюю (самую верхнюю) директорию с `package.json`.\n * Останавливается на первой директории с `.git` (включительно — её `package.json` ещё учитывается)\n * или, если `.git` не встретился, на корне файловой системы. Ничего не нашли → `null`.\n *\n * Принимает реальный путь и реальный `exists` — работает до создания `FsHost`.\n */\nexport function findRepoRoot(startDir: string, exists: (path: string) => boolean): string | null {\n let current = toPosix(startDir);\n let found: string | null = null;\n\n debug('searching repo root from %s', current);\n\n for (;;) {\n if (exists(joinReal(current, 'package.json'))) {\n found = current;\n debug('found package.json at %s', current);\n }\n\n if (exists(joinReal(current, '.git'))) {\n debug('found .git at %s, stopping', current);\n break;\n }\n\n const parent = parentOf(current);\n if (parent === current) {\n debug('reached filesystem root at %s', current);\n break;\n }\n current = parent;\n }\n\n debug('resolved repo root: %o', found);\n return found;\n}\n","import { existsSync } from 'node:fs';\n\nimport { createLogger } from '@/debug.js';\nimport { ENTRY_FILE_NAMES } from '@/extensions.js';\nimport { dirname, joinSegments, segments, toPosix } from '@/path/index.js';\nimport { getRepoRoot } from '@/settings/index.js';\n\nimport {\n commonRealDirectory,\n joinReal,\n normalizeRoot,\n resolveRealPath,\n sameSegment,\n isAbsoluteRealPath,\n} from '@/host/real-path.js';\nimport { findRepoRoot } from '@/host/root.js';\n\nconst debug = createLogger('fs');\n\n/** Граница ФС для правил: только виртуальные пути от корня репозитория (`/src/feature/x.ts`). */\nexport type FsHost = {\n /**\n * Есть ли в директории `dir` (виртуальный путь) точка входа — файл `index.<ext>` с одним из\n * `ENTRY_EXTENSIONS`. Именно этот вопрос, а не сырое «существует ли файл»: ответ на\n * директорию один, и кэшируется он одной записью вместо записи на каждое расширение.\n *\n * Границу намеренно держим узкой — примитив добавляется, когда его просит правило, а не\n * заранее: каждый метод здесь обязаны реализовать все фейки.\n */\n hasEntryPoint(dir: string): boolean;\n /** `null` — `realPath` вне root или не абсолютный. */\n toVirtual(realPath: string): string | null;\n};\n\ntype CacheEntry = { value: boolean; expiresAt: number };\ntype Cache = Map<string, CacheEntry>;\n\ntype CreateFsHostOptions = {\n now?: () => number;\n exists?: (realPath: string) => boolean;\n};\n\n/**\n * TTL положительного ответа. Десять минут: линт монорепы идёт минутами, а найденный `index.<ext>`\n * в рамках одного прогона никуда не девается — под работающим ESLint файлы не удаляют. Меньший TTL\n * просто заставлял бы перепроверять диск посреди прогона.\n */\nconst TTL_MS = 600_000;\n\n/**\n * TTL отрицательного ответа — заметно короче. Асимметрия отвечает асимметрии самих ответов:\n * «баррель есть» опровергается удалением файла, «барреля нет» — тем, что его дописали, а именно это\n * и делают в редакторе через секунды после того, как правило отрепортило нарушение. Долгий TTL\n * заставлял бы плагин в языковом сервере репортить уже созданный `index.<ext>` ещё десять минут.\n *\n * Кэш от этого не обесценивается: он нужен, чтобы отсутствующая ветка стоила одного обращения к ФС\n * вместо обращения на каждый сегмент, а весь спуск укладывается в линт одного файла — то есть в\n * миллисекунды. Пяти секунд хватает на пачку файлов, ссылающихся в одну и ту же мёртвую ветку.\n */\nconst NEGATIVE_TTL_MS = 5_000;\n\n/** Живое (непротухшее) значение кэша или `undefined`. */\nfunction peek(cache: Cache, key: string, time: number): boolean | undefined {\n const entry = cache.get(key);\n return entry !== undefined && entry.expiresAt > time ? entry.value : undefined;\n}\n\nfunction remember(cache: Cache, key: string, time: number, value: boolean): boolean {\n cache.set(key, { value, expiresAt: time + (value ? TTL_MS : NEGATIVE_TTL_MS) });\n return value;\n}\n\n/**\n * `createFsHost` — единственное место, знающее про реальные пути. Всё, что уходит вглубь правил, —\n * виртуальные пути; реальный root в публичный тип не входит, живёт в замыкании.\n */\nexport function createFsHost(root: string, options: CreateFsHostOptions = {}): FsHost {\n const normalizedRoot = normalizeRoot(root);\n const rootSegments = segments(normalizedRoot);\n const now = options.now ?? Date.now;\n const nativeExists = options.exists ?? existsSync;\n const entryPointCache: Cache = new Map();\n const directoryCache: Cache = new Map();\n\n /**\n * Реальный путь директории по виртуальному. Виртуальный путь всегда начинается с `/`, поэтому\n * для root-как-корня-ФС (`normalizedRoot === ''`) склейка сама даёт корректный путь от `/`.\n */\n function toRealDir(dir: string): string {\n return `${normalizedRoot}${dir}`;\n }\n\n function toVirtual(realPath: string): string | null {\n const posixPath = toPosix(realPath);\n if (!isAbsoluteRealPath(posixPath)) {\n return null;\n }\n\n const pathSegments = segments(posixPath);\n if (pathSegments.length < rootSegments.length) {\n return null;\n }\n for (let i = 0; i < rootSegments.length; i += 1) {\n if (!sameSegment(pathSegments[i] as string, rootSegments[i] as string, i)) {\n return null;\n }\n }\n\n return joinSegments(pathSegments.slice(rootSegments.length));\n }\n\n /**\n * Существует ли сама директория. Отдельный кэш и отдельный вопрос: несуществующие пути приходят\n * не поодиночке, а целыми ветками (промах алиаса, опечатка в специфаере, чужой корень) — и\n * каждая такая директория стоила бы перебора всех точек входа вместо одного обращения к ФС.\n */\n function directoryExists(dir: string): boolean {\n const time = now();\n const cached = peek(directoryCache, dir, time);\n if (cached !== undefined) {\n return cached;\n }\n\n if (missingParent(dir, time)) {\n // Родителя нет — значит нет и потомка, спрашивать диск не о чем.\n return remember(directoryCache, dir, time, false);\n }\n\n const found = nativeExists(toRealDir(dir));\n debug('check directory %s: %o', dir, found);\n return remember(directoryCache, dir, time, found);\n }\n\n /**\n * Известно ли уже, что родителя `dir` нет. Только по кэшу, без обращения к диску: спуск к цели\n * идёт сверху вниз, поэтому про родителя ответ к этому моменту есть — а несуществующие пути\n * приходят ветками, и без этого каждый сегмент отсутствующей ветки стоил бы своего обращения к\n * ФС. Отвечать на промах кэша рекурсивной проверкой предков не годится: она стоила бы обращения\n * на каждого предка там, где хватало одного.\n */\n function missingParent(dir: string, time: number): boolean {\n const parent = dirname(dir);\n if (parent === dir) {\n return false;\n }\n\n return peek(directoryCache, parent, time) === false;\n }\n\n /** Кэш по директории, с TTL: диск опрашивается на промахе или после истечения записи. */\n function hasEntryPoint(dir: string): boolean {\n if (!dir.startsWith('/')) {\n return false;\n }\n\n const time = now();\n const cached = peek(entryPointCache, dir, time);\n if (cached !== undefined) {\n return cached;\n }\n\n if (!directoryExists(dir)) {\n // Ответ запоминается и здесь: иначе каждый повторный вопрос про ту же отсутствующую\n // директорию заново проходил бы промах кэша точек входа, хотя ответ уже известен.\n return remember(entryPointCache, dir, time, false);\n }\n\n const realDir = toRealDir(dir);\n const found = ENTRY_FILE_NAMES.some((name) => nativeExists(joinReal(realDir, name)));\n debug('check entry point %s: %o', dir, found);\n return remember(entryPointCache, dir, time, found);\n }\n\n return { hasEntryPoint, toVirtual };\n}\n\nconst instanceCache = new Map<string, FsHost>();\nconst repoRootCache = new Map<string, string | null>();\n\nfunction findRepoRootCached(cwd: string): string | null {\n // `findRepoRoot` возвращает `string | null`, поэтому `undefined` однозначно значит «не кэшировано».\n const cached = repoRootCache.get(cwd);\n if (cached !== undefined) {\n return cached;\n }\n\n const found = findRepoRoot(cwd, existsSync);\n repoRootCache.set(cwd, found);\n return found;\n}\n\nfunction resolveRoot(\n settings: unknown,\n cwd: string,\n repoRootOverride: unknown,\n coverDirs: string[],\n): string {\n const explicitRoot = getRepoRoot(settings, repoRootOverride);\n if (explicitRoot !== undefined) {\n // Явный root — воля пользователя; инвариант «root покрывает якоря» тут держит не подъём\n // root, а отбрасывание непокрытых якорей существующей валидацией у вызывающего.\n return resolveRealPath(cwd, explicitRoot);\n }\n\n const base = toPosix(findRepoRootCached(cwd) ?? cwd);\n let root = base;\n for (const coverDir of coverDirs) {\n const combined = commonRealDirectory(root, toPosix(coverDir));\n if (combined === null) {\n debug(\n 'resolveRoot: cover dir %s shares no prefix with root %s, ignoring',\n coverDir,\n root,\n );\n continue;\n }\n root = combined;\n }\n\n if (root !== base) {\n debug('resolveRoot: auto root raised from %s to %s to cover requested dirs', base, root);\n }\n\n return root;\n}\n\n/**\n * `resolveRoot` + кэш инстансов по root — иначе TTL-кэш обращений к диску обнулялся бы на каждом\n * файле.\n *\n * `repoRootOverride` — значение repoRoot из опций правила; разбирает и проверяет его всё тот же\n * `getRepoRoot`, `src/host/` про формат конфига по-прежнему ничего не знает. Кэш инстансов ключуется\n * уже резолвнутым root, поэтому файлы с разным override (или разными `coverDirs`) не делят инстанс.\n *\n * `coverDirs` — реальные директории, которые root обязан покрыть (якоря алиасов из tsconfig).\n * Действует только при автоопределении root: итог — общая директория `findRepoRoot(cwd) ?? cwd` и\n * всех `coverDirs`; директория без общего префикса (другой диск) игнорируется с debug. Явный root\n * побеждает и игнорирует параметр целиком.\n */\nexport function getFsHost(\n settings: unknown,\n cwd: string,\n repoRootOverride?: unknown,\n coverDirs: string[] = [],\n): FsHost {\n const root = resolveRoot(settings, cwd, repoRootOverride, coverDirs);\n\n const cached = instanceCache.get(root);\n if (cached) {\n return cached;\n }\n\n debug('getFsHost: creating instance for root %s', root);\n const created = createFsHost(root);\n instanceCache.set(root, created);\n return created;\n}\n\n/** Сбрасывает кэш инстансов `getFsHost` и мемоизацию `findRepoRoot` — для изоляции тестов. */\nexport function resetFsHostCaches(): void {\n instanceCache.clear();\n repoRootCache.clear();\n}\n","/**\n * Автопоиск ближайшего `tsconfig.json` и извлечение `compilerOptions.paths`.\n *\n * Второй «пограничный» модуль наряду с `src/host/`: знает формат tsconfig **и** читает диск (через\n * `get-tsconfig`). Наружу отдаёт **реальные** пути: виртуализация возможна только после фиксации\n * root, а root у вызывающей стороны сам зависит от найденных здесь якорей.\n *\n * Любая проблема tsconfig (не нашёлся, не распарсился, нет `paths`) — это `null`, а не исключение:\n * кривой tsconfig не должен валить линт, причина уходит в debug.\n */\n\nimport path from 'node:path';\n\nimport { getTsconfig } from 'get-tsconfig';\nimport type { TsConfigResult } from 'get-tsconfig';\n\nimport { createLogger } from '@/debug.js';\nimport { segments, toPosix } from '@/path/index.js';\nimport { hasValidStarShape, stripStarSuffix } from '@/settings/index.js';\n\nconst debug = createLogger('tsconfig');\n\n/** Результат поиска: сырые `paths` (валидация — дело `src/settings/`) и реальные пути. */\nexport type TsconfigPaths = {\n /** `compilerOptions.paths` как есть — формат значений проверяет вызывающая сторона. */\n paths: Record<string, unknown>;\n /** Реальная директория, от которой отсчитываются записи `paths`: директория конфига + его `baseUrl`. */\n realBaseDir: string;\n /** Реальный путь найденного tsconfig — для `source` сообщений разбора и debug. */\n configPath: string;\n /**\n * Реальные якоря записей `paths` — для инварианта «root покрывает якоря»: каждое строковое\n * значение, резолвнутое от `realBaseDir` без хвоста `/*`. Якоря внутри `node_modules` исключены:\n * root они поднимать не должны (в сами алиасы такие записи всё равно попадают — их судьбу решает\n * общая валидация у вызывающего).\n */\n realAnchors: string[];\n};\n\ntype CacheEntry = { value: TsconfigPaths | null; expiresAt: number };\n\ntype LoadTsconfigOptions = {\n /** Шов для тестов TTL — по образцу `createFsHost`. */\n now?: () => number;\n /** Шов для тестов: подмена чтения диска (по умолчанию — `getTsconfig` из `get-tsconfig`). */\n read?: (searchDir: string) => TsConfigResult | null;\n};\n\n/**\n * TTL найденного результата в мемоизации «директория файла → результат», то же значение, что\n * `TTL_MS` в `src/host/fs.ts`: tsconfig правят руками чаще, чем создают `index.ts`, а вечный кэш в\n * долгоживущем ESLint редактора неприемлем.\n */\nconst TTL_MS = 600_000;\n\n/**\n * TTL отрицательного результата (`null`) — заметно короче, та же асимметрия, что у\n * `NEGATIVE_TTL_MS` в `src/host/fs.ts`: «paths есть» опровергается их правкой раз в десять минут —\n * терпимо, а «paths нет» — тем, что их дописали (или починили битый tsconfig), и именно это делает\n * в редакторе пользователь, настраивающий алиасы. Долгий TTL заставлял бы автопоиск игнорировать\n * только что созданный tsconfig ещё десять минут.\n */\nconst NEGATIVE_TTL_MS = 5_000;\n\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Ищет ближайший `tsconfig.json` вверх от директории `realFilePath` (реальный путь линтуемого\n * файла) и возвращает его `paths` с базой. Неабсолютный/синтетический путь (`<input>` из\n * RuleTester) → сразу `null`. Отсев «tsconfig вне явного root» — дело вызывающей стороны.\n */\nexport function loadTsconfigPaths(\n realFilePath: string,\n options: LoadTsconfigOptions = {},\n): TsconfigPaths | null {\n if (!path.isAbsolute(realFilePath)) {\n debug('skip tsconfig lookup: file path is not absolute: %s', realFilePath);\n return null;\n }\n\n const dir = path.dirname(realFilePath);\n const now = options.now ?? Date.now;\n\n const time = now();\n const entry = cache.get(dir);\n if (entry && entry.expiresAt > time) {\n return entry.value;\n }\n\n // Кэш самого `get-tsconfig` (`Map` из его API) живёт **один** промах TTL-кэша: внутри вызова он\n // спасает от повторного чтения `extends`-цепочки, но переживи он TTL-запись — `getTsconfig`\n // после истечения TTL отдавал бы устаревшее содержимое файлов, и правки tsconfig не\n // подхватывались бы до конца жизни процесса.\n const readCache = new Map<string, unknown>();\n const read =\n options.read ?? ((searchDir: string) => getTsconfig(searchDir, 'tsconfig.json', readCache));\n\n const value = readTsconfigPaths(dir, read);\n cache.set(dir, { value, expiresAt: time + (value ? TTL_MS : NEGATIVE_TTL_MS) });\n return value;\n}\n\nfunction readTsconfigPaths(\n dir: string,\n read: (searchDir: string) => TsConfigResult | null,\n): TsconfigPaths | null {\n let found: TsConfigResult | null;\n try {\n found = read(dir);\n } catch (error) {\n debug('failed to read tsconfig near %s: %s', dir, error);\n return null;\n }\n\n if (!found) {\n debug('no tsconfig found upwards from %s', dir);\n return null;\n }\n\n const configPath = found.path;\n const compilerOptions: Record<string, unknown> = found.config.compilerOptions ?? {};\n const paths = compilerOptions['paths'];\n if (typeof paths !== 'object' || paths === null || Object.keys(paths).length === 0) {\n debug('tsconfig %s has no compilerOptions.paths', configPath);\n return null;\n }\n\n // `get-tsconfig` уже переписал `baseUrl` относительно найденного конфига (включая `extends`\n // и `${configDir}`). Без `baseUrl` записи `paths` по правилам TypeScript отсчитываются от\n // директории конфига, **объявившего** `paths` (при `extends` это не обязательно найденный\n // конфиг) — её `get-tsconfig` отдаёт symbol-свойством `implicitBaseUrl` на `compilerOptions`.\n const baseUrl =\n typeof compilerOptions['baseUrl'] === 'string' ? compilerOptions['baseUrl'] : undefined;\n const realBaseDir =\n baseUrl !== undefined\n ? path.resolve(path.dirname(configPath), baseUrl)\n : (getImplicitBaseUrl(compilerOptions) ?? path.dirname(configPath));\n\n const record = relativizePathsValues(paths as Record<string, unknown>, realBaseDir);\n debug('found paths in %s, base dir %s', configPath, realBaseDir);\n return {\n paths: record,\n realBaseDir,\n configPath,\n realAnchors: collectRealAnchors(record, realBaseDir),\n };\n}\n\n/**\n * `implicitBaseUrl` — приватный symbol `get-tsconfig` на `compilerOptions`: реальная директория\n * конфига, объявившего `paths` (его собственный `createPathsMatcher` резолвит от неё же).\n *\n * Завязка на внутренности `get-tsconfig` осознанная: диапазон `^4.14.3` в `package.json` выбран под\n * это поведение, а его исчезновение поймает тест про `extends` без `baseUrl` в `load.test.ts` —\n * тогда сработал бы fallback на директорию найденного конфига, и якоря записей `paths` из\n * `extends`-базы посчитались бы от чужой директории.\n */\nfunction getImplicitBaseUrl(compilerOptions: object): string | undefined {\n const symbol = Object.getOwnPropertySymbols(compilerOptions).find(\n (candidate) => candidate.description === 'implicitBaseUrl',\n );\n const value =\n symbol === undefined ? undefined : (compilerOptions as Record<symbol, unknown>)[symbol];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Реальный абсолютный путь: unix (`/…`) или Windows-диск (`C:…`). Сосед `isAbsoluteRealPath` из\n * `src/host/fs.ts` отвечает на тот же вопрос, но про уже нормализованные `toPosix`-пути и наружу из\n * слоя не выходит; здесь проверяются сырые строки из tsconfig — два «пограничных» модуля держат по\n * своей проверке.\n */\nconst ABSOLUTE_REAL_PATH = /^(?:\\/|[A-Za-z]:)/;\n\n/**\n * Подстановку `${configDir}` `get-tsconfig` разворачивает в **абсолютные реальные** пути прямо в\n * значениях `paths`. Дальше по конвейеру значения трактуются как записи относительно\n * (виртуализированного) `realBaseDir`, поэтому абсолютные значения переписываются в относительные\n * от него; остальные значения проходят как есть.\n */\nfunction relativizePathsValues(\n paths: Record<string, unknown>,\n realBaseDir: string,\n): Record<string, unknown> {\n const relativize = (item: unknown): unknown => {\n if (typeof item !== 'string' || !ABSOLUTE_REAL_PATH.test(item)) {\n return item;\n }\n\n const relative = toPosix(path.relative(realBaseDir, item));\n if (relative === '') {\n return '.';\n }\n // `${configDir}/*`, указывающий в саму базу, даёт голую `*` — без префикса `./` такая\n // запись не пройдёт проверку формы `/*` и алиас в корень проекта молча потерялся бы.\n return relative.startsWith('*') ? `./${relative}` : relative;\n };\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(paths)) {\n result[key] = Array.isArray(value) ? value.map(relativize) : relativize(value);\n }\n return result;\n}\n\n/**\n * Реальные якоря записей `paths` — см. {@link TsconfigPaths.realAnchors}. Нестроковые значения\n * молча пропускаются: их отбраковка с сообщением — дело разбора алиасов, а не расчёта root. Ключи\n * и значения, которые разбор алиасов отбросит по форме `*` (`hasValidStarShape` из\n * `src/settings/`), пропускаются по той же причине: поднимать root ради якоря записи, которую\n * разбор потом отбросит, нельзя.\n */\nfunction collectRealAnchors(paths: Record<string, unknown>, realBaseDir: string): string[] {\n const anchors = new Set<string>();\n\n for (const [key, value] of Object.entries(paths)) {\n if (!hasValidStarShape(key)) {\n continue;\n }\n\n // Строка вместо массива — частая опечатка; нестроковое значение отсеет проверка ниже.\n const items = Array.isArray(value) ? value : [value];\n for (const item of items) {\n if (typeof item !== 'string' || !hasValidStarShape(item)) {\n continue;\n }\n\n const anchor = path.resolve(realBaseDir, stripStarSuffix(item));\n if (segments(toPosix(anchor)).includes('node_modules')) {\n debug('anchor %s is inside node_modules, excluded from root computation', anchor);\n continue;\n }\n anchors.add(anchor);\n }\n }\n\n return [...anchors];\n}\n\n/** Сбрасывает TTL-кэш — для изоляции тестов. */\nexport function resetTsconfigCache(): void {\n cache.clear();\n}\n","/**\n * Общий каркас правил: настройки WELD, объявленные один раз, и связка «ESLint-контекст → всё, что\n * правилу нужно до первого узла».\n *\n * Живёт выше `src/host/` и `src/settings/`: `getRepoRoot`/`getAliases` знают формат конфига,\n * `getFsHost` знает про диск, а решение «настройки правила перекрывают `settings.weld`» не\n * принадлежит ни тому, ни другому. Сами слои при этом не изолированы полностью: `host` берёт корень\n * репозитория через `getRepoRoot` — осознанная односторонняя зависимость `host → settings`\n * (см. `getFsHost` в `src/host/fs.ts`).\n *\n * Правило не перечисляет `repoRoot`/`aliasesBaseUrl`/`aliases` ни у себя в `meta.schema`, ни в типе\n * своих опций и не собирает `FsHost` руками — иначе четвёртая общая настройка потребовала бы правки\n * каждого правила по отдельности.\n */\n\nimport type { Rule } from 'eslint';\n\nimport { createLogger } from '@/debug.js';\nimport type { FsHost } from '@/host/index.js';\nimport { getFsHost } from '@/host/index.js';\nimport type { Alias } from '@/settings/index.js';\nimport { getAliases, getAliasesFromPaths, getRepoRoot, hasAliases } from '@/settings/index.js';\nimport type { TsconfigPaths } from '@/tsconfig/index.js';\nimport { loadTsconfigPaths } from '@/tsconfig/index.js';\n\nconst debug = createLogger('rules');\n\n/**\n * Свойства `meta.schema`, общие для всех правил: те же настройки, что и в `settings.weld`, но\n * заданные на самом правиле. Правило подмешивает их к своим спредом.\n */\nexport const WELD_OPTION_PROPERTIES = {\n repoRoot: { type: 'string' },\n aliasesBaseUrl: { type: 'string' },\n aliases: { type: 'object' },\n} as const;\n\n/**\n * Значения общих опций правила. Типы уже отсеяны `meta.schema` — в геттеры они идут как `unknown`,\n * а здесь нужны, чтобы подмешать общие опции к собственным опциям правила. Наружу тип не выходит:\n * правилу незачем его называть, пересечение делается здесь.\n */\ntype WeldOptions = {\n repoRoot?: string;\n aliasesBaseUrl?: string;\n aliases?: Record<string, unknown>;\n};\n\n/** Всё, что правило берёт из контекста до обхода AST. */\nexport type WeldContext<TOwnOptions = unknown> = {\n /** Файл, который сейчас линтуется, — виртуальный путь от корня репозитория. */\n fromFile: string;\n aliases: Alias[];\n fsHost: FsHost;\n /**\n * Опции правила, уже разобранные: собственные опции правила плюс общие. Правило читает их\n * отсюда, а не из `context.options[0]`: иначе каждое повторяло бы каст и дефолт, и форма опций\n * знала бы о себе в двух местах.\n */\n options: TOwnOptions & WeldOptions;\n};\n\n/**\n * `null` — линтуемый файл вне корня репозитория, и правилу нечего проверять: виртуального пути у\n * него нет.\n *\n * `fsHostOverride` — шов для тестов, общий на все правила: подменяет файловую систему целиком, и\n * тогда опция `repoRoot` ни на что не влияет. Шов именно параметром, а не модульным состоянием,\n * чтобы тесты не зависели от порядка запуска.\n *\n * `TOwnOptions` — только собственные опции правила: общие известны здесь и подмешиваются сами.\n */\nexport function resolveWeldContext<TOwnOptions = unknown>(\n context: Rule.RuleContext,\n fsHostOverride?: FsHost,\n): WeldContext<TOwnOptions> | null {\n const options = (context.options[0] ?? {}) as TOwnOptions & WeldOptions;\n const { fsHost, aliases } = resolveHostAndAliases(context, options, fsHostOverride);\n\n const fromFile = fsHost.toVirtual(context.filename);\n if (fromFile === null) {\n return null;\n }\n\n return { fromFile, aliases, fsHost, options };\n}\n\n/** `FsHost` и алиасы — всё, что в `WeldContext` не зависит от виртуализуемости линтуемого файла. */\nfunction resolveHostAndAliases(\n context: Rule.RuleContext,\n options: WeldOptions,\n fsHostOverride?: FsHost,\n): { fsHost: FsHost; aliases: Alias[] } {\n // Приоритет источников алиасов: `options.aliases` → `settings.weld.aliases` → автопоиск\n // tsconfig. «Заданы явно» — это присутствие ключа: пустой `{}` тоже выключает автопоиск.\n if (hasAliases(context.settings, options.aliases)) {\n return {\n fsHost: fsHostOverride ?? getFsHost(context.settings, context.cwd, options.repoRoot),\n aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl),\n };\n }\n\n // Инъекция `fsHostOverride` (шов тестов) автопоиск выключает тоже: тесты на фейковой ФС не\n // должны зависеть от содержимого реального диска.\n if (fsHostOverride !== undefined) {\n return { fsHost: fsHostOverride, aliases: [] };\n }\n\n // Автопоиск: явных алиасов нет — `paths` ближайшего к линтуемому файлу tsconfig. При\n // автоопределении root обязан покрыть якоря, иначе алиас молча не работал бы — отсюда\n // `coverDirs`; при явном root их игнорирует сам `getFsHost`, а инвариант держит отбрасывание\n // непокрытого (ниже и в `parseAliases`).\n const found = loadTsconfigPaths(context.filename);\n const hasExplicitRoot = getRepoRoot(context.settings, options.repoRoot) !== undefined;\n const fsHost = getFsHost(\n context.settings,\n context.cwd,\n options.repoRoot,\n found?.realAnchors ?? [],\n );\n\n return { fsHost, aliases: aliasesFromTsconfig(found, fsHost, hasExplicitRoot) };\n}\n\n/**\n * Алиасы из найденного tsconfig. Любая проблема — не алиасы, а debug: кривой или чужой tsconfig не\n * должен валить линт (в отличие от явных `settings.weld.aliases`/`options.aliases`, где падение —\n * намеренное).\n */\nfunction aliasesFromTsconfig(\n found: TsconfigPaths | null,\n fsHost: FsHost,\n hasExplicitRoot: boolean,\n): Alias[] {\n if (found === null) {\n return [];\n }\n\n // tsconfig, найденный вне явного root, отбрасывается целиком: иначе поведение зависело бы от\n // содержимого директорий над root (например, tsconfig репозитория над тестовой фикстурой).\n if (hasExplicitRoot && fsHost.toVirtual(found.configPath) === null) {\n debug('discarding tsconfig %s: it is outside the explicit root', found.configPath);\n return [];\n }\n\n const virtualBase = fsHost.toVirtual(found.realBaseDir);\n if (virtualBase === null) {\n debug(\n 'discarding tsconfig %s: base dir %s is outside the root',\n found.configPath,\n found.realBaseDir,\n );\n return [];\n }\n\n try {\n return getAliasesFromPaths(found.paths, virtualBase, found.configPath);\n } catch (error) {\n debug('discarding tsconfig %s: %s', found.configPath, error);\n return [];\n }\n}\n","/**\n * Арифметика границ модулей: находит первую точку входа (`index`), которую пробивает импорт из\n * `fromDir` в `targetPath`, поднимаясь от их общей директории вниз к цели.\n */\n\nimport { isEntryBasename } from '@/extensions.js';\nimport {\n basename,\n commonDepth,\n dirname,\n joinPath,\n joinSegments,\n segments,\n splitExtension,\n} from '@/path/index.js';\n\nfunction isIndexFile(targetPath: string): boolean {\n return isEntryBasename(splitExtension(basename(targetPath)).name);\n}\n\n/**\n * Возвращает виртуальный путь границы, которую пробивает импорт, либо `null`, если нарушения нет.\n * `hasEntryPoint` вызывается только для директорий строго между общим предком `fromDir`/`targetPath`\n * и `targetPath` — границы, внутри которых уже находится `fromDir`, не проверяются.\n */\nexport function findBarrier(\n fromDir: string,\n targetPath: string,\n hasEntryPoint: (dir: string) => boolean,\n): string | null {\n const targetDir = dirname(targetPath);\n const targetDirSegments = segments(targetDir);\n\n // Глубина общей директории — она же индекс первого сегмента ниже неё.\n const depth = commonDepth(fromDir, targetDir);\n\n // Спуск от общей директории вниз к цели: путь наращивается посегментно, а не пересобирается.\n let dir = joinSegments(targetDirSegments.slice(0, depth));\n for (let i = depth; i < targetDirSegments.length; i += 1) {\n dir = joinPath(dir, targetDirSegments[i] as string);\n if (hasEntryPoint(dir)) {\n const atTargetDir = i === targetDirSegments.length - 1;\n return atTargetDir && isIndexFile(targetPath) ? null : dir;\n }\n }\n\n return null;\n}\n","/**\n * Вход в ядро: собирает `parseSpecifier` → `findBarrier` → `renderSpecifier` в одну проверку.\n */\n\nimport { createLogger } from '@/debug.js';\nimport type { FsHost } from '@/host/index.js';\nimport { parseSpecifier, renderSpecifier } from '@/imports/index.js';\nimport { dirname } from '@/path/index.js';\nimport type { Alias } from '@/settings/index.js';\n\nimport { findBarrier } from '@/rules/no-barrel-bypass/core/barrier.js';\n\nconst debug = createLogger('no-barrel-bypass');\n\ntype CheckerInput = { fromFile: string; aliases: Alias[] };\n\nexport type Host = Pick<FsHost, 'hasEntryPoint'>;\n\n/**\n * Проверка одного специфаера: возвращает исправленный специфаер при нарушении границы, иначе `null`\n * — нарушения нет. Найденная граница наружу не отдаётся: она нужна только для рендера пути.\n *\n * Проверка собирается на файл, а не на импорт: `fromDir` от линтуемого файла не зависит от того,\n * какой специфаер сейчас разбирается.\n */\nexport function createChecker(\n { fromFile, aliases }: CheckerInput,\n host: Host,\n): (specifier: string) => string | null {\n const fromDir = dirname(fromFile);\n\n return function checkImport(specifier: string): string | null {\n const target = parseSpecifier(specifier, fromDir, aliases);\n if (target === null) {\n debug('%s: %s external dependency', fromFile, specifier);\n return null;\n }\n\n const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);\n if (barrier === null) {\n debug('%s: %s -> %s, no barrier on the way', fromFile, specifier, target.path);\n return null;\n }\n\n return renderSpecifier(target.form, fromDir, barrier, aliases, target.extension);\n };\n}\n","/**\n * Правило `weld/no-barrel-bypass`: ловит импорты, входящие внутрь модуля мимо его точки входа\n * (`index.<ext>`), и предлагает исправленный путь через `checkImport`.\n *\n * Всё, что не про баррели, — общее: настройки и `FsHost` даёт `resolveWeldContext`, обход и правку\n * специфаеров — `src/imports/nodes.ts`. Здесь остаётся вердикт и текст сообщения.\n */\n\nimport type { Rule } from 'eslint';\n\nimport { createLogger } from '@/debug.js';\nimport type { FsHost } from '@/host/index.js';\nimport type { SpecifierNode } from '@/imports/index.js';\nimport { createSpecifierVisitor, replaceSpecifier } from '@/imports/index.js';\nimport { WELD_OPTION_PROPERTIES, resolveWeldContext } from '@/rules/context.js';\n\nimport { createChecker } from '@/rules/no-barrel-bypass/pipeline.js';\n\nconst debug = createLogger('no-barrel-bypass');\n\nconst messages = {\n bypass: \"Import bypasses the module barrel. Use '{{suggestion}}' instead of '{{original}}'.\",\n useBarrel: \"Import through '{{suggestion}}'.\",\n};\n\n/**\n * Своя опция у правила одна; общие к ней подмешивает `resolveWeldContext`, см.\n * {@link WELD_OPTION_PROPERTIES}.\n */\ntype RuleOptions = { fix?: boolean };\n\n/**\n * `fsHost` — для инъекции в тестах; без него файловую систему собирает `resolveWeldContext` из\n * `context.settings`/`context.cwd` и опции `repoRoot`. При инъекции опция `repoRoot` ни на что не\n * влияет.\n */\nexport function createRule(fsHost?: FsHost): Rule.RuleModule {\n return {\n meta: {\n type: 'problem',\n docs: {\n description: 'disallow imports that reach into a module past its barrel',\n url: 'https://github.com/jt4d-lab/weld/blob/master/docs/rules/no-barrel-bypass.md',\n },\n fixable: 'code',\n hasSuggestions: true,\n schema: [\n {\n type: 'object',\n properties: {\n fix: { type: 'boolean' },\n ...WELD_OPTION_PROPERTIES,\n },\n additionalProperties: false,\n },\n ],\n messages,\n },\n create(context) {\n const weld = resolveWeldContext<RuleOptions>(context, fsHost);\n if (weld === null) {\n return {};\n }\n\n const { fromFile } = weld;\n const applyFix = weld.options.fix ?? true;\n const checkImport = createChecker(weld, weld.fsHost);\n\n return createSpecifierVisitor((sourceNode: SpecifierNode) => {\n const original = sourceNode.value;\n const suggestion = checkImport(original);\n\n if (suggestion === null) {\n return;\n }\n\n debug(\n '%s: crosses barrier, %s -> %s (fix=%o)',\n fromFile,\n original,\n suggestion,\n applyFix,\n );\n const fix = replaceSpecifier(sourceNode, suggestion);\n\n context.report({\n node: sourceNode,\n messageId: 'bypass',\n data: { suggestion, original },\n ...(applyFix\n ? { fix }\n : { suggest: [{ messageId: 'useBarrel', data: { suggestion }, fix }] }),\n });\n });\n },\n };\n}\n","import type { Rule } from 'eslint';\n\nimport { createRule as createNoBarrelBypassRule } from '@/rules/no-barrel-bypass/index.js';\n\nexport const rules = {\n 'no-barrel-bypass': createNoBarrelBypassRule(),\n} satisfies Record<string, Rule.RuleModule>;\n","import { createRequire } from 'node:module';\n\nimport type { ESLint, Linter } from 'eslint';\n\nimport { PACKAGE_NAME } from '@/debug.js';\nimport { rules } from '@/rules/index.js';\n\n/** Версию проставляет release-workflow перед упаковкой, поэтому она читается из `package.json`. */\nconst { version } = createRequire(import.meta.url)('../package.json') as { version: string };\n\nconst recommended: Linter.Config = {\n name: 'weld/recommended',\n rules: {\n 'weld/no-barrel-bypass': 'error',\n },\n};\n\nconst strict: Linter.Config = {\n name: 'weld/strict',\n rules: {\n 'weld/no-barrel-bypass': 'error',\n },\n};\n\nconst plugin = {\n meta: {\n name: PACKAGE_NAME,\n version,\n },\n rules,\n configs: {\n recommended,\n strict,\n },\n} satisfies ESLint.Plugin;\n\nrecommended.plugins = { weld: plugin };\nstrict.plugins = { weld: plugin };\n\nexport default plugin;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-weld",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "ESLint-плагин для проверки правил WELD (Well-Encapsulated Layered Design)",
5
5
  "keywords": [
6
6
  "eslint",
@@ -57,7 +57,8 @@
57
57
  "*.{js,mjs,cjs,ts,tsx}": "eslint --fix"
58
58
  },
59
59
  "dependencies": {
60
- "debug": "^4.4.0"
60
+ "debug": "^4.4.0",
61
+ "get-tsconfig": "^4.14.3"
61
62
  },
62
63
  "devDependencies": {
63
64
  "@eslint/js": "^10.0.0",