@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.
- package/README.md +21 -7
- package/dist/cli.cjs +447 -87
- package/dist/cli.cjs.map +1 -1
- package/dist/{javascript-dom-CvtQ6_9W.cjs → javascript-dom-bySaQErv.cjs} +4 -4
- package/dist/{javascript-dom-CvtQ6_9W.cjs.map → javascript-dom-bySaQErv.cjs.map} +1 -1
- package/dist/{javascript-node-BNsy3loY.cjs → javascript-node-DZxexKer.cjs} +4 -4
- package/dist/{javascript-node-BNsy3loY.cjs.map → javascript-node-DZxexKer.cjs.map} +1 -1
- package/dist/{javascript-DH8-N38w.cjs → javascript-ycJ4W-e9.cjs} +5 -5
- package/dist/{javascript-DH8-N38w.cjs.map → javascript-ycJ4W-e9.cjs.map} +1 -1
- package/dist/{jest-4Es9Ggaa.cjs → jest-B2fpQmUB.cjs} +3 -3
- package/dist/{jest-4Es9Ggaa.cjs.map → jest-B2fpQmUB.cjs.map} +1 -1
- package/dist/main.cjs +35 -32
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +9 -6
- package/dist/main.js.map +1 -1
- package/dist/{oxlintOwnership-7hpdB3d1.cjs → removedOptions-BYMW8w__.cjs} +29 -1
- package/dist/removedOptions-BYMW8w__.cjs.map +1 -0
- package/dist/{oxlintOwnership-Cx0hgA_J.js → removedOptions-Cb8SG8SI.js} +18 -2
- package/dist/removedOptions-Cb8SG8SI.js.map +1 -0
- package/dist/{stylistic-CoP3MT1Y.cjs → stylistic-B2hCwBRu.cjs} +4 -4
- package/dist/{stylistic-CoP3MT1Y.cjs.map → stylistic-B2hCwBRu.cjs.map} +1 -1
- package/dist/{typescript-D3VeLnpn.cjs → typescript-DdgbCNKI.cjs} +7 -7
- package/dist/{typescript-D3VeLnpn.cjs.map → typescript-DdgbCNKI.cjs.map} +1 -1
- package/dist/{typescript-dom-iB_xBTAD.cjs → typescript-dom-C0NNSGLd.cjs} +4 -4
- package/dist/{typescript-dom-iB_xBTAD.cjs.map → typescript-dom-C0NNSGLd.cjs.map} +1 -1
- package/dist/{typescript-node-Cq6qhj53.cjs → typescript-node-CjG8N83q.cjs} +4 -4
- package/dist/{typescript-node-Cq6qhj53.cjs.map → typescript-node-CjG8N83q.cjs.map} +1 -1
- package/dist/utils/removedOptions.d.ts +10 -0
- package/dist/{vitest-BlGQswK2.cjs → vitest-B3kOWOzo.cjs} +3 -3
- package/dist/{vitest-BlGQswK2.cjs.map → vitest-B3kOWOzo.cjs.map} +1 -1
- package/dist/{vue2-Da1i_oVn.cjs → vue2-DfL5FqeZ.cjs} +8 -8
- package/dist/{vue2-Da1i_oVn.cjs.map → vue2-DfL5FqeZ.cjs.map} +1 -1
- package/dist/{vue2-options-api-BSdrUm8H.cjs → vue2-options-api-sSrBV5Si.cjs} +8 -8
- package/dist/{vue2-options-api-BSdrUm8H.cjs.map → vue2-options-api-sSrBV5Si.cjs.map} +1 -1
- package/dist/{vue2-typescript-options-api-LM7U5dWq.cjs → vue2-typescript-options-api-CbRC2wf-.cjs} +10 -10
- package/dist/{vue2-typescript-options-api-LM7U5dWq.cjs.map → vue2-typescript-options-api-CbRC2wf-.cjs.map} +1 -1
- package/dist/{vue2-typescript-dXLRSGNP.cjs → vue2-typescript-rJIMzNUI.cjs} +10 -10
- package/dist/{vue2-typescript-dXLRSGNP.cjs.map → vue2-typescript-rJIMzNUI.cjs.map} +1 -1
- package/dist/{vue3-621EM2Ii.cjs → vue3-DmrGRHbo.cjs} +8 -8
- package/dist/{vue3-621EM2Ii.cjs.map → vue3-DmrGRHbo.cjs.map} +1 -1
- package/dist/{vue3-options-api-CC9_fAiq.cjs → vue3-options-api-DPEk8rBk.cjs} +8 -8
- package/dist/{vue3-options-api-CC9_fAiq.cjs.map → vue3-options-api-DPEk8rBk.cjs.map} +1 -1
- package/dist/{vue3-typescript-DONe-IKz.cjs → vue3-typescript-Dz5BM0LJ.cjs} +10 -10
- package/dist/{vue3-typescript-DONe-IKz.cjs.map → vue3-typescript-Dz5BM0LJ.cjs.map} +1 -1
- package/dist/{vue3-typescript-options-api-D4pqpDFs.cjs → vue3-typescript-options-api-DiYz_vVd.cjs} +10 -10
- package/dist/{vue3-typescript-options-api-D4pqpDFs.cjs.map → vue3-typescript-options-api-DiYz_vVd.cjs.map} +1 -1
- package/package.json +1 -1
- package/dist/oxlintOwnership-7hpdB3d1.cjs.map +0 -1
- 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
|
|
3
|
+
const require_removedOptions = require("./removedOptions-BYMW8w__.cjs");
|
|
4
4
|
let globals = require("globals");
|
|
5
|
-
globals =
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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,
|
|
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)) ||
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
2227
|
-
return !(SFC_EXTENSIONS.includes(fileExtension) &&
|
|
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
|
-
|
|
2368
|
-
const
|
|
2369
|
-
if (
|
|
2370
|
-
|
|
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
|
-
|
|
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 {
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
3853
|
-
const
|
|
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
|
|
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
|
|
4816
|
-
if (format
|
|
4817
|
-
const parseResult =
|
|
4818
|
-
return
|
|
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 =
|
|
4821
|
-
|
|
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,
|
|
4907
|
-
if (clack.note)
|
|
4908
|
-
|
|
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
|
|
4960
|
-
if (!
|
|
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
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
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(
|
|
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(
|
|
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
|
|
5155
|
-
if (!
|
|
5156
|
-
|
|
5157
|
-
|
|
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
|
-
|
|
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.
|
|
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);
|