eslint-plugin-weld 0.2.0 → 0.4.0-rc.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
@@ -24,12 +24,6 @@
24
24
  Общие слои наверху, модули в `modules/` — со слоями или без, с вложенными модулями в собственной
25
25
  `modules/`. Правила одинаковы на любой глубине.
26
26
 
27
- ## Статус
28
-
29
- Ранняя стадия. Документация подхода пишется; в ESLint-плагине реализовано первое правило —
30
- [`weld/no-barrel-bypass`](docs/rules/no-barrel-bypass.md). Правила и их именование могут меняться
31
- без обратной совместимости.
32
-
33
27
  ## ESLint-плагин
34
28
 
35
29
  Пакет: [`eslint-plugin-weld`](https://www.npmjs.com/package/eslint-plugin-weld). Требует ESLint 9
@@ -46,14 +40,6 @@ import weld from 'eslint-plugin-weld';
46
40
  export default [weld.configs.recommended];
47
41
  ```
48
42
 
49
- Плагин предоставляет готовые наборы правил `recommended` и `strict`:
50
-
51
- ```js
52
- import weld from 'eslint-plugin-weld';
53
-
54
- export default [weld.configs.strict];
55
- ```
56
-
57
43
  Можно подключить и сам плагин, включая правила поштучно:
58
44
 
59
45
  ```js
@@ -75,24 +61,24 @@ export default [
75
61
  внутрь модуля мимо его точки входа (`index.*`), минуя баррель.
76
62
 
77
63
  Правило учитывает алиасы путей и корень репозитория. Если алиасы не заданы в конфиге, они
78
- автоматически подхватываются из `compilerOptions.paths` ближайшего `tsconfig.json` (с резолвом
79
- `extends`); задать их явно можно через `settings.weld`:
64
+ автоматически подхватываются из `compilerOptions.paths` ближайшего tsconfig с настроенным именем (по
65
+ умолчанию `tsconfig.json`, с резолвом `extends`); задать их явно можно через `settings.weld`:
80
66
 
81
67
  ```js
82
68
  export default [
83
69
  {
84
70
  settings: {
85
71
  weld: {
86
- repoRoot: '.', // необязательно; по умолчанию — авто-поиск
87
- aliasesBaseUrl: 'packages/app', // необязательно; по умолчанию '.'
88
72
  aliases: { '@/*': ['src/*'] },
73
+ aliasesBaseUrl: 'packages/app', // необязательно; по умолчанию '.'
74
+ repoRoot: '.', // необязательно; по умолчанию — авто-поиск
89
75
  },
90
76
  },
91
77
  },
92
78
  ];
93
79
  ```
94
80
 
95
- Формат и разбор этих настроек описаны в [документации настроек](docs/settings.md); подробности
81
+ Формат и разбор этих настроек описаны в [документации настроек](docs/rules/settings.md); подробности
96
82
  самого правила — в [документации правила](docs/rules/no-barrel-bypass.md).
97
83
 
98
84
  ### Отладка
package/dist/index.d.ts CHANGED
@@ -12,7 +12,6 @@ declare const plugin: {
12
12
  };
13
13
  configs: {
14
14
  recommended: Linter.Config<node_modules__eslint_core_dist_cjs_types_cjs.RulesConfig>;
15
- strict: Linter.Config<node_modules__eslint_core_dist_cjs_types_cjs.RulesConfig>;
16
15
  };
17
16
  };
18
17
 
package/dist/index.js CHANGED
@@ -40,9 +40,30 @@ function replaceSpecifier(node, specifier) {
40
40
  // src/extensions.ts
41
41
  var ENTRY_BASENAME = "index";
42
42
  var ENTRY_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"];
43
- var MODULE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"];
43
+ var MODULE_EXTENSIONS = [...ENTRY_EXTENSIONS, "vue", "svelte"];
44
+ var ASSET_EXTENSIONS = [
45
+ "css",
46
+ "scss",
47
+ "sass",
48
+ "less",
49
+ "json",
50
+ "svg",
51
+ "png",
52
+ "jpg",
53
+ "jpeg",
54
+ "gif",
55
+ "webp",
56
+ "ico",
57
+ "woff",
58
+ "woff2",
59
+ "ttf",
60
+ "eot",
61
+ "otf",
62
+ "md"
63
+ ];
44
64
  var ENTRY_EXTENSION_SET = new Set(ENTRY_EXTENSIONS);
45
65
  var MODULE_EXTENSION_SET = new Set(MODULE_EXTENSIONS);
66
+ var ASSET_EXTENSION_SET = new Set(ASSET_EXTENSIONS);
46
67
  function isEntryExtension(ext) {
47
68
  return ENTRY_EXTENSION_SET.has(ext);
48
69
  }
@@ -56,6 +77,9 @@ function isEntryFileName(name, ext) {
56
77
  function isEntryBasename(name) {
57
78
  return name === ENTRY_BASENAME;
58
79
  }
80
+ function isAssetExtension(ext) {
81
+ return ASSET_EXTENSION_SET.has(ext);
82
+ }
59
83
  function isModuleExtension(ext) {
60
84
  return MODULE_EXTENSION_SET.has(ext);
61
85
  }
@@ -127,19 +151,22 @@ function relativePath(from, to) {
127
151
  }
128
152
 
129
153
  // src/imports/specifier.ts
130
- function parseSpecifier(specifier, fromDir, aliases) {
131
- if (specifier.includes("?") || specifier.includes("!")) {
132
- return null;
154
+ function parseSpecifier(specifier, fromDir, aliases, resolve) {
155
+ if (specifier.startsWith("/") || specifier.includes("?") || specifier.includes("!")) {
156
+ return skip("asset");
133
157
  }
134
158
  if (isRelativeSpecifier(specifier)) {
135
- return finalize(resolvePath(fromDir, specifier), { kind: "relative" });
159
+ return finalize(resolvePath(fromDir, specifier), { kind: "relative" }, resolve);
136
160
  }
137
161
  const alias = matchAlias(specifier, aliases);
138
162
  if (alias !== null) {
139
163
  const suffix = specifier.slice(alias.prefix.length).replace(/^\/+/, "");
140
- return finalize(resolvePath(alias.anchor, suffix), { kind: "alias", alias });
164
+ return finalize(resolvePath(alias.anchor, suffix), { kind: "alias", alias }, resolve);
141
165
  }
142
- return null;
166
+ return skip("external-dependency");
167
+ }
168
+ function skip(reason) {
169
+ return { kind: "skip", reason };
143
170
  }
144
171
  function isRelativeSpecifier(specifier) {
145
172
  return specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../");
@@ -160,15 +187,28 @@ function pickLongest(aliases, key) {
160
187
  function matchAlias(specifier, aliases) {
161
188
  return pickLongest(aliases, (alias) => covers(alias.prefix, specifier) ? alias.prefix : null);
162
189
  }
163
- function finalize(path3, form) {
190
+ function finalize(path3, form, resolve) {
164
191
  if (path3 === null) {
165
- return null;
192
+ return skip("out-of-repo");
166
193
  }
167
194
  const { ext } = splitExtension(basename(path3));
168
- if (ext !== "" && !isModuleExtension(ext)) {
169
- return null;
195
+ if (ext === "") {
196
+ return target(path3, form, null);
197
+ }
198
+ if (isModuleExtension(ext)) {
199
+ return target(path3, form, ext);
200
+ }
201
+ if (isAssetExtension(ext)) {
202
+ return skip("asset");
170
203
  }
171
- return { path: path3, form, extension: ext === "" ? null : ext };
204
+ const resolved = resolve?.(path3);
205
+ if (resolved === void 0 || resolved === null) {
206
+ return skip("no-module-found");
207
+ }
208
+ return target(resolved, form, null);
209
+ }
210
+ function target(path3, form, extension) {
211
+ return { kind: "target", target: { path: path3, form, extension } };
172
212
  }
173
213
  function renderSpecifier(form, fromDir, barrier, aliases, extension = null) {
174
214
  const dir = renderDirectory(form, fromDir, barrier, aliases);
@@ -304,6 +344,7 @@ function normalizeAnchor(anchorRaw, base) {
304
344
 
305
345
  // src/settings/weld.ts
306
346
  var DEFAULT_ALIASES_BASE_URL = ".";
347
+ var DEFAULT_TSCONFIG_NAME = "tsconfig.json";
307
348
  function getWeldSettings(settings) {
308
349
  if (typeof settings !== "object" || settings === null) {
309
350
  return void 0;
@@ -333,6 +374,16 @@ function getRepoRoot(settings, override) {
333
374
  }
334
375
  return requireString(repoRoot, "settings.weld.repoRoot");
335
376
  }
377
+ function getTsconfigName(settings, override) {
378
+ if (override !== void 0) {
379
+ return requireString(override, "options.tsconfig");
380
+ }
381
+ const tsconfig = getWeldSettings(settings)?.tsconfig;
382
+ if (tsconfig === void 0) {
383
+ return DEFAULT_TSCONFIG_NAME;
384
+ }
385
+ return requireString(tsconfig, "settings.weld.tsconfig");
386
+ }
336
387
  function getAliasesBaseUrl(settings, override) {
337
388
  if (override !== void 0) {
338
389
  return requireString(override, "options.aliasesBaseUrl");
@@ -437,8 +488,8 @@ function commonRealDirectory(left, right) {
437
488
  const depth = commonDepth(leftRest, right.slice(rightPrefix.length));
438
489
  return `${prefix}${joinSegments(segments(leftRest).slice(0, depth))}`;
439
490
  }
440
- function resolveRealPath(base, target) {
441
- const posixTarget = toPosix(target);
491
+ function resolveRealPath(base, target2) {
492
+ const posixTarget = toPosix(target2);
442
493
  if (isAbsoluteRealPath(posixTarget)) {
443
494
  return posixTarget;
444
495
  }
@@ -493,6 +544,7 @@ function createFsHost(root, options = {}) {
493
544
  const nativeExists = options.exists ?? existsSync;
494
545
  const entryPointCache = /* @__PURE__ */ new Map();
495
546
  const directoryCache = /* @__PURE__ */ new Map();
547
+ const fileCache = /* @__PURE__ */ new Map();
496
548
  function toRealDir(dir) {
497
549
  return `${normalizedRoot}${dir}`;
498
550
  }
@@ -532,6 +584,16 @@ function createFsHost(root, options = {}) {
532
584
  }
533
585
  return peek(directoryCache, parent, time) === false;
534
586
  }
587
+ function fileExists(filePath) {
588
+ const time = now();
589
+ const cached = peek(fileCache, filePath, time);
590
+ if (cached !== void 0) {
591
+ return cached;
592
+ }
593
+ const found = nativeExists(`${normalizedRoot}${filePath}`);
594
+ debug3("check file %s: %o", filePath, found);
595
+ return remember(fileCache, filePath, time, found);
596
+ }
535
597
  function hasEntryPoint(dir) {
536
598
  if (!dir.startsWith("/")) {
537
599
  return false;
@@ -549,7 +611,28 @@ function createFsHost(root, options = {}) {
549
611
  debug3("check entry point %s: %o", dir, found);
550
612
  return remember(entryPointCache, dir, time, found);
551
613
  }
552
- return { hasEntryPoint, toVirtual };
614
+ function findModuleTarget(path3) {
615
+ if (!path3.startsWith("/")) {
616
+ return null;
617
+ }
618
+ for (const ext of MODULE_EXTENSIONS) {
619
+ const candidate = `${path3}.${ext}`;
620
+ if (fileExists(candidate)) {
621
+ return candidate;
622
+ }
623
+ }
624
+ if (!hasEntryPoint(path3)) {
625
+ return null;
626
+ }
627
+ for (const ext of ENTRY_EXTENSIONS) {
628
+ const candidate = `${path3}/index.${ext}`;
629
+ if (fileExists(candidate)) {
630
+ return candidate;
631
+ }
632
+ }
633
+ return null;
634
+ }
635
+ return { hasEntryPoint, findModuleTarget, toVirtual };
553
636
  }
554
637
  var instanceCache = /* @__PURE__ */ new Map();
555
638
  var repoRootCache = /* @__PURE__ */ new Map();
@@ -607,22 +690,31 @@ function loadTsconfigPaths(realFilePath, options = {}) {
607
690
  return null;
608
691
  }
609
692
  const dir = path2.dirname(realFilePath);
693
+ const configName = options.configName ?? "tsconfig.json";
610
694
  const now = options.now ?? Date.now;
611
695
  const time = now();
612
- const entry = cache.get(dir);
696
+ const entries = cache.get(dir);
697
+ const entry = entries?.get(configName);
613
698
  if (entry && entry.expiresAt > time) {
614
699
  return entry.value;
615
700
  }
616
701
  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) });
702
+ const read = options.read ?? ((searchDir, name) => getTsconfig(searchDir, name, readCache));
703
+ const value = readTsconfigPaths(dir, configName, read);
704
+ const directoryEntries = entries ?? /* @__PURE__ */ new Map();
705
+ directoryEntries.set(configName, {
706
+ value,
707
+ expiresAt: time + (value ? TTL_MS3 : NEGATIVE_TTL_MS2)
708
+ });
709
+ if (entries === void 0) {
710
+ cache.set(dir, directoryEntries);
711
+ }
620
712
  return value;
621
713
  }
622
- function readTsconfigPaths(dir, read) {
714
+ function readTsconfigPaths(dir, configName, read) {
623
715
  let found;
624
716
  try {
625
- found = read(dir);
717
+ found = read(dir, configName);
626
718
  } catch (error) {
627
719
  debug4("failed to read tsconfig near %s: %s", dir, error);
628
720
  return null;
@@ -701,7 +793,8 @@ var debug5 = createLogger("rules");
701
793
  var WELD_OPTION_PROPERTIES = {
702
794
  repoRoot: { type: "string" },
703
795
  aliasesBaseUrl: { type: "string" },
704
- aliases: { type: "object" }
796
+ aliases: { type: "object" },
797
+ tsconfig: { type: "string" }
705
798
  };
706
799
  function resolveWeldContext(context, fsHostOverride) {
707
800
  const options = context.options[0] ?? {};
@@ -719,7 +812,8 @@ function resolveHostAndAliases(context, options, fsHostOverride) {
719
812
  aliases: getAliases(context.settings, options.aliases, options.aliasesBaseUrl)
720
813
  };
721
814
  }
722
- const found = loadTsconfigPaths(context.filename);
815
+ const tsconfigName = getTsconfigName(context.settings, options.tsconfig);
816
+ const found = loadTsconfigPaths(context.filename, { configName: tsconfigName });
723
817
  const hasExplicitRoot = getRepoRoot(context.settings, options.repoRoot) !== void 0;
724
818
  const fsHost = getFsHost(
725
819
  context.settings,
@@ -774,26 +868,36 @@ function findBarrier(fromDir, targetPath, hasEntryPoint) {
774
868
  }
775
869
 
776
870
  // src/rules/no-barrel-bypass/pipeline.ts
777
- var debug6 = createLogger("no-barrel-bypass");
778
871
  function createChecker({ fromFile, aliases }, host) {
779
872
  const fromDir = dirname(fromFile);
873
+ const resolve = host.findModuleTarget.bind(host);
780
874
  return function checkImport(specifier) {
781
- const target = parseSpecifier(specifier, fromDir, aliases);
782
- if (target === null) {
783
- debug6("%s: %s external dependency", fromFile, specifier);
784
- return null;
875
+ const parse = parseSpecifier(specifier, fromDir, aliases, resolve);
876
+ if (parse.kind === "skip") {
877
+ return { kind: "skip", reason: parse.reason };
785
878
  }
786
- const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);
879
+ const { target: target2 } = parse;
880
+ const barrier = findBarrier(fromDir, target2.path, host.hasEntryPoint);
787
881
  if (barrier === null) {
788
- debug6("%s: %s -> %s, no barrier on the way", fromFile, specifier, target.path);
789
- return null;
882
+ return { kind: "intact", target: target2.path };
790
883
  }
791
- return renderSpecifier(target.form, fromDir, barrier, aliases, target.extension);
884
+ return {
885
+ kind: "replace",
886
+ target: target2.path,
887
+ barrier,
888
+ suggestion: renderSpecifier(target2.form, fromDir, barrier, aliases, target2.extension)
889
+ };
792
890
  };
793
891
  }
794
892
 
795
893
  // src/rules/no-barrel-bypass/index.ts
796
- var debug7 = createLogger("no-barrel-bypass");
894
+ var debug6 = createLogger("no-barrel-bypass");
895
+ var skipMessages = {
896
+ asset: "asset",
897
+ "external-dependency": "external dependency",
898
+ "out-of-repo": "path outside the repository",
899
+ "no-module-found": "module not found"
900
+ };
797
901
  var messages = {
798
902
  bypass: "Import bypasses the module barrel. Use '{{suggestion}}' instead of '{{original}}'.",
799
903
  useBarrel: "Import through '{{suggestion}}'."
@@ -830,17 +934,17 @@ function createRule(fsHost) {
830
934
  const checkImport = createChecker(weld, weld.fsHost);
831
935
  return createSpecifierVisitor((sourceNode) => {
832
936
  const original = sourceNode.value;
833
- const suggestion = checkImport(original);
834
- if (suggestion === null) {
937
+ const decision = checkImport(original);
938
+ if (decision.kind === "skip") {
939
+ debug6(`${fromFile}: skip ${original}: ${skipMessages[decision.reason]}`);
835
940
  return;
836
941
  }
837
- debug7(
838
- "%s: crosses barrier, %s -> %s (fix=%o)",
839
- fromFile,
840
- original,
841
- suggestion,
842
- applyFix
843
- );
942
+ if (decision.kind === "intact") {
943
+ debug6(`${fromFile}: good ${original}`);
944
+ return;
945
+ }
946
+ const { suggestion } = decision;
947
+ debug6(`${fromFile}: bad ${original}; fix: ${decision.barrier}`);
844
948
  const fix = replaceSpecifier(sourceNode, suggestion);
845
949
  context.report({
846
950
  node: sourceNode,
@@ -866,12 +970,6 @@ var recommended = {
866
970
  "weld/no-barrel-bypass": "error"
867
971
  }
868
972
  };
869
- var strict = {
870
- name: "weld/strict",
871
- rules: {
872
- "weld/no-barrel-bypass": "error"
873
- }
874
- };
875
973
  var plugin = {
876
974
  meta: {
877
975
  name: PACKAGE_NAME,
@@ -879,12 +977,10 @@ var plugin = {
879
977
  },
880
978
  rules,
881
979
  configs: {
882
- recommended,
883
- strict
980
+ recommended
884
981
  }
885
982
  };
886
983
  recommended.plugins = { weld: plugin };
887
- strict.plugins = { weld: plugin };
888
984
  var index_default = plugin;
889
985
 
890
986
  export { index_default as default };
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/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":";;;;;;;AAUO,IAAM,YAAA,GAAe,oBAAA;AAGrB,SAAS,aAAa,KAAA,EAAyB;AAClD,EAAA,OAAO,WAAA,CAAY,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE,CAAA;AACjD;;;ACSA,SAAS,gBAAgB,IAAA,EAAkE;AACvF,EAAA,OACI,QAAQ,IAAA,IACR,IAAA,CAAK,SAAS,SAAA,IACd,OAAQ,KAA4B,KAAA,KAAU,QAAA;AAEtD;AASO,SAAS,uBACZ,WAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACH,sCACI,IAAA,EACF;AACE,MAAA,IAAI,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC9B,QAAA,WAAA,CAAY,KAAK,MAAM,CAAA;AAAA,MAC3B;AAAA,IACJ,CAAA;AAAA,IACA,eAAe,IAAA,EAAM;AACjB,MAAA,IAAI,KAAK,MAAA,CAAO,IAAA,KAAS,gBAAgB,IAAA,CAAK,MAAA,CAAO,SAAS,SAAA,EAAW;AACrE,QAAA;AAAA,MACJ;AACA,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA;AAC5B,MAAA,IAAI,eAAA,CAAgB,GAAG,CAAA,EAAG;AACtB,QAAA,WAAA,CAAY,GAAG,CAAA;AAAA,MACnB;AAAA,IACJ;AAAA,GACJ;AACJ;AAMO,SAAS,gBAAA,CAAiB,MAAqB,SAAA,EAAqC;AACvF,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,IAAK,GAAA;AAC7B,EAAA,OAAO,CAAC,KAAA,KAAU,KAAA,CAAM,gBAAA,CAAiB,IAAA,CAAK,KAAA,EAAO,CAAA,EAAG,KAAK,CAAA,EAAG,SAAS,CAAA,EAAG,KAAK,CAAA,CAAE,CAAA;AACvF;;;ACvDA,IAAM,cAAA,GAAiB,OAAA;AAGhB,IAAM,gBAAA,GAAmB,CAAC,IAAA,EAAM,KAAA,EAAO,MAAM,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAK,CAAA;AAMrF,IAAM,iBAAA,GAAoB,CAAC,IAAA,EAAM,KAAA,EAAO,MAAM,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAK,CAAA;AAE/E,IAAM,mBAAA,GAA2C,IAAI,GAAA,CAAI,gBAAgB,CAAA;AACzE,IAAM,oBAAA,GAA4C,IAAI,GAAA,CAAI,iBAAiB,CAAA;AAG3E,SAAS,iBAAiB,GAAA,EAAsB;AAC5C,EAAA,OAAO,mBAAA,CAAoB,IAAI,GAAG,CAAA;AACtC;AAGO,SAAS,cAAc,GAAA,EAAqB;AAC/C,EAAA,OAAO,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AACnC;AAGO,IAAM,gBAAA,GAAsC,gBAAA,CAAiB,GAAA,CAAI,aAAa,CAAA;AAG9E,SAAS,eAAA,CAAgB,MAAc,GAAA,EAAsB;AAChE,EAAA,OAAO,eAAA,CAAgB,IAAI,CAAA,IAAK,gBAAA,CAAiB,GAAG,CAAA;AACxD;AAQO,SAAS,gBAAgB,IAAA,EAAuB;AACnD,EAAA,OAAO,IAAA,KAAS,cAAA;AACpB;AAGO,SAAS,kBAAkB,GAAA,EAAsB;AACpD,EAAA,OAAO,oBAAA,CAAqB,IAAI,GAAG,CAAA;AACvC;;;AClDO,SAAS,QAAQA,KAAAA,EAAsB;AAC1C,EAAA,OAAOA,KAAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAClC;AAGO,SAAS,SAASA,KAAAA,EAAwB;AAC7C,EAAA,OAAOA,KAAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AACzC;AAGO,SAAS,aAAa,IAAA,EAAwB;AACjD,EAAA,OAAO,IAAA,CAAK,WAAW,CAAA,GAAI,GAAA,GAAM,IAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AACvD;AAOO,SAAS,QAAA,CAAS,KAAa,OAAA,EAAyB;AAC3D,EAAA,OAAO,GAAA,KAAQ,MAAM,CAAA,CAAA,EAAI,OAAO,KAAK,CAAA,EAAG,GAAG,IAAI,OAAO,CAAA,CAAA;AAC1D;AAGO,SAAS,SAASA,KAAAA,EAAsB;AAC3C,EAAA,OAAOA,MAAK,KAAA,CAAMA,KAAAA,CAAK,WAAA,CAAY,GAAG,IAAI,CAAC,CAAA;AAC/C;AAGO,SAAS,eAAe,IAAA,EAA6C;AAExE,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA;AAChC,EAAA,IAAI,GAAA,IAAO,CAAA,IAAK,GAAA,KAAQ,IAAA,CAAK,SAAS,CAAA,EAAG;AACrC,IAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,GAAA,EAAK,EAAA,EAAG;AAAA,EACjC;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,EAAG,GAAA,EAAK,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA,EAAE;AAChE;AAGO,SAAS,QAAQA,KAAAA,EAAsB;AAC1C,EAAA,MAAM,IAAA,GAAO,SAASA,KAAI,CAAA;AAC1B,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACnB,IAAA,OAAO,GAAA;AAAA,EACX;AAEA,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AACzC;AAMO,SAAS,WAAA,CAAY,MAAcA,KAAAA,EAA6B;AACnE,EAAA,MAAM,IAAA,GAAOA,MAAK,UAAA,CAAW,GAAG,IAAI,EAAC,GAAI,SAAS,IAAI,CAAA;AAEtD,EAAA,KAAA,MAAW,IAAA,IAAQA,KAAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAG;AAChC,IAAA,IAAI,IAAA,KAAS,EAAA,IAAM,IAAA,KAAS,GAAA,EAAK;AAC7B,MAAA;AAAA,IACJ;AACA,IAAA,IAAI,SAAS,IAAA,EAAM;AACf,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACnB,QAAA,OAAO,IAAA;AAAA,MACX;AACA,MAAA,IAAA,CAAK,GAAA,EAAI;AACT,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,KAAK,IAAI,CAAA;AAAA,EAClB;AAEA,EAAA,OAAO,aAAa,IAAI,CAAA;AAC5B;AAGA,SAAS,kBAAA,CAAmB,OAAiB,KAAA,EAAyB;AAClE,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,OAAO,CAAA,GAAI,KAAA,CAAM,MAAA,IAAU,CAAA,GAAI,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,CAAA,EAAG;AAClE,IAAA,CAAA,IAAK,CAAA;AAAA,EACT;AAEA,EAAA,OAAO,CAAA;AACX;AASO,SAAS,WAAA,CAAY,GAAW,CAAA,EAAmB;AACtD,EAAA,OAAO,mBAAmB,QAAA,CAAS,CAAC,CAAA,EAAG,QAAA,CAAS,CAAC,CAAC,CAAA;AACtD;AAMO,SAAS,YAAA,CAAa,MAAc,EAAA,EAAoB;AAC3D,EAAA,MAAM,QAAA,GAAW,SAAS,IAAI,CAAA;AAC9B,EAAA,MAAM,MAAA,GAAS,SAAS,EAAE,CAAA;AAE1B,EAAA,MAAM,CAAA,GAAI,kBAAA,CAAmB,QAAA,EAAU,MAAM,CAAA;AAE7C,EAAA,MAAM,MAAM,KAAA,CAAc,QAAA,CAAS,SAAS,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA;AAE5B,EAAA,OAAO,CAAC,GAAG,GAAA,EAAK,GAAG,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AACtC;;;AC3FO,SAAS,cAAA,CACZ,SAAA,EACA,OAAA,EACA,OAAA,EACa;AACb,EAAA,IAAI,UAAU,QAAA,CAAS,GAAG,KAAK,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,EAAG;AACpD,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,mBAAA,CAAoB,SAAS,CAAA,EAAG;AAChC,IAAA,OAAO,QAAA,CAAS,YAAY,OAAA,EAAS,SAAS,GAAG,EAAE,IAAA,EAAM,YAAY,CAAA;AAAA,EACzE;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,SAAA,EAAW,OAAO,CAAA;AAC3C,EAAA,IAAI,UAAU,IAAA,EAAM;AAGhB,IAAA,MAAM,MAAA,GAAS,UAAU,KAAA,CAAM,KAAA,CAAM,OAAO,MAAM,CAAA,CAAE,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACtE,IAAA,OAAO,QAAA,CAAS,WAAA,CAAY,KAAA,CAAM,MAAA,EAAQ,MAAM,GAAG,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,CAAA;AAAA,EAC/E;AAEA,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,oBAAoB,SAAA,EAA4B;AACrD,EAAA,OACI,SAAA,KAAc,GAAA,IACd,SAAA,KAAc,IAAA,IACd,SAAA,CAAU,WAAW,IAAI,CAAA,IACzB,SAAA,CAAU,UAAA,CAAW,KAAK,CAAA;AAElC;AAMA,SAAS,WAAA,CAAY,SAAkB,GAAA,EAAoD;AACvF,EAAA,IAAI,IAAA,GAAqB,IAAA;AACzB,EAAA,IAAI,UAAA,GAAa,EAAA;AAEjB,EAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AACzB,IAAA,MAAM,OAAA,GAAU,IAAI,KAAK,CAAA;AACzB,IAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,OAAA,CAAQ,MAAA,IAAU,UAAA,EAAY;AAClD,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,GAAO,KAAA;AACP,IAAA,UAAA,GAAa,OAAA,CAAQ,MAAA;AAAA,EACzB;AAEA,EAAA,OAAO,IAAA;AACX;AAGA,SAAS,UAAA,CAAW,WAAmB,OAAA,EAAgC;AACnE,EAAA,OAAO,WAAA,CAAY,OAAA,EAAS,CAAC,KAAA,KAAW,MAAA,CAAO,KAAA,CAAM,MAAA,EAAQ,SAAS,CAAA,GAAI,KAAA,CAAM,MAAA,GAAS,IAAK,CAAA;AAClG;AAEA,SAAS,QAAA,CAASA,OAAqB,IAAA,EAA2B;AAC9D,EAAA,IAAIA,UAAS,IAAA,EAAM;AACf,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,EAAE,GAAA,EAAI,GAAI,cAAA,CAAe,QAAA,CAASA,KAAI,CAAC,CAAA;AAC7C,EAAA,IAAI,GAAA,KAAQ,EAAA,IAAM,CAAC,iBAAA,CAAkB,GAAG,CAAA,EAAG;AACvC,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,OAAO,EAAE,MAAAA,KAAAA,EAAM,IAAA,EAAM,WAAW,GAAA,KAAQ,EAAA,GAAK,OAAO,GAAA,EAAI;AAC5D;AAWO,SAAS,gBACZ,IAAA,EACA,OAAA,EACA,OAAA,EACA,OAAA,EACA,YAA2B,IAAA,EACrB;AACN,EAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,IAAA,EAAM,OAAA,EAAS,SAAS,OAAO,CAAA;AAC3D,EAAA,OAAO,SAAA,KAAc,IAAA,GAAO,GAAA,GAAM,SAAA,CAAU,KAAK,SAAS,CAAA;AAC9D;AAEA,SAAS,eAAA,CAAgB,IAAA,EAAY,OAAA,EAAiB,OAAA,EAAiB,OAAA,EAA0B;AAC7F,EAAA,IAAI,IAAA,CAAK,SAAS,UAAA,EAAY;AAC1B,IAAA,OAAO,cAAA,CAAe,SAAS,OAAO,CAAA;AAAA,EAC1C;AAEA,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,OAAO,CAAA,EAAG;AACpC,IAAA,OAAO,WAAA,CAAY,IAAA,CAAK,KAAA,EAAO,OAAO,CAAA;AAAA,EAC1C;AAEA,EAAA,MAAM,OAAA,GAAU,oBAAA,CAAqB,OAAA,EAAS,OAAO,CAAA;AACrD,EAAA,OAAO,OAAA,KAAY,OAAO,cAAA,CAAe,OAAA,EAAS,OAAO,CAAA,GAAI,WAAA,CAAY,SAAS,OAAO,CAAA;AAC7F;AAGA,SAAS,SAAA,CAAU,KAAa,SAAA,EAA2B;AACvD,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,QAAA,CAAS,GAAG,IAAI,EAAA,GAAK,GAAA;AAC3C,EAAA,OAAO,GAAG,GAAG,CAAA,EAAG,SAAS,CAAA,EAAG,aAAA,CAAc,SAAS,CAAC,CAAA,CAAA;AACxD;AAOA,SAAS,MAAA,CAAO,QAAgB,KAAA,EAAwB;AACpD,EAAA,OAAO,UAAU,MAAA,IAAU,KAAA,CAAM,UAAA,CAAW,CAAA,EAAG,MAAM,CAAA,CAAA,CAAG,CAAA;AAC5D;AAGA,SAAS,oBAAA,CAAqB,SAAkB,GAAA,EAA2B;AACvE,EAAA,OAAO,WAAA,CAAY,OAAA,EAAS,CAAC,KAAA,KAAW,MAAA,CAAO,KAAA,CAAM,MAAA,EAAQ,GAAG,CAAA,GAAI,KAAA,CAAM,MAAA,GAAS,IAAK,CAAA;AAC5F;AAEA,SAAS,WAAA,CAAY,OAAc,OAAA,EAAyB;AACxD,EAAA,IAAI,OAAA,KAAY,MAAM,MAAA,EAAQ;AAC1B,IAAA,OAAO,KAAA,CAAM,MAAA;AAAA,EACjB;AACA,EAAA,OAAO,CAAA,EAAG,MAAM,MAAM,CAAA,CAAA,EAAI,aAAa,KAAA,CAAM,MAAA,EAAQ,OAAO,CAAC,CAAA,CAAA;AACjE;AAEA,SAAS,cAAA,CAAe,SAAiB,OAAA,EAAyB;AAC9D,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,EAAS,OAAO,CAAA;AACzC,EAAA,OAAO,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,IAAK,GAAA,CAAI,WAAW,KAAK,CAAA,GAAI,GAAA,GAAM,CAAA,EAAA,EAAK,GAAG,CAAA,CAAA;AACzE;;;AC5JA,IAAM,KAAA,GAAQ,aAAa,SAAS,CAAA;AAY7B,SAAS,YAAA,CAAa,UAAA,EAAqB,cAAA,EAAwB,MAAA,EAAyB;AAC/F,EAAA,IAAI,OAAO,eAAe,QAAA,IAAY,UAAA,KAAe,QAAQ,KAAA,CAAM,OAAA,CAAQ,UAAU,CAAA,EAAG;AACpF,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,kBAAA,CAAoB,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,GAAA,EAAK,cAAc,CAAA,IAAK,GAAA;AAEjD,EAAA,MAAM,SAAkB,EAAC;AACzB,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAE7B,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,QAAQ,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AACtD,IAAA,IAAI,CAAC,iBAAA,CAAkB,GAAG,CAAA,EAAG;AACzB,MAAA,KAAA,CAAM,6DAAA,EAA0D,QAAQ,GAAG,CAAA;AAC3E,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,GAAA,EAAK,QAAA,EAAU,MAAM,CAAA;AACrD,IAAA,MAAM,MAAA,GAAS,gBAAgB,GAAG,CAAA;AAElC,IAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAChC,MAAA,IAAI,CAAC,iBAAA,CAAkB,SAAS,CAAA,EAAG;AAC/B,QAAA,KAAA;AAAA,UACI,sEAAA;AAAA,UACA,MAAA;AAAA,UACA,GAAA;AAAA,UACA;AAAA,SACJ;AACA,QAAA;AAAA,MACJ;AACA,MAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC9C,MAAA,IAAI,WAAW,IAAA,EAAM;AACjB,QAAA,KAAA;AAAA,UACI,4GAAA;AAAA,UACA,MAAA;AAAA,UACA,GAAA;AAAA,UACA;AAAA,SACJ;AACA,QAAA;AAAA,MACJ;AAEA,MAAA,MAAM,SAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,MAAM,CAAA,CAAA;AACtC,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,SAAS,CAAA,EAAG;AACrB,QAAA;AAAA,MACJ;AACA,MAAA,IAAA,CAAK,IAAI,SAAS,CAAA;AAClB,MAAA,MAAA,CAAO,IAAA,CAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAAA,IAClC;AAAA,EACJ;AAEA,EAAA,IAAI,MAAM,OAAA,EAAS;AACf,IAAA,KAAA;AAAA,MACI,CAAA,EAAG,MAAM,CAAA,iBAAA,EAAoB,cAAc,CAAA;AAAA,EAAmB,aAAA,CAAc,MAAM,CAAC,CAAA;AAAA,KACvF;AAAA,EACJ;AACA,EAAA,OAAO,MAAA;AACX;AAGA,SAAS,cAAc,OAAA,EAA0B;AAC7C,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,QAAA;AAAA,EACX;AAEA,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,EAAE,QAAQ,MAAA,EAAO,KAAM,CAAA,EAAG,MAAM,CAAA,GAAA,EAAM,MAAM,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AACjF;AAGA,SAAS,YAAA,CAAa,GAAA,EAAa,KAAA,EAAgB,MAAA,EAA0B;AACzE,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,IAAA,OAAO,CAAC,KAAK,CAAA;AAAA,EACjB;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACtB,MAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC1B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,GAAG,CAAA,4BAAA,CAA8B,CAAA;AAAA,MACnE;AAAA,IACJ;AACA,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,GAAG,CAAA,0CAAA,CAA4C,CAAA;AACjF;AAQO,SAAS,kBAAkB,KAAA,EAAwB;AACtD,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,KAAA,CAAM,GAAG,EAAE,MAAA,GAAS,CAAA;AAC5C,EAAA,IAAI,SAAA,GAAY,KAAM,SAAA,KAAc,CAAA,IAAK,CAAC,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,EAAI;AAC7D,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,eAAA,CAAgB,KAAK,CAAA,KAAM,EAAA;AACtC;AAEO,SAAS,gBAAgB,KAAA,EAAuB;AACnD,EAAA,OAAO,KAAA,CAAM,SAAS,IAAI,CAAA,GAAI,MAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA;AACvD;AAOA,SAAS,eAAA,CAAgB,WAAmB,IAAA,EAA6B;AACrE,EAAA,IAAI,SAAA,CAAU,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAO,YAAY,IAAA,EAAM,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,WAAA,GAAc,SAAS,SAAS,CAAA;AACtC,EAAA,MAAM,EAAE,IAAA,EAAM,GAAA,EAAI,GAAI,eAAe,WAAW,CAAA;AAEhD,EAAA,IAAI,QAAQ,EAAA,EAAI;AACZ,IAAA,OAAO,WAAA,CAAY,MAAM,SAAS,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,EAAM,GAAG,CAAA,EAAG;AAC7B,IAAA,OAAO,IAAA;AAAA,EACX;AAGA,EAAA,OAAO,WAAA,CAAY,MAAM,SAAA,CAAU,KAAA,CAAM,GAAG,CAAC,WAAA,CAAY,MAAM,CAAC,CAAA;AACpE;;;AC1HA,IAAM,wBAAA,GAA2B,GAAA;AAGjC,SAAS,gBAAgB,QAAA,EAAwD;AAC7E,EAAA,IAAI,OAAO,QAAA,KAAa,QAAA,IAAY,QAAA,KAAa,IAAA,EAAM;AACnD,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,OAAQ,QAAA,CAAqC,IAAA;AACnD,EAAA,IAAI,SAAS,MAAA,EAAW;AACpB,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AAC3C,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACrD;AAEA,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,aAAA,CAAc,OAAgB,MAAA,EAAwB;AAC3D,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,uBAAA,EAA0B,OAAO,KAAK,CAAA,CAAE,CAAA;AAAA,EACrE;AAEA,EAAA,OAAO,KAAA;AACX;AAOO,SAAS,WAAA,CAAY,UAAmB,QAAA,EAAwC;AACnF,EAAA,IAAI,aAAa,MAAA,EAAW;AACxB,IAAA,OAAO,aAAA,CAAc,UAAU,kBAAkB,CAAA;AAAA,EACrD;AAEA,EAAA,MAAM,QAAA,GAAW,eAAA,CAAgB,QAAQ,CAAA,EAAG,QAAA;AAC5C,EAAA,IAAI,aAAa,MAAA,EAAW;AACxB,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,OAAO,aAAA,CAAc,UAAU,wBAAwB,CAAA;AAC3D;AAGO,SAAS,iBAAA,CAAkB,UAAmB,QAAA,EAA4B;AAC7E,EAAA,IAAI,aAAa,MAAA,EAAW;AACxB,IAAA,OAAO,aAAA,CAAc,UAAU,wBAAwB,CAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,QAAQ,CAAA,EAAG,cAAA;AAClD,EAAA,IAAI,mBAAmB,MAAA,EAAW;AAC9B,IAAA,OAAO,wBAAA;AAAA,EACX;AAEA,EAAA,OAAO,aAAA,CAAc,gBAAgB,8BAA8B,CAAA;AACvE;AAEA,SAAS,WAAA,CAAY,QAAA,EAAmB,QAAA,EAAmB,eAAA,EAAmC;AAC1F,EAAA,MAAM,eAAe,QAAA,KAAa,MAAA;AAClC,EAAA,MAAM,UAAA,GAAa,YAAA,GAAe,QAAA,GAAW,eAAA,CAAgB,QAAQ,CAAA,EAAG,OAAA;AACxE,EAAA,IAAI,eAAe,MAAA,EAAW;AAC1B,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,MAAM,MAAA,GAAS,eAAe,iBAAA,GAAoB,uBAAA;AAClD,EAAA,OAAO,aAAa,UAAA,EAAY,iBAAA,CAAkB,QAAA,EAAU,eAAe,GAAG,MAAM,CAAA;AACxF;AASA,IAAM,MAAA,GAAS,GAAA;AAkBf,IAAI,eAAoC,EAAC;AAEzC,SAAS,OAAA,CAAQ,MAAuB,KAAA,EAAiC;AACrE,EAAA,OAAO,KAAK,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,KAAK,IAAA,CAAK,CAAC,CAAA,KAAM,KAAA,CAAM,CAAC,CAAA,IAAK,IAAA,CAAK,CAAC,CAAA,KAAM,MAAM,CAAC,CAAA;AAC9E;AAOA,SAAS,aAAA,CAAc,KAAsB,KAAA,EAA+B;AACxE,EAAA,MAAM,IAAA,GAAO,KAAK,GAAA,EAAI;AACtB,EAAA,KAAA,MAAW,SAAS,YAAA,EAAc;AAC9B,IAAA,IAAI,MAAM,SAAA,GAAY,IAAA,IAAQ,QAAQ,KAAA,CAAM,GAAA,EAAK,GAAG,CAAA,EAAG;AACnD,MAAA,OAAO,KAAA,CAAM,MAAA;AAAA,IACjB;AAAA,EACJ;AAEA,EAAA,MAAM,SAAS,KAAA,EAAM;AACrB,EAAA,YAAA,GAAe,YAAA,CAAa,MAAA;AAAA,IACxB,CAAC,UAAU,KAAA,CAAM,SAAA,GAAY,QAAQ,CAAC,OAAA,CAAQ,KAAA,CAAM,GAAA,EAAK,GAAG;AAAA,GAChE;AACA,EAAA,YAAA,CAAa,KAAK,EAAE,GAAA,EAAK,QAAQ,SAAA,EAAW,IAAA,GAAO,QAAQ,CAAA;AAC3D,EAAA,OAAO,MAAA;AACX;AAWO,SAAS,UAAA,CACZ,QAAA,EACA,QAAA,EACA,eAAA,EACO;AACP,EAAA,OAAO,aAAA;AAAA,IAAc,CAAC,QAAA,EAAU,QAAA,EAAU,eAAe,CAAA;AAAA,IAAG,MACxD,WAAA,CAAY,QAAA,EAAU,QAAA,EAAU,eAAe;AAAA,GACnD;AACJ;AASO,SAAS,mBAAA,CAAoB,KAAA,EAAgB,WAAA,EAAqB,MAAA,EAAyB;AAC9F,EAAA,OAAO,aAAA;AAAA,IAAc,CAAC,KAAA,EAAO,WAAA,EAAa,MAAM,CAAA;AAAA,IAAG,MAC/C,YAAA,CAAa,KAAA,EAAO,WAAA,EAAa,MAAM;AAAA,GAC3C;AACJ;AAOO,SAAS,UAAA,CAAW,UAAmB,QAAA,EAA6B;AACvE,EAAA,IAAI,aAAa,MAAA,EAAW;AACxB,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,OAAO,eAAA,CAAgB,QAAQ,CAAA,EAAG,OAAA,KAAY,MAAA;AAClD;AC3KA,IAAM,YAAA,GAAe,YAAA;AAGrB,SAAS,YAAY,QAAA,EAA2B;AAC5C,EAAA,OAAO,QAAA,CAAS,MAAA,KAAW,CAAA,IAAK,YAAA,CAAa,KAAK,QAAQ,CAAA;AAC9D;AAUO,SAAS,mBAAmB,QAAA,EAA2B;AAC1D,EAAA,OAAO,SAAS,UAAA,CAAW,GAAG,CAAA,IAAK,YAAA,CAAa,KAAK,QAAQ,CAAA;AACjE;AASO,SAAS,cAAc,IAAA,EAAsB;AAChD,EAAA,MAAM,KAAA,GAAQ,QAAQ,IAAI,CAAA;AAC1B,EAAA,IAAI,UAAU,GAAA,EAAK;AACf,IAAA,OAAO,EAAA;AAAA,EACX;AAEA,EAAA,OAAO,KAAA,CAAM,SAAS,GAAG,CAAA,GAAI,MAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,KAAA;AACtD;AAGO,SAAS,QAAA,CAAS,KAAa,IAAA,EAAsB;AACxD,EAAA,OAAO,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,CAAA,EAAG,GAAG,CAAA,EAAG,IAAI,CAAA,CAAA,GAAK,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAC/D;AAMO,SAAS,SAAS,GAAA,EAAqB;AAC1C,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AAC/B,EAAA,IAAI,QAAQ,EAAA,EAAI;AACZ,IAAA,OAAO,GAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AAC7B,EAAA,IAAI,SAAS,EAAA,EAAI;AACb,IAAA,OAAO,GAAA;AAAA,EACX;AACA,EAAA,IAAI,WAAA,CAAY,IAAI,CAAA,EAAG;AACnB,IAAA,OAAO,GAAG,IAAI,CAAA,CAAA,CAAA;AAAA,EAClB;AAEA,EAAA,OAAO,IAAA;AACX;AAQO,SAAS,WAAA,CAAY,IAAA,EAAc,KAAA,EAAe,KAAA,EAAwB;AAC7E,EAAA,IAAI,KAAA,KAAU,KAAK,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA,IAAK,YAAA,CAAa,IAAA,CAAK,KAAK,CAAA,EAAG;AACpE,IAAA,OAAO,IAAA,CAAK,WAAA,EAAY,KAAM,KAAA,CAAM,WAAA,EAAY;AAAA,EACpD;AAEA,EAAA,OAAO,IAAA,KAAS,KAAA;AACpB;AAUO,SAAS,mBAAA,CAAoB,MAAc,KAAA,EAA8B;AAC5E,EAAA,MAAM,QAAA,GAAW,CAAC,SAAA,KAA8B,YAAA,CAAa,KAAK,SAAS,CAAA,GAAI,CAAC,CAAA,IAAK,EAAA;AACrF,EAAA,MAAM,MAAA,GAAS,SAAS,IAAI,CAAA;AAC5B,EAAA,MAAM,WAAA,GAAc,SAAS,KAAK,CAAA;AAClC,EAAA,IAAI,CAAC,WAAA,CAAY,MAAA,EAAQ,WAAA,EAAa,CAAC,CAAA,EAAG;AACtC,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA;AACzC,EAAA,MAAM,QAAQ,WAAA,CAAY,QAAA,EAAU,MAAM,KAAA,CAAM,WAAA,CAAY,MAAM,CAAC,CAAA;AAEnE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,YAAA,CAAa,QAAA,CAAS,QAAQ,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,KAAK,CAAC,CAAC,CAAA,CAAA;AACvE;AAeO,SAAS,eAAA,CAAgB,MAAc,MAAA,EAAwB;AAClE,EAAA,MAAM,WAAA,GAAc,QAAQ,MAAM,CAAA;AAClC,EAAA,IAAI,kBAAA,CAAmB,WAAW,CAAA,EAAG;AACjC,IAAA,OAAO,WAAA;AAAA,EACX;AAEA,EAAA,MAAM,SAAA,GAAY,QAAQ,IAAI,CAAA;AAC9B,EAAA,MAAM,cAAc,WAAA,CAAY,SAAS,CAAA,GAAI,CAAA,EAAG,SAAS,CAAA,CAAA,CAAA,GAAM,SAAA;AAC/D,EAAA,MAAM,OAAA,GAAU,aAAa,IAAA,CAAK,WAAW,IAAIA,KAAA,CAAK,KAAA,CAAM,OAAA,GAAUA,KAAA,CAAK,KAAA,CAAM,OAAA;AAEjF,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,WAAW,CAAC,CAAA;AACpD;;;AClIA,IAAMC,MAAAA,GAAQ,aAAa,MAAM,CAAA;AAS1B,SAAS,YAAA,CAAa,UAAkB,MAAA,EAAkD;AAC7F,EAAA,IAAI,OAAA,GAAU,QAAQ,QAAQ,CAAA;AAC9B,EAAA,IAAI,KAAA,GAAuB,IAAA;AAE3B,EAAAA,MAAAA,CAAM,+BAA+B,OAAO,CAAA;AAE5C,EAAA,WAAS;AACL,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAA,EAAS,cAAc,CAAC,CAAA,EAAG;AAC3C,MAAA,KAAA,GAAQ,OAAA;AACR,MAAAA,MAAAA,CAAM,4BAA4B,OAAO,CAAA;AAAA,IAC7C;AAEA,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAA,EAAS,MAAM,CAAC,CAAA,EAAG;AACnC,MAAAA,MAAAA,CAAM,8BAA8B,OAAO,CAAA;AAC3C,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,MAAA,GAAS,SAAS,OAAO,CAAA;AAC/B,IAAA,IAAI,WAAW,OAAA,EAAS;AACpB,MAAAA,MAAAA,CAAM,iCAAiC,OAAO,CAAA;AAC9C,MAAA;AAAA,IACJ;AACA,IAAA,OAAA,GAAU,MAAA;AAAA,EACd;AAEA,EAAAA,MAAAA,CAAM,0BAA0B,KAAK,CAAA;AACrC,EAAA,OAAO,KAAA;AACX;;;ACxBA,IAAMA,MAAAA,GAAQ,aAAa,IAAI,CAAA;AA8B/B,IAAMC,OAAAA,GAAS,GAAA;AAYf,IAAM,eAAA,GAAkB,GAAA;AAGxB,SAAS,IAAA,CAAKC,MAAAA,EAAc,GAAA,EAAa,IAAA,EAAmC;AACxE,EAAA,MAAM,KAAA,GAAQA,MAAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC3B,EAAA,OAAO,UAAU,MAAA,IAAa,KAAA,CAAM,SAAA,GAAY,IAAA,GAAO,MAAM,KAAA,GAAQ,MAAA;AACzE;AAEA,SAAS,QAAA,CAASA,MAAAA,EAAc,GAAA,EAAa,IAAA,EAAc,KAAA,EAAyB;AAChF,EAAAA,MAAAA,CAAM,GAAA,CAAI,GAAA,EAAK,EAAE,KAAA,EAAO,WAAW,IAAA,IAAQ,KAAA,GAAQD,OAAAA,GAAS,eAAA,CAAA,EAAkB,CAAA;AAC9E,EAAA,OAAO,KAAA;AACX;AAMO,SAAS,YAAA,CAAa,IAAA,EAAc,OAAA,GAA+B,EAAC,EAAW;AAClF,EAAA,MAAM,cAAA,GAAiB,cAAc,IAAI,CAAA;AACzC,EAAA,MAAM,YAAA,GAAe,SAAS,cAAc,CAAA;AAC5C,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,IAAA,CAAK,GAAA;AAChC,EAAA,MAAM,YAAA,GAAe,QAAQ,MAAA,IAAU,UAAA;AACvC,EAAA,MAAM,eAAA,uBAA6B,GAAA,EAAI;AACvC,EAAA,MAAM,cAAA,uBAA4B,GAAA,EAAI;AAMtC,EAAA,SAAS,UAAU,GAAA,EAAqB;AACpC,IAAA,OAAO,CAAA,EAAG,cAAc,CAAA,EAAG,GAAG,CAAA,CAAA;AAAA,EAClC;AAEA,EAAA,SAAS,UAAU,QAAA,EAAiC;AAChD,IAAA,MAAM,SAAA,GAAY,QAAQ,QAAQ,CAAA;AAClC,IAAA,IAAI,CAAC,kBAAA,CAAmB,SAAS,CAAA,EAAG;AAChC,MAAA,OAAO,IAAA;AAAA,IACX;AAEA,IAAA,MAAM,YAAA,GAAe,SAAS,SAAS,CAAA;AACvC,IAAA,IAAI,YAAA,CAAa,MAAA,GAAS,YAAA,CAAa,MAAA,EAAQ;AAC3C,MAAA,OAAO,IAAA;AAAA,IACX;AACA,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,YAAA,CAAa,MAAA,EAAQ,KAAK,CAAA,EAAG;AAC7C,MAAA,IAAI,CAAC,YAAY,YAAA,CAAa,CAAC,GAAa,YAAA,CAAa,CAAC,CAAA,EAAa,CAAC,CAAA,EAAG;AACvE,QAAA,OAAO,IAAA;AAAA,MACX;AAAA,IACJ;AAEA,IAAA,OAAO,YAAA,CAAa,YAAA,CAAa,KAAA,CAAM,YAAA,CAAa,MAAM,CAAC,CAAA;AAAA,EAC/D;AAOA,EAAA,SAAS,gBAAgB,GAAA,EAAsB;AAC3C,IAAA,MAAM,OAAO,GAAA,EAAI;AACjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,cAAA,EAAgB,GAAA,EAAK,IAAI,CAAA;AAC7C,IAAA,IAAI,WAAW,MAAA,EAAW;AACtB,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,IAAI,aAAA,CAAc,GAAA,EAAK,IAAI,CAAA,EAAG;AAE1B,MAAA,OAAO,QAAA,CAAS,cAAA,EAAgB,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAAA,IACpD;AAEA,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,SAAA,CAAU,GAAG,CAAC,CAAA;AACzC,IAAAD,MAAAA,CAAM,wBAAA,EAA0B,GAAA,EAAK,KAAK,CAAA;AAC1C,IAAA,OAAO,QAAA,CAAS,cAAA,EAAgB,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAAA,EACpD;AASA,EAAA,SAAS,aAAA,CAAc,KAAa,IAAA,EAAuB;AACvD,IAAA,MAAM,MAAA,GAAS,QAAQ,GAAG,CAAA;AAC1B,IAAA,IAAI,WAAW,GAAA,EAAK;AAChB,MAAA,OAAO,KAAA;AAAA,IACX;AAEA,IAAA,OAAO,IAAA,CAAK,cAAA,EAAgB,MAAA,EAAQ,IAAI,CAAA,KAAM,KAAA;AAAA,EAClD;AAGA,EAAA,SAAS,cAAc,GAAA,EAAsB;AACzC,IAAA,IAAI,CAAC,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA;AAAA,IACX;AAEA,IAAA,MAAM,OAAO,GAAA,EAAI;AACjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,eAAA,EAAiB,GAAA,EAAK,IAAI,CAAA;AAC9C,IAAA,IAAI,WAAW,MAAA,EAAW;AACtB,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,IAAI,CAAC,eAAA,CAAgB,GAAG,CAAA,EAAG;AAGvB,MAAA,OAAO,QAAA,CAAS,eAAA,EAAiB,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAAA,IACrD;AAEA,IAAA,MAAM,OAAA,GAAU,UAAU,GAAG,CAAA;AAC7B,IAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,IAAA,CAAK,CAAC,IAAA,KAAS,aAAa,QAAA,CAAS,OAAA,EAAS,IAAI,CAAC,CAAC,CAAA;AACnF,IAAAA,MAAAA,CAAM,0BAAA,EAA4B,GAAA,EAAK,KAAK,CAAA;AAC5C,IAAA,OAAO,QAAA,CAAS,eAAA,EAAiB,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAAA,EACrD;AAEA,EAAA,OAAO,EAAE,eAAe,SAAA,EAAU;AACtC;AAEA,IAAM,aAAA,uBAAoB,GAAA,EAAoB;AAC9C,IAAM,aAAA,uBAAoB,GAAA,EAA2B;AAErD,SAAS,mBAAmB,GAAA,EAA4B;AAEpD,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA;AACpC,EAAA,IAAI,WAAW,MAAA,EAAW;AACtB,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,GAAA,EAAK,UAAU,CAAA;AAC1C,EAAA,aAAA,CAAc,GAAA,CAAI,KAAK,KAAK,CAAA;AAC5B,EAAA,OAAO,KAAA;AACX;AAEA,SAAS,WAAA,CACL,QAAA,EACA,GAAA,EACA,gBAAA,EACA,SAAA,EACM;AACN,EAAA,MAAM,YAAA,GAAe,WAAA,CAAY,QAAA,EAAU,gBAAgB,CAAA;AAC3D,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAG5B,IAAA,OAAO,eAAA,CAAgB,KAAK,YAAY,CAAA;AAAA,EAC5C;AAEA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,kBAAA,CAAmB,GAAG,KAAK,GAAG,CAAA;AACnD,EAAA,IAAI,IAAA,GAAO,IAAA;AACX,EAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAC9B,IAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,IAAA,EAAM,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC5D,IAAA,IAAI,aAAa,IAAA,EAAM;AACnB,MAAAA,MAAAA;AAAA,QACI,mEAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACJ;AACA,MAAA;AAAA,IACJ;AACA,IAAA,IAAA,GAAO,QAAA;AAAA,EACX;AAEA,EAAA,IAAI,SAAS,IAAA,EAAM;AACf,IAAAA,MAAAA,CAAM,qEAAA,EAAuE,IAAA,EAAM,IAAI,CAAA;AAAA,EAC3F;AAEA,EAAA,OAAO,IAAA;AACX;AAeO,SAAS,UACZ,QAAA,EACA,GAAA,EACA,gBAAA,EACA,SAAA,GAAsB,EAAC,EACjB;AACN,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,QAAA,EAAU,GAAA,EAAK,kBAAkB,SAAS,CAAA;AAEnE,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,GAAA,CAAI,IAAI,CAAA;AACrC,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAAA,MAAAA,CAAM,4CAA4C,IAAI,CAAA;AACtD,EAAA,MAAM,OAAA,GAAU,aAAa,IAAI,CAAA;AACjC,EAAA,aAAA,CAAc,GAAA,CAAI,MAAM,OAAO,CAAA;AAC/B,EAAA,OAAO,OAAA;AACX;AC5OA,IAAMA,MAAAA,GAAQ,aAAa,UAAU,CAAA;AAiCrC,IAAMC,OAAAA,GAAS,GAAA;AASf,IAAME,gBAAAA,GAAkB,GAAA;AAExB,IAAM,KAAA,uBAAY,GAAA,EAAwB;AAOnC,SAAS,iBAAA,CACZ,YAAA,EACA,OAAA,GAA+B,EAAC,EACZ;AACpB,EAAA,IAAI,CAACJ,KAAAA,CAAK,UAAA,CAAW,YAAY,CAAA,EAAG;AAChC,IAAAC,MAAAA,CAAM,uDAAuD,YAAY,CAAA;AACzE,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,GAAA,GAAMD,KAAAA,CAAK,OAAA,CAAQ,YAAY,CAAA;AACrC,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,IAAA,CAAK,GAAA;AAEhC,EAAA,MAAM,OAAO,GAAA,EAAI;AACjB,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC3B,EAAA,IAAI,KAAA,IAAS,KAAA,CAAM,SAAA,GAAY,IAAA,EAAM;AACjC,IAAA,OAAO,KAAA,CAAM,KAAA;AAAA,EACjB;AAMA,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAqB;AAC3C,EAAA,MAAM,IAAA,GACF,QAAQ,IAAA,KAAS,CAAC,cAAsB,WAAA,CAAY,SAAA,EAAW,iBAAiB,SAAS,CAAA,CAAA;AAE7F,EAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,GAAA,EAAK,IAAI,CAAA;AACzC,EAAA,KAAA,CAAM,GAAA,CAAI,KAAK,EAAE,KAAA,EAAO,WAAW,IAAA,IAAQ,KAAA,GAAQE,OAAAA,GAASE,gBAAAA,CAAAA,EAAkB,CAAA;AAC9E,EAAA,OAAO,KAAA;AACX;AAEA,SAAS,iBAAA,CACL,KACA,IAAA,EACoB;AACpB,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AACA,IAAA,KAAA,GAAQ,KAAK,GAAG,CAAA;AAAA,EACpB,SAAS,KAAA,EAAO;AACZ,IAAAH,MAAAA,CAAM,qCAAA,EAAuC,GAAA,EAAK,KAAK,CAAA;AACvD,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,IAAI,CAAC,KAAA,EAAO;AACR,IAAAA,MAAAA,CAAM,qCAAqC,GAAG,CAAA;AAC9C,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,MAAM,aAAa,KAAA,CAAM,IAAA;AACzB,EAAA,MAAM,eAAA,GAA2C,KAAA,CAAM,MAAA,CAAO,eAAA,IAAmB,EAAC;AAClF,EAAA,MAAM,KAAA,GAAQ,gBAAgB,OAAO,CAAA;AACrC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,IAAQ,OAAO,IAAA,CAAK,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA,EAAG;AAChF,IAAAA,MAAAA,CAAM,4CAA4C,UAAU,CAAA;AAC5D,IAAA,OAAO,IAAA;AAAA,EACX;AAMA,EAAA,MAAM,OAAA,GACF,OAAO,eAAA,CAAgB,SAAS,MAAM,QAAA,GAAW,eAAA,CAAgB,SAAS,CAAA,GAAI,MAAA;AAClF,EAAA,MAAM,cACF,OAAA,KAAY,MAAA,GACND,KAAAA,CAAK,OAAA,CAAQA,MAAK,OAAA,CAAQ,UAAU,CAAA,EAAG,OAAO,IAC7C,kBAAA,CAAmB,eAAe,CAAA,IAAKA,KAAAA,CAAK,QAAQ,UAAU,CAAA;AAEzE,EAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,KAAA,EAAkC,WAAW,CAAA;AAClF,EAAAC,MAAAA,CAAM,gCAAA,EAAkC,UAAA,EAAY,WAAW,CAAA;AAC/D,EAAA,OAAO;AAAA,IACH,KAAA,EAAO,MAAA;AAAA,IACP,WAAA;AAAA,IACA,UAAA;AAAA,IACA,WAAA,EAAa,kBAAA,CAAmB,MAAA,EAAQ,WAAW;AAAA,GACvD;AACJ;AAWA,SAAS,mBAAmB,eAAA,EAA6C;AACrE,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,qBAAA,CAAsB,eAAe,CAAA,CAAE,IAAA;AAAA,IACzD,CAAC,SAAA,KAAc,SAAA,CAAU,WAAA,KAAgB;AAAA,GAC7C;AACA,EAAA,MAAM,KAAA,GACF,MAAA,KAAW,MAAA,GAAY,MAAA,GAAa,gBAA4C,MAAM,CAAA;AAC1F,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,MAAA;AAC/C;AAQA,IAAM,kBAAA,GAAqB,mBAAA;AAQ3B,SAAS,qBAAA,CACL,OACA,WAAA,EACuB;AACvB,EAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAA2B;AAC3C,IAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,CAAC,kBAAA,CAAmB,IAAA,CAAK,IAAI,CAAA,EAAG;AAC5D,MAAA,OAAO,IAAA;AAAA,IACX;AAEA,IAAA,MAAM,WAAW,OAAA,CAAQD,KAAAA,CAAK,QAAA,CAAS,WAAA,EAAa,IAAI,CAAC,CAAA;AACzD,IAAA,IAAI,aAAa,EAAA,EAAI;AACjB,MAAA,OAAO,GAAA;AAAA,IACX;AAGA,IAAA,OAAO,SAAS,UAAA,CAAW,GAAG,CAAA,GAAI,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,GAAK,QAAA;AAAA,EACxD,CAAA;AAEA,EAAA,MAAM,SAAkC,EAAC;AACzC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC9C,IAAA,MAAA,CAAO,GAAG,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,UAAU,CAAA,GAAI,UAAA,CAAW,KAAK,CAAA;AAAA,EACjF;AACA,EAAA,OAAO,MAAA;AACX;AASA,SAAS,kBAAA,CAAmB,OAAgC,WAAA,EAA+B;AACvF,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAEhC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC9C,IAAA,IAAI,CAAC,iBAAA,CAAkB,GAAG,CAAA,EAAG;AACzB,MAAA;AAAA,IACJ;AAGA,IAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACnD,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACtB,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,CAAC,iBAAA,CAAkB,IAAI,CAAA,EAAG;AACtD,QAAA;AAAA,MACJ;AAEA,MAAA,MAAM,SAASA,KAAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,eAAA,CAAgB,IAAI,CAAC,CAAA;AAC9D,MAAA,IAAI,SAAS,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAE,QAAA,CAAS,cAAc,CAAA,EAAG;AACpD,QAAAC,MAAAA,CAAM,oEAAoE,MAAM,CAAA;AAChF,QAAA;AAAA,MACJ;AACA,MAAA,OAAA,CAAQ,IAAI,MAAM,CAAA;AAAA,IACtB;AAAA,EACJ;AAEA,EAAA,OAAO,CAAC,GAAG,OAAO,CAAA;AACtB;;;ACpNA,IAAMA,MAAAA,GAAQ,aAAa,OAAO,CAAA;AAM3B,IAAM,sBAAA,GAAyB;AAAA,EAClC,QAAA,EAAU,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,EAC3B,cAAA,EAAgB,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,EACjC,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA;AACrB,CAAA;AAqCO,SAAS,kBAAA,CACZ,SACA,cAAA,EAC+B;AAC/B,EAAA,MAAM,OAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAK,EAAC;AACxC,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,KAAY,qBAAA,CAAsB,OAAA,EAAS,SAAS,cAAc,CAAA;AAElF,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,SAAA,CAAU,OAAA,CAAQ,QAAQ,CAAA;AAClD,EAAA,IAAI,aAAa,IAAA,EAAM;AACnB,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,OAAA,EAAQ;AAChD;AAGA,SAAS,qBAAA,CACL,OAAA,EACA,OAAA,EACA,cAAA,EACoC;AAGpC,EAAA,IAAI,UAAA,CAAW,OAAA,CAAQ,QAAA,EAAU,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC/C,IAAA,OAAO;AAAA,MACH,MAAA,EAAQ,kBAAkB,SAAA,CAAU,OAAA,CAAQ,UAAU,OAAA,CAAQ,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAAA,MACnF,SAAS,UAAA,CAAW,OAAA,CAAQ,UAAU,OAAA,CAAQ,OAAA,EAAS,QAAQ,cAAc;AAAA,KACjF;AAAA,EACJ;AAYA,EAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,OAAA,CAAQ,QAAQ,CAAA;AAChD,EAAA,MAAM,kBAAkB,WAAA,CAAY,OAAA,CAAQ,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,KAAM,MAAA;AAC5E,EAAA,MAAM,MAAA,GAAS,SAAA;AAAA,IACX,OAAA,CAAQ,QAAA;AAAA,IACR,OAAA,CAAQ,GAAA;AAAA,IACR,OAAA,CAAQ,QAAA;AAAA,IACR,KAAA,EAAO,eAAe;AAAC,GAC3B;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,oBAAoB,KAAA,EAAO,MAAA,EAAQ,eAAe,CAAA,EAAE;AAClF;AAOA,SAAS,mBAAA,CACL,KAAA,EACA,MAAA,EACA,eAAA,EACO;AACP,EAAA,IAAI,UAAU,IAAA,EAAM;AAChB,IAAA,OAAO,EAAC;AAAA,EACZ;AAIA,EAAA,IAAI,mBAAmB,MAAA,CAAO,SAAA,CAAU,KAAA,CAAM,UAAU,MAAM,IAAA,EAAM;AAChE,IAAAA,MAAAA,CAAM,yDAAA,EAA2D,KAAA,CAAM,UAAU,CAAA;AACjF,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,SAAA,CAAU,KAAA,CAAM,WAAW,CAAA;AACtD,EAAA,IAAI,gBAAgB,IAAA,EAAM;AACtB,IAAAA,MAAAA;AAAA,MACI,yDAAA;AAAA,MACA,KAAA,CAAM,UAAA;AAAA,MACN,KAAA,CAAM;AAAA,KACV;AACA,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,IAAI;AACA,IAAA,OAAO,mBAAA,CAAoB,KAAA,CAAM,KAAA,EAAO,WAAA,EAAa,MAAM,UAAU,CAAA;AAAA,EACzE,SAAS,KAAA,EAAO;AACZ,IAAAA,MAAAA,CAAM,4BAAA,EAA8B,KAAA,CAAM,UAAA,EAAY,KAAK,CAAA;AAC3D,IAAA,OAAO,EAAC;AAAA,EACZ;AACJ;;;ACjJA,SAAS,YAAY,UAAA,EAA6B;AAC9C,EAAA,OAAO,gBAAgB,cAAA,CAAe,QAAA,CAAS,UAAU,CAAC,EAAE,IAAI,CAAA;AACpE;AAOO,SAAS,WAAA,CACZ,OAAA,EACA,UAAA,EACA,aAAA,EACa;AACb,EAAA,MAAM,SAAA,GAAY,QAAQ,UAAU,CAAA;AACpC,EAAA,MAAM,iBAAA,GAAoB,SAAS,SAAS,CAAA;AAG5C,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,OAAA,EAAS,SAAS,CAAA;AAG5C,EAAA,IAAI,MAAM,YAAA,CAAa,iBAAA,CAAkB,KAAA,CAAM,CAAA,EAAG,KAAK,CAAC,CAAA;AACxD,EAAA,KAAA,IAAS,IAAI,KAAA,EAAO,CAAA,GAAI,iBAAA,CAAkB,MAAA,EAAQ,KAAK,CAAA,EAAG;AACtD,IAAA,GAAA,GAAM,QAAA,CAAS,GAAA,EAAK,iBAAA,CAAkB,CAAC,CAAW,CAAA;AAClD,IAAA,IAAI,aAAA,CAAc,GAAG,CAAA,EAAG;AACpB,MAAA,MAAM,WAAA,GAAc,CAAA,KAAM,iBAAA,CAAkB,MAAA,GAAS,CAAA;AACrD,MAAA,OAAO,WAAA,IAAe,WAAA,CAAY,UAAU,CAAA,GAAI,IAAA,GAAO,GAAA;AAAA,IAC3D;AAAA,EACJ;AAEA,EAAA,OAAO,IAAA;AACX;;;ACnCA,IAAMA,MAAAA,GAAQ,aAAa,kBAAkB,CAAA;AAatC,SAAS,aAAA,CACZ,EAAE,QAAA,EAAU,OAAA,IACZ,IAAA,EACoC;AACpC,EAAA,MAAM,OAAA,GAAU,QAAQ,QAAQ,CAAA;AAEhC,EAAA,OAAO,SAAS,YAAY,SAAA,EAAkC;AAC1D,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,SAAA,EAAW,OAAA,EAAS,OAAO,CAAA;AACzD,IAAA,IAAI,WAAW,IAAA,EAAM;AACjB,MAAAA,MAAAA,CAAM,4BAAA,EAA8B,QAAA,EAAU,SAAS,CAAA;AACvD,MAAA,OAAO,IAAA;AAAA,IACX;AAEA,IAAA,MAAM,UAAU,WAAA,CAAY,OAAA,EAAS,MAAA,CAAO,IAAA,EAAM,KAAK,aAAa,CAAA;AACpE,IAAA,IAAI,YAAY,IAAA,EAAM;AAClB,MAAAA,MAAAA,CAAM,qCAAA,EAAuC,QAAA,EAAU,SAAA,EAAW,OAAO,IAAI,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACX;AAEA,IAAA,OAAO,gBAAgB,MAAA,CAAO,IAAA,EAAM,SAAS,OAAA,EAAS,OAAA,EAAS,OAAO,SAAS,CAAA;AAAA,EACnF,CAAA;AACJ;;;AC5BA,IAAMA,MAAAA,GAAQ,aAAa,kBAAkB,CAAA;AAE7C,IAAM,QAAA,GAAW;AAAA,EACb,MAAA,EAAQ,oFAAA;AAAA,EACR,SAAA,EAAW;AACf,CAAA;AAaO,SAAS,WAAW,MAAA,EAAkC;AACzD,EAAA,OAAO;AAAA,IACH,IAAA,EAAM;AAAA,MACF,IAAA,EAAM,SAAA;AAAA,MACN,IAAA,EAAM;AAAA,QACF,WAAA,EAAa,2DAAA;AAAA,QACb,GAAA,EAAK;AAAA,OACT;AAAA,MACA,OAAA,EAAS,MAAA;AAAA,MACT,cAAA,EAAgB,IAAA;AAAA,MAChB,MAAA,EAAQ;AAAA,QACJ;AAAA,UACI,IAAA,EAAM,QAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACR,GAAA,EAAK,EAAE,IAAA,EAAM,SAAA,EAAU;AAAA,YACvB,GAAG;AAAA,WACP;AAAA,UACA,oBAAA,EAAsB;AAAA;AAC1B,OACJ;AAAA,MACA;AAAA,KACJ;AAAA,IACA,OAAO,OAAA,EAAS;AACZ,MAAA,MAAM,IAAA,GAAO,kBAAA,CAAgC,OAAA,EAAS,MAAM,CAAA;AAC5D,MAAA,IAAI,SAAS,IAAA,EAAM;AACf,QAAA,OAAO,EAAC;AAAA,MACZ;AAEA,MAAA,MAAM,EAAE,UAAS,GAAI,IAAA;AACrB,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,GAAA,IAAO,IAAA;AACrC,MAAA,MAAM,WAAA,GAAc,aAAA,CAAc,IAAA,EAAM,IAAA,CAAK,MAAM,CAAA;AAEnD,MAAA,OAAO,sBAAA,CAAuB,CAAC,UAAA,KAA8B;AACzD,QAAA,MAAM,WAAW,UAAA,CAAW,KAAA;AAC5B,QAAA,MAAM,UAAA,GAAa,YAAY,QAAQ,CAAA;AAEvC,QAAA,IAAI,eAAe,IAAA,EAAM;AACrB,UAAA;AAAA,QACJ;AAEA,QAAAA,MAAAA;AAAA,UACI,wCAAA;AAAA,UACA,QAAA;AAAA,UACA,QAAA;AAAA,UACA,UAAA;AAAA,UACA;AAAA,SACJ;AACA,QAAA,MAAM,GAAA,GAAM,gBAAA,CAAiB,UAAA,EAAY,UAAU,CAAA;AAEnD,QAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,UACX,IAAA,EAAM,UAAA;AAAA,UACN,SAAA,EAAW,QAAA;AAAA,UACX,IAAA,EAAM,EAAE,UAAA,EAAY,QAAA,EAAS;AAAA,UAC7B,GAAI,QAAA,GACE,EAAE,GAAA,EAAI,GACN,EAAE,OAAA,EAAS,CAAC,EAAE,SAAA,EAAW,aAAa,IAAA,EAAM,EAAE,YAAW,EAAG,GAAA,EAAK,CAAA;AAAE,SAC5E,CAAA;AAAA,MACL,CAAC,CAAA;AAAA,IACL;AAAA,GACJ;AACJ;;;AC5FO,IAAM,KAAA,GAAQ;AAAA,EACjB,oBAAoB,UAAA;AACxB,CAAA;;;ACEA,IAAM,EAAE,OAAA,EAAQ,GAAI,cAAc,MAAA,CAAA,IAAA,CAAY,GAAG,EAAE,iBAAiB,CAAA;AAEpE,IAAM,WAAA,GAA6B;AAAA,EAC/B,IAAA,EAAM,kBAAA;AAAA,EACN,KAAA,EAAO;AAAA,IACH,uBAAA,EAAyB;AAAA;AAEjC,CAAA;AAEA,IAAM,MAAA,GAAwB;AAAA,EAC1B,IAAA,EAAM,aAAA;AAAA,EACN,KAAA,EAAO;AAAA,IACH,uBAAA,EAAyB;AAAA;AAEjC,CAAA;AAEA,IAAM,MAAA,GAAS;AAAA,EACX,IAAA,EAAM;AAAA,IACF,IAAA,EAAM,YAAA;AAAA,IACN;AAAA,GACJ;AAAA,EACA,KAAA;AAAA,EACA,OAAA,EAAS;AAAA,IACL,WAAA;AAAA,IACA;AAAA;AAER,CAAA;AAEA,WAAA,CAAY,OAAA,GAAU,EAAE,IAAA,EAAM,MAAA,EAAO;AACrC,MAAA,CAAO,OAAA,GAAU,EAAE,IAAA,EAAM,MAAA,EAAO;AAEhC,IAAO,aAAA,GAAQ","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"]}
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","target","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/** Расширения, при которых специфаер считается импортом кода внутрь репозитория */\nexport const MODULE_EXTENSIONS = [...ENTRY_EXTENSIONS, 'vue', 'svelte'] as const;\n\n/**\n * Расширения, при которых специфаер однозначно не является импортом кода: ассеты и данные.\n * Специфаеры с ними правила не проверяют и на диск не лезут.\n */\nconst ASSET_EXTENSIONS = [\n 'css',\n 'scss',\n 'sass',\n 'less',\n 'json',\n 'svg',\n 'png',\n 'jpg',\n 'jpeg',\n 'gif',\n 'webp',\n 'ico',\n 'woff',\n 'woff2',\n 'ttf',\n 'eot',\n 'otf',\n 'md',\n] as const;\n\nconst ENTRY_EXTENSION_SET: ReadonlySet<string> = new Set(ENTRY_EXTENSIONS);\nconst MODULE_EXTENSION_SET: ReadonlySet<string> = new Set(MODULE_EXTENSIONS);\nconst ASSET_EXTENSION_SET: ReadonlySet<string> = new Set(ASSET_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 ASSET_EXTENSIONS}. */\nexport function isAssetExtension(ext: string): boolean {\n return ASSET_EXTENSION_SET.has(ext);\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`; `resolve` нужен только\n * для расширения, которого нет ни среди модульных, ни среди ассетных (`./account.entity`) — там\n * форма записи не говорит, модуль это или нет.\n */\n\nimport { entryFileName, isAssetExtension, 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\nexport type SpecifierSkip = 'asset' | 'external-dependency' | 'out-of-repo' | 'no-module-found';\n\n/**\n * Итог разбора: `target` — цель для проверки, `skip` — причина, по которой проверять нечего.\n * Причина нужна вызывающему для debug-вывода; сам разбор по ней ничего не решает.\n */\nexport type ParseResult =\n { kind: 'target'; target: Target } | { kind: 'skip'; reason: SpecifierSkip };\n\n/**\n * `resolve` уточняет специфаер, расширение которого нет ни среди модульных, ни среди ассетных:\n * `./account.entity` — это и модуль `account.entity.ts`, и не-модуль вовсе, и различить их по форме\n * записи нельзя. Вернув `null`, резолвер говорит, что модуля по такому пути нет.\n */\nexport function parseSpecifier(\n specifier: string,\n fromDir: string,\n aliases: Alias[],\n resolve?: (path: string) => string | null,\n): ParseResult {\n if (specifier.startsWith('/') || specifier.includes('?') || specifier.includes('!')) {\n return skip('asset');\n }\n\n if (isRelativeSpecifier(specifier)) {\n return finalize(resolvePath(fromDir, specifier), { kind: 'relative' }, resolve);\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 }, resolve);\n }\n\n return skip('external-dependency');\n}\n\nfunction skip(reason: SpecifierSkip): ParseResult {\n return { kind: 'skip', reason };\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(\n path: string | null,\n form: Form,\n resolve?: (path: string) => string | null,\n): ParseResult {\n if (path === null) {\n return skip('out-of-repo');\n }\n\n const { ext } = splitExtension(basename(path));\n\n if (ext === '') {\n return target(path, form, null);\n }\n\n if (isModuleExtension(ext)) {\n return target(path, form, ext);\n }\n\n // Ассет отсекается до резолвера: `./x.css` — заведомо не модуль, спрашивать диск незачем.\n if (isAssetExtension(ext)) {\n return skip('asset');\n }\n\n const resolved = resolve?.(path);\n if (resolved === undefined || resolved === null) {\n return skip('no-module-found');\n }\n\n // Расширение в цель не переносится: `extension` — факт про форму записи специфаера, а в нём\n // расширения не было.\n return target(resolved, form, null);\n}\n\nfunction target(path: string, form: Form, extension: string | null): ParseResult {\n return { kind: 'target', target: { path, form, extension } };\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 = '.';\nconst DEFAULT_TSCONFIG_NAME = 'tsconfig.json';\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.tsconfig`; не задан — `'tsconfig.json'`. */\nexport function getTsconfigName(settings: unknown, override?: unknown): string {\n if (override !== undefined) {\n return requireString(override, 'options.tsconfig');\n }\n\n const tsconfig = getWeldSettings(settings)?.tsconfig;\n if (tsconfig === undefined) {\n return DEFAULT_TSCONFIG_NAME;\n }\n\n return requireString(tsconfig, 'settings.weld.tsconfig');\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_EXTENSIONS, ENTRY_FILE_NAMES, MODULE_EXTENSIONS } 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 /**\n * Находит виртуальный путь реального модуля по виртуальному пути специфаера без явного\n * расширения. Перебирает `MODULE_EXTENSIONS` как файлы, затем `ENTRY_EXTENSIONS` как\n * `index.<ext>` в директории. Возвращает `null`, если модуль не найден.\n */\n findModuleTarget(path: string): string | null;\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 const fileCache: 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 /**\n * Существует ли файл с виртуальным путём. Кэш отделён от директорий: одни и те же строки не\n * могут быть одновременно файлом и директорией, а промахи приходят пачками.\n */\n function fileExists(filePath: string): boolean {\n const time = now();\n const cached = peek(fileCache, filePath, time);\n if (cached !== undefined) {\n return cached;\n }\n\n const found = nativeExists(`${normalizedRoot}${filePath}`);\n debug('check file %s: %o', filePath, found);\n return remember(fileCache, filePath, time, found);\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 /**\n * Резолвит виртуальный путь специфаера с неоднозначным расширением в виртуальный путь\n * реального модуля. Сначала ищет файл с одним из `MODULE_EXTENSIONS`, затем директорию с\n * точкой входа. Приоритет файла над директорией совпадает с Node/TS resolution.\n */\n function findModuleTarget(path: string): string | null {\n if (!path.startsWith('/')) {\n return null;\n }\n\n for (const ext of MODULE_EXTENSIONS) {\n const candidate = `${path}.${ext}`;\n if (fileExists(candidate)) {\n return candidate;\n }\n }\n\n if (!hasEntryPoint(path)) {\n return null;\n }\n\n for (const ext of ENTRY_EXTENSIONS) {\n const candidate = `${path}/index.${ext}`;\n if (fileExists(candidate)) {\n return candidate;\n }\n }\n\n return null;\n }\n\n return { hasEntryPoint, findModuleTarget, 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 (по умолчанию `tsconfig.json`) и извлечение\n * `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 /** Имя файла tsconfig для поиска. */\n configName?: string;\n /** Шов для тестов TTL — по образцу `createFsHost`. */\n now?: () => number;\n /** Шов для тестов: подмена чтения диска (по умолчанию — `getTsconfig` из `get-tsconfig`). */\n read?: (searchDir: string, configName: string) => TsConfigResult | null;\n};\n\n/**\n * TTL найденного результата в мемоизации «директория файла + имя tsconfig → результат», то же значение, что\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, Map<string, CacheEntry>>();\n\n/**\n * Ищет ближайший файл с заданным именем tsconfig вверх от директории `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 configName = options.configName ?? 'tsconfig.json';\n const now = options.now ?? Date.now;\n\n const time = now();\n const entries = cache.get(dir);\n const entry = entries?.get(configName);\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 ??\n ((searchDir: string, name: string) => getTsconfig(searchDir, name, readCache));\n\n const value = readTsconfigPaths(dir, configName, read);\n const directoryEntries = entries ?? new Map<string, CacheEntry>();\n directoryEntries.set(configName, {\n value,\n expiresAt: time + (value ? TTL_MS : NEGATIVE_TTL_MS),\n });\n if (entries === undefined) {\n cache.set(dir, directoryEntries);\n }\n return value;\n}\n\nfunction readTsconfigPaths(\n dir: string,\n configName: string,\n read: (searchDir: string, configName: string) => TsConfigResult | null,\n): TsconfigPaths | null {\n let found: TsConfigResult | null;\n try {\n found = read(dir, configName);\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`/`tsconfig` ни у себя в `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 {\n getAliases,\n getAliasesFromPaths,\n getRepoRoot,\n getTsconfigName,\n hasAliases,\n} 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 tsconfig: { type: 'string' },\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 tsconfig?: string;\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 tsconfigName = getTsconfigName(context.settings, options.tsconfig);\n const found = loadTsconfigPaths(context.filename, { configName: tsconfigName });\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 type { FsHost } from '@/host/index.js';\nimport type { SpecifierSkip } from '@/imports/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\ntype CheckerInput = { fromFile: string; aliases: Alias[] };\n\nexport type Host = Pick<FsHost, 'findModuleTarget' | 'hasEntryPoint'>;\n\n/**\n * Итог проверки специфаера: `replace` — нарушение с исправленным специфаером, `intact` — нарушения\n * нет, `skip` — специфаер пропущен с причиной. Наружу это нужно debug-выводу правила: тексты живут\n * там же, не в ядре.\n */\nexport type CheckDecision =\n | { kind: 'replace'; target: string; barrier: string; suggestion: string }\n | { kind: 'intact'; target: string }\n | { kind: 'skip'; reason: SpecifierSkip };\n\n/**\n * Проверка собирается на файл, а не на импорт: `fromDir` от линтуемого файла не зависит от того,\n * какой специфаер сейчас разбирается.\n */\nexport function createChecker(\n { fromFile, aliases }: CheckerInput,\n host: Host,\n): (specifier: string) => CheckDecision {\n const fromDir = dirname(fromFile);\n // Связка \"виртуальный путь → реальный модуль\" нужна разбору только для специфаеров с точкой в\n // имени файла, поэтому привязывается один раз на файл, а не на каждый импорт.\n const resolve = host.findModuleTarget.bind(host);\n\n return function checkImport(specifier: string): CheckDecision {\n const parse = parseSpecifier(specifier, fromDir, aliases, resolve);\n if (parse.kind === 'skip') {\n return { kind: 'skip', reason: parse.reason };\n }\n\n const { target } = parse;\n const barrier = findBarrier(fromDir, target.path, host.hasEntryPoint);\n if (barrier === null) {\n return { kind: 'intact', target: target.path };\n }\n\n return {\n kind: 'replace',\n target: target.path,\n barrier,\n suggestion: renderSpecifier(target.form, fromDir, barrier, aliases, target.extension),\n };\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 type { SpecifierSkip } 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\n/** Текст причины пропуска: разбор называет причину, вывод — что это значит. */\nconst skipMessages: Record<SpecifierSkip, string> = {\n asset: 'asset',\n 'external-dependency': 'external dependency',\n 'out-of-repo': 'path outside the repository',\n 'no-module-found': 'module not found',\n};\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 decision = checkImport(original);\n\n if (decision.kind === 'skip') {\n debug(`${fromFile}: skip ${original}: ${skipMessages[decision.reason]}`);\n return;\n }\n\n if (decision.kind === 'intact') {\n debug(`${fromFile}: good ${original}`);\n return;\n }\n\n const { suggestion } = decision;\n debug(`${fromFile}: bad ${original}; fix: ${decision.barrier}`);\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 plugin = {\n meta: {\n name: PACKAGE_NAME,\n version,\n },\n rules,\n configs: {\n recommended,\n },\n} satisfies ESLint.Plugin;\n\nrecommended.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.2.0",
3
+ "version": "0.4.0-rc.0",
4
4
  "description": "ESLint-плагин для проверки правил WELD (Well-Encapsulated Layered Design)",
5
5
  "keywords": [
6
6
  "eslint",
@@ -40,6 +40,9 @@
40
40
  "scripts": {
41
41
  "build": "tsup",
42
42
  "checks": "yarn lint && yarn format:check && yarn typecheck && yarn test",
43
+ "docs:build": "vitepress build docs",
44
+ "docs:dev": "vitepress dev docs",
45
+ "docs:preview": "vitepress preview docs",
43
46
  "format": "prettier --write .",
44
47
  "format:check": "prettier --check .",
45
48
  "lint": "eslint .",
@@ -67,10 +70,13 @@
67
70
  "eslint": "^10.0.0",
68
71
  "husky": "^9.1.7",
69
72
  "lint-staged": "^17.4.1",
73
+ "mermaid": "^12.0.0",
70
74
  "prettier": "^3.9.0",
71
75
  "tsup": "^8.5.0",
72
76
  "typescript": "^5.9.0",
73
77
  "typescript-eslint": "^8.69.0",
78
+ "vitepress": "^2.0.0-alpha.20",
79
+ "vitepress-plugin-mermaid": "^2.0.17",
74
80
  "vitest": "^4.1.0"
75
81
  },
76
82
  "peerDependencies": {