@useinsider/eslint-config 4.0.0-beta.2 → 4.0.0-beta.4

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.
Files changed (49) hide show
  1. package/README.md +21 -7
  2. package/dist/cli.cjs +447 -87
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/{javascript-dom-CvtQ6_9W.cjs → javascript-dom-bySaQErv.cjs} +4 -4
  5. package/dist/{javascript-dom-CvtQ6_9W.cjs.map → javascript-dom-bySaQErv.cjs.map} +1 -1
  6. package/dist/{javascript-node-BNsy3loY.cjs → javascript-node-DZxexKer.cjs} +4 -4
  7. package/dist/{javascript-node-BNsy3loY.cjs.map → javascript-node-DZxexKer.cjs.map} +1 -1
  8. package/dist/{javascript-DH8-N38w.cjs → javascript-ycJ4W-e9.cjs} +5 -5
  9. package/dist/{javascript-DH8-N38w.cjs.map → javascript-ycJ4W-e9.cjs.map} +1 -1
  10. package/dist/{jest-4Es9Ggaa.cjs → jest-B2fpQmUB.cjs} +3 -3
  11. package/dist/{jest-4Es9Ggaa.cjs.map → jest-B2fpQmUB.cjs.map} +1 -1
  12. package/dist/main.cjs +35 -32
  13. package/dist/main.cjs.map +1 -1
  14. package/dist/main.js +9 -6
  15. package/dist/main.js.map +1 -1
  16. package/dist/{oxlintOwnership-7hpdB3d1.cjs → removedOptions-BYMW8w__.cjs} +29 -1
  17. package/dist/removedOptions-BYMW8w__.cjs.map +1 -0
  18. package/dist/{oxlintOwnership-Cx0hgA_J.js → removedOptions-Cb8SG8SI.js} +18 -2
  19. package/dist/removedOptions-Cb8SG8SI.js.map +1 -0
  20. package/dist/{stylistic-CoP3MT1Y.cjs → stylistic-B2hCwBRu.cjs} +4 -4
  21. package/dist/{stylistic-CoP3MT1Y.cjs.map → stylistic-B2hCwBRu.cjs.map} +1 -1
  22. package/dist/{typescript-D3VeLnpn.cjs → typescript-DdgbCNKI.cjs} +7 -7
  23. package/dist/{typescript-D3VeLnpn.cjs.map → typescript-DdgbCNKI.cjs.map} +1 -1
  24. package/dist/{typescript-dom-iB_xBTAD.cjs → typescript-dom-C0NNSGLd.cjs} +4 -4
  25. package/dist/{typescript-dom-iB_xBTAD.cjs.map → typescript-dom-C0NNSGLd.cjs.map} +1 -1
  26. package/dist/{typescript-node-Cq6qhj53.cjs → typescript-node-CjG8N83q.cjs} +4 -4
  27. package/dist/{typescript-node-Cq6qhj53.cjs.map → typescript-node-CjG8N83q.cjs.map} +1 -1
  28. package/dist/utils/removedOptions.d.ts +10 -0
  29. package/dist/{vitest-BlGQswK2.cjs → vitest-B3kOWOzo.cjs} +3 -3
  30. package/dist/{vitest-BlGQswK2.cjs.map → vitest-B3kOWOzo.cjs.map} +1 -1
  31. package/dist/{vue2-Da1i_oVn.cjs → vue2-DfL5FqeZ.cjs} +8 -8
  32. package/dist/{vue2-Da1i_oVn.cjs.map → vue2-DfL5FqeZ.cjs.map} +1 -1
  33. package/dist/{vue2-options-api-BSdrUm8H.cjs → vue2-options-api-sSrBV5Si.cjs} +8 -8
  34. package/dist/{vue2-options-api-BSdrUm8H.cjs.map → vue2-options-api-sSrBV5Si.cjs.map} +1 -1
  35. package/dist/{vue2-typescript-options-api-LM7U5dWq.cjs → vue2-typescript-options-api-CbRC2wf-.cjs} +10 -10
  36. package/dist/{vue2-typescript-options-api-LM7U5dWq.cjs.map → vue2-typescript-options-api-CbRC2wf-.cjs.map} +1 -1
  37. package/dist/{vue2-typescript-dXLRSGNP.cjs → vue2-typescript-rJIMzNUI.cjs} +10 -10
  38. package/dist/{vue2-typescript-dXLRSGNP.cjs.map → vue2-typescript-rJIMzNUI.cjs.map} +1 -1
  39. package/dist/{vue3-621EM2Ii.cjs → vue3-DmrGRHbo.cjs} +8 -8
  40. package/dist/{vue3-621EM2Ii.cjs.map → vue3-DmrGRHbo.cjs.map} +1 -1
  41. package/dist/{vue3-options-api-CC9_fAiq.cjs → vue3-options-api-DPEk8rBk.cjs} +8 -8
  42. package/dist/{vue3-options-api-CC9_fAiq.cjs.map → vue3-options-api-DPEk8rBk.cjs.map} +1 -1
  43. package/dist/{vue3-typescript-DONe-IKz.cjs → vue3-typescript-Dz5BM0LJ.cjs} +10 -10
  44. package/dist/{vue3-typescript-DONe-IKz.cjs.map → vue3-typescript-Dz5BM0LJ.cjs.map} +1 -1
  45. package/dist/{vue3-typescript-options-api-D4pqpDFs.cjs → vue3-typescript-options-api-DiYz_vVd.cjs} +10 -10
  46. package/dist/{vue3-typescript-options-api-D4pqpDFs.cjs.map → vue3-typescript-options-api-DiYz_vVd.cjs.map} +1 -1
  47. package/package.json +1 -1
  48. package/dist/oxlintOwnership-7hpdB3d1.cjs.map +0 -1
  49. package/dist/oxlintOwnership-Cx0hgA_J.js.map +0 -1
package/dist/cli.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_oxlintOwnership = require("./oxlintOwnership-7hpdB3d1.cjs");
3
+ const require_removedOptions = require("./removedOptions-BYMW8w__.cjs");
4
4
  let globals = require("globals");
5
- globals = require_oxlintOwnership.__toESM(globals, 1);
5
+ globals = require_removedOptions.__toESM(globals, 1);
6
6
  let node_fs = require("node:fs");
7
7
  let node_path = require("node:path");
8
8
  let node_url = require("node:url");
@@ -10,7 +10,7 @@ let _typescript_eslint_types = require("@typescript-eslint/types");
10
10
  let node_crypto = require("node:crypto");
11
11
  let node_os = require("node:os");
12
12
  let typescript = require("typescript");
13
- typescript = require_oxlintOwnership.__toESM(typescript, 1);
13
+ typescript = require_removedOptions.__toESM(typescript, 1);
14
14
  let _typescript_eslint_parser = require("@typescript-eslint/parser");
15
15
  //#region src/cli/detect.ts
16
16
  var FLAT_CONFIG_FILENAMES = [
@@ -423,6 +423,7 @@ var dependencies$7 = {
423
423
  };
424
424
  //#endregion
425
425
  //#region src/cli/resolveEslintConfigManifest.ts
426
+ var ESLINT_CONFIG_PACKAGE_NAME = "@useinsider/eslint-config";
426
427
  function tryReadManifest(candidate) {
427
428
  try {
428
429
  const raw = (0, node_fs.readFileSync)(candidate, "utf8");
@@ -451,11 +452,24 @@ function readOwnEslintConfigManifestFrom(runtimeDir) {
451
452
  function readOwnEslintConfigManifest$1() {
452
453
  return readOwnEslintConfigManifestFrom(resolveRuntimeDir());
453
454
  }
455
+ /**
456
+ * Install spec for the copy of `@useinsider/eslint-config` that is running right now, pinned to its
457
+ * own version. A dist-tag can never stand in for it: `latest` points at the previous major for as
458
+ * long as the current one ships on a prerelease tag, so resolving it downgrades the very consumer
459
+ * being scaffolded or migrated.
460
+ * @example
461
+ * ownEslintConfigSpec({ name: '@useinsider/eslint-config', version: '4.0.0-beta.3' });
462
+ * // '@useinsider/eslint-config@4.0.0-beta.3'
463
+ */
464
+ function ownEslintConfigSpec(manifest) {
465
+ const version = manifest.version?.trim();
466
+ if (!version) throw new Error(`Unable to read the running ${ESLINT_CONFIG_PACKAGE_NAME} version from its package.json. Refusing to install it from a dist-tag, which would downgrade this project.`);
467
+ return `${ESLINT_CONFIG_PACKAGE_NAME}@${version}`;
468
+ }
454
469
  //#endregion
455
470
  //#region src/cli/install.ts
456
471
  function toRequiredManifest(manifest) {
457
472
  return {
458
- version: manifest.version ?? "0.0.0",
459
473
  dependencies: manifest.dependencies ?? {},
460
474
  devDependencies: manifest.devDependencies ?? {},
461
475
  peerDependencies: manifest.peerDependencies ?? {},
@@ -482,7 +496,7 @@ function applyRecord(deps, manifest, record) {
482
496
  function buildDependencyList(answers, rawManifest = readOwnEslintConfigManifest$1()) {
483
497
  const manifest = toRequiredManifest(rawManifest);
484
498
  const deps = /* @__PURE__ */ new Set();
485
- deps.add(`@useinsider/eslint-config@${manifest.version}`);
499
+ deps.add(ownEslintConfigSpec(rawManifest));
486
500
  deps.add(pinned(manifest, "eslint"));
487
501
  deps.add(pinned(manifest, "globals"));
488
502
  applyRecord(deps, manifest, dependencies$14);
@@ -741,6 +755,22 @@ async function installWithRetry(opts) {
741
755
  }
742
756
  }
743
757
  //#endregion
758
+ //#region src/cli/detectIndent.ts
759
+ var DEFAULT_INDENT = " ";
760
+ /**
761
+ * The indentation a JSON source already uses, as the string `JSON.stringify` takes for its third
762
+ * argument. A width would be wrong: `JSON.stringify` emits that many *spaces*, so a tab-indented
763
+ * manifest comes back re-indented with spaces and every line of it shows up in the diff.
764
+ * @example
765
+ * detectIndent('{\n\t"name": "app"\n}'); // '\t'
766
+ * @example
767
+ * detectIndent('{"name":"app"}', ' '); // ' '
768
+ */
769
+ function detectIndent(raw, fallback = DEFAULT_INDENT) {
770
+ const match = /\n([\t ]+)\S/u.exec(raw);
771
+ return match ? match[1] : fallback;
772
+ }
773
+ //#endregion
744
774
  //#region src/cli/lintLayout.ts
745
775
  var OXLINT_COMMAND = "oxlint --disable-nested-config";
746
776
  var OXLINT_LINT_SCRIPT = `${OXLINT_COMMAND} && eslint .`;
@@ -842,14 +872,7 @@ function renderLintLayoutFiles(layout) {
842
872
  source: renderOxlintConfigFile(layout.lintModule)
843
873
  }];
844
874
  }
845
- var DEFAULT_INDENT = 2;
846
- function detectIndent$1(raw) {
847
- const firstBreak = raw.indexOf("\n");
848
- if (firstBreak === -1) return DEFAULT_INDENT;
849
- const rest = raw.slice(firstBreak + 1);
850
- const width = rest.length - rest.trimStart().length;
851
- return width > 0 ? width : DEFAULT_INDENT;
852
- }
875
+ var DEFAULT_MANIFEST_INDENT = " ";
853
876
  var SEGMENT_SEPARATOR = /&&|\|\||[;|]/u;
854
877
  var ASSIGNMENT = /^[A-Za-z_]\w*=/u;
855
878
  var WRAPPER_COMMANDS = /* @__PURE__ */ new Set([
@@ -1003,7 +1026,7 @@ function setLintScript(rawManifest) {
1003
1026
  parsed.scripts[change.script] = change.next;
1004
1027
  });
1005
1028
  return {
1006
- manifest: `${JSON.stringify(parsed, null, detectIndent$1(rawManifest))}\n`,
1029
+ manifest: `${JSON.stringify(parsed, null, detectIndent(rawManifest, DEFAULT_MANIFEST_INDENT))}\n`,
1007
1030
  changed: true,
1008
1031
  changes,
1009
1032
  alreadyRunsOxlint,
@@ -1785,6 +1808,18 @@ var ALL_PRESET_DEPS = [
1785
1808
  ...Object.values(SCOPE_TO_DEPS)
1786
1809
  ];
1787
1810
  var PRESET_DEPENDENCY_KEYS = new Set(ALL_PRESET_DEPS.flatMap((record) => Object.keys(record)));
1811
+ var MIGRATABLE_SCOPES = new Set(Object.keys(SCOPE_TO_DEPS));
1812
+ /**
1813
+ * Whether a `useInsider()` option key is a scope this migration can derive dependencies for. The
1814
+ * four `*OptionsApi` scopes are deliberately absent from the registry, so a config using one is
1815
+ * carried over untouched while its dependencies are reported as unresolved.
1816
+ * @example
1817
+ * isMigratableScope('vue3Typescript'); // true
1818
+ * isMigratableScope('vue3OptionsApi'); // false
1819
+ */
1820
+ function isMigratableScope(key) {
1821
+ return MIGRATABLE_SCOPES.has(key);
1822
+ }
1788
1823
  function isEslintRelatedDependency(name) {
1789
1824
  if (SAFELIST.has(name)) return false;
1790
1825
  if (EXACT_PATTERNS.has(name)) return true;
@@ -1879,7 +1914,7 @@ function resolveSpec(name, manifest, fallbacks) {
1879
1914
  function buildInstallList(scopes, manifest) {
1880
1915
  const specs = [];
1881
1916
  const fallbacks = [];
1882
- appendSpec(specs, "@useinsider/eslint-config@latest");
1917
+ appendSpec(specs, ownEslintConfigSpec(manifest));
1883
1918
  appendSpec(specs, resolveSpec("eslint", manifest, fallbacks));
1884
1919
  Object.entries(dependencies$14).forEach(([packageName, shouldInstall]) => {
1885
1920
  if (shouldInstall) appendSpec(specs, resolveSpec(packageName, manifest, fallbacks));
@@ -1899,11 +1934,6 @@ function buildInstallList(scopes, manifest) {
1899
1934
  var readOwnEslintConfigManifest = readOwnEslintConfigManifest$1;
1900
1935
  //#endregion
1901
1936
  //#region src/cli/prunePackageJson.ts
1902
- function detectIndent(raw) {
1903
- const match = /\n([ \t]+)"/u.exec(raw);
1904
- if (!match) return " ";
1905
- return match[1];
1906
- }
1907
1937
  function serializeManifest(manifest, indent) {
1908
1938
  return `${JSON.stringify(manifest, null, indent)}\n`;
1909
1939
  }
@@ -1952,7 +1982,6 @@ var TS_EXTENSIONS_SNIPPET = [
1952
1982
  " }",
1953
1983
  "}"
1954
1984
  ].join("\n");
1955
- var DEFAULT_INDENT_UNIT = " ";
1956
1985
  function acceptsTsExtensionsFlag(options) {
1957
1986
  return options.noEmit === true || options.emitDeclarationOnly === true || options.rewriteRelativeImportExtensions === true;
1958
1987
  }
@@ -2049,10 +2078,6 @@ function indentAt(raw, position) {
2049
2078
  const prefix = raw.slice(lineStart, position);
2050
2079
  return prefix.trim().length === 0 ? prefix : "";
2051
2080
  }
2052
- function detectIndentUnit(raw) {
2053
- const match = /\n([\t ]+)\S/u.exec(raw);
2054
- return match ? match[1] : DEFAULT_INDENT_UNIT;
2055
- }
2056
2081
  function splice(raw, start, end, text) {
2057
2082
  return raw.slice(0, start) + text + raw.slice(end);
2058
2083
  }
@@ -2062,14 +2087,14 @@ function setExistingFlag(raw, source, property) {
2062
2087
  function insertIntoCompilerOptions(raw, source, compilerOptions, object) {
2063
2088
  const openBrace = object.getStart(source) + 1;
2064
2089
  const [first] = object.properties;
2065
- if (first) return splice(raw, openBrace, openBrace, `\n${indentAt(raw, first.getStart(source)) || detectIndentUnit(raw).repeat(2)}"${TS_EXTENSIONS_FLAG}": true,`);
2090
+ if (first) return splice(raw, openBrace, openBrace, `\n${indentAt(raw, first.getStart(source)) || detectIndent(raw).repeat(2)}"${TS_EXTENSIONS_FLAG}": true,`);
2066
2091
  const outer = indentAt(raw, compilerOptions.getStart(source));
2067
- const unit = detectIndentUnit(raw);
2092
+ const unit = detectIndent(raw);
2068
2093
  return splice(raw, openBrace, object.getEnd() - 1, `\n${outer}${unit}"${TS_EXTENSIONS_FLAG}": true\n${outer}`);
2069
2094
  }
2070
2095
  function insertCompilerOptions(raw, source, root) {
2071
2096
  const openBrace = root.getStart(source) + 1;
2072
- const unit = detectIndentUnit(raw);
2097
+ const unit = detectIndent(raw);
2073
2098
  const [first] = root.properties;
2074
2099
  const indent = first ? indentAt(raw, first.getStart(source)) || unit : unit;
2075
2100
  const block = `\n${indent}"compilerOptions": {\n${indent}${unit}"${TS_EXTENSIONS_FLAG}": true\n${indent}}`;
@@ -2223,8 +2248,8 @@ function formatDirective(owner, block, kind, rules, reason) {
2223
2248
  * movesToOxlint('@typescript-eslint/no-unused-vars', '.vue'); // false — ESLint still runs it in SFCs
2224
2249
  */
2225
2250
  function movesToOxlint(rule, fileExtension) {
2226
- if (!require_oxlintOwnership.isOxlintOwned(rule)) return false;
2227
- return !(SFC_EXTENSIONS.includes(fileExtension) && require_oxlintOwnership.isVueUnsafeRule(rule));
2251
+ if (!require_removedOptions.isOxlintOwned(rule)) return false;
2252
+ return !(SFC_EXTENSIONS.includes(fileExtension) && require_removedOptions.isVueUnsafeRule(rule));
2228
2253
  }
2229
2254
  function toSpans(comments, offset) {
2230
2255
  return comments.map((comment) => ({
@@ -2354,35 +2379,75 @@ function collectFiles(directory, accumulator, visited) {
2354
2379
  if (entry.isFile() && isLintedFile(full)) accumulator.push(full);
2355
2380
  });
2356
2381
  }
2382
+ function readSource(filePath) {
2383
+ try {
2384
+ return (0, node_fs.readFileSync)(filePath, "utf8");
2385
+ } catch {
2386
+ return null;
2387
+ }
2388
+ }
2357
2389
  /**
2358
2390
  * Every file whose `eslint-disable` comments would be re-pointed, and the rewritten source, without
2359
2391
  * touching disk. The directive rewrite is the largest diff a migration produces, so the count is
2360
- * reported — and confirmed — before anything is written.
2392
+ * reported — and confirmed — before anything is written. Files that could not be read come back in
2393
+ * `skipped` instead of aborting the walk.
2361
2394
  * @example
2362
- * previewDisableDirectives('/repo').length; // 131
2395
+ * previewDisableDirectives('/repo').candidates.length; // 131
2363
2396
  */
2364
2397
  function previewDisableDirectives(cwd) {
2365
2398
  const files = [];
2399
+ const candidates = [];
2400
+ const skipped = [];
2366
2401
  collectFiles(cwd, files, /* @__PURE__ */ new Set());
2367
- return files.reduce((candidates, filePath) => {
2368
- const result = rewriteDirectives((0, node_fs.readFileSync)(filePath, "utf8"), (0, node_path.extname)(filePath));
2369
- if (!result.changed) return candidates;
2370
- return [...candidates, {
2402
+ files.forEach((filePath) => {
2403
+ const source = readSource(filePath);
2404
+ if (source === null) {
2405
+ skipped.push(filePath);
2406
+ return;
2407
+ }
2408
+ const result = rewriteDirectives(source, (0, node_path.extname)(filePath));
2409
+ if (result.changed) candidates.push({
2371
2410
  path: filePath,
2372
2411
  source: result.source
2373
- }];
2374
- }, []);
2412
+ });
2413
+ });
2414
+ return {
2415
+ candidates,
2416
+ skipped
2417
+ };
2418
+ }
2419
+ /**
2420
+ * What the consumer is told about the files the rewrite could not touch: the paths, then why it
2421
+ * matters and how to finish the job. Nothing else in the migration is affected, and saying so is the
2422
+ * point — the layout was already written by the time the walk ran.
2423
+ * @example
2424
+ * formatUntouchedDirectiveFiles(['/repo/src/vendor.ts']); // '/repo/src/vendor.ts\n\nThese could not…'
2425
+ */
2426
+ function formatUntouchedDirectiveFiles(skipped) {
2427
+ return `${skipped.join("\n")}\n\nThese could not be read or written, so their \`eslint-disable\` comments still name rules oxlint now owns and ESLint will report each one as an unused directive. Everything else was migrated. Fix the access and re-run \`npx @useinsider/eslint-config migrate\` to pick them up.`;
2375
2428
  }
2376
2429
  /**
2377
2430
  * Applies {@link rewriteDirectives} across a consumer tree, skipping build output and dependencies.
2431
+ * A candidate whose write fails comes back in `skipped`, so one failed write costs its own
2432
+ * directives rather than every later file's.
2378
2433
  * @example
2379
2434
  * rewriteDisableDirectives('/repo').rewritten; // ['/repo/src/logger.ts']
2380
2435
  */
2381
- function rewriteDisableDirectives(cwd, candidates = previewDisableDirectives(cwd)) {
2436
+ function rewriteDisableDirectives(cwd, candidates = previewDisableDirectives(cwd).candidates) {
2437
+ const rewritten = [];
2438
+ const skipped = [];
2382
2439
  candidates.forEach((candidate) => {
2383
- (0, node_fs.writeFileSync)(candidate.path, candidate.source, "utf8");
2440
+ try {
2441
+ (0, node_fs.writeFileSync)(candidate.path, candidate.source, "utf8");
2442
+ rewritten.push(candidate.path);
2443
+ } catch {
2444
+ skipped.push(candidate.path);
2445
+ }
2384
2446
  });
2385
- return { rewritten: candidates.map((candidate) => candidate.path) };
2447
+ return {
2448
+ rewritten,
2449
+ skipped
2450
+ };
2386
2451
  }
2387
2452
  //#endregion
2388
2453
  //#region src/cli/migrate/stringEntry.ts
@@ -2411,7 +2476,7 @@ function leadingCommentTextFor(sourceFile, node) {
2411
2476
  }
2412
2477
  //#endregion
2413
2478
  //#region src/cli/migrate/eslintrc/parse.ts
2414
- function lineOf$1(sourceFile, node) {
2479
+ function lineOf$2(sourceFile, node) {
2415
2480
  const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
2416
2481
  return line + 1;
2417
2482
  }
@@ -2705,7 +2770,7 @@ function readSharedBlockFields(input) {
2705
2770
  };
2706
2771
  }
2707
2772
  function readOverride(object, context, elementNode) {
2708
- const line = lineOf$1(context.sourceFile, object);
2773
+ const line = lineOf$2(context.sourceFile, object);
2709
2774
  const filesProperty = findProperty$1(object, "files");
2710
2775
  const files = filesProperty ? readStringOrEntries(filesProperty.initializer, context.sourceFile) : [];
2711
2776
  if (files.length === 0) {
@@ -2762,7 +2827,7 @@ function findExportObject(sourceFile) {
2762
2827
  return result;
2763
2828
  }
2764
2829
  function buildRoot(object, context) {
2765
- const line = lineOf$1(context.sourceFile, object);
2830
+ const line = lineOf$2(context.sourceFile, object);
2766
2831
  const shared = readSharedBlockFields({
2767
2832
  object,
2768
2833
  line,
@@ -3392,7 +3457,7 @@ var knownPresets = /* @__PURE__ */ new Set([
3392
3457
  "dom",
3393
3458
  "node"
3394
3459
  ]);
3395
- function lineOf(sourceFile, node) {
3460
+ function lineOf$1(sourceFile, node) {
3396
3461
  const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3397
3462
  return line + 1;
3398
3463
  }
@@ -3695,7 +3760,7 @@ function classifyPresets(presetList, line, warnings) {
3695
3760
  };
3696
3761
  }
3697
3762
  function parseUseInsiderCall(callExpression, sourceFile, warnings) {
3698
- const line = lineOf(sourceFile, callExpression);
3763
+ const line = lineOf$1(sourceFile, callExpression);
3699
3764
  const [firstArgument] = callExpression.arguments;
3700
3765
  if (!firstArgument || !typescript.isObjectLiteralExpression(firstArgument)) {
3701
3766
  warnings.push({
@@ -3795,7 +3860,7 @@ function tryClassifyStandaloneGlobals(objectLiteral, keys, line, sourceFile) {
3795
3860
  };
3796
3861
  }
3797
3862
  function classifyObjectElement(objectLiteral, sourceFile, warnings) {
3798
- const line = lineOf(sourceFile, objectLiteral);
3863
+ const line = lineOf$1(sourceFile, objectLiteral);
3799
3864
  const keys = objectLiteral.properties.map(propertyKeyName).filter((name) => name !== null);
3800
3865
  const ignoresBlock = tryClassifyIgnoresBlock(objectLiteral, keys, line, sourceFile);
3801
3866
  if (ignoresBlock) return ignoresBlock;
@@ -3832,7 +3897,7 @@ function classifyArrayElement(element, sourceFile, warnings) {
3832
3897
  if (leadingComment !== void 0) parsed.leadingComment = leadingComment;
3833
3898
  return parsed;
3834
3899
  }
3835
- const line = lineOf(sourceFile, element);
3900
+ const line = lineOf$1(sourceFile, element);
3836
3901
  warnings.push({
3837
3902
  kind: "unrecognized-block",
3838
3903
  line,
@@ -3849,18 +3914,19 @@ function classifyArrayElement(element, sourceFile, warnings) {
3849
3914
  function isDefineConfigCall(expression) {
3850
3915
  return typescript.isCallExpression(expression) && typescript.isIdentifier(expression.expression) && expression.expression.text === "defineConfig";
3851
3916
  }
3852
- function findDefaultExportArray(sourceFile) {
3853
- const exportExpressions = sourceFile.statements.filter(typescript.isExportAssignment).map((statement) => unwrapTypeCasts(statement.expression));
3854
- const directArray = exportExpressions.find(typescript.isArrayLiteralExpression);
3855
- if (directArray) return directArray;
3856
- const defineConfigCall = exportExpressions.find(isDefineConfigCall);
3857
- if (!defineConfigCall) return null;
3858
- const [first] = defineConfigCall.arguments;
3917
+ function defineConfigArray(exportExpressions) {
3918
+ const [first] = exportExpressions.find(isDefineConfigCall)?.arguments ?? [];
3859
3919
  if (!first) return null;
3860
3920
  const unwrappedFirst = unwrapTypeCasts(first);
3861
3921
  if (typescript.isArrayLiteralExpression(unwrappedFirst)) return unwrappedFirst;
3862
3922
  return null;
3863
3923
  }
3924
+ function findDefaultExportElements(sourceFile) {
3925
+ const exportExpressions = sourceFile.statements.filter(typescript.isExportAssignment).map((statement) => unwrapTypeCasts(statement.expression));
3926
+ const array = exportExpressions.find(typescript.isArrayLiteralExpression) ?? defineConfigArray(exportExpressions);
3927
+ if (array) return [...array.elements];
3928
+ return exportExpressions;
3929
+ }
3864
3930
  function checkDefineConfigWrap(sourceFile) {
3865
3931
  return sourceFile.statements.some((statement) => {
3866
3932
  if (!typescript.isExportAssignment(statement)) return false;
@@ -3899,7 +3965,7 @@ function classifyInsiderImport(statement, sourceFile, flags) {
3899
3965
  return;
3900
3966
  }
3901
3967
  flags.hasUseInsider = true;
3902
- const line = lineOf(sourceFile, statement);
3968
+ const line = lineOf$1(sourceFile, statement);
3903
3969
  flags.preserved.push({
3904
3970
  raw: statement.getText(sourceFile),
3905
3971
  moduleSpecifier: "@useinsider/eslint-config"
@@ -3944,7 +4010,7 @@ function collectSilenceRulesCalls(sourceFile) {
3944
4010
  if (!rules) return null;
3945
4011
  const leadingComment = leadingCommentTextFor(sourceFile, statement);
3946
4012
  const call = {
3947
- line: lineOf(sourceFile, statement),
4013
+ line: lineOf$1(sourceFile, statement),
3948
4014
  rules
3949
4015
  };
3950
4016
  if (leadingComment !== void 0) call.leadingComment = leadingComment;
@@ -3957,8 +4023,7 @@ function parseV1Config(source, filePath) {
3957
4023
  const imports = inspectImports(sourceFile);
3958
4024
  warnings.push(...imports.warnings);
3959
4025
  const silenceRulesCalls = collectSilenceRulesCalls(sourceFile);
3960
- const arrayLiteral = findDefaultExportArray(sourceFile);
3961
- const arrayElements = arrayLiteral ? arrayLiteral.elements.map((element) => classifyArrayElement(element, sourceFile, warnings)).filter((entry) => entry !== null) : [];
4026
+ const arrayElements = findDefaultExportElements(sourceFile).map((element) => classifyArrayElement(element, sourceFile, warnings)).filter((entry) => entry !== null);
3962
4027
  return {
3963
4028
  hasUseInsiderImport: imports.hasUseInsider,
3964
4029
  hasSilenceRulesImport: imports.hasSilenceRules,
@@ -3970,6 +4035,225 @@ function parseV1Config(source, filePath) {
3970
4035
  };
3971
4036
  }
3972
4037
  //#endregion
4038
+ //#region src/cli/migrate/rehouseV3.ts
4039
+ var INSIDER_MODULE = "@useinsider/eslint-config";
4040
+ var COMPOSER_NAME = "useInsider";
4041
+ var DEFINE_CONFIG_NAME = "defineConfig";
4042
+ var SILENCE_RULES_NAME = "silenceRules";
4043
+ var RENDERER_EXPORT = "export const { useInsiderESLint, useInsiderOXC } = ";
4044
+ var RESERVED_KEYS = /* @__PURE__ */ new Set([
4045
+ "ignores",
4046
+ "jquery",
4047
+ "strict",
4048
+ "spellChecker",
4049
+ "silenceRules",
4050
+ "configFiles"
4051
+ ]);
4052
+ function isInsiderImport(statement) {
4053
+ const { moduleSpecifier } = statement;
4054
+ return typescript.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === INSIDER_MODULE;
4055
+ }
4056
+ function composerAliases(sourceFile) {
4057
+ const aliases = /* @__PURE__ */ new Set();
4058
+ sourceFile.statements.forEach((statement) => {
4059
+ if (!typescript.isImportDeclaration(statement) || !isInsiderImport(statement)) return;
4060
+ const bindings = statement.importClause?.namedBindings;
4061
+ if (!bindings || !typescript.isNamedImports(bindings)) return;
4062
+ bindings.elements.forEach((element) => {
4063
+ if ((element.propertyName?.text ?? element.name.text) === COMPOSER_NAME) aliases.add(element.name.text);
4064
+ });
4065
+ });
4066
+ return aliases;
4067
+ }
4068
+ function isCallOf(node, names) {
4069
+ return typescript.isCallExpression(node) && typescript.isIdentifier(node.expression) && names.has(node.expression.text);
4070
+ }
4071
+ function collectCalls(sourceFile, names) {
4072
+ const found = [];
4073
+ const visit = (node) => {
4074
+ if (isCallOf(node, names)) found.push(node);
4075
+ typescript.forEachChild(node, visit);
4076
+ };
4077
+ visit(sourceFile);
4078
+ return found;
4079
+ }
4080
+ function peel(expression, state) {
4081
+ if (typescript.isParenthesizedExpression(expression) || typescript.isSpreadElement(expression)) return peel(expression.expression, state);
4082
+ if (typescript.isAsExpression(expression) || typescript.isSatisfiesExpression(expression)) {
4083
+ state.droppedAnnotation = true;
4084
+ return peel(expression.expression, state);
4085
+ }
4086
+ if (typescript.isAwaitExpression(expression)) {
4087
+ state.awaited = true;
4088
+ return peel(expression.expression, state);
4089
+ }
4090
+ return expression;
4091
+ }
4092
+ function peelArray(expression, state) {
4093
+ if (!typescript.isArrayLiteralExpression(expression) || expression.elements.length === 0) return expression;
4094
+ state.arrayWrapped = true;
4095
+ state.siblingElements = expression.elements.length - 1;
4096
+ return peel(expression.elements[0], state);
4097
+ }
4098
+ function unwrapDefaultExport(expression, state) {
4099
+ const peeled = peel(expression, state);
4100
+ if (isCallOf(peeled, /* @__PURE__ */ new Set([DEFINE_CONFIG_NAME]))) {
4101
+ state.definedConfig = true;
4102
+ return peeled;
4103
+ }
4104
+ return peelArray(peeled, state);
4105
+ }
4106
+ function argumentObject(call) {
4107
+ const [first] = call.arguments;
4108
+ if (!first || !typescript.isObjectLiteralExpression(first)) return null;
4109
+ return first;
4110
+ }
4111
+ function propertyKey(property) {
4112
+ const { name } = property;
4113
+ if (name === void 0) return null;
4114
+ if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) return name.text;
4115
+ return null;
4116
+ }
4117
+ function unreadableReason(argument, keysRead) {
4118
+ if (!argument) return "is not an object literal";
4119
+ if (argument.properties.some(typescript.isSpreadAssignment)) return "spreads another object";
4120
+ return keysRead < argument.properties.length ? "has a property whose key is not a literal name" : null;
4121
+ }
4122
+ function readArgument(call) {
4123
+ const argument = argumentObject(call);
4124
+ const optionKeys = (argument?.properties ?? []).map(propertyKey).filter((key) => key !== null);
4125
+ const candidates = optionKeys.filter((key) => !RESERVED_KEYS.has(key));
4126
+ return {
4127
+ optionKeys,
4128
+ scopes: candidates.filter(isMigratableScope),
4129
+ unregisteredKeys: candidates.filter((key) => !isMigratableScope(key)),
4130
+ unreadable: unreadableReason(argument, optionKeys.length)
4131
+ };
4132
+ }
4133
+ function refuse(reason) {
4134
+ return {
4135
+ kind: "refused",
4136
+ reason
4137
+ };
4138
+ }
4139
+ function multipleCallsReason(name) {
4140
+ return `${name} calls ${COMPOSER_NAME}() more than once. v4 has exactly one call — in the lint module — and it returns the two renderers instead of a config array, so the results cannot be concatenated. Merge them into a single call, one entry per scope key, then re-run the migration.`;
4141
+ }
4142
+ function mixedArrayReason(name, siblings) {
4143
+ return `${name}'s default export combines ${COMPOSER_NAME}() with ${siblings} other flat-config ${siblings === 1 ? "block" : "blocks"}, which have nowhere to go in a lint module. Fold them into the call first — directory ignores into \`ignores\`, per-file rule overrides into \`configFiles\` — then re-run the migration.`;
4144
+ }
4145
+ function indirectCallReason(name) {
4146
+ return `${name} calls ${COMPOSER_NAME}(), but its default export is not that call. The migration re-houses the call itself, so make it the default export (\`export default ${COMPOSER_NAME}({ ... })\`) and re-run the migration.`;
4147
+ }
4148
+ function defineConfigReason(name) {
4149
+ return `${name} wraps ${COMPOSER_NAME}() in ${DEFINE_CONFIG_NAME}(). v4's lint module exports the two renderers rather than a flat-config array, so there is nothing left for ${DEFINE_CONFIG_NAME}() to validate — drop the wrapper and re-run the migration.`;
4150
+ }
4151
+ function presetReason(name) {
4152
+ return `${name} still passes the v1 \`preset\` option, and this release could not read it — the preset list has to be an array of string literals. Restore the literal list and re-run the migration, or convert the call to the scope-keyed form by hand.`;
4153
+ }
4154
+ function removedOptionReason(name, details) {
4155
+ return `${name} passes option(s) this release removed. The migration copies the call across unchanged, so carrying them over would produce a lint module that fails on the next lint run. Update the call, then re-run the migration:\n${details}`;
4156
+ }
4157
+ function annotationWarning(line) {
4158
+ return {
4159
+ kind: "dropped-type-annotation",
4160
+ line,
4161
+ message: `The type annotation on the default export was dropped — ${COMPOSER_NAME}() now returns \`{ useInsiderESLint, useInsiderOXC }\` rather than a config array. Delete the type import if nothing else uses it.`
4162
+ };
4163
+ }
4164
+ function unregisteredScopeWarning(key, line) {
4165
+ return {
4166
+ kind: "unregistered-scope",
4167
+ line,
4168
+ message: `\`${key}\` has no dependency manifest in the migration, so nothing was added to the install list for it. The key itself was carried over untouched — check its plugins are installed.`
4169
+ };
4170
+ }
4171
+ function unreadableArgumentWarning(line, reason) {
4172
+ return {
4173
+ kind: "unrecognized-block",
4174
+ line,
4175
+ message: `The ${COMPOSER_NAME}() argument ${reason}, so its scope list could not be read in full. The call was carried over untouched; the dependency list may be incomplete.`
4176
+ };
4177
+ }
4178
+ function lineOf(sourceFile, node) {
4179
+ return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
4180
+ }
4181
+ function collectWarnings(line, reading, state) {
4182
+ const warnings = [];
4183
+ if (state.droppedAnnotation) warnings.push(annotationWarning(line));
4184
+ if (reading.unreadable) warnings.push(unreadableArgumentWarning(line, reading.unreadable));
4185
+ reading.unregisteredKeys.forEach((key) => {
4186
+ warnings.push(unregisteredScopeWarning(key, line));
4187
+ });
4188
+ return warnings;
4189
+ }
4190
+ function spliceExport(input) {
4191
+ const start = input.statement.getStart(input.sourceFile);
4192
+ const prefix = input.source.slice(0, start);
4193
+ const suffix = input.source.slice(input.statement.getEnd());
4194
+ const rehoused = `${prefix}${RENDERER_EXPORT}${input.callText};${suffix}`;
4195
+ return rehoused.endsWith("\n") ? rehoused : `${rehoused}\n`;
4196
+ }
4197
+ function defaultExports(sourceFile) {
4198
+ return sourceFile.statements.filter(typescript.isExportAssignment).filter((statement) => statement.isExportEquals !== true);
4199
+ }
4200
+ function emptyState() {
4201
+ return {
4202
+ awaited: false,
4203
+ arrayWrapped: false,
4204
+ droppedAnnotation: false,
4205
+ siblingElements: 0,
4206
+ definedConfig: false
4207
+ };
4208
+ }
4209
+ /**
4210
+ * Re-houses a v3 single-file config as a v4 lint module. Nothing about the configuration itself
4211
+ * changes between the two majors — only where the call lives — so the source is spliced rather than
4212
+ * re-rendered: every import, `silenceRules(...)` call, comment and blank line outside the export
4213
+ * statement survives byte-for-byte.
4214
+ * @example
4215
+ * rehouseV3Config("import { useInsider } from '@useinsider/eslint-config';\nexport default useInsider({});", 'eslint.config.js');
4216
+ * // { kind: 'rehoused', rehoused: { lintModule: '…export const { useInsiderESLint, useInsiderOXC } = useInsider({});\n', … } }
4217
+ */
4218
+ function rehouseV3Config(source, filePath) {
4219
+ const name = (0, node_path.basename)(filePath);
4220
+ const sourceFile = typescript.createSourceFile(filePath, source, typescript.ScriptTarget.Latest, true);
4221
+ const aliases = composerAliases(sourceFile);
4222
+ const calls = collectCalls(sourceFile, aliases);
4223
+ const exportStatements = defaultExports(sourceFile);
4224
+ if (calls.length === 0) return { kind: "not-v3" };
4225
+ if (exportStatements.length !== 1) return refuse(indirectCallReason(name));
4226
+ if (calls.length > 1) return refuse(multipleCallsReason(name));
4227
+ const [statement] = exportStatements;
4228
+ const state = emptyState();
4229
+ const unwrapped = unwrapDefaultExport(statement.expression, state);
4230
+ if (state.definedConfig) return refuse(defineConfigReason(name));
4231
+ if (state.siblingElements > 0) return refuse(mixedArrayReason(name, state.siblingElements));
4232
+ if (!isCallOf(unwrapped, aliases)) return refuse(indirectCallReason(name));
4233
+ const reading = readArgument(unwrapped);
4234
+ if (reading.optionKeys.includes("preset")) return refuse(presetReason(name));
4235
+ const removed = require_removedOptions.describeRemovedOptions(reading.optionKeys);
4236
+ if (removed !== null) return refuse(removedOptionReason(name, removed));
4237
+ return {
4238
+ kind: "rehoused",
4239
+ rehoused: {
4240
+ lintModule: spliceExport({
4241
+ source,
4242
+ sourceFile,
4243
+ statement,
4244
+ callText: unwrapped.getText(sourceFile)
4245
+ }),
4246
+ scopes: reading.scopes,
4247
+ optionKeys: reading.optionKeys,
4248
+ unregisteredKeys: reading.unregisteredKeys,
4249
+ silenceRulesCallCount: collectCalls(sourceFile, /* @__PURE__ */ new Set([SILENCE_RULES_NAME])).length,
4250
+ droppedAwait: state.awaited,
4251
+ droppedArrayWrapper: state.arrayWrapped,
4252
+ warnings: collectWarnings(lineOf(sourceFile, unwrapped), reading, state)
4253
+ }
4254
+ };
4255
+ }
4256
+ //#endregion
3973
4257
  //#region src/cli/migrate/pruneUnusedImports.ts
3974
4258
  var INSIDER_MODULE_SPECIFIER = "@useinsider/eslint-config";
3975
4259
  function readModuleSpecifier(statement) {
@@ -4800,6 +5084,22 @@ function describeStrict(plan) {
4800
5084
  if (plan.config.strictGlobs.length > 0) return `Strict scopes: 1 (${plan.config.strictGlobs.length} globs)`;
4801
5085
  return "Strict scopes: 0";
4802
5086
  }
5087
+ function describeRehousedKeys(label, keys) {
5088
+ return keys.length === 0 ? `${label}: (none)` : `${label}: ${keys.join(", ")}`;
5089
+ }
5090
+ function rehouseSummary(rehoused) {
5091
+ const lines = [
5092
+ "Source: v3 single-file eslint.config.*",
5093
+ "Re-housed as the lint module: only the export statement changed, every other line is byte-for-byte.",
5094
+ describeRehousedKeys("Options carried over", rehoused.optionKeys),
5095
+ describeRehousedKeys("Scopes with dependencies", rehoused.scopes),
5096
+ `silenceRules() calls kept: ${rehoused.silenceRulesCallCount}`
5097
+ ];
5098
+ if (rehoused.droppedAwait) lines.push("Dropped the `await` on the call — useInsider() is synchronous in v4.");
5099
+ if (rehoused.droppedArrayWrapper) lines.push("Dropped the array wrapper around the call — the lint module exports the renderers.");
5100
+ lines.push(`Warnings: ${rehoused.warnings.length}`);
5101
+ return lines.join("\n");
5102
+ }
4803
5103
  function summary(plan, format) {
4804
5104
  const lines = [];
4805
5105
  if (format === "eslintrc") lines.push("Source: legacy .eslintrc");
@@ -4812,13 +5112,47 @@ function summary(plan, format) {
4812
5112
  lines.push(`Warnings: ${plan.warnings.length}`);
4813
5113
  return lines.join("\n");
4814
5114
  }
4815
- function planFromSource(source, filePath, format) {
4816
- if (format === "flat") {
4817
- const parseResult = parseV1Config(source, filePath);
4818
- return isV1Shape(parseResult) ? transformToV2(parseResult) : null;
5115
+ function sourceModelFrom(resolved) {
5116
+ if (resolved.format !== "flat") {
5117
+ const parseResult = parseEslintrc(resolved.source, resolved.targetPath);
5118
+ return parseResult.found ? {
5119
+ ok: true,
5120
+ model: {
5121
+ kind: "plan",
5122
+ plan: transformEslintrcToV2(parseResult)
5123
+ }
5124
+ } : {
5125
+ ok: false,
5126
+ reason: null
5127
+ };
4819
5128
  }
4820
- const parseResult = parseEslintrc(source, filePath);
4821
- return parseResult.found ? transformEslintrcToV2(parseResult) : null;
5129
+ const parseResult = parseV1Config(resolved.source, resolved.targetPath);
5130
+ if (isV1Shape(parseResult)) return {
5131
+ ok: true,
5132
+ model: {
5133
+ kind: "plan",
5134
+ plan: transformToV2(parseResult)
5135
+ }
5136
+ };
5137
+ const outcome = rehouseV3Config(resolved.source, resolved.targetPath);
5138
+ if (outcome.kind === "rehoused") return {
5139
+ ok: true,
5140
+ model: {
5141
+ kind: "rehouse",
5142
+ rehoused: outcome.rehoused
5143
+ }
5144
+ };
5145
+ return {
5146
+ ok: false,
5147
+ reason: outcome.kind === "refused" ? outcome.reason : null
5148
+ };
5149
+ }
5150
+ function scopesOf(model) {
5151
+ if (model.kind === "plan") return new Set(model.plan.config.scopes.keys());
5152
+ return new Set(model.rehoused.scopes);
5153
+ }
5154
+ function warningsOf(model) {
5155
+ return model.kind === "plan" ? model.plan.warnings : model.rehoused.warnings;
4822
5156
  }
4823
5157
  function targetWritePath(cwd, sourcePath) {
4824
5158
  const directory = (0, node_path.dirname)(sourcePath) || cwd;
@@ -4903,9 +5237,11 @@ function emitInstallNextSteps(clack, depSummary, cwd) {
4903
5237
  if (message === null) return;
4904
5238
  clack.note(message, "Next steps");
4905
5239
  }
4906
- function emitSummary(clack, plan, format) {
4907
- if (clack.note) clack.note(summary(plan, format), "Migration summary");
4908
- if (plan.warnings.length > 0 && clack.note) clack.note(formatWarnings(plan.warnings), "Warnings");
5240
+ function emitSummary(clack, model, format) {
5241
+ if (!clack.note) return;
5242
+ clack.note(model.kind === "plan" ? summary(model.plan, format) : rehouseSummary(model.rehoused), "Migration summary");
5243
+ const warnings = warningsOf(model);
5244
+ if (warnings.length > 0) clack.note(formatWarnings(warnings), "Warnings");
4909
5245
  }
4910
5246
  function writeAndAnnounce(clack, targetPath, writePath, rendered, format) {
4911
5247
  const result = format === "eslintrc" ? writeMigrationLegacy(targetPath, writePath, rendered) : writeMigration(targetPath, rendered);
@@ -4955,17 +5291,24 @@ function resolveSource(input, clack) {
4955
5291
  }
4956
5292
  };
4957
5293
  }
5294
+ function renderModel(model, eslintIgnore) {
5295
+ if (model.kind === "rehouse") return model.rehoused.lintModule;
5296
+ if (eslintIgnore && eslintIgnore.patterns.length > 0) mergeIgnorePatterns(model.plan, eslintIgnore.patterns);
5297
+ return renderV2(model.plan);
5298
+ }
4958
5299
  function prepareMigration(resolved, input) {
4959
- const plan = planFromSource(resolved.source, resolved.targetPath, resolved.format);
4960
- if (!plan) return null;
5300
+ const outcome = sourceModelFrom(resolved);
5301
+ if (!outcome.ok) return outcome;
4961
5302
  const writePath = targetWritePath(input.cwd, resolved.targetPath);
4962
5303
  const eslintIgnore = findEslintIgnore(resolved.targetPath);
4963
- if (eslintIgnore && eslintIgnore.patterns.length > 0) mergeIgnorePatterns(plan, eslintIgnore.patterns);
4964
5304
  return {
4965
- plan,
4966
- rendered: renderV2(plan),
4967
- writePath,
4968
- eslintIgnore
5305
+ ok: true,
5306
+ prepared: {
5307
+ model: outcome.model,
5308
+ rendered: renderModel(outcome.model, eslintIgnore),
5309
+ writePath,
5310
+ eslintIgnore
5311
+ }
4969
5312
  };
4970
5313
  }
4971
5314
  async function runConfirmations(clack, input, targetPath, writePath, previous) {
@@ -4995,7 +5338,7 @@ function tryReadConsumerManifest(path) {
4995
5338
  return null;
4996
5339
  }
4997
5340
  }
4998
- function computeDepSummary(plan, writePath, overrides) {
5341
+ function computeDepSummary(model, writePath, overrides) {
4999
5342
  const pkgPath = consumerPackageJsonPath(writePath);
5000
5343
  const manifest = tryReadConsumerManifest(pkgPath);
5001
5344
  if (!manifest) return {
@@ -5008,7 +5351,7 @@ function computeDepSummary(plan, writePath, overrides) {
5008
5351
  };
5009
5352
  const pruneOutcome = pruneEslintDependencies(manifest);
5010
5353
  const ownManifest = (overrides?.readEslintConfigManifest ?? readOwnEslintConfigManifest)();
5011
- const install = buildInstallList(new Set(plan.config.scopes.keys()), ownManifest);
5354
+ const install = buildInstallList(scopesOf(model), ownManifest);
5012
5355
  return {
5013
5356
  removed: pruneOutcome.removed,
5014
5357
  install,
@@ -5063,7 +5406,7 @@ async function applyDepChanges(clack, depSummary, input) {
5063
5406
  }
5064
5407
  function performWriteAndCleanup(clack, resolved, prepared) {
5065
5408
  const outcome = writeAndAnnounce(clack, resolved.targetPath, prepared.writePath, prepared.rendered, resolved.format);
5066
- if (prepared.eslintIgnore) safeUnlink(prepared.eslintIgnore.path, clack, ".eslintignore");
5409
+ if (prepared.eslintIgnore && prepared.model.kind === "plan") safeUnlink(prepared.eslintIgnore.path, clack, ".eslintignore");
5067
5410
  outcome.deletedSourceCandidates.forEach((path) => {
5068
5411
  safeUnlink(path, clack, "superseded config");
5069
5412
  });
@@ -5089,8 +5432,13 @@ function applyLintScript$1(clack, directory) {
5089
5432
  if (result.changed) (0, node_fs.writeFileSync)(manifestPath, result.manifest, "utf8");
5090
5433
  noteLintScriptOutcome$1(clack, result);
5091
5434
  }
5435
+ function noteUntouchedDirectiveFiles(clack, skipped) {
5436
+ if (skipped.length === 0 || !clack.note) return;
5437
+ clack.note(formatUntouchedDirectiveFiles(skipped), `${skipped.length} file(s) left untouched`);
5438
+ }
5092
5439
  async function rewriteDirectivesWithNote(clack, directory, autoConfirm) {
5093
- const candidates = previewDisableDirectives(directory);
5440
+ const { candidates, skipped } = previewDisableDirectives(directory);
5441
+ noteUntouchedDirectiveFiles(clack, skipped);
5094
5442
  if (candidates.length === 0) return;
5095
5443
  const count = candidates.length;
5096
5444
  if (clack.note) clack.note(`${count} file(s) carry an \`eslint-disable\` naming a rule oxlint now owns. Left as they are, ESLint reports each one as an unused directive.
@@ -5106,8 +5454,9 @@ This is a separate, source-wide diff — review it on its own commit.`, "Disable
5106
5454
  return;
5107
5455
  }
5108
5456
  }
5109
- const { rewritten } = rewriteDisableDirectives(directory, candidates);
5457
+ const { rewritten, skipped: unwritten } = rewriteDisableDirectives(directory, candidates);
5110
5458
  if (clack.note) clack.note(`Re-pointed directives in ${rewritten.length} file(s).`, "Disable directives");
5459
+ noteUntouchedDirectiveFiles(clack, unwritten);
5111
5460
  }
5112
5461
  function loadPreviousMigrate(clack, cwd) {
5113
5462
  const restored = loadSession({ cwd });
@@ -5116,7 +5465,12 @@ function loadPreviousMigrate(clack, cwd) {
5116
5465
  return previousMigrate;
5117
5466
  }
5118
5467
  function emitNothingToMigrate(clack, format) {
5119
- clack.outro(format === "eslintrc" ? "No legacy `.eslintrc` shape found — nothing to migrate." : "No v1 useInsider patterns found — nothing to migrate.");
5468
+ clack.outro(format === "eslintrc" ? "No legacy `.eslintrc` shape found — nothing to migrate." : "No v1 or v3 useInsider patterns found — nothing to migrate.");
5469
+ }
5470
+ function noteUnfoldedEslintIgnore(clack, prepared) {
5471
+ const patterns = prepared.eslintIgnore?.patterns ?? [];
5472
+ if (!clack.note || prepared.model.kind !== "rehouse" || patterns.length === 0) return;
5473
+ clack.note(`${prepared.eslintIgnore?.path} still holds ${patterns.length} pattern(s). Flat config does not read it, and the v3 config was carried over verbatim, so they were not folded into \`ignores\`. Move the ones you still need into the \`ignores\` array and delete the file.`, ".eslintignore left alone");
5120
5474
  }
5121
5475
  function persistMigrateSession(input, prepared, resolved) {
5122
5476
  const overwriteExisting = supersededFlatConfigs(resolved.targetPath, prepared.writePath).length > 0;
@@ -5151,14 +5505,20 @@ async function runMigrate(input, clackOverride) {
5151
5505
  return declinedExitCode;
5152
5506
  }
5153
5507
  const previousMigrate = loadPreviousMigrate(clack, input.cwd);
5154
- const prepared = prepareMigration(resolved, input);
5155
- if (!prepared) {
5156
- emitNothingToMigrate(clack, resolved.format);
5157
- return declinedExitCode;
5508
+ const outcome = prepareMigration(resolved, input);
5509
+ if (!outcome.ok) {
5510
+ if (outcome.reason === null) {
5511
+ emitNothingToMigrate(clack, resolved.format);
5512
+ return declinedExitCode;
5513
+ }
5514
+ clack.cancel(outcome.reason);
5515
+ return 1;
5158
5516
  }
5159
- emitSummary(clack, prepared.plan, resolved.format);
5517
+ const { prepared } = outcome;
5518
+ emitSummary(clack, prepared.model, resolved.format);
5519
+ noteUnfoldedEslintIgnore(clack, prepared);
5160
5520
  noteConfigRename(clack, resolved.targetPath, prepared.writePath);
5161
- const depSummary = computeDepSummary(prepared.plan, prepared.writePath, input.overrides);
5521
+ const depSummary = computeDepSummary(prepared.model, prepared.writePath, input.overrides);
5162
5522
  if (!await runConfirmations(clack, input, resolved.targetPath, prepared.writePath, previousMigrate)) return declinedExitCode;
5163
5523
  persistMigrateSession(input, prepared, resolved);
5164
5524
  performWriteAndCleanup(clack, resolved, prepared);