@useinsider/eslint-config 4.0.0-beta.2 → 4.0.0-beta.3
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 +420 -79
- package/dist/cli.cjs.map +1 -1
- package/dist/main.cjs +5 -0
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -741,6 +741,22 @@ async function installWithRetry(opts) {
|
|
|
741
741
|
}
|
|
742
742
|
}
|
|
743
743
|
//#endregion
|
|
744
|
+
//#region src/cli/detectIndent.ts
|
|
745
|
+
var DEFAULT_INDENT = " ";
|
|
746
|
+
/**
|
|
747
|
+
* The indentation a JSON source already uses, as the string `JSON.stringify` takes for its third
|
|
748
|
+
* argument. A width would be wrong: `JSON.stringify` emits that many *spaces*, so a tab-indented
|
|
749
|
+
* manifest comes back re-indented with spaces and every line of it shows up in the diff.
|
|
750
|
+
* @example
|
|
751
|
+
* detectIndent('{\n\t"name": "app"\n}'); // '\t'
|
|
752
|
+
* @example
|
|
753
|
+
* detectIndent('{"name":"app"}', ' '); // ' '
|
|
754
|
+
*/
|
|
755
|
+
function detectIndent(raw, fallback = DEFAULT_INDENT) {
|
|
756
|
+
const match = /\n([\t ]+)\S/u.exec(raw);
|
|
757
|
+
return match ? match[1] : fallback;
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
744
760
|
//#region src/cli/lintLayout.ts
|
|
745
761
|
var OXLINT_COMMAND = "oxlint --disable-nested-config";
|
|
746
762
|
var OXLINT_LINT_SCRIPT = `${OXLINT_COMMAND} && eslint .`;
|
|
@@ -842,14 +858,7 @@ function renderLintLayoutFiles(layout) {
|
|
|
842
858
|
source: renderOxlintConfigFile(layout.lintModule)
|
|
843
859
|
}];
|
|
844
860
|
}
|
|
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
|
-
}
|
|
861
|
+
var DEFAULT_MANIFEST_INDENT = " ";
|
|
853
862
|
var SEGMENT_SEPARATOR = /&&|\|\||[;|]/u;
|
|
854
863
|
var ASSIGNMENT = /^[A-Za-z_]\w*=/u;
|
|
855
864
|
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -1003,7 +1012,7 @@ function setLintScript(rawManifest) {
|
|
|
1003
1012
|
parsed.scripts[change.script] = change.next;
|
|
1004
1013
|
});
|
|
1005
1014
|
return {
|
|
1006
|
-
manifest: `${JSON.stringify(parsed, null, detectIndent
|
|
1015
|
+
manifest: `${JSON.stringify(parsed, null, detectIndent(rawManifest, DEFAULT_MANIFEST_INDENT))}\n`,
|
|
1007
1016
|
changed: true,
|
|
1008
1017
|
changes,
|
|
1009
1018
|
alreadyRunsOxlint,
|
|
@@ -1785,6 +1794,18 @@ var ALL_PRESET_DEPS = [
|
|
|
1785
1794
|
...Object.values(SCOPE_TO_DEPS)
|
|
1786
1795
|
];
|
|
1787
1796
|
var PRESET_DEPENDENCY_KEYS = new Set(ALL_PRESET_DEPS.flatMap((record) => Object.keys(record)));
|
|
1797
|
+
var MIGRATABLE_SCOPES = new Set(Object.keys(SCOPE_TO_DEPS));
|
|
1798
|
+
/**
|
|
1799
|
+
* Whether a `useInsider()` option key is a scope this migration can derive dependencies for. The
|
|
1800
|
+
* four `*OptionsApi` scopes are deliberately absent from the registry, so a config using one is
|
|
1801
|
+
* carried over untouched while its dependencies are reported as unresolved.
|
|
1802
|
+
* @example
|
|
1803
|
+
* isMigratableScope('vue3Typescript'); // true
|
|
1804
|
+
* isMigratableScope('vue3OptionsApi'); // false
|
|
1805
|
+
*/
|
|
1806
|
+
function isMigratableScope(key) {
|
|
1807
|
+
return MIGRATABLE_SCOPES.has(key);
|
|
1808
|
+
}
|
|
1788
1809
|
function isEslintRelatedDependency(name) {
|
|
1789
1810
|
if (SAFELIST.has(name)) return false;
|
|
1790
1811
|
if (EXACT_PATTERNS.has(name)) return true;
|
|
@@ -1899,11 +1920,6 @@ function buildInstallList(scopes, manifest) {
|
|
|
1899
1920
|
var readOwnEslintConfigManifest = readOwnEslintConfigManifest$1;
|
|
1900
1921
|
//#endregion
|
|
1901
1922
|
//#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
1923
|
function serializeManifest(manifest, indent) {
|
|
1908
1924
|
return `${JSON.stringify(manifest, null, indent)}\n`;
|
|
1909
1925
|
}
|
|
@@ -1952,7 +1968,6 @@ var TS_EXTENSIONS_SNIPPET = [
|
|
|
1952
1968
|
" }",
|
|
1953
1969
|
"}"
|
|
1954
1970
|
].join("\n");
|
|
1955
|
-
var DEFAULT_INDENT_UNIT = " ";
|
|
1956
1971
|
function acceptsTsExtensionsFlag(options) {
|
|
1957
1972
|
return options.noEmit === true || options.emitDeclarationOnly === true || options.rewriteRelativeImportExtensions === true;
|
|
1958
1973
|
}
|
|
@@ -2049,10 +2064,6 @@ function indentAt(raw, position) {
|
|
|
2049
2064
|
const prefix = raw.slice(lineStart, position);
|
|
2050
2065
|
return prefix.trim().length === 0 ? prefix : "";
|
|
2051
2066
|
}
|
|
2052
|
-
function detectIndentUnit(raw) {
|
|
2053
|
-
const match = /\n([\t ]+)\S/u.exec(raw);
|
|
2054
|
-
return match ? match[1] : DEFAULT_INDENT_UNIT;
|
|
2055
|
-
}
|
|
2056
2067
|
function splice(raw, start, end, text) {
|
|
2057
2068
|
return raw.slice(0, start) + text + raw.slice(end);
|
|
2058
2069
|
}
|
|
@@ -2062,14 +2073,14 @@ function setExistingFlag(raw, source, property) {
|
|
|
2062
2073
|
function insertIntoCompilerOptions(raw, source, compilerOptions, object) {
|
|
2063
2074
|
const openBrace = object.getStart(source) + 1;
|
|
2064
2075
|
const [first] = object.properties;
|
|
2065
|
-
if (first) return splice(raw, openBrace, openBrace, `\n${indentAt(raw, first.getStart(source)) ||
|
|
2076
|
+
if (first) return splice(raw, openBrace, openBrace, `\n${indentAt(raw, first.getStart(source)) || detectIndent(raw).repeat(2)}"${TS_EXTENSIONS_FLAG}": true,`);
|
|
2066
2077
|
const outer = indentAt(raw, compilerOptions.getStart(source));
|
|
2067
|
-
const unit =
|
|
2078
|
+
const unit = detectIndent(raw);
|
|
2068
2079
|
return splice(raw, openBrace, object.getEnd() - 1, `\n${outer}${unit}"${TS_EXTENSIONS_FLAG}": true\n${outer}`);
|
|
2069
2080
|
}
|
|
2070
2081
|
function insertCompilerOptions(raw, source, root) {
|
|
2071
2082
|
const openBrace = root.getStart(source) + 1;
|
|
2072
|
-
const unit =
|
|
2083
|
+
const unit = detectIndent(raw);
|
|
2073
2084
|
const [first] = root.properties;
|
|
2074
2085
|
const indent = first ? indentAt(raw, first.getStart(source)) || unit : unit;
|
|
2075
2086
|
const block = `\n${indent}"compilerOptions": {\n${indent}${unit}"${TS_EXTENSIONS_FLAG}": true\n${indent}}`;
|
|
@@ -2354,35 +2365,75 @@ function collectFiles(directory, accumulator, visited) {
|
|
|
2354
2365
|
if (entry.isFile() && isLintedFile(full)) accumulator.push(full);
|
|
2355
2366
|
});
|
|
2356
2367
|
}
|
|
2368
|
+
function readSource(filePath) {
|
|
2369
|
+
try {
|
|
2370
|
+
return (0, node_fs.readFileSync)(filePath, "utf8");
|
|
2371
|
+
} catch {
|
|
2372
|
+
return null;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2357
2375
|
/**
|
|
2358
2376
|
* Every file whose `eslint-disable` comments would be re-pointed, and the rewritten source, without
|
|
2359
2377
|
* touching disk. The directive rewrite is the largest diff a migration produces, so the count is
|
|
2360
|
-
* reported — and confirmed — before anything is written.
|
|
2378
|
+
* reported — and confirmed — before anything is written. Files that could not be read come back in
|
|
2379
|
+
* `skipped` instead of aborting the walk.
|
|
2361
2380
|
* @example
|
|
2362
|
-
* previewDisableDirectives('/repo').length; // 131
|
|
2381
|
+
* previewDisableDirectives('/repo').candidates.length; // 131
|
|
2363
2382
|
*/
|
|
2364
2383
|
function previewDisableDirectives(cwd) {
|
|
2365
2384
|
const files = [];
|
|
2385
|
+
const candidates = [];
|
|
2386
|
+
const skipped = [];
|
|
2366
2387
|
collectFiles(cwd, files, /* @__PURE__ */ new Set());
|
|
2367
|
-
|
|
2368
|
-
const
|
|
2369
|
-
if (
|
|
2370
|
-
|
|
2388
|
+
files.forEach((filePath) => {
|
|
2389
|
+
const source = readSource(filePath);
|
|
2390
|
+
if (source === null) {
|
|
2391
|
+
skipped.push(filePath);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const result = rewriteDirectives(source, (0, node_path.extname)(filePath));
|
|
2395
|
+
if (result.changed) candidates.push({
|
|
2371
2396
|
path: filePath,
|
|
2372
2397
|
source: result.source
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2398
|
+
});
|
|
2399
|
+
});
|
|
2400
|
+
return {
|
|
2401
|
+
candidates,
|
|
2402
|
+
skipped
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* What the consumer is told about the files the rewrite could not touch: the paths, then why it
|
|
2407
|
+
* matters and how to finish the job. Nothing else in the migration is affected, and saying so is the
|
|
2408
|
+
* point — the layout was already written by the time the walk ran.
|
|
2409
|
+
* @example
|
|
2410
|
+
* formatUntouchedDirectiveFiles(['/repo/src/vendor.ts']); // '/repo/src/vendor.ts\n\nThese could not…'
|
|
2411
|
+
*/
|
|
2412
|
+
function formatUntouchedDirectiveFiles(skipped) {
|
|
2413
|
+
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
2414
|
}
|
|
2376
2415
|
/**
|
|
2377
2416
|
* Applies {@link rewriteDirectives} across a consumer tree, skipping build output and dependencies.
|
|
2417
|
+
* A candidate whose write fails comes back in `skipped`, so one failed write costs its own
|
|
2418
|
+
* directives rather than every later file's.
|
|
2378
2419
|
* @example
|
|
2379
2420
|
* rewriteDisableDirectives('/repo').rewritten; // ['/repo/src/logger.ts']
|
|
2380
2421
|
*/
|
|
2381
|
-
function rewriteDisableDirectives(cwd, candidates = previewDisableDirectives(cwd)) {
|
|
2422
|
+
function rewriteDisableDirectives(cwd, candidates = previewDisableDirectives(cwd).candidates) {
|
|
2423
|
+
const rewritten = [];
|
|
2424
|
+
const skipped = [];
|
|
2382
2425
|
candidates.forEach((candidate) => {
|
|
2383
|
-
|
|
2426
|
+
try {
|
|
2427
|
+
(0, node_fs.writeFileSync)(candidate.path, candidate.source, "utf8");
|
|
2428
|
+
rewritten.push(candidate.path);
|
|
2429
|
+
} catch {
|
|
2430
|
+
skipped.push(candidate.path);
|
|
2431
|
+
}
|
|
2384
2432
|
});
|
|
2385
|
-
return {
|
|
2433
|
+
return {
|
|
2434
|
+
rewritten,
|
|
2435
|
+
skipped
|
|
2436
|
+
};
|
|
2386
2437
|
}
|
|
2387
2438
|
//#endregion
|
|
2388
2439
|
//#region src/cli/migrate/stringEntry.ts
|
|
@@ -2411,7 +2462,7 @@ function leadingCommentTextFor(sourceFile, node) {
|
|
|
2411
2462
|
}
|
|
2412
2463
|
//#endregion
|
|
2413
2464
|
//#region src/cli/migrate/eslintrc/parse.ts
|
|
2414
|
-
function lineOf$
|
|
2465
|
+
function lineOf$2(sourceFile, node) {
|
|
2415
2466
|
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
2416
2467
|
return line + 1;
|
|
2417
2468
|
}
|
|
@@ -2705,7 +2756,7 @@ function readSharedBlockFields(input) {
|
|
|
2705
2756
|
};
|
|
2706
2757
|
}
|
|
2707
2758
|
function readOverride(object, context, elementNode) {
|
|
2708
|
-
const line = lineOf$
|
|
2759
|
+
const line = lineOf$2(context.sourceFile, object);
|
|
2709
2760
|
const filesProperty = findProperty$1(object, "files");
|
|
2710
2761
|
const files = filesProperty ? readStringOrEntries(filesProperty.initializer, context.sourceFile) : [];
|
|
2711
2762
|
if (files.length === 0) {
|
|
@@ -2762,7 +2813,7 @@ function findExportObject(sourceFile) {
|
|
|
2762
2813
|
return result;
|
|
2763
2814
|
}
|
|
2764
2815
|
function buildRoot(object, context) {
|
|
2765
|
-
const line = lineOf$
|
|
2816
|
+
const line = lineOf$2(context.sourceFile, object);
|
|
2766
2817
|
const shared = readSharedBlockFields({
|
|
2767
2818
|
object,
|
|
2768
2819
|
line,
|
|
@@ -3392,7 +3443,7 @@ var knownPresets = /* @__PURE__ */ new Set([
|
|
|
3392
3443
|
"dom",
|
|
3393
3444
|
"node"
|
|
3394
3445
|
]);
|
|
3395
|
-
function lineOf(sourceFile, node) {
|
|
3446
|
+
function lineOf$1(sourceFile, node) {
|
|
3396
3447
|
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
3397
3448
|
return line + 1;
|
|
3398
3449
|
}
|
|
@@ -3695,7 +3746,7 @@ function classifyPresets(presetList, line, warnings) {
|
|
|
3695
3746
|
};
|
|
3696
3747
|
}
|
|
3697
3748
|
function parseUseInsiderCall(callExpression, sourceFile, warnings) {
|
|
3698
|
-
const line = lineOf(sourceFile, callExpression);
|
|
3749
|
+
const line = lineOf$1(sourceFile, callExpression);
|
|
3699
3750
|
const [firstArgument] = callExpression.arguments;
|
|
3700
3751
|
if (!firstArgument || !typescript.isObjectLiteralExpression(firstArgument)) {
|
|
3701
3752
|
warnings.push({
|
|
@@ -3795,7 +3846,7 @@ function tryClassifyStandaloneGlobals(objectLiteral, keys, line, sourceFile) {
|
|
|
3795
3846
|
};
|
|
3796
3847
|
}
|
|
3797
3848
|
function classifyObjectElement(objectLiteral, sourceFile, warnings) {
|
|
3798
|
-
const line = lineOf(sourceFile, objectLiteral);
|
|
3849
|
+
const line = lineOf$1(sourceFile, objectLiteral);
|
|
3799
3850
|
const keys = objectLiteral.properties.map(propertyKeyName).filter((name) => name !== null);
|
|
3800
3851
|
const ignoresBlock = tryClassifyIgnoresBlock(objectLiteral, keys, line, sourceFile);
|
|
3801
3852
|
if (ignoresBlock) return ignoresBlock;
|
|
@@ -3832,7 +3883,7 @@ function classifyArrayElement(element, sourceFile, warnings) {
|
|
|
3832
3883
|
if (leadingComment !== void 0) parsed.leadingComment = leadingComment;
|
|
3833
3884
|
return parsed;
|
|
3834
3885
|
}
|
|
3835
|
-
const line = lineOf(sourceFile, element);
|
|
3886
|
+
const line = lineOf$1(sourceFile, element);
|
|
3836
3887
|
warnings.push({
|
|
3837
3888
|
kind: "unrecognized-block",
|
|
3838
3889
|
line,
|
|
@@ -3849,18 +3900,19 @@ function classifyArrayElement(element, sourceFile, warnings) {
|
|
|
3849
3900
|
function isDefineConfigCall(expression) {
|
|
3850
3901
|
return typescript.isCallExpression(expression) && typescript.isIdentifier(expression.expression) && expression.expression.text === "defineConfig";
|
|
3851
3902
|
}
|
|
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;
|
|
3903
|
+
function defineConfigArray(exportExpressions) {
|
|
3904
|
+
const [first] = exportExpressions.find(isDefineConfigCall)?.arguments ?? [];
|
|
3859
3905
|
if (!first) return null;
|
|
3860
3906
|
const unwrappedFirst = unwrapTypeCasts(first);
|
|
3861
3907
|
if (typescript.isArrayLiteralExpression(unwrappedFirst)) return unwrappedFirst;
|
|
3862
3908
|
return null;
|
|
3863
3909
|
}
|
|
3910
|
+
function findDefaultExportElements(sourceFile) {
|
|
3911
|
+
const exportExpressions = sourceFile.statements.filter(typescript.isExportAssignment).map((statement) => unwrapTypeCasts(statement.expression));
|
|
3912
|
+
const array = exportExpressions.find(typescript.isArrayLiteralExpression) ?? defineConfigArray(exportExpressions);
|
|
3913
|
+
if (array) return [...array.elements];
|
|
3914
|
+
return exportExpressions;
|
|
3915
|
+
}
|
|
3864
3916
|
function checkDefineConfigWrap(sourceFile) {
|
|
3865
3917
|
return sourceFile.statements.some((statement) => {
|
|
3866
3918
|
if (!typescript.isExportAssignment(statement)) return false;
|
|
@@ -3899,7 +3951,7 @@ function classifyInsiderImport(statement, sourceFile, flags) {
|
|
|
3899
3951
|
return;
|
|
3900
3952
|
}
|
|
3901
3953
|
flags.hasUseInsider = true;
|
|
3902
|
-
const line = lineOf(sourceFile, statement);
|
|
3954
|
+
const line = lineOf$1(sourceFile, statement);
|
|
3903
3955
|
flags.preserved.push({
|
|
3904
3956
|
raw: statement.getText(sourceFile),
|
|
3905
3957
|
moduleSpecifier: "@useinsider/eslint-config"
|
|
@@ -3944,7 +3996,7 @@ function collectSilenceRulesCalls(sourceFile) {
|
|
|
3944
3996
|
if (!rules) return null;
|
|
3945
3997
|
const leadingComment = leadingCommentTextFor(sourceFile, statement);
|
|
3946
3998
|
const call = {
|
|
3947
|
-
line: lineOf(sourceFile, statement),
|
|
3999
|
+
line: lineOf$1(sourceFile, statement),
|
|
3948
4000
|
rules
|
|
3949
4001
|
};
|
|
3950
4002
|
if (leadingComment !== void 0) call.leadingComment = leadingComment;
|
|
@@ -3957,8 +4009,7 @@ function parseV1Config(source, filePath) {
|
|
|
3957
4009
|
const imports = inspectImports(sourceFile);
|
|
3958
4010
|
warnings.push(...imports.warnings);
|
|
3959
4011
|
const silenceRulesCalls = collectSilenceRulesCalls(sourceFile);
|
|
3960
|
-
const
|
|
3961
|
-
const arrayElements = arrayLiteral ? arrayLiteral.elements.map((element) => classifyArrayElement(element, sourceFile, warnings)).filter((entry) => entry !== null) : [];
|
|
4012
|
+
const arrayElements = findDefaultExportElements(sourceFile).map((element) => classifyArrayElement(element, sourceFile, warnings)).filter((entry) => entry !== null);
|
|
3962
4013
|
return {
|
|
3963
4014
|
hasUseInsiderImport: imports.hasUseInsider,
|
|
3964
4015
|
hasSilenceRulesImport: imports.hasSilenceRules,
|
|
@@ -3970,6 +4021,220 @@ function parseV1Config(source, filePath) {
|
|
|
3970
4021
|
};
|
|
3971
4022
|
}
|
|
3972
4023
|
//#endregion
|
|
4024
|
+
//#region src/cli/migrate/rehouseV3.ts
|
|
4025
|
+
var INSIDER_MODULE = "@useinsider/eslint-config";
|
|
4026
|
+
var COMPOSER_NAME = "useInsider";
|
|
4027
|
+
var DEFINE_CONFIG_NAME = "defineConfig";
|
|
4028
|
+
var SILENCE_RULES_NAME = "silenceRules";
|
|
4029
|
+
var RENDERER_EXPORT = "export const { useInsiderESLint, useInsiderOXC } = ";
|
|
4030
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set([
|
|
4031
|
+
"ignores",
|
|
4032
|
+
"jquery",
|
|
4033
|
+
"strict",
|
|
4034
|
+
"spellChecker",
|
|
4035
|
+
"silenceRules",
|
|
4036
|
+
"configFiles"
|
|
4037
|
+
]);
|
|
4038
|
+
function isInsiderImport(statement) {
|
|
4039
|
+
const { moduleSpecifier } = statement;
|
|
4040
|
+
return typescript.isStringLiteral(moduleSpecifier) && moduleSpecifier.text === INSIDER_MODULE;
|
|
4041
|
+
}
|
|
4042
|
+
function composerAliases(sourceFile) {
|
|
4043
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
4044
|
+
sourceFile.statements.forEach((statement) => {
|
|
4045
|
+
if (!typescript.isImportDeclaration(statement) || !isInsiderImport(statement)) return;
|
|
4046
|
+
const bindings = statement.importClause?.namedBindings;
|
|
4047
|
+
if (!bindings || !typescript.isNamedImports(bindings)) return;
|
|
4048
|
+
bindings.elements.forEach((element) => {
|
|
4049
|
+
if ((element.propertyName?.text ?? element.name.text) === COMPOSER_NAME) aliases.add(element.name.text);
|
|
4050
|
+
});
|
|
4051
|
+
});
|
|
4052
|
+
return aliases;
|
|
4053
|
+
}
|
|
4054
|
+
function isCallOf(node, names) {
|
|
4055
|
+
return typescript.isCallExpression(node) && typescript.isIdentifier(node.expression) && names.has(node.expression.text);
|
|
4056
|
+
}
|
|
4057
|
+
function collectCalls(sourceFile, names) {
|
|
4058
|
+
const found = [];
|
|
4059
|
+
const visit = (node) => {
|
|
4060
|
+
if (isCallOf(node, names)) found.push(node);
|
|
4061
|
+
typescript.forEachChild(node, visit);
|
|
4062
|
+
};
|
|
4063
|
+
visit(sourceFile);
|
|
4064
|
+
return found;
|
|
4065
|
+
}
|
|
4066
|
+
function peel(expression, state) {
|
|
4067
|
+
if (typescript.isParenthesizedExpression(expression) || typescript.isSpreadElement(expression)) return peel(expression.expression, state);
|
|
4068
|
+
if (typescript.isAsExpression(expression) || typescript.isSatisfiesExpression(expression)) {
|
|
4069
|
+
state.droppedAnnotation = true;
|
|
4070
|
+
return peel(expression.expression, state);
|
|
4071
|
+
}
|
|
4072
|
+
if (typescript.isAwaitExpression(expression)) {
|
|
4073
|
+
state.awaited = true;
|
|
4074
|
+
return peel(expression.expression, state);
|
|
4075
|
+
}
|
|
4076
|
+
return expression;
|
|
4077
|
+
}
|
|
4078
|
+
function peelArray(expression, state) {
|
|
4079
|
+
if (!typescript.isArrayLiteralExpression(expression) || expression.elements.length === 0) return expression;
|
|
4080
|
+
state.arrayWrapped = true;
|
|
4081
|
+
state.siblingElements = expression.elements.length - 1;
|
|
4082
|
+
return peel(expression.elements[0], state);
|
|
4083
|
+
}
|
|
4084
|
+
function unwrapDefaultExport(expression, state) {
|
|
4085
|
+
const peeled = peel(expression, state);
|
|
4086
|
+
if (isCallOf(peeled, /* @__PURE__ */ new Set([DEFINE_CONFIG_NAME]))) {
|
|
4087
|
+
state.definedConfig = true;
|
|
4088
|
+
return peeled;
|
|
4089
|
+
}
|
|
4090
|
+
return peelArray(peeled, state);
|
|
4091
|
+
}
|
|
4092
|
+
function argumentObject(call) {
|
|
4093
|
+
const [first] = call.arguments;
|
|
4094
|
+
if (!first || !typescript.isObjectLiteralExpression(first)) return null;
|
|
4095
|
+
return first;
|
|
4096
|
+
}
|
|
4097
|
+
function propertyKey(property) {
|
|
4098
|
+
const { name } = property;
|
|
4099
|
+
if (name === void 0) return null;
|
|
4100
|
+
if (typescript.isIdentifier(name) || typescript.isStringLiteral(name)) return name.text;
|
|
4101
|
+
return null;
|
|
4102
|
+
}
|
|
4103
|
+
function unreadableReason(argument, keysRead) {
|
|
4104
|
+
if (!argument) return "is not an object literal";
|
|
4105
|
+
if (argument.properties.some(typescript.isSpreadAssignment)) return "spreads another object";
|
|
4106
|
+
return keysRead < argument.properties.length ? "has a property whose key is not a literal name" : null;
|
|
4107
|
+
}
|
|
4108
|
+
function readArgument(call) {
|
|
4109
|
+
const argument = argumentObject(call);
|
|
4110
|
+
const optionKeys = (argument?.properties ?? []).map(propertyKey).filter((key) => key !== null);
|
|
4111
|
+
const candidates = optionKeys.filter((key) => !RESERVED_KEYS.has(key));
|
|
4112
|
+
return {
|
|
4113
|
+
optionKeys,
|
|
4114
|
+
scopes: candidates.filter(isMigratableScope),
|
|
4115
|
+
unregisteredKeys: candidates.filter((key) => !isMigratableScope(key)),
|
|
4116
|
+
unreadable: unreadableReason(argument, optionKeys.length)
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
4119
|
+
function refuse(reason) {
|
|
4120
|
+
return {
|
|
4121
|
+
kind: "refused",
|
|
4122
|
+
reason
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
function multipleCallsReason(name) {
|
|
4126
|
+
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.`;
|
|
4127
|
+
}
|
|
4128
|
+
function mixedArrayReason(name, siblings) {
|
|
4129
|
+
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.`;
|
|
4130
|
+
}
|
|
4131
|
+
function indirectCallReason(name) {
|
|
4132
|
+
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.`;
|
|
4133
|
+
}
|
|
4134
|
+
function defineConfigReason(name) {
|
|
4135
|
+
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.`;
|
|
4136
|
+
}
|
|
4137
|
+
function presetReason(name) {
|
|
4138
|
+
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.`;
|
|
4139
|
+
}
|
|
4140
|
+
function annotationWarning(line) {
|
|
4141
|
+
return {
|
|
4142
|
+
kind: "dropped-type-annotation",
|
|
4143
|
+
line,
|
|
4144
|
+
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.`
|
|
4145
|
+
};
|
|
4146
|
+
}
|
|
4147
|
+
function unregisteredScopeWarning(key, line) {
|
|
4148
|
+
return {
|
|
4149
|
+
kind: "unregistered-scope",
|
|
4150
|
+
line,
|
|
4151
|
+
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.`
|
|
4152
|
+
};
|
|
4153
|
+
}
|
|
4154
|
+
function unreadableArgumentWarning(line, reason) {
|
|
4155
|
+
return {
|
|
4156
|
+
kind: "unrecognized-block",
|
|
4157
|
+
line,
|
|
4158
|
+
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.`
|
|
4159
|
+
};
|
|
4160
|
+
}
|
|
4161
|
+
function lineOf(sourceFile, node) {
|
|
4162
|
+
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
4163
|
+
}
|
|
4164
|
+
function collectWarnings(line, reading, state) {
|
|
4165
|
+
const warnings = [];
|
|
4166
|
+
if (state.droppedAnnotation) warnings.push(annotationWarning(line));
|
|
4167
|
+
if (reading.unreadable) warnings.push(unreadableArgumentWarning(line, reading.unreadable));
|
|
4168
|
+
reading.unregisteredKeys.forEach((key) => {
|
|
4169
|
+
warnings.push(unregisteredScopeWarning(key, line));
|
|
4170
|
+
});
|
|
4171
|
+
return warnings;
|
|
4172
|
+
}
|
|
4173
|
+
function spliceExport(input) {
|
|
4174
|
+
const start = input.statement.getStart(input.sourceFile);
|
|
4175
|
+
const prefix = input.source.slice(0, start);
|
|
4176
|
+
const suffix = input.source.slice(input.statement.getEnd());
|
|
4177
|
+
const rehoused = `${prefix}${RENDERER_EXPORT}${input.callText};${suffix}`;
|
|
4178
|
+
return rehoused.endsWith("\n") ? rehoused : `${rehoused}\n`;
|
|
4179
|
+
}
|
|
4180
|
+
function defaultExports(sourceFile) {
|
|
4181
|
+
return sourceFile.statements.filter(typescript.isExportAssignment).filter((statement) => statement.isExportEquals !== true);
|
|
4182
|
+
}
|
|
4183
|
+
function emptyState() {
|
|
4184
|
+
return {
|
|
4185
|
+
awaited: false,
|
|
4186
|
+
arrayWrapped: false,
|
|
4187
|
+
droppedAnnotation: false,
|
|
4188
|
+
siblingElements: 0,
|
|
4189
|
+
definedConfig: false
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
/**
|
|
4193
|
+
* Re-houses a v3 single-file config as a v4 lint module. Nothing about the configuration itself
|
|
4194
|
+
* changes between the two majors — only where the call lives — so the source is spliced rather than
|
|
4195
|
+
* re-rendered: every import, `silenceRules(...)` call, comment and blank line outside the export
|
|
4196
|
+
* statement survives byte-for-byte.
|
|
4197
|
+
* @example
|
|
4198
|
+
* rehouseV3Config("import { useInsider } from '@useinsider/eslint-config';\nexport default useInsider({});", 'eslint.config.js');
|
|
4199
|
+
* // { kind: 'rehoused', rehoused: { lintModule: '…export const { useInsiderESLint, useInsiderOXC } = useInsider({});\n', … } }
|
|
4200
|
+
*/
|
|
4201
|
+
function rehouseV3Config(source, filePath) {
|
|
4202
|
+
const name = (0, node_path.basename)(filePath);
|
|
4203
|
+
const sourceFile = typescript.createSourceFile(filePath, source, typescript.ScriptTarget.Latest, true);
|
|
4204
|
+
const aliases = composerAliases(sourceFile);
|
|
4205
|
+
const calls = collectCalls(sourceFile, aliases);
|
|
4206
|
+
const exportStatements = defaultExports(sourceFile);
|
|
4207
|
+
if (calls.length === 0) return { kind: "not-v3" };
|
|
4208
|
+
if (exportStatements.length !== 1) return refuse(indirectCallReason(name));
|
|
4209
|
+
if (calls.length > 1) return refuse(multipleCallsReason(name));
|
|
4210
|
+
const [statement] = exportStatements;
|
|
4211
|
+
const state = emptyState();
|
|
4212
|
+
const unwrapped = unwrapDefaultExport(statement.expression, state);
|
|
4213
|
+
if (state.definedConfig) return refuse(defineConfigReason(name));
|
|
4214
|
+
if (state.siblingElements > 0) return refuse(mixedArrayReason(name, state.siblingElements));
|
|
4215
|
+
if (!isCallOf(unwrapped, aliases)) return refuse(indirectCallReason(name));
|
|
4216
|
+
const reading = readArgument(unwrapped);
|
|
4217
|
+
if (reading.optionKeys.includes("preset")) return refuse(presetReason(name));
|
|
4218
|
+
return {
|
|
4219
|
+
kind: "rehoused",
|
|
4220
|
+
rehoused: {
|
|
4221
|
+
lintModule: spliceExport({
|
|
4222
|
+
source,
|
|
4223
|
+
sourceFile,
|
|
4224
|
+
statement,
|
|
4225
|
+
callText: unwrapped.getText(sourceFile)
|
|
4226
|
+
}),
|
|
4227
|
+
scopes: reading.scopes,
|
|
4228
|
+
optionKeys: reading.optionKeys,
|
|
4229
|
+
unregisteredKeys: reading.unregisteredKeys,
|
|
4230
|
+
silenceRulesCallCount: collectCalls(sourceFile, /* @__PURE__ */ new Set([SILENCE_RULES_NAME])).length,
|
|
4231
|
+
droppedAwait: state.awaited,
|
|
4232
|
+
droppedArrayWrapper: state.arrayWrapped,
|
|
4233
|
+
warnings: collectWarnings(lineOf(sourceFile, unwrapped), reading, state)
|
|
4234
|
+
}
|
|
4235
|
+
};
|
|
4236
|
+
}
|
|
4237
|
+
//#endregion
|
|
3973
4238
|
//#region src/cli/migrate/pruneUnusedImports.ts
|
|
3974
4239
|
var INSIDER_MODULE_SPECIFIER = "@useinsider/eslint-config";
|
|
3975
4240
|
function readModuleSpecifier(statement) {
|
|
@@ -4800,6 +5065,22 @@ function describeStrict(plan) {
|
|
|
4800
5065
|
if (plan.config.strictGlobs.length > 0) return `Strict scopes: 1 (${plan.config.strictGlobs.length} globs)`;
|
|
4801
5066
|
return "Strict scopes: 0";
|
|
4802
5067
|
}
|
|
5068
|
+
function describeRehousedKeys(label, keys) {
|
|
5069
|
+
return keys.length === 0 ? `${label}: (none)` : `${label}: ${keys.join(", ")}`;
|
|
5070
|
+
}
|
|
5071
|
+
function rehouseSummary(rehoused) {
|
|
5072
|
+
const lines = [
|
|
5073
|
+
"Source: v3 single-file eslint.config.*",
|
|
5074
|
+
"Re-housed as the lint module: only the export statement changed, every other line is byte-for-byte.",
|
|
5075
|
+
describeRehousedKeys("Options carried over", rehoused.optionKeys),
|
|
5076
|
+
describeRehousedKeys("Scopes with dependencies", rehoused.scopes),
|
|
5077
|
+
`silenceRules() calls kept: ${rehoused.silenceRulesCallCount}`
|
|
5078
|
+
];
|
|
5079
|
+
if (rehoused.droppedAwait) lines.push("Dropped the `await` on the call — useInsider() is synchronous in v4.");
|
|
5080
|
+
if (rehoused.droppedArrayWrapper) lines.push("Dropped the array wrapper around the call — the lint module exports the renderers.");
|
|
5081
|
+
lines.push(`Warnings: ${rehoused.warnings.length}`);
|
|
5082
|
+
return lines.join("\n");
|
|
5083
|
+
}
|
|
4803
5084
|
function summary(plan, format) {
|
|
4804
5085
|
const lines = [];
|
|
4805
5086
|
if (format === "eslintrc") lines.push("Source: legacy .eslintrc");
|
|
@@ -4812,13 +5093,47 @@ function summary(plan, format) {
|
|
|
4812
5093
|
lines.push(`Warnings: ${plan.warnings.length}`);
|
|
4813
5094
|
return lines.join("\n");
|
|
4814
5095
|
}
|
|
4815
|
-
function
|
|
4816
|
-
if (format
|
|
4817
|
-
const parseResult =
|
|
4818
|
-
return
|
|
5096
|
+
function sourceModelFrom(resolved) {
|
|
5097
|
+
if (resolved.format !== "flat") {
|
|
5098
|
+
const parseResult = parseEslintrc(resolved.source, resolved.targetPath);
|
|
5099
|
+
return parseResult.found ? {
|
|
5100
|
+
ok: true,
|
|
5101
|
+
model: {
|
|
5102
|
+
kind: "plan",
|
|
5103
|
+
plan: transformEslintrcToV2(parseResult)
|
|
5104
|
+
}
|
|
5105
|
+
} : {
|
|
5106
|
+
ok: false,
|
|
5107
|
+
reason: null
|
|
5108
|
+
};
|
|
4819
5109
|
}
|
|
4820
|
-
const parseResult =
|
|
4821
|
-
|
|
5110
|
+
const parseResult = parseV1Config(resolved.source, resolved.targetPath);
|
|
5111
|
+
if (isV1Shape(parseResult)) return {
|
|
5112
|
+
ok: true,
|
|
5113
|
+
model: {
|
|
5114
|
+
kind: "plan",
|
|
5115
|
+
plan: transformToV2(parseResult)
|
|
5116
|
+
}
|
|
5117
|
+
};
|
|
5118
|
+
const outcome = rehouseV3Config(resolved.source, resolved.targetPath);
|
|
5119
|
+
if (outcome.kind === "rehoused") return {
|
|
5120
|
+
ok: true,
|
|
5121
|
+
model: {
|
|
5122
|
+
kind: "rehouse",
|
|
5123
|
+
rehoused: outcome.rehoused
|
|
5124
|
+
}
|
|
5125
|
+
};
|
|
5126
|
+
return {
|
|
5127
|
+
ok: false,
|
|
5128
|
+
reason: outcome.kind === "refused" ? outcome.reason : null
|
|
5129
|
+
};
|
|
5130
|
+
}
|
|
5131
|
+
function scopesOf(model) {
|
|
5132
|
+
if (model.kind === "plan") return new Set(model.plan.config.scopes.keys());
|
|
5133
|
+
return new Set(model.rehoused.scopes);
|
|
5134
|
+
}
|
|
5135
|
+
function warningsOf(model) {
|
|
5136
|
+
return model.kind === "plan" ? model.plan.warnings : model.rehoused.warnings;
|
|
4822
5137
|
}
|
|
4823
5138
|
function targetWritePath(cwd, sourcePath) {
|
|
4824
5139
|
const directory = (0, node_path.dirname)(sourcePath) || cwd;
|
|
@@ -4903,9 +5218,11 @@ function emitInstallNextSteps(clack, depSummary, cwd) {
|
|
|
4903
5218
|
if (message === null) return;
|
|
4904
5219
|
clack.note(message, "Next steps");
|
|
4905
5220
|
}
|
|
4906
|
-
function emitSummary(clack,
|
|
4907
|
-
if (clack.note)
|
|
4908
|
-
|
|
5221
|
+
function emitSummary(clack, model, format) {
|
|
5222
|
+
if (!clack.note) return;
|
|
5223
|
+
clack.note(model.kind === "plan" ? summary(model.plan, format) : rehouseSummary(model.rehoused), "Migration summary");
|
|
5224
|
+
const warnings = warningsOf(model);
|
|
5225
|
+
if (warnings.length > 0) clack.note(formatWarnings(warnings), "Warnings");
|
|
4909
5226
|
}
|
|
4910
5227
|
function writeAndAnnounce(clack, targetPath, writePath, rendered, format) {
|
|
4911
5228
|
const result = format === "eslintrc" ? writeMigrationLegacy(targetPath, writePath, rendered) : writeMigration(targetPath, rendered);
|
|
@@ -4955,17 +5272,24 @@ function resolveSource(input, clack) {
|
|
|
4955
5272
|
}
|
|
4956
5273
|
};
|
|
4957
5274
|
}
|
|
5275
|
+
function renderModel(model, eslintIgnore) {
|
|
5276
|
+
if (model.kind === "rehouse") return model.rehoused.lintModule;
|
|
5277
|
+
if (eslintIgnore && eslintIgnore.patterns.length > 0) mergeIgnorePatterns(model.plan, eslintIgnore.patterns);
|
|
5278
|
+
return renderV2(model.plan);
|
|
5279
|
+
}
|
|
4958
5280
|
function prepareMigration(resolved, input) {
|
|
4959
|
-
const
|
|
4960
|
-
if (!
|
|
5281
|
+
const outcome = sourceModelFrom(resolved);
|
|
5282
|
+
if (!outcome.ok) return outcome;
|
|
4961
5283
|
const writePath = targetWritePath(input.cwd, resolved.targetPath);
|
|
4962
5284
|
const eslintIgnore = findEslintIgnore(resolved.targetPath);
|
|
4963
|
-
if (eslintIgnore && eslintIgnore.patterns.length > 0) mergeIgnorePatterns(plan, eslintIgnore.patterns);
|
|
4964
5285
|
return {
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
5286
|
+
ok: true,
|
|
5287
|
+
prepared: {
|
|
5288
|
+
model: outcome.model,
|
|
5289
|
+
rendered: renderModel(outcome.model, eslintIgnore),
|
|
5290
|
+
writePath,
|
|
5291
|
+
eslintIgnore
|
|
5292
|
+
}
|
|
4969
5293
|
};
|
|
4970
5294
|
}
|
|
4971
5295
|
async function runConfirmations(clack, input, targetPath, writePath, previous) {
|
|
@@ -4995,7 +5319,7 @@ function tryReadConsumerManifest(path) {
|
|
|
4995
5319
|
return null;
|
|
4996
5320
|
}
|
|
4997
5321
|
}
|
|
4998
|
-
function computeDepSummary(
|
|
5322
|
+
function computeDepSummary(model, writePath, overrides) {
|
|
4999
5323
|
const pkgPath = consumerPackageJsonPath(writePath);
|
|
5000
5324
|
const manifest = tryReadConsumerManifest(pkgPath);
|
|
5001
5325
|
if (!manifest) return {
|
|
@@ -5008,7 +5332,7 @@ function computeDepSummary(plan, writePath, overrides) {
|
|
|
5008
5332
|
};
|
|
5009
5333
|
const pruneOutcome = pruneEslintDependencies(manifest);
|
|
5010
5334
|
const ownManifest = (overrides?.readEslintConfigManifest ?? readOwnEslintConfigManifest)();
|
|
5011
|
-
const install = buildInstallList(
|
|
5335
|
+
const install = buildInstallList(scopesOf(model), ownManifest);
|
|
5012
5336
|
return {
|
|
5013
5337
|
removed: pruneOutcome.removed,
|
|
5014
5338
|
install,
|
|
@@ -5063,7 +5387,7 @@ async function applyDepChanges(clack, depSummary, input) {
|
|
|
5063
5387
|
}
|
|
5064
5388
|
function performWriteAndCleanup(clack, resolved, prepared) {
|
|
5065
5389
|
const outcome = writeAndAnnounce(clack, resolved.targetPath, prepared.writePath, prepared.rendered, resolved.format);
|
|
5066
|
-
if (prepared.eslintIgnore) safeUnlink(prepared.eslintIgnore.path, clack, ".eslintignore");
|
|
5390
|
+
if (prepared.eslintIgnore && prepared.model.kind === "plan") safeUnlink(prepared.eslintIgnore.path, clack, ".eslintignore");
|
|
5067
5391
|
outcome.deletedSourceCandidates.forEach((path) => {
|
|
5068
5392
|
safeUnlink(path, clack, "superseded config");
|
|
5069
5393
|
});
|
|
@@ -5089,8 +5413,13 @@ function applyLintScript$1(clack, directory) {
|
|
|
5089
5413
|
if (result.changed) (0, node_fs.writeFileSync)(manifestPath, result.manifest, "utf8");
|
|
5090
5414
|
noteLintScriptOutcome$1(clack, result);
|
|
5091
5415
|
}
|
|
5416
|
+
function noteUntouchedDirectiveFiles(clack, skipped) {
|
|
5417
|
+
if (skipped.length === 0 || !clack.note) return;
|
|
5418
|
+
clack.note(formatUntouchedDirectiveFiles(skipped), `${skipped.length} file(s) left untouched`);
|
|
5419
|
+
}
|
|
5092
5420
|
async function rewriteDirectivesWithNote(clack, directory, autoConfirm) {
|
|
5093
|
-
const candidates = previewDisableDirectives(directory);
|
|
5421
|
+
const { candidates, skipped } = previewDisableDirectives(directory);
|
|
5422
|
+
noteUntouchedDirectiveFiles(clack, skipped);
|
|
5094
5423
|
if (candidates.length === 0) return;
|
|
5095
5424
|
const count = candidates.length;
|
|
5096
5425
|
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 +5435,9 @@ This is a separate, source-wide diff — review it on its own commit.`, "Disable
|
|
|
5106
5435
|
return;
|
|
5107
5436
|
}
|
|
5108
5437
|
}
|
|
5109
|
-
const { rewritten } = rewriteDisableDirectives(directory, candidates);
|
|
5438
|
+
const { rewritten, skipped: unwritten } = rewriteDisableDirectives(directory, candidates);
|
|
5110
5439
|
if (clack.note) clack.note(`Re-pointed directives in ${rewritten.length} file(s).`, "Disable directives");
|
|
5440
|
+
noteUntouchedDirectiveFiles(clack, unwritten);
|
|
5111
5441
|
}
|
|
5112
5442
|
function loadPreviousMigrate(clack, cwd) {
|
|
5113
5443
|
const restored = loadSession({ cwd });
|
|
@@ -5116,7 +5446,12 @@ function loadPreviousMigrate(clack, cwd) {
|
|
|
5116
5446
|
return previousMigrate;
|
|
5117
5447
|
}
|
|
5118
5448
|
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.");
|
|
5449
|
+
clack.outro(format === "eslintrc" ? "No legacy `.eslintrc` shape found — nothing to migrate." : "No v1 or v3 useInsider patterns found — nothing to migrate.");
|
|
5450
|
+
}
|
|
5451
|
+
function noteUnfoldedEslintIgnore(clack, prepared) {
|
|
5452
|
+
const patterns = prepared.eslintIgnore?.patterns ?? [];
|
|
5453
|
+
if (!clack.note || prepared.model.kind !== "rehouse" || patterns.length === 0) return;
|
|
5454
|
+
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
5455
|
}
|
|
5121
5456
|
function persistMigrateSession(input, prepared, resolved) {
|
|
5122
5457
|
const overwriteExisting = supersededFlatConfigs(resolved.targetPath, prepared.writePath).length > 0;
|
|
@@ -5151,14 +5486,20 @@ async function runMigrate(input, clackOverride) {
|
|
|
5151
5486
|
return declinedExitCode;
|
|
5152
5487
|
}
|
|
5153
5488
|
const previousMigrate = loadPreviousMigrate(clack, input.cwd);
|
|
5154
|
-
const
|
|
5155
|
-
if (!
|
|
5156
|
-
|
|
5157
|
-
|
|
5489
|
+
const outcome = prepareMigration(resolved, input);
|
|
5490
|
+
if (!outcome.ok) {
|
|
5491
|
+
if (outcome.reason === null) {
|
|
5492
|
+
emitNothingToMigrate(clack, resolved.format);
|
|
5493
|
+
return declinedExitCode;
|
|
5494
|
+
}
|
|
5495
|
+
clack.cancel(outcome.reason);
|
|
5496
|
+
return 1;
|
|
5158
5497
|
}
|
|
5159
|
-
|
|
5498
|
+
const { prepared } = outcome;
|
|
5499
|
+
emitSummary(clack, prepared.model, resolved.format);
|
|
5500
|
+
noteUnfoldedEslintIgnore(clack, prepared);
|
|
5160
5501
|
noteConfigRename(clack, resolved.targetPath, prepared.writePath);
|
|
5161
|
-
const depSummary = computeDepSummary(prepared.
|
|
5502
|
+
const depSummary = computeDepSummary(prepared.model, prepared.writePath, input.overrides);
|
|
5162
5503
|
if (!await runConfirmations(clack, input, resolved.targetPath, prepared.writePath, previousMigrate)) return declinedExitCode;
|
|
5163
5504
|
persistMigrateSession(input, prepared, resolved);
|
|
5164
5505
|
performWriteAndCleanup(clack, resolved, prepared);
|