@useinsider/eslint-config 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +366 -58
- package/dist/builders/configFiles.d.ts +12 -0
- package/dist/builders/jquery.d.ts +5 -0
- package/dist/builders/scopeBlocks.d.ts +49 -0
- package/dist/builders/spellChecker.d.ts +7 -0
- package/dist/builders/strict.d.ts +2 -0
- package/dist/builders/typescript.d.ts +6 -0
- package/dist/builders/vue.d.ts +8 -0
- package/dist/cli.cjs +4538 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/configs/config/dependencies.d.ts +1 -0
- package/dist/configs/javascript/dependencies.d.ts +1 -0
- package/dist/configs/javascript-dom/dependencies.d.ts +1 -0
- package/dist/configs/javascript-node/dependencies.d.ts +1 -0
- package/dist/configs/jest/dependencies.d.ts +3 -0
- package/dist/configs/jsdoc-based-ts/dependencies.d.ts +6 -0
- package/dist/configs/strict/dependencies.d.ts +3 -0
- package/dist/configs/typescript/dependencies.d.ts +6 -0
- package/dist/configs/typescript-dom/dependencies.d.ts +6 -0
- package/dist/configs/typescript-node/dependencies.d.ts +6 -0
- package/dist/configs/vitest/dependencies.d.ts +3 -0
- package/dist/configs/vitest/index.d.ts +3 -0
- package/dist/configs/vue2/dependencies.d.ts +5 -0
- package/dist/configs/vue2-options-api/dependencies.d.ts +5 -0
- package/dist/configs/vue2-options-api/index.d.ts +1208 -0
- package/dist/configs/vue2-typescript/dependencies.d.ts +9 -0
- package/dist/configs/vue2-typescript-options-api/dependencies.d.ts +9 -0
- package/dist/configs/vue2-typescript-options-api/index.d.ts +3 -0
- package/dist/configs/vue3/dependencies.d.ts +5 -0
- package/dist/configs/vue3-options-api/dependencies.d.ts +5 -0
- package/dist/configs/vue3-options-api/index.d.ts +1208 -0
- package/dist/configs/vue3-typescript/dependencies.d.ts +9 -0
- package/dist/configs/vue3-typescript-options-api/dependencies.d.ts +9 -0
- package/dist/configs/vue3-typescript-options-api/index.d.ts +3 -0
- package/dist/{javascript-ZbGsFE17.cjs → javascript-CrNAuq8X.cjs} +2 -2
- package/dist/{javascript-ZbGsFE17.cjs.map → javascript-CrNAuq8X.cjs.map} +1 -1
- package/dist/{javascript-BXgxOHrE.js → javascript-DBh1tdoj.js} +2 -2
- package/dist/{javascript-BXgxOHrE.js.map → javascript-DBh1tdoj.js.map} +1 -1
- package/dist/{javascript-dom-D7RX0w0r.cjs → javascript-dom-qddIl6lD.cjs} +2 -2
- package/dist/{javascript-dom-D7RX0w0r.cjs.map → javascript-dom-qddIl6lD.cjs.map} +1 -1
- package/dist/{javascript-dom-DQiJS6LF.js → javascript-dom-vHj4V_jJ.js} +2 -2
- package/dist/{javascript-dom-DQiJS6LF.js.map → javascript-dom-vHj4V_jJ.js.map} +1 -1
- package/dist/{javascript-node-fII8MlI4.cjs → javascript-node-CymkpeIN.cjs} +2 -2
- package/dist/{javascript-node-fII8MlI4.cjs.map → javascript-node-CymkpeIN.cjs.map} +1 -1
- package/dist/{javascript-node-DDpIBysN.js → javascript-node-rbQ7OZX2.js} +2 -2
- package/dist/{javascript-node-DDpIBysN.js.map → javascript-node-rbQ7OZX2.js.map} +1 -1
- package/dist/main.cjs +530 -52
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.ts +56 -21
- package/dist/main.js +531 -55
- package/dist/main.js.map +1 -1
- package/dist/rules/vue-options-api.d.ts +2 -0
- package/dist/{stylistic-Cca81A-Y.js → stylistic-C527N_5D.js} +22 -20
- package/dist/stylistic-C527N_5D.js.map +1 -0
- package/dist/{stylistic-BltudTa0.cjs → stylistic-_qagyIah.cjs} +22 -20
- package/dist/stylistic-_qagyIah.cjs.map +1 -0
- package/dist/{typescript-Z0gBgZeY.js → typescript-BkxC1Ebj.js} +3 -3
- package/dist/{typescript-Z0gBgZeY.js.map → typescript-BkxC1Ebj.js.map} +1 -1
- package/dist/{typescript-RDy5oH7o.cjs → typescript-Cm5y5BRu.cjs} +3 -3
- package/dist/{typescript-RDy5oH7o.cjs.map → typescript-Cm5y5BRu.cjs.map} +1 -1
- package/dist/{typescript-dom-DUh1spmb.cjs → typescript-dom-B5cwc97i.cjs} +2 -2
- package/dist/{typescript-dom-DUh1spmb.cjs.map → typescript-dom-B5cwc97i.cjs.map} +1 -1
- package/dist/{typescript-dom-BFinGO4G.js → typescript-dom-BMG-VaTV.js} +2 -2
- package/dist/{typescript-dom-BFinGO4G.js.map → typescript-dom-BMG-VaTV.js.map} +1 -1
- package/dist/{typescript-node-ClKSHtBn.cjs → typescript-node-BPHyiIve.cjs} +2 -2
- package/dist/{typescript-node-ClKSHtBn.cjs.map → typescript-node-BPHyiIve.cjs.map} +1 -1
- package/dist/{typescript-node-B3akGwfD.js → typescript-node-D_KHq9pf.js} +2 -2
- package/dist/{typescript-node-B3akGwfD.js.map → typescript-node-D_KHq9pf.js.map} +1 -1
- package/dist/utils/ensureConfigStructure.d.ts +17 -0
- package/dist/vitest-BgPQ6GrC.js +15 -0
- package/dist/vitest-BgPQ6GrC.js.map +1 -0
- package/dist/vitest-Cxe6To8m.cjs +17 -0
- package/dist/vitest-Cxe6To8m.cjs.map +1 -0
- package/dist/{vue-ABpPpVRF.cjs → vue-BSB1UZUR.cjs} +1 -1
- package/dist/{vue-ABpPpVRF.cjs.map → vue-BSB1UZUR.cjs.map} +1 -1
- package/dist/{vue-DN4l4tpE.js → vue-W9Dr0NU_.js} +1 -1
- package/dist/{vue-DN4l4tpE.js.map → vue-W9Dr0NU_.js.map} +1 -1
- package/dist/vue-options-api-DOwpLXkl.cjs +21 -0
- package/dist/vue-options-api-DOwpLXkl.cjs.map +1 -0
- package/dist/vue-options-api-Dx5bF7ak.js +16 -0
- package/dist/vue-options-api-Dx5bF7ak.js.map +1 -0
- package/dist/{vue2-DSL0XmaS.cjs → vue2-BO5tkjc4.cjs} +2 -2
- package/dist/{vue2-DSL0XmaS.cjs.map → vue2-BO5tkjc4.cjs.map} +1 -1
- package/dist/{vue2-BiP0fuu1.js → vue2-DkvfPqh_.js} +2 -2
- package/dist/{vue2-BiP0fuu1.js.map → vue2-DkvfPqh_.js.map} +1 -1
- package/dist/{vue2-Bh7W369C.cjs → vue2-XNhU8qvc.cjs} +3 -3
- package/dist/{vue2-Bh7W369C.cjs.map → vue2-XNhU8qvc.cjs.map} +1 -1
- package/dist/{vue2-DDKBhsLm.js → vue2-npomS1zY.js} +3 -3
- package/dist/{vue2-DDKBhsLm.js.map → vue2-npomS1zY.js.map} +1 -1
- package/dist/vue2-options-api-BPw5U0yK.js +25 -0
- package/dist/vue2-options-api-BPw5U0yK.js.map +1 -0
- package/dist/vue2-options-api-zm_Ppsnb.cjs +31 -0
- package/dist/vue2-options-api-zm_Ppsnb.cjs.map +1 -0
- package/dist/vue2-typescript-B2ubjEbm.cjs +18 -0
- package/dist/vue2-typescript-B2ubjEbm.cjs.map +1 -0
- package/dist/{vue2-typescript-CUqjxRHT.js → vue2-typescript-DV30OSCU.js} +4 -13
- package/dist/vue2-typescript-DV30OSCU.js.map +1 -0
- package/dist/vue2-typescript-DkeW8a4q.js +13 -0
- package/dist/vue2-typescript-DkeW8a4q.js.map +1 -0
- package/dist/vue2-typescript-options-api-Dxl4Cj_u.js +42 -0
- package/dist/vue2-typescript-options-api-Dxl4Cj_u.js.map +1 -0
- package/dist/vue2-typescript-options-api-Tj8_pTDc.cjs +50 -0
- package/dist/vue2-typescript-options-api-Tj8_pTDc.cjs.map +1 -0
- package/dist/{vue2-typescript-GmtjjCxx.cjs → vue2-typescript-qUvTF_0O.cjs} +5 -14
- package/dist/vue2-typescript-qUvTF_0O.cjs.map +1 -0
- package/dist/{vue3-CKwnKeS6.js → vue3-BnQYXB3K.js} +3 -3
- package/dist/{vue3-CKwnKeS6.js.map → vue3-BnQYXB3K.js.map} +1 -1
- package/dist/{vue3-BtNhSn_w.cjs → vue3-Dexa-ftV.cjs} +3 -3
- package/dist/{vue3-BtNhSn_w.cjs.map → vue3-Dexa-ftV.cjs.map} +1 -1
- package/dist/vue3-options-api-Fl27U2nN.cjs +31 -0
- package/dist/vue3-options-api-Fl27U2nN.cjs.map +1 -0
- package/dist/vue3-options-api-cejoqzVH.js +25 -0
- package/dist/vue3-options-api-cejoqzVH.js.map +1 -0
- package/dist/{vue3-typescript-CbM4dYzN.js → vue3-typescript-C65tDgFp.js} +3 -3
- package/dist/{vue3-typescript-CbM4dYzN.js.map → vue3-typescript-C65tDgFp.js.map} +1 -1
- package/dist/{vue3-typescript-P2UoKs0V.cjs → vue3-typescript-CzR7L5ep.cjs} +3 -3
- package/dist/{vue3-typescript-P2UoKs0V.cjs.map → vue3-typescript-CzR7L5ep.cjs.map} +1 -1
- package/dist/vue3-typescript-options-api-D56mHjp4.cjs +49 -0
- package/dist/vue3-typescript-options-api-D56mHjp4.cjs.map +1 -0
- package/dist/vue3-typescript-options-api-DL7JFqMJ.js +41 -0
- package/dist/vue3-typescript-options-api-DL7JFqMJ.js.map +1 -0
- package/package.json +29 -6
- package/dist/stylistic-BltudTa0.cjs.map +0 -1
- package/dist/stylistic-Cca81A-Y.js.map +0 -1
- package/dist/vue2-typescript-CUqjxRHT.js.map +0 -1
- package/dist/vue2-typescript-GmtjjCxx.cjs.map +0 -1
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,4538 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_chunk = require("./chunk-CKQMccvm.cjs");
|
|
4
|
+
let globals = require("globals");
|
|
5
|
+
globals = require_chunk.__toESM(globals, 1);
|
|
6
|
+
let node_fs = require("node:fs");
|
|
7
|
+
let node_path = require("node:path");
|
|
8
|
+
let node_url = require("node:url");
|
|
9
|
+
let node_crypto = require("node:crypto");
|
|
10
|
+
let node_os = require("node:os");
|
|
11
|
+
let typescript = require("typescript");
|
|
12
|
+
typescript = require_chunk.__toESM(typescript, 1);
|
|
13
|
+
//#region src/cli/detect.ts
|
|
14
|
+
var flatConfigOrder = [
|
|
15
|
+
"eslint.config.mjs",
|
|
16
|
+
"eslint.config.js",
|
|
17
|
+
"eslint.config.cjs",
|
|
18
|
+
"eslint.config.ts",
|
|
19
|
+
"eslint.config.mts",
|
|
20
|
+
"eslint.config.cts"
|
|
21
|
+
];
|
|
22
|
+
var legacyConfigOrder = [
|
|
23
|
+
".eslintrc.cjs",
|
|
24
|
+
".eslintrc.js",
|
|
25
|
+
".eslintrc.mjs",
|
|
26
|
+
".eslintrc.json",
|
|
27
|
+
".eslintrc.yml",
|
|
28
|
+
".eslintrc.yaml",
|
|
29
|
+
".eslintrc"
|
|
30
|
+
];
|
|
31
|
+
function detectExistingEslintConfig(cwd) {
|
|
32
|
+
const flatHit = flatConfigOrder.map((name) => (0, node_path.join)(cwd, name)).find((candidate) => (0, node_fs.existsSync)(candidate));
|
|
33
|
+
if (flatHit) return {
|
|
34
|
+
path: flatHit,
|
|
35
|
+
kind: "flat"
|
|
36
|
+
};
|
|
37
|
+
const legacyHit = legacyConfigOrder.map((name) => (0, node_path.join)(cwd, name)).find((candidate) => (0, node_fs.existsSync)(candidate));
|
|
38
|
+
if (legacyHit) return {
|
|
39
|
+
path: legacyHit,
|
|
40
|
+
kind: "legacy"
|
|
41
|
+
};
|
|
42
|
+
const pkgPath = (0, node_path.join)(cwd, "package.json");
|
|
43
|
+
if (!(0, node_fs.existsSync)(pkgPath)) return null;
|
|
44
|
+
try {
|
|
45
|
+
const raw = (0, node_fs.readFileSync)(pkgPath, "utf8");
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
if (parsed.eslintConfig && typeof parsed.eslintConfig === "object") return {
|
|
48
|
+
path: pkgPath,
|
|
49
|
+
kind: "legacy"
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
var stringTokenPattern = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/gu;
|
|
57
|
+
var lineCommentPattern = /\/\/[^\n]*/gu;
|
|
58
|
+
var blockCommentPattern = /\/\*[\s\S]*?\*\//gu;
|
|
59
|
+
var placeholderPattern = /__JSONC_TOKEN_(\d+)__/gu;
|
|
60
|
+
function stripJsonComments(source) {
|
|
61
|
+
const tokens = [];
|
|
62
|
+
return source.replace(stringTokenPattern, (match) => {
|
|
63
|
+
const index = tokens.length;
|
|
64
|
+
tokens.push(match);
|
|
65
|
+
return `__JSONC_TOKEN_${index}__`;
|
|
66
|
+
}).replace(blockCommentPattern, "").replace(lineCommentPattern, "").replace(placeholderPattern, (_match, indexValue) => tokens[Number(indexValue)]);
|
|
67
|
+
}
|
|
68
|
+
function stripTrailingCommas(source) {
|
|
69
|
+
return source.replace(/,(\s*[}\]])/g, "$1");
|
|
70
|
+
}
|
|
71
|
+
function readTsconfig(absPath) {
|
|
72
|
+
const stripped = stripTrailingCommas(stripJsonComments((0, node_fs.readFileSync)(absPath, "utf8")));
|
|
73
|
+
return JSON.parse(stripped);
|
|
74
|
+
}
|
|
75
|
+
function resolveTsconfigReferences(cwd) {
|
|
76
|
+
const rootPath = (0, node_path.join)(cwd, "tsconfig.json");
|
|
77
|
+
if (!(0, node_fs.existsSync)(rootPath)) return null;
|
|
78
|
+
const parsed = readTsconfig(rootPath);
|
|
79
|
+
const refs = parsed.references;
|
|
80
|
+
if (!refs || refs.length === 0) return null;
|
|
81
|
+
const hasFiles = Array.isArray(parsed.files) && parsed.files.length > 0;
|
|
82
|
+
const hasInclude = Array.isArray(parsed.include) && parsed.include.length > 0;
|
|
83
|
+
if (hasFiles || hasInclude) return null;
|
|
84
|
+
return {
|
|
85
|
+
rootPath,
|
|
86
|
+
refs: refs.map((ref) => ref.path)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/cli/detectVueApiPattern.ts
|
|
91
|
+
var SKIP_DIRS = new Set([
|
|
92
|
+
"node_modules",
|
|
93
|
+
".git",
|
|
94
|
+
".svn",
|
|
95
|
+
".hg",
|
|
96
|
+
"dist",
|
|
97
|
+
"build",
|
|
98
|
+
"out",
|
|
99
|
+
"coverage",
|
|
100
|
+
".next",
|
|
101
|
+
".nuxt",
|
|
102
|
+
".output",
|
|
103
|
+
".cache",
|
|
104
|
+
".turbo",
|
|
105
|
+
".parcel-cache",
|
|
106
|
+
".vite"
|
|
107
|
+
]);
|
|
108
|
+
var MAX_FILES = 5e3;
|
|
109
|
+
var MATCH_THRESHOLD = .95;
|
|
110
|
+
var MIN_SAMPLES_PER_BUCKET = 2;
|
|
111
|
+
var SUFFIX_CANDIDATES = [
|
|
112
|
+
".options",
|
|
113
|
+
".classic",
|
|
114
|
+
".legacy",
|
|
115
|
+
".opts",
|
|
116
|
+
".setup",
|
|
117
|
+
".composition",
|
|
118
|
+
".modern"
|
|
119
|
+
];
|
|
120
|
+
var DIRECTORY_CANDIDATES = [
|
|
121
|
+
{
|
|
122
|
+
options: "options",
|
|
123
|
+
composition: "composition"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
options: "Options",
|
|
127
|
+
composition: "Composition"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
options: "options-api",
|
|
131
|
+
composition: "composition-api"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
options: "classic",
|
|
135
|
+
composition: "modern"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
options: "legacy",
|
|
139
|
+
composition: "next"
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
function safeReaddir(path) {
|
|
143
|
+
try {
|
|
144
|
+
return (0, node_fs.readdirSync)(path, {
|
|
145
|
+
withFileTypes: true,
|
|
146
|
+
encoding: "utf8"
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function walkDir(dir, skipDirs, limit, accumulator) {
|
|
153
|
+
if (accumulator.truncated || accumulator.files.length >= limit) return;
|
|
154
|
+
safeReaddir(dir).forEach((entry) => {
|
|
155
|
+
if (accumulator.files.length >= limit) {
|
|
156
|
+
accumulator.truncated = true;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const full = (0, node_path.join)(dir, entry.name);
|
|
160
|
+
if (entry.isDirectory()) {
|
|
161
|
+
if (!skipDirs.has(entry.name)) walkDir(full, skipDirs, limit, accumulator);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (entry.isFile() && entry.name.endsWith(".vue")) accumulator.files.push(full);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function scanVueFiles(options) {
|
|
168
|
+
const limit = options.maxFiles ?? MAX_FILES;
|
|
169
|
+
const skipDirs = options.skipDirs ?? SKIP_DIRS;
|
|
170
|
+
const accumulator = {
|
|
171
|
+
files: [],
|
|
172
|
+
truncated: false
|
|
173
|
+
};
|
|
174
|
+
walkDir(options.cwd, skipDirs, limit, accumulator);
|
|
175
|
+
return {
|
|
176
|
+
files: accumulator.files,
|
|
177
|
+
truncated: accumulator.truncated
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function classifyVueContent(content) {
|
|
181
|
+
const hasSetupScript = /<script[^>]*\bsetup\b/i.test(content);
|
|
182
|
+
const hasPlainScript = /<script(?:(?!\bsetup\b)[^>])*>/i.test(content);
|
|
183
|
+
if (hasSetupScript && hasPlainScript) return "mixed";
|
|
184
|
+
if (hasSetupScript) return "composition";
|
|
185
|
+
return "options";
|
|
186
|
+
}
|
|
187
|
+
function classifyVueFiles(absolutePaths, cwd) {
|
|
188
|
+
return absolutePaths.reduce((classifications, absolutePath) => {
|
|
189
|
+
let content;
|
|
190
|
+
try {
|
|
191
|
+
if ((0, node_fs.statSync)(absolutePath).size > 2e6) return classifications;
|
|
192
|
+
content = (0, node_fs.readFileSync)(absolutePath, "utf8");
|
|
193
|
+
} catch {
|
|
194
|
+
return classifications;
|
|
195
|
+
}
|
|
196
|
+
const api = classifyVueContent(content);
|
|
197
|
+
const relativePath = (0, node_path.relative)(cwd, absolutePath);
|
|
198
|
+
classifications.push({
|
|
199
|
+
relativePath: relativePath.split(node_path.sep).join("/"),
|
|
200
|
+
api: api === "mixed" ? "composition" : api
|
|
201
|
+
});
|
|
202
|
+
return classifications;
|
|
203
|
+
}, []);
|
|
204
|
+
}
|
|
205
|
+
function basenameOf(relativePath) {
|
|
206
|
+
const lastSlash = relativePath.lastIndexOf("/");
|
|
207
|
+
return lastSlash === -1 ? relativePath : relativePath.slice(lastSlash + 1);
|
|
208
|
+
}
|
|
209
|
+
function ratio(matched, total) {
|
|
210
|
+
return total === 0 ? 0 : matched / total;
|
|
211
|
+
}
|
|
212
|
+
function suffixIsViable(targetMatches, otherMatches, targetTotal, otherTotal) {
|
|
213
|
+
const targetRatio = ratio(targetMatches.length, targetTotal);
|
|
214
|
+
const otherRatio = ratio(otherMatches.length, otherTotal);
|
|
215
|
+
if (targetRatio < MATCH_THRESHOLD || otherRatio < MATCH_THRESHOLD) return false;
|
|
216
|
+
return targetMatches.length >= MIN_SAMPLES_PER_BUCKET && otherMatches.length >= MIN_SAMPLES_PER_BUCKET;
|
|
217
|
+
}
|
|
218
|
+
function suffixMatchesTargetingApi(suffix, targetApi, optionsFiles, compositionFiles) {
|
|
219
|
+
const fullSuffix = `${suffix}.vue`;
|
|
220
|
+
const targetIsOptions = targetApi === "options";
|
|
221
|
+
const targetFiles = targetIsOptions ? optionsFiles : compositionFiles;
|
|
222
|
+
const otherFiles = targetIsOptions ? compositionFiles : optionsFiles;
|
|
223
|
+
const targetMatches = targetFiles.filter((path) => basenameOf(path).endsWith(fullSuffix));
|
|
224
|
+
const otherMatches = otherFiles.filter((path) => !basenameOf(path).endsWith(fullSuffix));
|
|
225
|
+
if (!suffixIsViable(targetMatches, otherMatches, targetFiles.length, otherFiles.length)) return null;
|
|
226
|
+
const optionsMatches = targetIsOptions ? targetMatches : otherMatches;
|
|
227
|
+
const compositionMatches = targetIsOptions ? otherMatches : targetMatches;
|
|
228
|
+
const optionsPattern = targetIsOptions ? `**/*${fullSuffix}` : "**/*.vue";
|
|
229
|
+
return {
|
|
230
|
+
kind: "suffix",
|
|
231
|
+
compositionPattern: targetIsOptions ? "**/*.vue" : `**/*${fullSuffix}`,
|
|
232
|
+
optionsPattern,
|
|
233
|
+
suffix: fullSuffix,
|
|
234
|
+
targetApi,
|
|
235
|
+
samples: {
|
|
236
|
+
composition: compositionMatches.slice(0, 5),
|
|
237
|
+
options: optionsMatches.slice(0, 5)
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function suffixMatches(suffix, optionsFiles, compositionFiles) {
|
|
242
|
+
return suffixMatchesTargetingApi(suffix, "options", optionsFiles, compositionFiles) ?? suffixMatchesTargetingApi(suffix, "composition", optionsFiles, compositionFiles);
|
|
243
|
+
}
|
|
244
|
+
function detectSuffix(optionsFiles, compositionFiles) {
|
|
245
|
+
return SUFFIX_CANDIDATES.reduce((found, suffix) => {
|
|
246
|
+
if (found) return found;
|
|
247
|
+
return suffixMatches(suffix, optionsFiles, compositionFiles);
|
|
248
|
+
}, null);
|
|
249
|
+
}
|
|
250
|
+
function directoryMatches(options, composition, optionsFiles, compositionFiles) {
|
|
251
|
+
const optionsToken = `/${options}/`;
|
|
252
|
+
const optionsPrefix = `${options}/`;
|
|
253
|
+
const inOptionsDir = (path) => path.includes(optionsToken) || path.startsWith(optionsPrefix);
|
|
254
|
+
const optionsMatches = optionsFiles.filter(inOptionsDir);
|
|
255
|
+
const compositionMatches = compositionFiles.filter((path) => !inOptionsDir(path));
|
|
256
|
+
const optionsRatio = ratio(optionsMatches.length, optionsFiles.length);
|
|
257
|
+
const compositionRatio = ratio(compositionMatches.length, compositionFiles.length);
|
|
258
|
+
if (optionsRatio < MATCH_THRESHOLD || compositionRatio < MATCH_THRESHOLD) return null;
|
|
259
|
+
if (optionsMatches.length < MIN_SAMPLES_PER_BUCKET || compositionMatches.length < MIN_SAMPLES_PER_BUCKET) return null;
|
|
260
|
+
return {
|
|
261
|
+
kind: "directory",
|
|
262
|
+
compositionPattern: `**/${composition}/**/*.vue`,
|
|
263
|
+
optionsPattern: `**/${options}/**/*.vue`,
|
|
264
|
+
compositionDir: composition,
|
|
265
|
+
optionsDir: options,
|
|
266
|
+
samples: {
|
|
267
|
+
composition: compositionMatches.slice(0, 5),
|
|
268
|
+
options: optionsMatches.slice(0, 5)
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function detectDirectory(optionsFiles, compositionFiles) {
|
|
273
|
+
return DIRECTORY_CANDIDATES.reduce((found, candidate) => {
|
|
274
|
+
if (found) return found;
|
|
275
|
+
return directoryMatches(candidate.options, candidate.composition, optionsFiles, compositionFiles);
|
|
276
|
+
}, null);
|
|
277
|
+
}
|
|
278
|
+
function detectVueApiPattern(input) {
|
|
279
|
+
const classifications = input.classifications ?? classifyVueFiles(scanVueFiles(input).files, input.cwd);
|
|
280
|
+
if (classifications.length === 0) return {
|
|
281
|
+
kind: "none",
|
|
282
|
+
compositionFiles: [],
|
|
283
|
+
optionsFiles: []
|
|
284
|
+
};
|
|
285
|
+
const optionsFiles = classifications.filter((item) => item.api === "options").map((item) => item.relativePath);
|
|
286
|
+
const compositionFiles = classifications.filter((item) => item.api === "composition").map((item) => item.relativePath);
|
|
287
|
+
if (optionsFiles.length === 0 || compositionFiles.length === 0) return {
|
|
288
|
+
kind: "none",
|
|
289
|
+
compositionFiles,
|
|
290
|
+
optionsFiles
|
|
291
|
+
};
|
|
292
|
+
const suffixHit = detectSuffix(optionsFiles, compositionFiles);
|
|
293
|
+
if (suffixHit) return suffixHit;
|
|
294
|
+
const directoryHit = detectDirectory(optionsFiles, compositionFiles);
|
|
295
|
+
if (directoryHit) return directoryHit;
|
|
296
|
+
return {
|
|
297
|
+
kind: "ambiguous",
|
|
298
|
+
reason: "Options and Composition files do not share a consistent suffix or directory split.",
|
|
299
|
+
compositionFiles: compositionFiles.slice(0, 5),
|
|
300
|
+
optionsFiles: optionsFiles.slice(0, 5)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region src/cli/dispatch.ts
|
|
305
|
+
var yesFlags = new Set(["--yes", "-y"]);
|
|
306
|
+
var noInstallFlags = new Set(["--no-install"]);
|
|
307
|
+
function isYesFlag(arg) {
|
|
308
|
+
return yesFlags.has(arg);
|
|
309
|
+
}
|
|
310
|
+
function isNoInstallFlag(arg) {
|
|
311
|
+
return noInstallFlags.has(arg);
|
|
312
|
+
}
|
|
313
|
+
function parseMigrateArgs(rest) {
|
|
314
|
+
const yes = rest.some(isYesFlag);
|
|
315
|
+
const noInstall = rest.some(isNoInstallFlag);
|
|
316
|
+
const positional = rest.filter((arg) => !isYesFlag(arg) && !isNoInstallFlag(arg));
|
|
317
|
+
return {
|
|
318
|
+
target: positional.length > 0 ? positional[positional.length - 1] : void 0,
|
|
319
|
+
yes,
|
|
320
|
+
noInstall
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function parseScaffoldArgs(argv) {
|
|
324
|
+
return { yes: argv.some(isYesFlag) };
|
|
325
|
+
}
|
|
326
|
+
function printUsage(stderr, unknown) {
|
|
327
|
+
stderr(`[useinsider/eslint-config] Unknown subcommand: ${unknown}`);
|
|
328
|
+
stderr("Usage:");
|
|
329
|
+
stderr(" npx @useinsider/eslint-config [--yes|-y] Scaffold a new eslint.config.mjs");
|
|
330
|
+
stderr(" npx @useinsider/eslint-config migrate [path] [--yes|-y] [--no-install]");
|
|
331
|
+
stderr(" Migrate an existing v1 useInsider() config to v2");
|
|
332
|
+
}
|
|
333
|
+
function isScaffoldArgv(argv) {
|
|
334
|
+
return argv.length === 0 || argv.every((arg) => isYesFlag(arg));
|
|
335
|
+
}
|
|
336
|
+
async function dispatch(argv, cwd, deps) {
|
|
337
|
+
if (isScaffoldArgv(argv)) {
|
|
338
|
+
const { yes } = parseScaffoldArgs(argv);
|
|
339
|
+
return deps.scaffold({
|
|
340
|
+
cwd,
|
|
341
|
+
yes
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const [subcommand, ...rest] = argv;
|
|
345
|
+
if (subcommand === "migrate") {
|
|
346
|
+
const { target, yes, noInstall } = parseMigrateArgs(rest);
|
|
347
|
+
return deps.migrate({
|
|
348
|
+
cwd,
|
|
349
|
+
target,
|
|
350
|
+
yes,
|
|
351
|
+
noInstall
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
printUsage(deps.stderr, subcommand);
|
|
355
|
+
return 1;
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/configs/jest/dependencies.ts
|
|
359
|
+
var dependencies$14 = { "eslint-plugin-jest": true };
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/configs/typescript/dependencies.ts
|
|
362
|
+
var dependencies$13 = {
|
|
363
|
+
typescript: false,
|
|
364
|
+
"typescript-eslint": false,
|
|
365
|
+
"@typescript-eslint/parser": false,
|
|
366
|
+
"@typescript-eslint/eslint-plugin": false
|
|
367
|
+
};
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region src/configs/vitest/dependencies.ts
|
|
370
|
+
var dependencies$12 = { "@vitest/eslint-plugin": true };
|
|
371
|
+
//#endregion
|
|
372
|
+
//#region src/configs/vue2/dependencies.ts
|
|
373
|
+
var dependencies$11 = {
|
|
374
|
+
"eslint-plugin-vue": true,
|
|
375
|
+
"eslint-plugin-vue-scoped-css": true,
|
|
376
|
+
"vue-eslint-parser": true
|
|
377
|
+
};
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/configs/vue2-typescript/dependencies.ts
|
|
380
|
+
var dependencies$10 = {
|
|
381
|
+
"eslint-plugin-vue": true,
|
|
382
|
+
"eslint-plugin-vue-scoped-css": true,
|
|
383
|
+
"vue-eslint-parser": true,
|
|
384
|
+
typescript: false,
|
|
385
|
+
"typescript-eslint": false,
|
|
386
|
+
"@typescript-eslint/parser": false,
|
|
387
|
+
"@typescript-eslint/eslint-plugin": false
|
|
388
|
+
};
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/configs/vue3/dependencies.ts
|
|
391
|
+
var dependencies$9 = {
|
|
392
|
+
"eslint-plugin-vue": true,
|
|
393
|
+
"eslint-plugin-vue-scoped-css": true,
|
|
394
|
+
"vue-eslint-parser": true
|
|
395
|
+
};
|
|
396
|
+
//#endregion
|
|
397
|
+
//#region src/configs/vue3-typescript/dependencies.ts
|
|
398
|
+
var dependencies$8 = {
|
|
399
|
+
"eslint-plugin-vue": true,
|
|
400
|
+
"eslint-plugin-vue-scoped-css": true,
|
|
401
|
+
"vue-eslint-parser": true,
|
|
402
|
+
typescript: false,
|
|
403
|
+
"typescript-eslint": false,
|
|
404
|
+
"@typescript-eslint/parser": false,
|
|
405
|
+
"@typescript-eslint/eslint-plugin": false
|
|
406
|
+
};
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/cli/resolveEslintConfigManifest.ts
|
|
409
|
+
function tryReadManifest(candidate) {
|
|
410
|
+
try {
|
|
411
|
+
const raw = (0, node_fs.readFileSync)(candidate, "utf8");
|
|
412
|
+
const parsed = JSON.parse(raw);
|
|
413
|
+
return parsed.name === "@useinsider/eslint-config" ? parsed : null;
|
|
414
|
+
} catch {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function resolveRuntimeDir() {
|
|
419
|
+
const { url } = {};
|
|
420
|
+
if (typeof url === "string" && url.startsWith("file:")) return (0, node_path.dirname)((0, node_url.fileURLToPath)(url));
|
|
421
|
+
if (typeof __dirname === "string") return __dirname;
|
|
422
|
+
return (0, node_path.resolve)(process.cwd(), "node_modules/@useinsider/eslint-config/dist");
|
|
423
|
+
}
|
|
424
|
+
function readOwnEslintConfigManifestFrom(runtimeDir) {
|
|
425
|
+
const found = [
|
|
426
|
+
(0, node_path.resolve)(runtimeDir, "../../package.json"),
|
|
427
|
+
(0, node_path.resolve)(runtimeDir, "../../../package.json"),
|
|
428
|
+
(0, node_path.resolve)(runtimeDir, "../package.json"),
|
|
429
|
+
(0, node_path.resolve)(process.cwd(), "node_modules/@useinsider/eslint-config/package.json")
|
|
430
|
+
].map(tryReadManifest).find((manifest) => manifest !== null);
|
|
431
|
+
if (!found) throw new Error("Unable to locate @useinsider/eslint-config package.json from CLI runtime");
|
|
432
|
+
return found;
|
|
433
|
+
}
|
|
434
|
+
function readOwnEslintConfigManifest$1() {
|
|
435
|
+
return readOwnEslintConfigManifestFrom(resolveRuntimeDir());
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/cli/install.ts
|
|
439
|
+
function toRequiredManifest(manifest) {
|
|
440
|
+
return {
|
|
441
|
+
version: manifest.version ?? "0.0.0",
|
|
442
|
+
dependencies: manifest.dependencies ?? {},
|
|
443
|
+
devDependencies: manifest.devDependencies ?? {},
|
|
444
|
+
peerDependencies: manifest.peerDependencies ?? {},
|
|
445
|
+
optionalDependencies: manifest.optionalDependencies ?? {}
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function lookup(manifest, name) {
|
|
449
|
+
return manifest.dependencies[name] ?? manifest.devDependencies[name] ?? manifest.peerDependencies[name] ?? manifest.optionalDependencies[name];
|
|
450
|
+
}
|
|
451
|
+
function pinned(manifest, name) {
|
|
452
|
+
const version = lookup(manifest, name);
|
|
453
|
+
if (!version) throw new Error(`Unable to resolve pinned version for ${name} from @useinsider/eslint-config manifest`);
|
|
454
|
+
return `${name}@${version.replace(/^(?:\^|~|>=|<=|>|<|=)/u, "")}`;
|
|
455
|
+
}
|
|
456
|
+
function vueDepsFor(major, wantsTypescript) {
|
|
457
|
+
if (wantsTypescript) return major === 2 ? dependencies$10 : dependencies$8;
|
|
458
|
+
return major === 2 ? dependencies$11 : dependencies$9;
|
|
459
|
+
}
|
|
460
|
+
function applyRecord(deps, manifest, record) {
|
|
461
|
+
Object.entries(record).forEach(([name, install]) => {
|
|
462
|
+
if (install) deps.add(pinned(manifest, name));
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
function buildDependencyList(answers, rawManifest = readOwnEslintConfigManifest$1()) {
|
|
466
|
+
const manifest = toRequiredManifest(rawManifest);
|
|
467
|
+
const deps = /* @__PURE__ */ new Set();
|
|
468
|
+
deps.add(`@useinsider/eslint-config@${manifest.version}`);
|
|
469
|
+
deps.add(pinned(manifest, "eslint"));
|
|
470
|
+
deps.add(pinned(manifest, "globals"));
|
|
471
|
+
const wantsTypescript = answers.languages.includes("typescript");
|
|
472
|
+
if (answers.framework === "vue") applyRecord(deps, manifest, vueDepsFor(answers.vueMajor ?? 3, wantsTypescript));
|
|
473
|
+
else if (wantsTypescript) applyRecord(deps, manifest, dependencies$13);
|
|
474
|
+
if (answers.testFramework === "jest") applyRecord(deps, manifest, dependencies$14);
|
|
475
|
+
if (answers.testFramework === "vitest") applyRecord(deps, manifest, dependencies$12);
|
|
476
|
+
return [...deps];
|
|
477
|
+
}
|
|
478
|
+
async function installDependencies(deps) {
|
|
479
|
+
const { installPackage } = await import("@antfu/install-pkg");
|
|
480
|
+
await installPackage([...deps], {
|
|
481
|
+
dev: true,
|
|
482
|
+
additionalArgs: ["-E"]
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
//#endregion
|
|
486
|
+
//#region src/cli/installWithRetry.ts
|
|
487
|
+
var lockfileMap = [
|
|
488
|
+
{
|
|
489
|
+
file: "pnpm-lock.yaml",
|
|
490
|
+
manager: "pnpm"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
file: "package-lock.json",
|
|
494
|
+
manager: "npm"
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
file: "yarn.lock",
|
|
498
|
+
manager: "yarn"
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
file: "bun.lockb",
|
|
502
|
+
manager: "bun"
|
|
503
|
+
}
|
|
504
|
+
];
|
|
505
|
+
var knownErrorCodes = [
|
|
506
|
+
"ENOSPC",
|
|
507
|
+
"EACCES",
|
|
508
|
+
"EPERM",
|
|
509
|
+
"ENOTFOUND",
|
|
510
|
+
"ETIMEDOUT",
|
|
511
|
+
"ECONNREFUSED",
|
|
512
|
+
"ECONNRESET",
|
|
513
|
+
"EAI_AGAIN",
|
|
514
|
+
"E404",
|
|
515
|
+
"ERR_PNPM_FETCH_404",
|
|
516
|
+
"ERR_PNPM_NO_MATCHING_VERSION"
|
|
517
|
+
];
|
|
518
|
+
function defaultWipeFs() {
|
|
519
|
+
return {
|
|
520
|
+
rmDir: (path) => {
|
|
521
|
+
(0, node_fs.rmSync)(path, {
|
|
522
|
+
recursive: true,
|
|
523
|
+
force: true
|
|
524
|
+
});
|
|
525
|
+
},
|
|
526
|
+
rmFile: (path) => {
|
|
527
|
+
(0, node_fs.unlinkSync)(path);
|
|
528
|
+
},
|
|
529
|
+
exists: (path) => (0, node_fs.existsSync)(path)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
async function defaultInstaller(specs) {
|
|
533
|
+
const { installPackage } = await import("@antfu/install-pkg");
|
|
534
|
+
await installPackage([...specs], {
|
|
535
|
+
dev: true,
|
|
536
|
+
additionalArgs: ["-E"]
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
function detectLockfile(dir, exists) {
|
|
540
|
+
return lockfileMap.map((entry) => ({
|
|
541
|
+
manager: entry.manager,
|
|
542
|
+
lockfilePath: (0, node_path.join)(dir, entry.file)
|
|
543
|
+
})).find((candidate) => exists(candidate.lockfilePath)) ?? null;
|
|
544
|
+
}
|
|
545
|
+
var addCommandMap = {
|
|
546
|
+
pnpm: "pnpm add -D -E",
|
|
547
|
+
npm: "npm install -D -E",
|
|
548
|
+
yarn: "yarn add -D -E",
|
|
549
|
+
bun: "bun add -d -E"
|
|
550
|
+
};
|
|
551
|
+
var removeCommandMap = {
|
|
552
|
+
pnpm: "pnpm remove",
|
|
553
|
+
npm: "npm uninstall",
|
|
554
|
+
yarn: "yarn remove",
|
|
555
|
+
bun: "bun remove"
|
|
556
|
+
};
|
|
557
|
+
function detectPackageManager(dir, fsOps) {
|
|
558
|
+
return detectLockfile(dir, (fsOps ?? defaultWipeFs()).exists)?.manager ?? "pnpm";
|
|
559
|
+
}
|
|
560
|
+
function sortPackagesCaseInsensitive(packages) {
|
|
561
|
+
return [...packages].sort((left, right) => left.toLowerCase().localeCompare(right.toLowerCase()));
|
|
562
|
+
}
|
|
563
|
+
function formatMultilineCommand(header, packages, blockIndent, packageIndent) {
|
|
564
|
+
if (packages.length === 0) return "";
|
|
565
|
+
const sorted = sortPackagesCaseInsensitive(packages);
|
|
566
|
+
const lastIndex = sorted.length - 1;
|
|
567
|
+
const lines = [`${blockIndent}${header} \\`];
|
|
568
|
+
sorted.forEach((pkg, index) => {
|
|
569
|
+
const suffix = index === lastIndex ? "" : " \\";
|
|
570
|
+
lines.push(`${packageIndent}${pkg}${suffix}`);
|
|
571
|
+
});
|
|
572
|
+
return lines.join("\n");
|
|
573
|
+
}
|
|
574
|
+
function formatNextStepsAddCommand(manager, specs, blockIndent = " ", packageIndent = " ") {
|
|
575
|
+
return formatMultilineCommand(addCommandMap[manager], specs, blockIndent, packageIndent);
|
|
576
|
+
}
|
|
577
|
+
function formatNextStepsRemoveCommand(manager, names, blockIndent = " ", packageIndent = " ") {
|
|
578
|
+
return formatMultilineCommand(removeCommandMap[manager], names, blockIndent, packageIndent);
|
|
579
|
+
}
|
|
580
|
+
function manualRecoveryCommand(dir, detected) {
|
|
581
|
+
return `cd ${dir} && rm -rf node_modules ${detected ? (0, node_path.basename)(detected.lockfilePath) : "package-lock.json"} && ${detected?.manager ?? "npm"} install`;
|
|
582
|
+
}
|
|
583
|
+
function buildManualHint(specs, dir, detected) {
|
|
584
|
+
return `Manually add: ${specs.join(" ")}\nRecover with: ${manualRecoveryCommand(dir, detected)}`;
|
|
585
|
+
}
|
|
586
|
+
function isPeerDepFailure(reason) {
|
|
587
|
+
return reason.toLowerCase().includes("--legacy-peer-deps");
|
|
588
|
+
}
|
|
589
|
+
function firstNonEmptyLine(reason) {
|
|
590
|
+
return reason.split(/\r?\n/u).map((segment) => segment.trim()).find((segment) => segment.length > 0) ?? reason.trim();
|
|
591
|
+
}
|
|
592
|
+
function detectErrorCode(reason) {
|
|
593
|
+
return knownErrorCodes.find((code) => new RegExp(`\\b${code}\\b`, "u").test(reason)) ?? null;
|
|
594
|
+
}
|
|
595
|
+
function buildGenericSummary(reason) {
|
|
596
|
+
const code = detectErrorCode(reason);
|
|
597
|
+
const snippet = firstNonEmptyLine(reason);
|
|
598
|
+
if (code && !snippet.includes(code)) return `Dependency install failed (${code}): ${snippet}`;
|
|
599
|
+
return `Dependency install failed: ${snippet}`;
|
|
600
|
+
}
|
|
601
|
+
async function confirmPeerDepWipe(clack, autoConfirm) {
|
|
602
|
+
if (autoConfirm) return true;
|
|
603
|
+
const result = await clack.confirm({
|
|
604
|
+
message: "Install failed.\n\nThis usually means stale dependencies in node_modules conflict with the new ESLint plugin versions.\n\nWipe `node_modules` and the lockfile, then retry install?",
|
|
605
|
+
initialValue: false
|
|
606
|
+
});
|
|
607
|
+
return !clack.isCancel(result) && result === true;
|
|
608
|
+
}
|
|
609
|
+
async function confirmCleanReinstall(clack, autoConfirm, summary) {
|
|
610
|
+
if (autoConfirm) return true;
|
|
611
|
+
const message = `${summary}\n\nWould you like to try a clean reinstall? (removes \`node_modules\` and the lockfile, then re-runs install)`;
|
|
612
|
+
const result = await clack.confirm({
|
|
613
|
+
message,
|
|
614
|
+
initialValue: true
|
|
615
|
+
});
|
|
616
|
+
return !clack.isCancel(result) && result === true;
|
|
617
|
+
}
|
|
618
|
+
function noteWipeFailure(clack, label, path, reason) {
|
|
619
|
+
if (clack.note) clack.note(`Could not remove ${label} at ${path}: ${reason}`, "Warning");
|
|
620
|
+
}
|
|
621
|
+
function safeRemoveDir(clack, fs, path) {
|
|
622
|
+
if (!fs.exists(path)) return;
|
|
623
|
+
try {
|
|
624
|
+
fs.rmDir(path);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
noteWipeFailure(clack, "node_modules", path, error instanceof Error ? error.message : String(error));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function safeRemoveFile(clack, fs, path) {
|
|
630
|
+
if (!fs.exists(path)) return;
|
|
631
|
+
try {
|
|
632
|
+
fs.rmFile(path);
|
|
633
|
+
} catch (error) {
|
|
634
|
+
noteWipeFailure(clack, "lockfile", path, error instanceof Error ? error.message : String(error));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function performFullWipe(clack, context) {
|
|
638
|
+
safeRemoveDir(clack, context.fs, (0, node_path.join)(context.dir, "node_modules"));
|
|
639
|
+
if (context.detected) safeRemoveFile(clack, context.fs, context.detected.lockfilePath);
|
|
640
|
+
}
|
|
641
|
+
function noteError(clack, prefix, reason, context, specs) {
|
|
642
|
+
if (!clack.note) return;
|
|
643
|
+
const hint = buildManualHint(specs, context.dir, context.detected);
|
|
644
|
+
clack.note(`${prefix}${reason}\n${hint}`, "Error");
|
|
645
|
+
}
|
|
646
|
+
function noteGenericError(clack, summary, context, specs) {
|
|
647
|
+
if (!clack.note) return;
|
|
648
|
+
const hint = buildManualHint(specs, context.dir, context.detected);
|
|
649
|
+
clack.note(`${summary}\n${hint}`, "Error");
|
|
650
|
+
}
|
|
651
|
+
async function tryRetryInstall(clack, installer, deps, context) {
|
|
652
|
+
try {
|
|
653
|
+
await installer(deps);
|
|
654
|
+
} catch (retryError) {
|
|
655
|
+
noteError(clack, "Retry install failed after wipe: ", retryError instanceof Error ? retryError.message : String(retryError), context, deps);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async function tryGenericRetryInstall(clack, installer, deps, context) {
|
|
659
|
+
try {
|
|
660
|
+
await installer(deps);
|
|
661
|
+
} catch (retryError) {
|
|
662
|
+
noteGenericError(clack, buildGenericSummary(`Clean reinstall failed: ${retryError instanceof Error ? retryError.message : String(retryError)}`), context, deps);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function handlePeerDepFailure(clack, deps, autoConfirm, installer, reason, context) {
|
|
666
|
+
if (!await confirmPeerDepWipe(clack, autoConfirm)) {
|
|
667
|
+
noteError(clack, "Install failed: ", reason, context, deps);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
performFullWipe(clack, context);
|
|
671
|
+
await tryRetryInstall(clack, installer, deps, context);
|
|
672
|
+
}
|
|
673
|
+
async function handleGenericFailure(clack, deps, autoConfirm, installer, reason, context) {
|
|
674
|
+
const summary = buildGenericSummary(reason);
|
|
675
|
+
if (!await confirmCleanReinstall(clack, autoConfirm, summary)) {
|
|
676
|
+
if (clack.note) clack.note(`${summary}\nSkipping clean reinstall. Please resolve the issue manually and re-run.`, "Error");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
performFullWipe(clack, context);
|
|
680
|
+
await tryGenericRetryInstall(clack, installer, deps, context);
|
|
681
|
+
}
|
|
682
|
+
async function installWithRetry(opts) {
|
|
683
|
+
const installer = opts.installer ?? defaultInstaller;
|
|
684
|
+
const fs = opts.fsOps ?? defaultWipeFs();
|
|
685
|
+
try {
|
|
686
|
+
await installer(opts.deps);
|
|
687
|
+
} catch (error) {
|
|
688
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
689
|
+
const context = {
|
|
690
|
+
dir: opts.cwd,
|
|
691
|
+
fs,
|
|
692
|
+
detected: detectLockfile(opts.cwd, fs.exists)
|
|
693
|
+
};
|
|
694
|
+
if (isPeerDepFailure(reason)) {
|
|
695
|
+
await handlePeerDepFailure(opts.clack, opts.deps, opts.autoConfirm, installer, reason, context);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
await handleGenericFailure(opts.clack, opts.deps, opts.autoConfirm, installer, reason, context);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/cli/template.ts
|
|
703
|
+
var COMPOSITION_DEFAULT_SUFFIX_CHOICES = [
|
|
704
|
+
{
|
|
705
|
+
value: ".options.vue",
|
|
706
|
+
label: "*.options.vue"
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
value: ".classic.vue",
|
|
710
|
+
label: "*.classic.vue"
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
value: ".legacy.vue",
|
|
714
|
+
label: "*.legacy.vue"
|
|
715
|
+
}
|
|
716
|
+
];
|
|
717
|
+
var OPTIONS_DEFAULT_SUFFIX_CHOICES = [
|
|
718
|
+
{
|
|
719
|
+
value: ".composition.vue",
|
|
720
|
+
label: "*.composition.vue"
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
value: ".setup.vue",
|
|
724
|
+
label: "*.setup.vue"
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
value: ".modern.vue",
|
|
728
|
+
label: "*.modern.vue"
|
|
729
|
+
}
|
|
730
|
+
];
|
|
731
|
+
function getHybridSuffixChoices(defaultApi) {
|
|
732
|
+
return defaultApi === "composition" ? COMPOSITION_DEFAULT_SUFFIX_CHOICES : OPTIONS_DEFAULT_SUFFIX_CHOICES;
|
|
733
|
+
}
|
|
734
|
+
function getHybridDefaultSuffix(defaultApi) {
|
|
735
|
+
return getHybridSuffixChoices(defaultApi)[0].value;
|
|
736
|
+
}
|
|
737
|
+
var indent$1 = " ";
|
|
738
|
+
function hasJavascript(languages) {
|
|
739
|
+
return languages.includes("javascript");
|
|
740
|
+
}
|
|
741
|
+
function hasTypescript(languages) {
|
|
742
|
+
return languages.includes("typescript");
|
|
743
|
+
}
|
|
744
|
+
function isVueBrowserLike(answers) {
|
|
745
|
+
return answers.framework === "vue" && answers.projectType !== "backend";
|
|
746
|
+
}
|
|
747
|
+
function isVueOptionsApi(answers) {
|
|
748
|
+
return answers.vueApiStyle === "options";
|
|
749
|
+
}
|
|
750
|
+
function isVueHybridApi(answers) {
|
|
751
|
+
return answers.vueApiStyle === "hybrid";
|
|
752
|
+
}
|
|
753
|
+
function vueJsScope(major, api) {
|
|
754
|
+
if (major === 2) return api === "options" ? "vue2OptionsApi" : "vue2";
|
|
755
|
+
return api === "options" ? "vue3OptionsApi" : "vue3";
|
|
756
|
+
}
|
|
757
|
+
function vueTsScope(major, api) {
|
|
758
|
+
if (major === 2) return api === "options" ? "vue2TypescriptOptionsApi" : "vue2Typescript";
|
|
759
|
+
return api === "options" ? "vue3TypescriptOptionsApi" : "vue3Typescript";
|
|
760
|
+
}
|
|
761
|
+
function jsAppScopes(answers) {
|
|
762
|
+
if (isVueBrowserLike(answers) && !hasTypescript(answers.languages)) return [vueJsScope(answers.vueMajor ?? 3, isVueOptionsApi(answers) ? "options" : "composition")];
|
|
763
|
+
if (answers.projectType === "backend") return ["javascriptNode"];
|
|
764
|
+
if (answers.projectType === "hybrid") return ["javascriptNode", "javascriptDom"];
|
|
765
|
+
return ["javascriptDom"];
|
|
766
|
+
}
|
|
767
|
+
function tsAppScopes(answers) {
|
|
768
|
+
if (isVueBrowserLike(answers)) return [vueTsScope(answers.vueMajor ?? 3, isVueOptionsApi(answers) ? "options" : "composition")];
|
|
769
|
+
if (answers.projectType === "backend") return ["typescriptNode"];
|
|
770
|
+
if (answers.projectType === "hybrid") return ["typescriptNode", "typescriptDom"];
|
|
771
|
+
return ["typescriptDom"];
|
|
772
|
+
}
|
|
773
|
+
function jsAppFiles(answers) {
|
|
774
|
+
if (isVueBrowserLike(answers) && !hasTypescript(answers.languages)) return ["**/*.{js,jsx,cjs,mjs,vue}"];
|
|
775
|
+
return ["**/*.{js,jsx,cjs,mjs}"];
|
|
776
|
+
}
|
|
777
|
+
function normalizeHybridSuffix(raw) {
|
|
778
|
+
const trimmed = raw.trim();
|
|
779
|
+
const withDot = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
|
|
780
|
+
return withDot.endsWith(".vue") ? withDot : `${withDot}.vue`;
|
|
781
|
+
}
|
|
782
|
+
function tsAppFiles(answers, scoped) {
|
|
783
|
+
const vue = isVueBrowserLike(answers);
|
|
784
|
+
const prefix = scoped === "app" ? "src/**/*" : "**/*";
|
|
785
|
+
if (vue) return [`${prefix}.{ts,tsx,vue}`];
|
|
786
|
+
return [`${prefix}.{ts,tsx}`];
|
|
787
|
+
}
|
|
788
|
+
function testFilesGlob(languages) {
|
|
789
|
+
const ts = hasTypescript(languages);
|
|
790
|
+
const js = hasJavascript(languages);
|
|
791
|
+
if (ts && js) return ["**/*.{test,spec}.{ts,tsx,js,jsx}"];
|
|
792
|
+
if (ts) return ["**/*.{test,spec}.{ts,tsx}"];
|
|
793
|
+
return ["**/*.{test,spec}.{js,jsx}"];
|
|
794
|
+
}
|
|
795
|
+
function jqueryFilesGlob(answers) {
|
|
796
|
+
const vue = isVueBrowserLike(answers);
|
|
797
|
+
const ts = hasTypescript(answers.languages);
|
|
798
|
+
const js = hasJavascript(answers.languages);
|
|
799
|
+
if (vue) {
|
|
800
|
+
if (ts && js) return ["**/*.{ts,tsx,js,jsx,cjs,mjs,vue}"];
|
|
801
|
+
if (ts) return ["**/*.{ts,tsx,vue}"];
|
|
802
|
+
return ["**/*.{js,jsx,cjs,mjs,vue}"];
|
|
803
|
+
}
|
|
804
|
+
if (ts && js) return ["**/*.{ts,tsx,js,jsx,cjs,mjs}"];
|
|
805
|
+
if (ts) return ["**/*.{ts,tsx}"];
|
|
806
|
+
return ["**/*.{js,jsx,cjs,mjs}"];
|
|
807
|
+
}
|
|
808
|
+
function jsRootConfigFiles() {
|
|
809
|
+
return [
|
|
810
|
+
"*.js",
|
|
811
|
+
"*.cjs",
|
|
812
|
+
"*.mjs"
|
|
813
|
+
];
|
|
814
|
+
}
|
|
815
|
+
function tsRootConfigFiles() {
|
|
816
|
+
return [
|
|
817
|
+
"*.ts",
|
|
818
|
+
"*.cts",
|
|
819
|
+
"*.mts"
|
|
820
|
+
];
|
|
821
|
+
}
|
|
822
|
+
function findRole(plan, role) {
|
|
823
|
+
return plan.entries.find((entry) => entry.role === role);
|
|
824
|
+
}
|
|
825
|
+
function isHybridVueJsContext(answers) {
|
|
826
|
+
return isVueBrowserLike(answers) && isVueHybridApi(answers) && !hasTypescript(answers.languages) && Boolean(answers.vueHybridSuffix);
|
|
827
|
+
}
|
|
828
|
+
function isHybridVueTsContext(answers) {
|
|
829
|
+
return isVueBrowserLike(answers) && isVueHybridApi(answers) && hasTypescript(answers.languages) && Boolean(answers.vueHybridSuffix);
|
|
830
|
+
}
|
|
831
|
+
function buildHybridVueJsBlocks(answers) {
|
|
832
|
+
if (!isHybridVueJsContext(answers) || !answers.vueHybridSuffix) return null;
|
|
833
|
+
const major = answers.vueMajor ?? 3;
|
|
834
|
+
const { defaultApi, suffix: rawSuffix } = answers.vueHybridSuffix;
|
|
835
|
+
const targetGlob = `**/*${normalizeHybridSuffix(rawSuffix)}`;
|
|
836
|
+
const nonDefaultApi = defaultApi === "composition" ? "options" : "composition";
|
|
837
|
+
const defaultEntry = {
|
|
838
|
+
files: ["**/*.{js,jsx,cjs,mjs,vue}"],
|
|
839
|
+
ignores: [targetGlob]
|
|
840
|
+
};
|
|
841
|
+
return [{
|
|
842
|
+
scope: vueJsScope(major, defaultApi),
|
|
843
|
+
entry: defaultEntry
|
|
844
|
+
}, {
|
|
845
|
+
scope: vueJsScope(major, nonDefaultApi),
|
|
846
|
+
entry: { files: [targetGlob] }
|
|
847
|
+
}];
|
|
848
|
+
}
|
|
849
|
+
function makeHybridTsEntries(answers, suffix, appEntry) {
|
|
850
|
+
const targetGlob = `**/*${suffix}`;
|
|
851
|
+
const baseFiles = tsAppFiles(answers, answers.tsconfig?.referencesOnly === true && Boolean(appEntry) ? "app" : "all");
|
|
852
|
+
const tsconfigPaths = appEntry ? [appEntry.path] : void 0;
|
|
853
|
+
const defaultEntry = {
|
|
854
|
+
files: baseFiles,
|
|
855
|
+
ignores: [targetGlob]
|
|
856
|
+
};
|
|
857
|
+
const targetEntry = { files: [targetGlob] };
|
|
858
|
+
if (tsconfigPaths) {
|
|
859
|
+
defaultEntry.tsconfigPaths = tsconfigPaths;
|
|
860
|
+
targetEntry.tsconfigPaths = tsconfigPaths;
|
|
861
|
+
}
|
|
862
|
+
return {
|
|
863
|
+
defaultEntry,
|
|
864
|
+
targetEntry
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
function buildHybridVueTsBlocks(answers, appEntry) {
|
|
868
|
+
if (!isHybridVueTsContext(answers) || !answers.vueHybridSuffix) return null;
|
|
869
|
+
const major = answers.vueMajor ?? 3;
|
|
870
|
+
const { defaultApi, suffix: rawSuffix } = answers.vueHybridSuffix;
|
|
871
|
+
const suffix = normalizeHybridSuffix(rawSuffix);
|
|
872
|
+
const nonDefaultApi = defaultApi === "composition" ? "options" : "composition";
|
|
873
|
+
const { defaultEntry, targetEntry } = makeHybridTsEntries(answers, suffix, appEntry);
|
|
874
|
+
return [{
|
|
875
|
+
scope: vueTsScope(major, defaultApi),
|
|
876
|
+
entry: defaultEntry
|
|
877
|
+
}, {
|
|
878
|
+
scope: vueTsScope(major, nonDefaultApi),
|
|
879
|
+
entry: targetEntry
|
|
880
|
+
}];
|
|
881
|
+
}
|
|
882
|
+
function buildJsAppBlocks(answers) {
|
|
883
|
+
if (!hasJavascript(answers.languages)) return [];
|
|
884
|
+
const hybridBlocks = buildHybridVueJsBlocks(answers);
|
|
885
|
+
if (hybridBlocks) return hybridBlocks;
|
|
886
|
+
const files = jsAppFiles(answers);
|
|
887
|
+
return jsAppScopes(answers).map((scope) => ({
|
|
888
|
+
scope,
|
|
889
|
+
entry: { files }
|
|
890
|
+
}));
|
|
891
|
+
}
|
|
892
|
+
function buildTsAppBlocks(answers, appEntry) {
|
|
893
|
+
if (!hasTypescript(answers.languages)) return [];
|
|
894
|
+
const hybridBlocks = buildHybridVueTsBlocks(answers, appEntry);
|
|
895
|
+
if (hybridBlocks) return hybridBlocks;
|
|
896
|
+
const files = tsAppFiles(answers, answers.tsconfig?.referencesOnly === true && Boolean(appEntry) ? "app" : "all");
|
|
897
|
+
return tsAppScopes(answers).map((scope) => {
|
|
898
|
+
const entry = { files };
|
|
899
|
+
if (appEntry) entry.tsconfigPaths = [appEntry.path];
|
|
900
|
+
return {
|
|
901
|
+
scope,
|
|
902
|
+
entry
|
|
903
|
+
};
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
function tsConfigPathsForRoot(appEntry, configEntry) {
|
|
907
|
+
if (configEntry) return [configEntry.path];
|
|
908
|
+
if (appEntry) return [appEntry.path];
|
|
909
|
+
}
|
|
910
|
+
function configScopeFiles(answers) {
|
|
911
|
+
const files = [...jsRootConfigFiles()];
|
|
912
|
+
if (hasTypescript(answers.languages)) files.push(...tsRootConfigFiles());
|
|
913
|
+
return files;
|
|
914
|
+
}
|
|
915
|
+
function buildRootConfigBlocks(answers, appEntry, configEntry) {
|
|
916
|
+
const blocks = [{
|
|
917
|
+
scope: "config",
|
|
918
|
+
entry: { files: configScopeFiles(answers) }
|
|
919
|
+
}, {
|
|
920
|
+
scope: "javascriptNode",
|
|
921
|
+
entry: { files: jsRootConfigFiles() }
|
|
922
|
+
}];
|
|
923
|
+
if (hasTypescript(answers.languages)) {
|
|
924
|
+
const entry = { files: tsRootConfigFiles() };
|
|
925
|
+
const tsconfigPaths = tsConfigPathsForRoot(appEntry, configEntry);
|
|
926
|
+
if (tsconfigPaths) entry.tsconfigPaths = tsconfigPaths;
|
|
927
|
+
blocks.push({
|
|
928
|
+
scope: "typescriptNode",
|
|
929
|
+
entry
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
return blocks;
|
|
933
|
+
}
|
|
934
|
+
function buildTestBlock(answers, testEntry) {
|
|
935
|
+
if (answers.testFramework === "none") return null;
|
|
936
|
+
const entry = { files: testFilesGlob(answers.languages) };
|
|
937
|
+
if (hasTypescript(answers.languages) && testEntry) entry.tsconfigPaths = [testEntry.path];
|
|
938
|
+
return {
|
|
939
|
+
scope: answers.testFramework,
|
|
940
|
+
entry
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function buildJqueryBlock(answers) {
|
|
944
|
+
if (!answers.extras.jquery) return null;
|
|
945
|
+
return {
|
|
946
|
+
scope: "jquery",
|
|
947
|
+
entry: { files: jqueryFilesGlob(answers) }
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function buildScopedConfigs(answers) {
|
|
951
|
+
const plan = answers.tsconfig;
|
|
952
|
+
const referencesOnly = plan?.referencesOnly === true;
|
|
953
|
+
const appEntry = plan ? findRole(plan, "app") : void 0;
|
|
954
|
+
const configEntry = referencesOnly ? findRole(plan, "config") : void 0;
|
|
955
|
+
const tail = [buildTestBlock(answers, referencesOnly ? findRole(plan, "test") : appEntry), buildJqueryBlock(answers)];
|
|
956
|
+
return [
|
|
957
|
+
...buildRootConfigBlocks(answers, appEntry, configEntry),
|
|
958
|
+
...buildJsAppBlocks(answers),
|
|
959
|
+
...buildTsAppBlocks(answers, appEntry),
|
|
960
|
+
...tail.filter((block) => block !== null)
|
|
961
|
+
];
|
|
962
|
+
}
|
|
963
|
+
function mergeByScope(blocks) {
|
|
964
|
+
return blocks.reduce((merged, { scope, entry }) => {
|
|
965
|
+
const existing = merged.get(scope);
|
|
966
|
+
if (existing) existing.push(entry);
|
|
967
|
+
else merged.set(scope, [entry]);
|
|
968
|
+
return merged;
|
|
969
|
+
}, /* @__PURE__ */ new Map());
|
|
970
|
+
}
|
|
971
|
+
function quote$1(value) {
|
|
972
|
+
return `'${value}'`;
|
|
973
|
+
}
|
|
974
|
+
function formatStringArray(values) {
|
|
975
|
+
return `[${values.map(quote$1).join(", ")}]`;
|
|
976
|
+
}
|
|
977
|
+
function formatEntry$1(entry, baseIndent) {
|
|
978
|
+
const filesLine = `files: ${formatStringArray(entry.files)}`;
|
|
979
|
+
const ignoresLine = entry.ignores ? `ignores: ${formatStringArray(entry.ignores)}` : null;
|
|
980
|
+
const tsconfigLine = entry.tsconfigPaths ? `tsconfigPaths: ${formatStringArray(entry.tsconfigPaths)}` : null;
|
|
981
|
+
if (!ignoresLine && !tsconfigLine) return `${baseIndent}{ ${filesLine} },`;
|
|
982
|
+
const childIndent = `${baseIndent}${indent$1}`;
|
|
983
|
+
const lines = [`${baseIndent}{`, `${childIndent}${filesLine},`];
|
|
984
|
+
if (ignoresLine) lines.push(`${childIndent}${ignoresLine},`);
|
|
985
|
+
if (tsconfigLine) lines.push(`${childIndent}${tsconfigLine},`);
|
|
986
|
+
lines.push(`${baseIndent}},`);
|
|
987
|
+
return lines.join("\n");
|
|
988
|
+
}
|
|
989
|
+
function formatScope$1(scope, entries, baseIndent) {
|
|
990
|
+
const childIndent = `${baseIndent}${indent$1}`;
|
|
991
|
+
const entryLines = entries.map((entry) => formatEntry$1(entry, childIndent));
|
|
992
|
+
return [
|
|
993
|
+
`${baseIndent}${scope}: [`,
|
|
994
|
+
...entryLines,
|
|
995
|
+
`${baseIndent}],`
|
|
996
|
+
].join("\n");
|
|
997
|
+
}
|
|
998
|
+
function renderUseInsiderBody(merged) {
|
|
999
|
+
return [...merged.entries()].map(([scope, entries]) => formatScope$1(scope, entries, indent$1)).join("\n");
|
|
1000
|
+
}
|
|
1001
|
+
function renderConfigFile(answers) {
|
|
1002
|
+
return [
|
|
1003
|
+
"import { useInsider } from '@useinsider/eslint-config';",
|
|
1004
|
+
"",
|
|
1005
|
+
"export default useInsider({",
|
|
1006
|
+
renderUseInsiderBody(mergeByScope(buildScopedConfigs(answers))),
|
|
1007
|
+
"});",
|
|
1008
|
+
""
|
|
1009
|
+
].join("\n");
|
|
1010
|
+
}
|
|
1011
|
+
//#endregion
|
|
1012
|
+
//#region src/cli/questions.ts
|
|
1013
|
+
var CliCancelledError = class extends Error {
|
|
1014
|
+
constructor(message) {
|
|
1015
|
+
super(message);
|
|
1016
|
+
this.name = "CliCancelledError";
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
async function loadClack() {
|
|
1020
|
+
return await import("@clack/prompts");
|
|
1021
|
+
}
|
|
1022
|
+
function bail(clack, message) {
|
|
1023
|
+
clack.cancel(message);
|
|
1024
|
+
throw new CliCancelledError(message);
|
|
1025
|
+
}
|
|
1026
|
+
function ensure(clack, value) {
|
|
1027
|
+
if (clack.isCancel(value)) bail(clack, "Cancelled.");
|
|
1028
|
+
return value;
|
|
1029
|
+
}
|
|
1030
|
+
async function askProjectType(clack, initial = "backend") {
|
|
1031
|
+
return ensure(clack, await clack.select({
|
|
1032
|
+
message: "What kind of project are you setting up?",
|
|
1033
|
+
options: [
|
|
1034
|
+
{
|
|
1035
|
+
value: "backend",
|
|
1036
|
+
label: "Backend / Node.js"
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
value: "browser",
|
|
1040
|
+
label: "Browser / frontend"
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
value: "hybrid",
|
|
1044
|
+
label: "Hybrid",
|
|
1045
|
+
hint: "Node + browser (isomorphic, SSR, full-stack)"
|
|
1046
|
+
}
|
|
1047
|
+
],
|
|
1048
|
+
initialValue: initial
|
|
1049
|
+
}));
|
|
1050
|
+
}
|
|
1051
|
+
async function askLanguages(clack, initial = ["typescript"]) {
|
|
1052
|
+
const picked = ensure(clack, await clack.multiselect({
|
|
1053
|
+
message: "Which languages will the project use? (press space to toggle, enter to confirm)",
|
|
1054
|
+
options: [{
|
|
1055
|
+
value: "javascript",
|
|
1056
|
+
label: "JavaScript"
|
|
1057
|
+
}, {
|
|
1058
|
+
value: "typescript",
|
|
1059
|
+
label: "TypeScript"
|
|
1060
|
+
}],
|
|
1061
|
+
required: true,
|
|
1062
|
+
initialValues: initial
|
|
1063
|
+
}));
|
|
1064
|
+
if (picked.length === 0) bail(clack, "At least one language must be selected.");
|
|
1065
|
+
return picked;
|
|
1066
|
+
}
|
|
1067
|
+
async function askFramework(clack, initial = "vanilla") {
|
|
1068
|
+
return ensure(clack, await clack.select({
|
|
1069
|
+
message: "Which frontend framework?",
|
|
1070
|
+
options: [{
|
|
1071
|
+
value: "vanilla",
|
|
1072
|
+
label: "None / Vanilla"
|
|
1073
|
+
}, {
|
|
1074
|
+
value: "vue",
|
|
1075
|
+
label: "Vue"
|
|
1076
|
+
}],
|
|
1077
|
+
initialValue: initial
|
|
1078
|
+
}));
|
|
1079
|
+
}
|
|
1080
|
+
async function askVueMajor(clack, initial = 3) {
|
|
1081
|
+
return ensure(clack, await clack.select({
|
|
1082
|
+
message: "Which Vue major version?",
|
|
1083
|
+
options: [{
|
|
1084
|
+
value: 3,
|
|
1085
|
+
label: "Vue 3"
|
|
1086
|
+
}, {
|
|
1087
|
+
value: 2,
|
|
1088
|
+
label: "Vue 2"
|
|
1089
|
+
}],
|
|
1090
|
+
initialValue: initial
|
|
1091
|
+
}));
|
|
1092
|
+
}
|
|
1093
|
+
async function askVueApiStyle(clack, initial = "composition") {
|
|
1094
|
+
return ensure(clack, await clack.select({
|
|
1095
|
+
message: "What API style does your Vue codebase use?",
|
|
1096
|
+
options: [
|
|
1097
|
+
{
|
|
1098
|
+
value: "composition",
|
|
1099
|
+
label: "Composition API (setup)"
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
value: "options",
|
|
1103
|
+
label: "Options API"
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
value: "hybrid",
|
|
1107
|
+
label: "Hybrid (both)"
|
|
1108
|
+
}
|
|
1109
|
+
],
|
|
1110
|
+
initialValue: initial
|
|
1111
|
+
}));
|
|
1112
|
+
}
|
|
1113
|
+
async function askHybridProceedWithSuffix(clack, initial = true) {
|
|
1114
|
+
return ensure(clack, await clack.confirm({
|
|
1115
|
+
message: "Want me to scaffold both Options + Composition blocks using a filename suffix convention?",
|
|
1116
|
+
initialValue: initial
|
|
1117
|
+
}));
|
|
1118
|
+
}
|
|
1119
|
+
async function askVueHybridDefaultApi(clack, initial = "composition") {
|
|
1120
|
+
return ensure(clack, await clack.select({
|
|
1121
|
+
message: "Which API style is the default in your codebase?",
|
|
1122
|
+
options: [{
|
|
1123
|
+
value: "composition",
|
|
1124
|
+
label: "Composition API (setup)"
|
|
1125
|
+
}, {
|
|
1126
|
+
value: "options",
|
|
1127
|
+
label: "Options API"
|
|
1128
|
+
}],
|
|
1129
|
+
initialValue: initial
|
|
1130
|
+
}));
|
|
1131
|
+
}
|
|
1132
|
+
async function askHybridSuffix(clack, defaultApi, initial) {
|
|
1133
|
+
const choices = getHybridSuffixChoices(defaultApi);
|
|
1134
|
+
const message = defaultApi === "composition" ? "Pick a suffix for Options-API components:" : "Pick a suffix for Composition-API components:";
|
|
1135
|
+
const fallback = getHybridDefaultSuffix(defaultApi);
|
|
1136
|
+
const initialValue = initial && choices.some((choice) => choice.value === initial) ? initial : fallback;
|
|
1137
|
+
return ensure(clack, await clack.select({
|
|
1138
|
+
message,
|
|
1139
|
+
options: choices.map((choice) => ({
|
|
1140
|
+
value: choice.value,
|
|
1141
|
+
label: choice.label
|
|
1142
|
+
})),
|
|
1143
|
+
initialValue
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
async function askTestFramework(clack, initial = "vitest") {
|
|
1147
|
+
return ensure(clack, await clack.select({
|
|
1148
|
+
message: "Which testing framework?",
|
|
1149
|
+
options: [
|
|
1150
|
+
{
|
|
1151
|
+
value: "vitest",
|
|
1152
|
+
label: "Vitest"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
value: "jest",
|
|
1156
|
+
label: "Jest"
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
value: "none",
|
|
1160
|
+
label: "None"
|
|
1161
|
+
}
|
|
1162
|
+
],
|
|
1163
|
+
initialValue: initial
|
|
1164
|
+
}));
|
|
1165
|
+
}
|
|
1166
|
+
async function askExtras(clack, initial = { jquery: false }) {
|
|
1167
|
+
const initialValues = initial.jquery ? ["jquery"] : [];
|
|
1168
|
+
return { jquery: ensure(clack, await clack.multiselect({
|
|
1169
|
+
message: "Which extra libraries do you use?",
|
|
1170
|
+
options: [{
|
|
1171
|
+
value: "jquery",
|
|
1172
|
+
label: "jQuery (adds globals)"
|
|
1173
|
+
}],
|
|
1174
|
+
required: false,
|
|
1175
|
+
initialValues
|
|
1176
|
+
})).includes("jquery") };
|
|
1177
|
+
}
|
|
1178
|
+
async function askTsconfigRole(clack, refPath, initial = "app") {
|
|
1179
|
+
return ensure(clack, await clack.select({
|
|
1180
|
+
message: `Map ${refPath} to a role:`,
|
|
1181
|
+
options: [
|
|
1182
|
+
{
|
|
1183
|
+
value: "app",
|
|
1184
|
+
label: "App / source code"
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
value: "test",
|
|
1188
|
+
label: "Test code"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
value: "config",
|
|
1192
|
+
label: "Config / root files"
|
|
1193
|
+
}
|
|
1194
|
+
],
|
|
1195
|
+
initialValue: initial
|
|
1196
|
+
}));
|
|
1197
|
+
}
|
|
1198
|
+
async function collectRoleEntries(clack, refs, previous) {
|
|
1199
|
+
const previousByPath = new Map(previous?.map((entry) => [entry.path, entry.role]) ?? []);
|
|
1200
|
+
return refs.reduce(async (accumulator, ref) => {
|
|
1201
|
+
const entries = await accumulator;
|
|
1202
|
+
const role = await askTsconfigRole(clack, ref, previousByPath.get(ref));
|
|
1203
|
+
return [...entries, {
|
|
1204
|
+
path: ref,
|
|
1205
|
+
role
|
|
1206
|
+
}];
|
|
1207
|
+
}, Promise.resolve([]));
|
|
1208
|
+
}
|
|
1209
|
+
async function askTsconfigPlan(clack, input, previous) {
|
|
1210
|
+
if (!input.referencesOnly) return {
|
|
1211
|
+
referencesOnly: false,
|
|
1212
|
+
entries: [{
|
|
1213
|
+
path: "./tsconfig.json",
|
|
1214
|
+
role: "app"
|
|
1215
|
+
}]
|
|
1216
|
+
};
|
|
1217
|
+
return {
|
|
1218
|
+
referencesOnly: true,
|
|
1219
|
+
entries: await collectRoleEntries(clack, input.refs, previous?.entries)
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
async function askExistingConfigDecision(clack, configPath, initial = "migrate") {
|
|
1223
|
+
return ensure(clack, await clack.select({
|
|
1224
|
+
message: `Found existing config at ${configPath}. How should we proceed?`,
|
|
1225
|
+
options: [
|
|
1226
|
+
{
|
|
1227
|
+
value: "migrate",
|
|
1228
|
+
label: "Migrate",
|
|
1229
|
+
hint: "preserve current base config, port them to the v2 shape"
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
value: "override",
|
|
1233
|
+
label: "Override",
|
|
1234
|
+
hint: "discard the existing config and scaffold a new one"
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
value: "exit",
|
|
1238
|
+
label: "Exit",
|
|
1239
|
+
hint: "leave the project untouched"
|
|
1240
|
+
}
|
|
1241
|
+
],
|
|
1242
|
+
initialValue: initial
|
|
1243
|
+
}));
|
|
1244
|
+
}
|
|
1245
|
+
async function askBrowserBranch(clack, previous) {
|
|
1246
|
+
const framework = await askFramework(clack, previous?.framework);
|
|
1247
|
+
if (framework !== "vue") return {
|
|
1248
|
+
framework,
|
|
1249
|
+
vueMajor: void 0,
|
|
1250
|
+
vueApiStyle: void 0
|
|
1251
|
+
};
|
|
1252
|
+
return {
|
|
1253
|
+
framework,
|
|
1254
|
+
vueMajor: await askVueMajor(clack, previous?.vueMajor),
|
|
1255
|
+
vueApiStyle: await askVueApiStyle(clack, previous?.vueApiStyle)
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
function noteResumed(clack, previous) {
|
|
1259
|
+
if (!previous || !clack.note) return;
|
|
1260
|
+
clack.note("Restored your previous choices. Press Enter on each prompt to confirm, or arrow keys to change.", "Resuming previous session");
|
|
1261
|
+
}
|
|
1262
|
+
function guardTsconfig(clack, wantsTypescript, context) {
|
|
1263
|
+
if (wantsTypescript && context.tsconfigMissing) bail(clack, "No tsconfig.json found in current directory. Create one and re-run.");
|
|
1264
|
+
}
|
|
1265
|
+
async function resolveBrowserBranch(clack, projectType, previous) {
|
|
1266
|
+
if (projectType !== "browser" && projectType !== "hybrid") return {
|
|
1267
|
+
framework: void 0,
|
|
1268
|
+
vueMajor: void 0,
|
|
1269
|
+
vueApiStyle: void 0
|
|
1270
|
+
};
|
|
1271
|
+
return askBrowserBranch(clack, previous);
|
|
1272
|
+
}
|
|
1273
|
+
async function resolveTsconfigPlan(clack, wantsTypescript, context, previous) {
|
|
1274
|
+
if (!wantsTypescript || !context.tsconfigInput) return;
|
|
1275
|
+
return askTsconfigPlan(clack, context.tsconfigInput, previous?.tsconfig);
|
|
1276
|
+
}
|
|
1277
|
+
async function runQuestions(context, clackOverride) {
|
|
1278
|
+
const clack = clackOverride ?? await loadClack();
|
|
1279
|
+
clack.intro("useinsider eslint-config setup");
|
|
1280
|
+
noteResumed(clack, context.previousAnswers);
|
|
1281
|
+
const previous = context.previousAnswers;
|
|
1282
|
+
const projectType = await askProjectType(clack, previous?.projectType);
|
|
1283
|
+
const languages = await askLanguages(clack, previous?.languages);
|
|
1284
|
+
const wantsTypescript = languages.includes("typescript");
|
|
1285
|
+
guardTsconfig(clack, wantsTypescript, context);
|
|
1286
|
+
const browserBranch = await resolveBrowserBranch(clack, projectType, previous);
|
|
1287
|
+
const testFramework = await askTestFramework(clack, previous?.testFramework);
|
|
1288
|
+
const extras = await askExtras(clack, previous?.extras);
|
|
1289
|
+
const tsconfig = await resolveTsconfigPlan(clack, wantsTypescript, context, previous);
|
|
1290
|
+
return {
|
|
1291
|
+
projectType,
|
|
1292
|
+
languages,
|
|
1293
|
+
framework: browserBranch.framework,
|
|
1294
|
+
vueMajor: browserBranch.vueMajor,
|
|
1295
|
+
vueApiStyle: browserBranch.vueApiStyle,
|
|
1296
|
+
testFramework,
|
|
1297
|
+
extras,
|
|
1298
|
+
tsconfig
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
//#endregion
|
|
1302
|
+
//#region src/cli/sessionStore.ts
|
|
1303
|
+
var SCHEMA_VERSION = 4;
|
|
1304
|
+
var DEFAULT_TTL_MS = 3600 * 1e3;
|
|
1305
|
+
var FILE_PREFIX = "useinsider-eslint-config-session-";
|
|
1306
|
+
function hashCwd(cwd) {
|
|
1307
|
+
return (0, node_crypto.createHash)("sha1").update(cwd).digest("hex").slice(0, 16);
|
|
1308
|
+
}
|
|
1309
|
+
function getSessionFilePath(cwd, tmpDirOverride) {
|
|
1310
|
+
return (0, node_path.join)(tmpDirOverride ?? (0, node_os.tmpdir)(), `${FILE_PREFIX}${hashCwd(cwd)}.json`);
|
|
1311
|
+
}
|
|
1312
|
+
function safeUnlink$1(path) {
|
|
1313
|
+
try {
|
|
1314
|
+
(0, node_fs.unlinkSync)(path);
|
|
1315
|
+
} catch {}
|
|
1316
|
+
}
|
|
1317
|
+
function ensureDir(path) {
|
|
1318
|
+
if (!(0, node_fs.existsSync)(path)) (0, node_fs.mkdirSync)(path, { recursive: true });
|
|
1319
|
+
}
|
|
1320
|
+
function isSessionShape(value) {
|
|
1321
|
+
if (!value || typeof value !== "object") return false;
|
|
1322
|
+
const candidate = value;
|
|
1323
|
+
return candidate.v === SCHEMA_VERSION && typeof candidate.savedAt === "number" && typeof candidate.cwd === "string" && (candidate.command === "scaffold" || candidate.command === "migrate");
|
|
1324
|
+
}
|
|
1325
|
+
function saveSession(session, options) {
|
|
1326
|
+
const now = options.now ?? Date.now;
|
|
1327
|
+
const dir = options.tmpDir ?? (0, node_os.tmpdir)();
|
|
1328
|
+
ensureDir(dir);
|
|
1329
|
+
const path = getSessionFilePath(options.cwd, dir);
|
|
1330
|
+
const payload = {
|
|
1331
|
+
v: SCHEMA_VERSION,
|
|
1332
|
+
savedAt: now(),
|
|
1333
|
+
cwd: options.cwd,
|
|
1334
|
+
...session
|
|
1335
|
+
};
|
|
1336
|
+
(0, node_fs.writeFileSync)(path, JSON.stringify(payload), {
|
|
1337
|
+
encoding: "utf8",
|
|
1338
|
+
mode: 384
|
|
1339
|
+
});
|
|
1340
|
+
try {
|
|
1341
|
+
(0, node_fs.chmodSync)(path, 384);
|
|
1342
|
+
} catch {}
|
|
1343
|
+
return path;
|
|
1344
|
+
}
|
|
1345
|
+
function loadSession(options) {
|
|
1346
|
+
const now = options.now ?? Date.now;
|
|
1347
|
+
const ttl = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
1348
|
+
const path = getSessionFilePath(options.cwd, options.tmpDir);
|
|
1349
|
+
if (!(0, node_fs.existsSync)(path)) return null;
|
|
1350
|
+
let parsed;
|
|
1351
|
+
try {
|
|
1352
|
+
const raw = (0, node_fs.readFileSync)(path, "utf8");
|
|
1353
|
+
parsed = JSON.parse(raw);
|
|
1354
|
+
} catch {
|
|
1355
|
+
safeUnlink$1(path);
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
if (!isSessionShape(parsed)) {
|
|
1359
|
+
safeUnlink$1(path);
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
if (parsed.cwd !== options.cwd) {
|
|
1363
|
+
safeUnlink$1(path);
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
if (now() - parsed.savedAt > ttl) {
|
|
1367
|
+
safeUnlink$1(path);
|
|
1368
|
+
return null;
|
|
1369
|
+
}
|
|
1370
|
+
return parsed;
|
|
1371
|
+
}
|
|
1372
|
+
function clearSession(options) {
|
|
1373
|
+
safeUnlink$1(getSessionFilePath(options.cwd, options.tmpDir));
|
|
1374
|
+
}
|
|
1375
|
+
/* v8 ignore stop */
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region src/configs/config/dependencies.ts
|
|
1378
|
+
var dependencies$7 = {};
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region src/configs/javascript/dependencies.ts
|
|
1381
|
+
var dependencies$6 = {};
|
|
1382
|
+
//#endregion
|
|
1383
|
+
//#region src/configs/javascript-dom/dependencies.ts
|
|
1384
|
+
var dependencies$5 = {};
|
|
1385
|
+
//#endregion
|
|
1386
|
+
//#region src/configs/javascript-node/dependencies.ts
|
|
1387
|
+
var dependencies$4 = {};
|
|
1388
|
+
//#endregion
|
|
1389
|
+
//#region src/configs/jsdoc-based-ts/dependencies.ts
|
|
1390
|
+
var dependencies$3 = {
|
|
1391
|
+
typescript: false,
|
|
1392
|
+
"typescript-eslint": false,
|
|
1393
|
+
"@typescript-eslint/parser": false,
|
|
1394
|
+
"@typescript-eslint/eslint-plugin": false
|
|
1395
|
+
};
|
|
1396
|
+
//#endregion
|
|
1397
|
+
//#region src/configs/strict/dependencies.ts
|
|
1398
|
+
var dependencies$2 = { "eslint-plugin-sonarjs": false };
|
|
1399
|
+
//#endregion
|
|
1400
|
+
//#region src/configs/typescript-dom/dependencies.ts
|
|
1401
|
+
var dependencies$1 = {
|
|
1402
|
+
typescript: false,
|
|
1403
|
+
"typescript-eslint": false,
|
|
1404
|
+
"@typescript-eslint/parser": false,
|
|
1405
|
+
"@typescript-eslint/eslint-plugin": false
|
|
1406
|
+
};
|
|
1407
|
+
//#endregion
|
|
1408
|
+
//#region src/configs/typescript-node/dependencies.ts
|
|
1409
|
+
var dependencies = {
|
|
1410
|
+
typescript: false,
|
|
1411
|
+
"typescript-eslint": false,
|
|
1412
|
+
"@typescript-eslint/parser": false,
|
|
1413
|
+
"@typescript-eslint/eslint-plugin": false
|
|
1414
|
+
};
|
|
1415
|
+
//#endregion
|
|
1416
|
+
//#region src/cli/migrate/dependencies.ts
|
|
1417
|
+
var EXACT_PATTERNS = new Set([
|
|
1418
|
+
"eslint",
|
|
1419
|
+
"@useinsider/eslint-config",
|
|
1420
|
+
"globals",
|
|
1421
|
+
"vue-eslint-parser",
|
|
1422
|
+
"typescript-eslint"
|
|
1423
|
+
]);
|
|
1424
|
+
var PREFIX_PATTERNS = [
|
|
1425
|
+
"eslint-plugin-",
|
|
1426
|
+
"eslint-config-",
|
|
1427
|
+
"eslint-import-resolver-",
|
|
1428
|
+
"eslint-formatter-",
|
|
1429
|
+
"eslint-"
|
|
1430
|
+
];
|
|
1431
|
+
var SCOPED_ESLINT_PATTERNS = [/^@[^/]+\/eslint-/u, /^@typescript-eslint\//u];
|
|
1432
|
+
var SAFELIST = new Set(["typescript", "@types/eslint"]);
|
|
1433
|
+
var SCOPE_TO_DEPS = {
|
|
1434
|
+
javascript: dependencies$6,
|
|
1435
|
+
javascriptDom: dependencies$5,
|
|
1436
|
+
javascriptNode: dependencies$4,
|
|
1437
|
+
typescript: dependencies$13,
|
|
1438
|
+
typescriptDom: dependencies$1,
|
|
1439
|
+
typescriptNode: dependencies,
|
|
1440
|
+
vue2: dependencies$11,
|
|
1441
|
+
vue3: dependencies$9,
|
|
1442
|
+
vue2Typescript: dependencies$10,
|
|
1443
|
+
vue3Typescript: dependencies$8,
|
|
1444
|
+
jest: dependencies$14,
|
|
1445
|
+
vitest: dependencies$12,
|
|
1446
|
+
jsdocBasedTs: dependencies$3
|
|
1447
|
+
};
|
|
1448
|
+
var ALL_PRESET_DEPS = [
|
|
1449
|
+
dependencies$7,
|
|
1450
|
+
dependencies$2,
|
|
1451
|
+
...Object.values(SCOPE_TO_DEPS)
|
|
1452
|
+
];
|
|
1453
|
+
var PRESET_DEPENDENCY_KEYS = new Set(ALL_PRESET_DEPS.flatMap((record) => Object.keys(record)));
|
|
1454
|
+
function isEslintRelatedDependency(name) {
|
|
1455
|
+
if (SAFELIST.has(name)) return false;
|
|
1456
|
+
if (EXACT_PATTERNS.has(name)) return true;
|
|
1457
|
+
if (PRESET_DEPENDENCY_KEYS.has(name)) return true;
|
|
1458
|
+
if (SCOPED_ESLINT_PATTERNS.some((pattern) => pattern.test(name))) return true;
|
|
1459
|
+
return PREFIX_PATTERNS.some((prefix) => name.startsWith(prefix));
|
|
1460
|
+
}
|
|
1461
|
+
function sortRecord(record) {
|
|
1462
|
+
return Object.keys(record).sort((left, right) => left.localeCompare(right)).reduce((accumulator, key) => {
|
|
1463
|
+
accumulator[key] = record[key];
|
|
1464
|
+
return accumulator;
|
|
1465
|
+
}, {});
|
|
1466
|
+
}
|
|
1467
|
+
function pruneSection(record, removed) {
|
|
1468
|
+
if (!record) return record;
|
|
1469
|
+
const kept = {};
|
|
1470
|
+
Object.entries(record).forEach(([name, version]) => {
|
|
1471
|
+
if (isEslintRelatedDependency(name)) {
|
|
1472
|
+
if (!removed.includes(name)) removed.push(name);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
kept[name] = version;
|
|
1476
|
+
});
|
|
1477
|
+
return sortRecord(kept);
|
|
1478
|
+
}
|
|
1479
|
+
function parseOverrideKey(key) {
|
|
1480
|
+
if (key.length === 0) return { baseName: key };
|
|
1481
|
+
const searchFrom = key.startsWith("@") ? 1 : 0;
|
|
1482
|
+
const atIndex = key.indexOf("@", searchFrom);
|
|
1483
|
+
if (atIndex === -1) return { baseName: key };
|
|
1484
|
+
return { baseName: key.slice(0, atIndex) };
|
|
1485
|
+
}
|
|
1486
|
+
function sortOverrides(record) {
|
|
1487
|
+
return Object.keys(record).sort((left, right) => left.localeCompare(right)).reduce((accumulator, key) => {
|
|
1488
|
+
accumulator[key] = record[key];
|
|
1489
|
+
return accumulator;
|
|
1490
|
+
}, {});
|
|
1491
|
+
}
|
|
1492
|
+
function pruneOverridesSection(record, removed) {
|
|
1493
|
+
if (!record) return record;
|
|
1494
|
+
const kept = {};
|
|
1495
|
+
Object.entries(record).forEach(([key, value]) => {
|
|
1496
|
+
if (!(typeof value === "string")) {
|
|
1497
|
+
kept[key] = value;
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const { baseName } = parseOverrideKey(key);
|
|
1501
|
+
if (isEslintRelatedDependency(baseName)) {
|
|
1502
|
+
if (!removed.includes(baseName)) removed.push(baseName);
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
kept[key] = value;
|
|
1506
|
+
});
|
|
1507
|
+
return sortOverrides(kept);
|
|
1508
|
+
}
|
|
1509
|
+
function pruneEslintDependencies(manifest) {
|
|
1510
|
+
const removed = [];
|
|
1511
|
+
const next = { ...manifest };
|
|
1512
|
+
if (manifest.dependencies) next.dependencies = pruneSection(manifest.dependencies, removed);
|
|
1513
|
+
if (manifest.devDependencies) next.devDependencies = pruneSection(manifest.devDependencies, removed);
|
|
1514
|
+
if (manifest.peerDependencies) next.peerDependencies = pruneSection(manifest.peerDependencies, removed);
|
|
1515
|
+
if (manifest.optionalDependencies) next.optionalDependencies = pruneSection(manifest.optionalDependencies, removed);
|
|
1516
|
+
if (manifest.overrides) next.overrides = pruneOverridesSection(manifest.overrides, removed);
|
|
1517
|
+
if (manifest.pnpm && typeof manifest.pnpm === "object") {
|
|
1518
|
+
const nextPnpm = { ...manifest.pnpm };
|
|
1519
|
+
if (manifest.pnpm.overrides) nextPnpm.overrides = pruneOverridesSection(manifest.pnpm.overrides, removed);
|
|
1520
|
+
next.pnpm = nextPnpm;
|
|
1521
|
+
}
|
|
1522
|
+
if (manifest.resolutions) next.resolutions = pruneSection(manifest.resolutions, removed);
|
|
1523
|
+
return {
|
|
1524
|
+
manifest: next,
|
|
1525
|
+
removed
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function lookupVersion(manifest, name) {
|
|
1529
|
+
return manifest.dependencies?.[name] ?? manifest.devDependencies?.[name] ?? manifest.peerDependencies?.[name] ?? manifest.optionalDependencies?.[name];
|
|
1530
|
+
}
|
|
1531
|
+
function stripRange(version) {
|
|
1532
|
+
return version.replace(/^(?:\^|~|>=|<=|>|<|=)/u, "");
|
|
1533
|
+
}
|
|
1534
|
+
function appendSpec(specs, spec) {
|
|
1535
|
+
if (!specs.includes(spec)) specs.push(spec);
|
|
1536
|
+
}
|
|
1537
|
+
function resolveSpec(name, manifest, fallbacks) {
|
|
1538
|
+
const version = lookupVersion(manifest, name);
|
|
1539
|
+
if (!version) {
|
|
1540
|
+
if (!fallbacks.includes(name)) fallbacks.push(name);
|
|
1541
|
+
return `${name}@latest`;
|
|
1542
|
+
}
|
|
1543
|
+
return `${name}@${stripRange(version)}`;
|
|
1544
|
+
}
|
|
1545
|
+
function buildInstallList(scopes, manifest) {
|
|
1546
|
+
const specs = [];
|
|
1547
|
+
const fallbacks = [];
|
|
1548
|
+
appendSpec(specs, "@useinsider/eslint-config@latest");
|
|
1549
|
+
appendSpec(specs, resolveSpec("eslint", manifest, fallbacks));
|
|
1550
|
+
[...scopes].forEach((scope) => {
|
|
1551
|
+
const record = SCOPE_TO_DEPS[scope];
|
|
1552
|
+
if (!record) return;
|
|
1553
|
+
Object.entries(record).forEach(([packageName, shouldInstall]) => {
|
|
1554
|
+
if (shouldInstall) appendSpec(specs, resolveSpec(packageName, manifest, fallbacks));
|
|
1555
|
+
});
|
|
1556
|
+
});
|
|
1557
|
+
return {
|
|
1558
|
+
specs,
|
|
1559
|
+
fallbacks
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
var readOwnEslintConfigManifest = readOwnEslintConfigManifest$1;
|
|
1563
|
+
//#endregion
|
|
1564
|
+
//#region src/cli/prunePackageJson.ts
|
|
1565
|
+
function detectIndent(raw) {
|
|
1566
|
+
const match = /\n([ \t]+)"/u.exec(raw);
|
|
1567
|
+
if (!match) return " ";
|
|
1568
|
+
return match[1];
|
|
1569
|
+
}
|
|
1570
|
+
function serializeManifest(manifest, indent) {
|
|
1571
|
+
return `${JSON.stringify(manifest, null, indent)}\n`;
|
|
1572
|
+
}
|
|
1573
|
+
function tryReadConsumerManifest$1(path) {
|
|
1574
|
+
try {
|
|
1575
|
+
const raw = (0, node_fs.readFileSync)(path, "utf8");
|
|
1576
|
+
return {
|
|
1577
|
+
raw,
|
|
1578
|
+
manifest: JSON.parse(raw)
|
|
1579
|
+
};
|
|
1580
|
+
} catch {
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
function pruneConsumerPackageJson(cwd) {
|
|
1585
|
+
const packageJsonPath = (0, node_path.join)(cwd, "package.json");
|
|
1586
|
+
if (!(0, node_fs.existsSync)(packageJsonPath)) return {
|
|
1587
|
+
packageJsonPath: null,
|
|
1588
|
+
removed: []
|
|
1589
|
+
};
|
|
1590
|
+
const read = tryReadConsumerManifest$1(packageJsonPath);
|
|
1591
|
+
if (!read) return {
|
|
1592
|
+
packageJsonPath: null,
|
|
1593
|
+
removed: []
|
|
1594
|
+
};
|
|
1595
|
+
const { manifest, raw } = read;
|
|
1596
|
+
const outcome = pruneEslintDependencies(manifest);
|
|
1597
|
+
if (outcome.removed.length === 0) return {
|
|
1598
|
+
packageJsonPath,
|
|
1599
|
+
removed: []
|
|
1600
|
+
};
|
|
1601
|
+
const indent = detectIndent(raw);
|
|
1602
|
+
(0, node_fs.writeFileSync)(packageJsonPath, serializeManifest(outcome.manifest, indent), "utf8");
|
|
1603
|
+
return {
|
|
1604
|
+
packageJsonPath,
|
|
1605
|
+
removed: outcome.removed
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
//#endregion
|
|
1609
|
+
//#region src/cli/migrate/stringEntry.ts
|
|
1610
|
+
function toEntries(values) {
|
|
1611
|
+
return values.map((value) => ({ value }));
|
|
1612
|
+
}
|
|
1613
|
+
function entryValues(entries) {
|
|
1614
|
+
return entries.map((entry) => entry.value);
|
|
1615
|
+
}
|
|
1616
|
+
function cloneEntry(entry) {
|
|
1617
|
+
return entry.leadingComment === void 0 ? { value: entry.value } : {
|
|
1618
|
+
value: entry.value,
|
|
1619
|
+
leadingComment: entry.leadingComment
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
function cloneEntries(entries) {
|
|
1623
|
+
return entries.map(cloneEntry);
|
|
1624
|
+
}
|
|
1625
|
+
function hasValue(entries, value) {
|
|
1626
|
+
return entries.some((entry) => entry.value === value);
|
|
1627
|
+
}
|
|
1628
|
+
function leadingCommentTextFor(sourceFile, node) {
|
|
1629
|
+
const ranges = typescript.getLeadingCommentRanges(sourceFile.text, node.getFullStart());
|
|
1630
|
+
if (!ranges || ranges.length === 0) return;
|
|
1631
|
+
return ranges.map((range) => sourceFile.text.slice(range.pos, range.end)).join("\n");
|
|
1632
|
+
}
|
|
1633
|
+
//#endregion
|
|
1634
|
+
//#region src/cli/migrate/eslintrc/parse.ts
|
|
1635
|
+
function lineOf$1(sourceFile, node) {
|
|
1636
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
1637
|
+
return line + 1;
|
|
1638
|
+
}
|
|
1639
|
+
function isStringLiteralLike(node) {
|
|
1640
|
+
return typescript.isStringLiteral(node) || typescript.isNoSubstitutionTemplateLiteral(node);
|
|
1641
|
+
}
|
|
1642
|
+
function propertyKeyName$1(property) {
|
|
1643
|
+
if (!typescript.isPropertyAssignment(property) && !typescript.isShorthandPropertyAssignment(property)) return null;
|
|
1644
|
+
const key = property.name;
|
|
1645
|
+
if (typescript.isIdentifier(key) || typescript.isStringLiteral(key)) return key.text;
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
function findProperty$1(object, name) {
|
|
1649
|
+
return object.properties.filter((property) => typescript.isPropertyAssignment(property)).find((property) => propertyKeyName$1(property) === name);
|
|
1650
|
+
}
|
|
1651
|
+
function readStringArray(initializer) {
|
|
1652
|
+
if (!initializer || !typescript.isArrayLiteralExpression(initializer)) return [];
|
|
1653
|
+
return initializer.elements.map((element) => isStringLiteralLike(element) ? element.text : null).filter((value) => value !== null);
|
|
1654
|
+
}
|
|
1655
|
+
function readStringEntries(initializer, sourceFile) {
|
|
1656
|
+
if (!initializer || !typescript.isArrayLiteralExpression(initializer)) return [];
|
|
1657
|
+
return initializer.elements.map((element) => {
|
|
1658
|
+
if (!isStringLiteralLike(element)) return null;
|
|
1659
|
+
const leadingComment = leadingCommentTextFor(sourceFile, element);
|
|
1660
|
+
return leadingComment === void 0 ? { value: element.text } : {
|
|
1661
|
+
value: element.text,
|
|
1662
|
+
leadingComment
|
|
1663
|
+
};
|
|
1664
|
+
}).filter((entry) => entry !== null);
|
|
1665
|
+
}
|
|
1666
|
+
function readStringOrArray(initializer) {
|
|
1667
|
+
if (!initializer) return [];
|
|
1668
|
+
if (isStringLiteralLike(initializer)) return [initializer.text];
|
|
1669
|
+
return readStringArray(initializer);
|
|
1670
|
+
}
|
|
1671
|
+
function readStringOrEntries(initializer, sourceFile) {
|
|
1672
|
+
if (!initializer) return [];
|
|
1673
|
+
if (isStringLiteralLike(initializer)) return [{ value: initializer.text }];
|
|
1674
|
+
return readStringEntries(initializer, sourceFile);
|
|
1675
|
+
}
|
|
1676
|
+
function readGlobalValue$1(initializer) {
|
|
1677
|
+
if (isStringLiteralLike(initializer)) return { value: initializer.text };
|
|
1678
|
+
if (initializer.kind === typescript.SyntaxKind.TrueKeyword) return { value: true };
|
|
1679
|
+
if (initializer.kind === typescript.SyntaxKind.FalseKeyword) return { value: false };
|
|
1680
|
+
if (typescript.isNumericLiteral(initializer)) return { value: Number(initializer.text) };
|
|
1681
|
+
return null;
|
|
1682
|
+
}
|
|
1683
|
+
function readGlobalsObject$1(initializer) {
|
|
1684
|
+
if (!initializer || !typescript.isObjectLiteralExpression(initializer)) return;
|
|
1685
|
+
const entries = [];
|
|
1686
|
+
initializer.properties.forEach((property) => {
|
|
1687
|
+
if (!typescript.isPropertyAssignment(property)) return;
|
|
1688
|
+
const name = propertyKeyName$1(property);
|
|
1689
|
+
if (name === null) return;
|
|
1690
|
+
const wrapper = readGlobalValue$1(property.initializer);
|
|
1691
|
+
if (wrapper === null) return;
|
|
1692
|
+
entries.push([name, wrapper.value]);
|
|
1693
|
+
});
|
|
1694
|
+
if (entries.length === 0) return;
|
|
1695
|
+
return Object.fromEntries(entries);
|
|
1696
|
+
}
|
|
1697
|
+
function readGlobalsTokens$1(initializer, sourceFile) {
|
|
1698
|
+
if (!initializer || !typescript.isObjectLiteralExpression(initializer)) return;
|
|
1699
|
+
const tokens = [];
|
|
1700
|
+
initializer.properties.forEach((property) => {
|
|
1701
|
+
if (typescript.isSpreadAssignment(property)) {
|
|
1702
|
+
tokens.push({
|
|
1703
|
+
kind: "spread",
|
|
1704
|
+
raw: property.expression.getText(sourceFile)
|
|
1705
|
+
});
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
if (!typescript.isPropertyAssignment(property)) return;
|
|
1709
|
+
const name = propertyKeyName$1(property);
|
|
1710
|
+
if (name === null) return;
|
|
1711
|
+
const wrapper = readGlobalValue$1(property.initializer);
|
|
1712
|
+
if (wrapper === null) return;
|
|
1713
|
+
tokens.push({
|
|
1714
|
+
kind: "named",
|
|
1715
|
+
key: name,
|
|
1716
|
+
value: wrapper.value
|
|
1717
|
+
});
|
|
1718
|
+
});
|
|
1719
|
+
return tokens.length > 0 ? tokens : void 0;
|
|
1720
|
+
}
|
|
1721
|
+
function readPluginsArray(initializer) {
|
|
1722
|
+
if (!initializer) return [];
|
|
1723
|
+
if (typescript.isArrayLiteralExpression(initializer)) return readStringArray(initializer);
|
|
1724
|
+
if (typescript.isObjectLiteralExpression(initializer)) return initializer.properties.map(propertyKeyName$1).filter((name) => name !== null);
|
|
1725
|
+
return [];
|
|
1726
|
+
}
|
|
1727
|
+
function readEnvBoolean(value) {
|
|
1728
|
+
if (value.kind === typescript.SyntaxKind.TrueKeyword) return true;
|
|
1729
|
+
if (value.kind === typescript.SyntaxKind.FalseKeyword) return false;
|
|
1730
|
+
if (typescript.isNumericLiteral(value)) return Number(value.text) !== 0;
|
|
1731
|
+
if (isStringLiteralLike(value)) return value.text.length > 0;
|
|
1732
|
+
return false;
|
|
1733
|
+
}
|
|
1734
|
+
function readEnvFromInitializer(initializer) {
|
|
1735
|
+
const env = {
|
|
1736
|
+
browser: false,
|
|
1737
|
+
node: false,
|
|
1738
|
+
jest: false,
|
|
1739
|
+
jestGlobals: false
|
|
1740
|
+
};
|
|
1741
|
+
if (!initializer || !typescript.isObjectLiteralExpression(initializer)) return env;
|
|
1742
|
+
initializer.properties.forEach((property) => {
|
|
1743
|
+
if (!typescript.isPropertyAssignment(property)) return;
|
|
1744
|
+
const name = propertyKeyName$1(property);
|
|
1745
|
+
if (!readEnvBoolean(property.initializer)) return;
|
|
1746
|
+
if (name === "browser") env.browser = true;
|
|
1747
|
+
else if (name === "node") env.node = true;
|
|
1748
|
+
else if (name === "jest") env.jest = true;
|
|
1749
|
+
else if (name === "jest/globals") env.jestGlobals = true;
|
|
1750
|
+
});
|
|
1751
|
+
return env;
|
|
1752
|
+
}
|
|
1753
|
+
function classifyExtendValue(raw) {
|
|
1754
|
+
if (raw.startsWith("@useinsider/eslint-config")) {
|
|
1755
|
+
const slashIndex = raw.indexOf("/", 25);
|
|
1756
|
+
return {
|
|
1757
|
+
raw,
|
|
1758
|
+
kind: "insider",
|
|
1759
|
+
insiderPreset: slashIndex >= 0 ? raw.slice(slashIndex + 1) : ""
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
if (raw.startsWith("plugin:vue/")) return {
|
|
1763
|
+
raw,
|
|
1764
|
+
kind: "vue",
|
|
1765
|
+
vueMajor: raw.slice(11).startsWith("vue2-") ? 2 : 3
|
|
1766
|
+
};
|
|
1767
|
+
if (raw.startsWith("plugin:jest/")) return {
|
|
1768
|
+
raw,
|
|
1769
|
+
kind: "jest"
|
|
1770
|
+
};
|
|
1771
|
+
if (raw.startsWith("plugin:jsdoc/")) return {
|
|
1772
|
+
raw,
|
|
1773
|
+
kind: "jsdoc"
|
|
1774
|
+
};
|
|
1775
|
+
if (raw === "airbnb" || raw === "airbnb-base" || raw.startsWith("airbnb-")) return {
|
|
1776
|
+
raw,
|
|
1777
|
+
kind: "airbnb"
|
|
1778
|
+
};
|
|
1779
|
+
return {
|
|
1780
|
+
raw,
|
|
1781
|
+
kind: "other"
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
function readExtends(initializer) {
|
|
1785
|
+
if (!initializer) return [];
|
|
1786
|
+
if (isStringLiteralLike(initializer)) return [classifyExtendValue(initializer.text)];
|
|
1787
|
+
return readStringArray(initializer).map(classifyExtendValue);
|
|
1788
|
+
}
|
|
1789
|
+
function readParserMarker(initializer) {
|
|
1790
|
+
if (!initializer || !isStringLiteralLike(initializer)) return false;
|
|
1791
|
+
return initializer.text.includes("typescript-eslint");
|
|
1792
|
+
}
|
|
1793
|
+
function readParserOptions(initializer, env) {
|
|
1794
|
+
const options = {};
|
|
1795
|
+
let parserMarker = false;
|
|
1796
|
+
if (!initializer || !typescript.isObjectLiteralExpression(initializer)) return {
|
|
1797
|
+
options,
|
|
1798
|
+
parserMarker
|
|
1799
|
+
};
|
|
1800
|
+
const projectProperty = findProperty$1(initializer, "project");
|
|
1801
|
+
if (projectProperty) {
|
|
1802
|
+
const project = readStringOrArray(projectProperty.initializer);
|
|
1803
|
+
if (project.length > 0) options.project = project;
|
|
1804
|
+
}
|
|
1805
|
+
const parserProperty = findProperty$1(initializer, "parser");
|
|
1806
|
+
if (parserProperty && readParserMarker(parserProperty.initializer)) parserMarker = true;
|
|
1807
|
+
return {
|
|
1808
|
+
options,
|
|
1809
|
+
parserMarker
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function extractGlobalComponents$1(rulesNode, sourceFile) {
|
|
1813
|
+
const rule = findProperty$1(rulesNode, "vue/no-undef-components");
|
|
1814
|
+
if (!rule || !typescript.isArrayLiteralExpression(rule.initializer)) return;
|
|
1815
|
+
const [, optionsNode] = rule.initializer.elements;
|
|
1816
|
+
if (!optionsNode || !typescript.isObjectLiteralExpression(optionsNode)) return;
|
|
1817
|
+
const ignorePatterns = findProperty$1(optionsNode, "ignorePatterns");
|
|
1818
|
+
if (!ignorePatterns) return;
|
|
1819
|
+
const values = readStringEntries(ignorePatterns.initializer, sourceFile);
|
|
1820
|
+
return values.length > 0 ? values : void 0;
|
|
1821
|
+
}
|
|
1822
|
+
function detectSpellCheckerOff(rulesNode) {
|
|
1823
|
+
const rule = findProperty$1(rulesNode, "@cspell/spellchecker");
|
|
1824
|
+
if (!rule) return false;
|
|
1825
|
+
return isStringLiteralLike(rule.initializer) && rule.initializer.text === "off";
|
|
1826
|
+
}
|
|
1827
|
+
function resolveSpreadKeyCount(expression, context) {
|
|
1828
|
+
if (!typescript.isIdentifier(expression)) return null;
|
|
1829
|
+
const target = context.constantRulesObjects.get(expression.text);
|
|
1830
|
+
if (!target) return null;
|
|
1831
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1832
|
+
target.properties.forEach((property) => {
|
|
1833
|
+
const name = propertyKeyName$1(property);
|
|
1834
|
+
if (name !== null && name !== "vue/no-undef-components" && name !== "@cspell/spellchecker") keys.add(name);
|
|
1835
|
+
});
|
|
1836
|
+
return keys.size;
|
|
1837
|
+
}
|
|
1838
|
+
function readRulesBlock$1(rulesPropertyInitializer, context) {
|
|
1839
|
+
const block = {
|
|
1840
|
+
customRuleCount: 0,
|
|
1841
|
+
spellCheckerOff: false,
|
|
1842
|
+
unresolvedSpreads: []
|
|
1843
|
+
};
|
|
1844
|
+
if (!rulesPropertyInitializer || !typescript.isObjectLiteralExpression(rulesPropertyInitializer)) return block;
|
|
1845
|
+
block.globalComponents = extractGlobalComponents$1(rulesPropertyInitializer, context.sourceFile);
|
|
1846
|
+
block.spellCheckerOff = detectSpellCheckerOff(rulesPropertyInitializer);
|
|
1847
|
+
rulesPropertyInitializer.properties.forEach((property) => {
|
|
1848
|
+
if (typescript.isPropertyAssignment(property)) {
|
|
1849
|
+
const name = propertyKeyName$1(property);
|
|
1850
|
+
if (name === null || name === "vue/no-undef-components" || name === "@cspell/spellchecker") return;
|
|
1851
|
+
block.customRuleCount += 1;
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
if (typescript.isSpreadAssignment(property)) {
|
|
1855
|
+
const resolved = resolveSpreadKeyCount(property.expression, context);
|
|
1856
|
+
if (resolved === null) {
|
|
1857
|
+
const identifierName = typescript.isIdentifier(property.expression) ? property.expression.text : "<expr>";
|
|
1858
|
+
block.unresolvedSpreads.push(identifierName);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
block.customRuleCount += resolved;
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1864
|
+
return block;
|
|
1865
|
+
}
|
|
1866
|
+
function collectConstantObjects(sourceFile) {
|
|
1867
|
+
const map = /* @__PURE__ */ new Map();
|
|
1868
|
+
sourceFile.statements.forEach((statement) => {
|
|
1869
|
+
if (!typescript.isVariableStatement(statement)) return;
|
|
1870
|
+
statement.declarationList.declarations.forEach((declaration) => {
|
|
1871
|
+
if (!typescript.isIdentifier(declaration.name) || !declaration.initializer) return;
|
|
1872
|
+
if (typescript.isObjectLiteralExpression(declaration.initializer)) map.set(declaration.name.text, declaration.initializer);
|
|
1873
|
+
});
|
|
1874
|
+
});
|
|
1875
|
+
return map;
|
|
1876
|
+
}
|
|
1877
|
+
function assignCommentIfPresent(target, key, comment) {
|
|
1878
|
+
if (comment !== void 0) target[key] = comment;
|
|
1879
|
+
}
|
|
1880
|
+
function collectBlockFieldComments(object, parserOptionsProperty, rulesProperty, rulesBlock, sourceFile) {
|
|
1881
|
+
const fieldComments = {};
|
|
1882
|
+
const filesProperty = findProperty$1(object, "files");
|
|
1883
|
+
if (filesProperty) assignCommentIfPresent(fieldComments, "files", leadingCommentTextFor(sourceFile, filesProperty));
|
|
1884
|
+
if (parserOptionsProperty && typescript.isObjectLiteralExpression(parserOptionsProperty.initializer)) {
|
|
1885
|
+
const projectProperty = findProperty$1(parserOptionsProperty.initializer, "project");
|
|
1886
|
+
if (projectProperty) assignCommentIfPresent(fieldComments, "project", leadingCommentTextFor(sourceFile, projectProperty));
|
|
1887
|
+
}
|
|
1888
|
+
const globalsProperty = findProperty$1(object, "globals");
|
|
1889
|
+
if (globalsProperty) assignCommentIfPresent(fieldComments, "globals", leadingCommentTextFor(sourceFile, globalsProperty));
|
|
1890
|
+
if (rulesProperty && rulesBlock.globalComponents) assignCommentIfPresent(fieldComments, "globalComponents", leadingCommentTextFor(sourceFile, rulesProperty));
|
|
1891
|
+
return Object.keys(fieldComments).length > 0 ? fieldComments : void 0;
|
|
1892
|
+
}
|
|
1893
|
+
function readSharedBlockFields(input) {
|
|
1894
|
+
const { object, line, context } = input;
|
|
1895
|
+
const extendsList = readExtends(findProperty$1(object, "extends")?.initializer);
|
|
1896
|
+
const env = readEnvFromInitializer(findProperty$1(object, "env")?.initializer);
|
|
1897
|
+
const parserOptionsProperty = findProperty$1(object, "parserOptions");
|
|
1898
|
+
const parserOptionsResult = readParserOptions(parserOptionsProperty?.initializer, env);
|
|
1899
|
+
const parserProperty = findProperty$1(object, "parser");
|
|
1900
|
+
const parserMarker = parserOptionsResult.parserMarker || readParserMarker(parserProperty?.initializer);
|
|
1901
|
+
const globalsInit = findProperty$1(object, "globals")?.initializer;
|
|
1902
|
+
const globalsTokens = readGlobalsTokens$1(globalsInit, context.sourceFile);
|
|
1903
|
+
const globals = readGlobalsObject$1(globalsInit);
|
|
1904
|
+
const rulesProperty = findProperty$1(object, "rules");
|
|
1905
|
+
const rulesBlock = readRulesBlock$1(rulesProperty?.initializer, context);
|
|
1906
|
+
const insiderPluginNames = new Set([
|
|
1907
|
+
"vue",
|
|
1908
|
+
"@typescript-eslint",
|
|
1909
|
+
"jsdoc",
|
|
1910
|
+
"import",
|
|
1911
|
+
"jest",
|
|
1912
|
+
"@cspell"
|
|
1913
|
+
]);
|
|
1914
|
+
const customPluginNames = readPluginsArray(findProperty$1(object, "plugins")?.initializer).filter((name) => !insiderPluginNames.has(name));
|
|
1915
|
+
const fieldComments = collectBlockFieldComments(object, parserOptionsProperty, rulesProperty, rulesBlock, context.sourceFile);
|
|
1916
|
+
return {
|
|
1917
|
+
extendsList,
|
|
1918
|
+
env,
|
|
1919
|
+
parserOptions: parserOptionsResult.options,
|
|
1920
|
+
parserMarker,
|
|
1921
|
+
globals,
|
|
1922
|
+
globalsTokens,
|
|
1923
|
+
customPluginNames: customPluginNames.length > 0 ? customPluginNames : void 0,
|
|
1924
|
+
rulesBlock,
|
|
1925
|
+
fieldComments
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
function readOverride(object, context, elementNode) {
|
|
1929
|
+
const line = lineOf$1(context.sourceFile, object);
|
|
1930
|
+
const filesProperty = findProperty$1(object, "files");
|
|
1931
|
+
const files = filesProperty ? readStringOrEntries(filesProperty.initializer, context.sourceFile) : [];
|
|
1932
|
+
if (files.length === 0) {
|
|
1933
|
+
context.warnings.push({
|
|
1934
|
+
kind: "unrecognized-block",
|
|
1935
|
+
line,
|
|
1936
|
+
message: "overrides[] entry missing `files`; skipping"
|
|
1937
|
+
});
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
const shared = readSharedBlockFields({
|
|
1941
|
+
object,
|
|
1942
|
+
line,
|
|
1943
|
+
context
|
|
1944
|
+
});
|
|
1945
|
+
const leadingComment = leadingCommentTextFor(context.sourceFile, elementNode);
|
|
1946
|
+
const override = {
|
|
1947
|
+
line,
|
|
1948
|
+
files,
|
|
1949
|
+
extends: shared.extendsList,
|
|
1950
|
+
env: shared.env,
|
|
1951
|
+
parserMarker: shared.parserMarker,
|
|
1952
|
+
parserOptions: shared.parserOptions,
|
|
1953
|
+
globals: shared.globals,
|
|
1954
|
+
globalsTokens: shared.globalsTokens,
|
|
1955
|
+
customPluginNames: shared.customPluginNames,
|
|
1956
|
+
rulesBlock: shared.rulesBlock,
|
|
1957
|
+
fieldComments: shared.fieldComments
|
|
1958
|
+
};
|
|
1959
|
+
if (leadingComment !== void 0) override.leadingComment = leadingComment;
|
|
1960
|
+
return override;
|
|
1961
|
+
}
|
|
1962
|
+
function readOverrides(initializer, context) {
|
|
1963
|
+
if (!initializer || !typescript.isArrayLiteralExpression(initializer)) return [];
|
|
1964
|
+
return initializer.elements.map((element) => typescript.isObjectLiteralExpression(element) ? readOverride(element, context, element) : null).filter((value) => value !== null);
|
|
1965
|
+
}
|
|
1966
|
+
function matchesPropertyAccess(expression, owner, member) {
|
|
1967
|
+
if (!typescript.isPropertyAccessExpression(expression)) return false;
|
|
1968
|
+
return typescript.isIdentifier(expression.expression) && expression.expression.text === owner && expression.name.text === member;
|
|
1969
|
+
}
|
|
1970
|
+
function isExportsTarget(expression) {
|
|
1971
|
+
return matchesPropertyAccess(expression, "module", "exports") || matchesPropertyAccess(expression, "exports", "default");
|
|
1972
|
+
}
|
|
1973
|
+
function findExportObject(sourceFile) {
|
|
1974
|
+
let result = null;
|
|
1975
|
+
sourceFile.statements.forEach((statement) => {
|
|
1976
|
+
if (result !== null) return;
|
|
1977
|
+
if (!typescript.isExpressionStatement(statement)) return;
|
|
1978
|
+
const { expression } = statement;
|
|
1979
|
+
if (!typescript.isBinaryExpression(expression) || expression.operatorToken.kind !== typescript.SyntaxKind.EqualsToken) return;
|
|
1980
|
+
if (!isExportsTarget(expression.left)) return;
|
|
1981
|
+
if (typescript.isObjectLiteralExpression(expression.right)) result = expression.right;
|
|
1982
|
+
});
|
|
1983
|
+
return result;
|
|
1984
|
+
}
|
|
1985
|
+
function buildRoot(object, context) {
|
|
1986
|
+
const line = lineOf$1(context.sourceFile, object);
|
|
1987
|
+
const shared = readSharedBlockFields({
|
|
1988
|
+
object,
|
|
1989
|
+
line,
|
|
1990
|
+
context
|
|
1991
|
+
});
|
|
1992
|
+
const overrides = readOverrides(findProperty$1(object, "overrides")?.initializer, context);
|
|
1993
|
+
const ignorePatterns = readStringOrEntries(findProperty$1(object, "ignorePatterns")?.initializer, context.sourceFile);
|
|
1994
|
+
const settingsProperty = findProperty$1(object, "settings");
|
|
1995
|
+
return {
|
|
1996
|
+
line,
|
|
1997
|
+
extends: shared.extendsList,
|
|
1998
|
+
env: shared.env,
|
|
1999
|
+
parserMarker: shared.parserMarker,
|
|
2000
|
+
parserOptions: shared.parserOptions,
|
|
2001
|
+
globals: shared.globals,
|
|
2002
|
+
globalsTokens: shared.globalsTokens,
|
|
2003
|
+
customPluginNames: shared.customPluginNames,
|
|
2004
|
+
rulesBlock: shared.rulesBlock,
|
|
2005
|
+
overrides,
|
|
2006
|
+
ignorePatterns,
|
|
2007
|
+
hasSettings: Boolean(settingsProperty),
|
|
2008
|
+
fieldComments: shared.fieldComments
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function parseEslintrc(source, filePath) {
|
|
2012
|
+
const sourceFile = typescript.createSourceFile(filePath, source, typescript.ScriptTarget.Latest, true);
|
|
2013
|
+
const warnings = [];
|
|
2014
|
+
const context = {
|
|
2015
|
+
sourceFile,
|
|
2016
|
+
warnings,
|
|
2017
|
+
constantRulesObjects: collectConstantObjects(sourceFile)
|
|
2018
|
+
};
|
|
2019
|
+
const exportObject = findExportObject(sourceFile);
|
|
2020
|
+
if (!exportObject) return {
|
|
2021
|
+
found: false,
|
|
2022
|
+
root: null,
|
|
2023
|
+
warnings
|
|
2024
|
+
};
|
|
2025
|
+
return {
|
|
2026
|
+
found: true,
|
|
2027
|
+
root: buildRoot(exportObject, context),
|
|
2028
|
+
warnings
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
//#endregion
|
|
2032
|
+
//#region src/cli/migrate/globalsFolding.ts
|
|
2033
|
+
var FOLDABLE_ENVS = [
|
|
2034
|
+
"jest",
|
|
2035
|
+
"vitest",
|
|
2036
|
+
"jquery",
|
|
2037
|
+
"serviceworker",
|
|
2038
|
+
"worker",
|
|
2039
|
+
"webextensions",
|
|
2040
|
+
"node",
|
|
2041
|
+
"browser"
|
|
2042
|
+
];
|
|
2043
|
+
var HIGH_THRESHOLD_ENVS = new Set(["node", "browser"]);
|
|
2044
|
+
function normalizeGlobalValue(value) {
|
|
2045
|
+
if (value === true || value === "writable" || value === "writeable") return "writable";
|
|
2046
|
+
if (value === "off") return "off";
|
|
2047
|
+
return "readonly";
|
|
2048
|
+
}
|
|
2049
|
+
function envEntries(env) {
|
|
2050
|
+
return globals.default[env];
|
|
2051
|
+
}
|
|
2052
|
+
function envHasName(env, name) {
|
|
2053
|
+
return name in envEntries(env);
|
|
2054
|
+
}
|
|
2055
|
+
function envValueFor(env, name) {
|
|
2056
|
+
const entries = envEntries(env);
|
|
2057
|
+
if (!(name in entries)) return null;
|
|
2058
|
+
return normalizeGlobalValue(entries[name]);
|
|
2059
|
+
}
|
|
2060
|
+
var SPREAD_PATTERN = /^globals\.([A-Za-z_$][\w$]*)$/;
|
|
2061
|
+
function spreadEnvKey(raw) {
|
|
2062
|
+
const match = SPREAD_PATTERN.exec(raw.trim());
|
|
2063
|
+
if (!match) return null;
|
|
2064
|
+
const [, key] = match;
|
|
2065
|
+
return FOLDABLE_ENVS.includes(key) ? key : null;
|
|
2066
|
+
}
|
|
2067
|
+
function foldGlobalsTokens(input) {
|
|
2068
|
+
const preservedSpreads = [];
|
|
2069
|
+
const preservedSpreadEnvs = /* @__PURE__ */ new Set();
|
|
2070
|
+
const namedTokens = [];
|
|
2071
|
+
input.forEach((token) => {
|
|
2072
|
+
if (token.kind === "spread") {
|
|
2073
|
+
preservedSpreads.push({ ...token });
|
|
2074
|
+
const envKey = spreadEnvKey(token.raw);
|
|
2075
|
+
if (envKey) preservedSpreadEnvs.add(envKey);
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
namedTokens.push({ ...token });
|
|
2079
|
+
});
|
|
2080
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
2081
|
+
const namedOrder = [];
|
|
2082
|
+
namedTokens.forEach((token) => {
|
|
2083
|
+
if (!remaining.has(token.key)) namedOrder.push(token.key);
|
|
2084
|
+
remaining.set(token.key, token);
|
|
2085
|
+
});
|
|
2086
|
+
const syntheticSpreads = [];
|
|
2087
|
+
const addedSpreadEnvs = [];
|
|
2088
|
+
FOLDABLE_ENVS.forEach((env) => {
|
|
2089
|
+
if (preservedSpreadEnvs.has(env)) {
|
|
2090
|
+
namedOrder.forEach((name) => {
|
|
2091
|
+
const named = remaining.get(name);
|
|
2092
|
+
if (!named) return;
|
|
2093
|
+
const envValue = envValueFor(env, name);
|
|
2094
|
+
if (envValue === null) return;
|
|
2095
|
+
if (envValue !== normalizeGlobalValue(named.value)) return;
|
|
2096
|
+
remaining.delete(name);
|
|
2097
|
+
});
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
const matches = [];
|
|
2101
|
+
namedOrder.forEach((name) => {
|
|
2102
|
+
const named = remaining.get(name);
|
|
2103
|
+
if (!named) return;
|
|
2104
|
+
if (!envHasName(env, name)) return;
|
|
2105
|
+
const envValue = envValueFor(env, name);
|
|
2106
|
+
if (envValue === null) return;
|
|
2107
|
+
if (envValue !== normalizeGlobalValue(named.value)) return;
|
|
2108
|
+
matches.push(name);
|
|
2109
|
+
});
|
|
2110
|
+
const threshold = HIGH_THRESHOLD_ENVS.has(env) ? 3 : 1;
|
|
2111
|
+
if (matches.length < threshold) return;
|
|
2112
|
+
matches.forEach((name) => remaining.delete(name));
|
|
2113
|
+
syntheticSpreads.push({
|
|
2114
|
+
kind: "spread",
|
|
2115
|
+
raw: `globals.${env}`
|
|
2116
|
+
});
|
|
2117
|
+
addedSpreadEnvs.push(env);
|
|
2118
|
+
});
|
|
2119
|
+
const finalNamed = namedOrder.map((name) => remaining.get(name)).filter((token) => token !== void 0).map((token) => ({ ...token }));
|
|
2120
|
+
return {
|
|
2121
|
+
tokens: [
|
|
2122
|
+
...syntheticSpreads,
|
|
2123
|
+
...preservedSpreads,
|
|
2124
|
+
...finalNamed
|
|
2125
|
+
],
|
|
2126
|
+
addedSpreadEnvs
|
|
2127
|
+
};
|
|
2128
|
+
}
|
|
2129
|
+
//#endregion
|
|
2130
|
+
//#region src/cli/migrate/eslintrc/transform.ts
|
|
2131
|
+
var presetToScope$1 = {
|
|
2132
|
+
javascript: "javascript",
|
|
2133
|
+
"javascript-dom": "javascriptDom",
|
|
2134
|
+
"javascript-node": "javascriptNode",
|
|
2135
|
+
typescript: "typescript",
|
|
2136
|
+
"typescript-dom": "typescriptDom",
|
|
2137
|
+
"typescript-node": "typescriptNode",
|
|
2138
|
+
vue2: "vue2",
|
|
2139
|
+
vue3: "vue3",
|
|
2140
|
+
"vue2-typescript": "vue2Typescript",
|
|
2141
|
+
"vue3-typescript": "vue3Typescript",
|
|
2142
|
+
jest: "jest",
|
|
2143
|
+
vitest: "vitest",
|
|
2144
|
+
"jsdoc-based-ts": "jsdocBasedTs"
|
|
2145
|
+
};
|
|
2146
|
+
var tsScopes$1 = new Set([
|
|
2147
|
+
"typescript",
|
|
2148
|
+
"typescriptDom",
|
|
2149
|
+
"typescriptNode",
|
|
2150
|
+
"vue2Typescript",
|
|
2151
|
+
"vue3Typescript",
|
|
2152
|
+
"jsdocBasedTs"
|
|
2153
|
+
]);
|
|
2154
|
+
var vueScopes$1 = new Set([
|
|
2155
|
+
"vue2",
|
|
2156
|
+
"vue3",
|
|
2157
|
+
"vue2Typescript",
|
|
2158
|
+
"vue3Typescript"
|
|
2159
|
+
]);
|
|
2160
|
+
var defaultJestGlob = "**/*.{test,spec}.{ts,tsx,js,jsx,vue}";
|
|
2161
|
+
function hasVueExtend(extendsList) {
|
|
2162
|
+
return extendsList.some((extend) => extend.kind === "vue");
|
|
2163
|
+
}
|
|
2164
|
+
function hasJestExtend(extendsList) {
|
|
2165
|
+
return extendsList.some((extend) => extend.kind === "jest");
|
|
2166
|
+
}
|
|
2167
|
+
function inferVueMajor(extendsList) {
|
|
2168
|
+
if (extendsList.find((extend) => extend.kind === "vue")?.vueMajor === 2) return 2;
|
|
2169
|
+
return 3;
|
|
2170
|
+
}
|
|
2171
|
+
function mapVueScope(vueMajor, tsMarker) {
|
|
2172
|
+
if (vueMajor === 2) return tsMarker ? "vue2Typescript" : "vue2";
|
|
2173
|
+
return tsMarker ? "vue3Typescript" : "vue3";
|
|
2174
|
+
}
|
|
2175
|
+
function warnEnvBothBrowserAndNode(env, line, warnings) {
|
|
2176
|
+
if (!(env.browser && env.node)) return;
|
|
2177
|
+
if (warnings.some((warning) => warning.kind === "env-both-browser-and-node")) return;
|
|
2178
|
+
warnings.push({
|
|
2179
|
+
kind: "env-both-browser-and-node",
|
|
2180
|
+
line,
|
|
2181
|
+
message: "env.browser and env.node both true — defaulting to Dom scope"
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
function inferPrimaryScope(input) {
|
|
2185
|
+
const { env, extendsList, parserMarker, line, warnings } = input;
|
|
2186
|
+
const jestMarker = env.jest || env.jestGlobals || hasJestExtend(extendsList);
|
|
2187
|
+
warnEnvBothBrowserAndNode(env, line, warnings);
|
|
2188
|
+
if (hasVueExtend(extendsList)) {
|
|
2189
|
+
const vueMajor = inferVueMajor(extendsList);
|
|
2190
|
+
return {
|
|
2191
|
+
primary: mapVueScope(vueMajor, parserMarker),
|
|
2192
|
+
addJest: jestMarker,
|
|
2193
|
+
vueMajor
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
if (env.browser) return {
|
|
2197
|
+
primary: parserMarker ? "typescriptDom" : "javascriptDom",
|
|
2198
|
+
addJest: jestMarker
|
|
2199
|
+
};
|
|
2200
|
+
if (env.node) return {
|
|
2201
|
+
primary: parserMarker ? "typescriptNode" : "javascriptNode",
|
|
2202
|
+
addJest: jestMarker
|
|
2203
|
+
};
|
|
2204
|
+
return {
|
|
2205
|
+
primary: null,
|
|
2206
|
+
addJest: jestMarker
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
function freshConfig$1() {
|
|
2210
|
+
return {
|
|
2211
|
+
ignores: [],
|
|
2212
|
+
scopes: /* @__PURE__ */ new Map(),
|
|
2213
|
+
strictGlobs: [],
|
|
2214
|
+
strictWholeFile: false,
|
|
2215
|
+
spellChecker: { ignoredPaths: [] },
|
|
2216
|
+
configFiles: []
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
function appendScope$1(config, scope, entry) {
|
|
2220
|
+
const existing = config.scopes.get(scope);
|
|
2221
|
+
if (existing) existing.push(entry);
|
|
2222
|
+
else config.scopes.set(scope, [entry]);
|
|
2223
|
+
}
|
|
2224
|
+
function mergeGlobals(base, extra) {
|
|
2225
|
+
if (!base && !extra) return;
|
|
2226
|
+
const merged = { ...base ?? {} };
|
|
2227
|
+
if (extra) Object.entries(extra).forEach(([key, value]) => {
|
|
2228
|
+
if (!(key in merged)) merged[key] = value;
|
|
2229
|
+
});
|
|
2230
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
2231
|
+
}
|
|
2232
|
+
function applyTsconfig(entry, scope, perBlockProject, fallbackProject) {
|
|
2233
|
+
if (!tsScopes$1.has(scope)) return;
|
|
2234
|
+
const project = perBlockProject && perBlockProject.length > 0 ? perBlockProject : fallbackProject;
|
|
2235
|
+
if (project && project.length > 0) entry.tsconfigPaths = toEntries(project);
|
|
2236
|
+
}
|
|
2237
|
+
function applyGlobalComponents(entry, scope, globalComponents) {
|
|
2238
|
+
if (!vueScopes$1.has(scope) || !globalComponents || globalComponents.length === 0) return;
|
|
2239
|
+
entry.globalComponents = cloneEntries(globalComponents);
|
|
2240
|
+
}
|
|
2241
|
+
function mapEslintrcFieldComments(incoming, scope) {
|
|
2242
|
+
if (!incoming) return;
|
|
2243
|
+
const result = {};
|
|
2244
|
+
if (incoming.files !== void 0) result.files = incoming.files;
|
|
2245
|
+
if (incoming.globals !== void 0) result.globals = incoming.globals;
|
|
2246
|
+
if (incoming.project !== void 0 && tsScopes$1.has(scope)) result.tsconfigPaths = incoming.project;
|
|
2247
|
+
if (incoming.globalComponents !== void 0 && vueScopes$1.has(scope)) result.globalComponents = incoming.globalComponents;
|
|
2248
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
2249
|
+
}
|
|
2250
|
+
function mergeGlobalsTokens(base, extra) {
|
|
2251
|
+
if (!base && !extra) return;
|
|
2252
|
+
const merged = [];
|
|
2253
|
+
const seenNamed = /* @__PURE__ */ new Map();
|
|
2254
|
+
const append = (tokens) => {
|
|
2255
|
+
tokens.forEach((token) => {
|
|
2256
|
+
if (token.kind === "spread") {
|
|
2257
|
+
merged.push({ ...token });
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
const seenIndex = seenNamed.get(token.key);
|
|
2261
|
+
if (seenIndex === void 0) {
|
|
2262
|
+
seenNamed.set(token.key, merged.length);
|
|
2263
|
+
merged.push({ ...token });
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
merged[seenIndex] = { ...token };
|
|
2267
|
+
});
|
|
2268
|
+
};
|
|
2269
|
+
if (base) append(base);
|
|
2270
|
+
if (extra) append(extra);
|
|
2271
|
+
return merged.length > 0 ? merged : void 0;
|
|
2272
|
+
}
|
|
2273
|
+
function buildEntry$1(input) {
|
|
2274
|
+
const entry = { files: cloneEntries(input.files) };
|
|
2275
|
+
applyTsconfig(entry, input.scope, input.perBlockProject, input.fallbackProject);
|
|
2276
|
+
applyGlobalComponents(entry, input.scope, input.globalComponents);
|
|
2277
|
+
const globalsTokens = mergeGlobalsTokens(input.blockGlobalsTokens, input.topLevelGlobalsTokens);
|
|
2278
|
+
if (globalsTokens) entry.globalsTokens = globalsTokens;
|
|
2279
|
+
const globals = mergeGlobals(input.blockGlobals, input.topLevelGlobals);
|
|
2280
|
+
if (globals) entry.globals = globals;
|
|
2281
|
+
const fieldComments = mapEslintrcFieldComments(input.fieldComments, input.scope);
|
|
2282
|
+
if (fieldComments) entry.fieldComments = fieldComments;
|
|
2283
|
+
return entry;
|
|
2284
|
+
}
|
|
2285
|
+
function collectInsiderPresets(extendsList) {
|
|
2286
|
+
return extendsList.filter((extend) => extend.kind === "insider" && extend.insiderPreset).map((extend) => extend.insiderPreset ?? "");
|
|
2287
|
+
}
|
|
2288
|
+
function mapInsiderPreset(preset) {
|
|
2289
|
+
if (preset === "config") return "config";
|
|
2290
|
+
return presetToScope$1[preset] ?? null;
|
|
2291
|
+
}
|
|
2292
|
+
function captureConfigPresetFiles$1(input, context) {
|
|
2293
|
+
input.files.forEach((file) => {
|
|
2294
|
+
if (!context.config.configFiles.some((existing) => existing.value === file.value)) context.config.configFiles.push({ ...file });
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
function emitInsiderScopes(presets, input, context) {
|
|
2298
|
+
let firstEntry = null;
|
|
2299
|
+
presets.forEach((preset) => {
|
|
2300
|
+
const mapped = mapInsiderPreset(preset);
|
|
2301
|
+
if (mapped === null) {
|
|
2302
|
+
context.warnings.push({
|
|
2303
|
+
kind: "unknown-preset",
|
|
2304
|
+
line: input.line,
|
|
2305
|
+
message: `Unknown @useinsider preset '${preset}' — dropped`
|
|
2306
|
+
});
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
if (mapped === "config") {
|
|
2310
|
+
captureConfigPresetFiles$1(input, context);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
const entry = buildEntry$1({
|
|
2314
|
+
files: input.files,
|
|
2315
|
+
scope: mapped,
|
|
2316
|
+
perBlockProject: input.perBlockProject,
|
|
2317
|
+
fallbackProject: context.topLevelProject,
|
|
2318
|
+
globalComponents: input.globalComponents,
|
|
2319
|
+
blockGlobals: input.blockGlobals,
|
|
2320
|
+
blockGlobalsTokens: input.blockGlobalsTokens,
|
|
2321
|
+
topLevelGlobals: context.topLevelGlobals,
|
|
2322
|
+
topLevelGlobalsTokens: context.topLevelGlobalsTokens,
|
|
2323
|
+
fieldComments: input.fieldComments
|
|
2324
|
+
});
|
|
2325
|
+
appendScope$1(context.config, mapped, entry);
|
|
2326
|
+
if (!firstEntry) firstEntry = entry;
|
|
2327
|
+
});
|
|
2328
|
+
return firstEntry;
|
|
2329
|
+
}
|
|
2330
|
+
function emitInferredScopes(input, context) {
|
|
2331
|
+
const inference = inferPrimaryScope({
|
|
2332
|
+
env: input.env,
|
|
2333
|
+
extendsList: input.extendsList,
|
|
2334
|
+
parserMarker: input.parserMarker,
|
|
2335
|
+
line: input.line,
|
|
2336
|
+
warnings: context.warnings
|
|
2337
|
+
});
|
|
2338
|
+
let firstEntry = null;
|
|
2339
|
+
if (inference.primary) {
|
|
2340
|
+
const entry = buildEntry$1({
|
|
2341
|
+
files: input.files,
|
|
2342
|
+
scope: inference.primary,
|
|
2343
|
+
perBlockProject: input.perBlockProject,
|
|
2344
|
+
fallbackProject: context.topLevelProject,
|
|
2345
|
+
globalComponents: input.globalComponents,
|
|
2346
|
+
blockGlobals: input.blockGlobals,
|
|
2347
|
+
blockGlobalsTokens: input.blockGlobalsTokens,
|
|
2348
|
+
topLevelGlobals: context.topLevelGlobals,
|
|
2349
|
+
topLevelGlobalsTokens: context.topLevelGlobalsTokens,
|
|
2350
|
+
fieldComments: input.fieldComments
|
|
2351
|
+
});
|
|
2352
|
+
appendScope$1(context.config, inference.primary, entry);
|
|
2353
|
+
firstEntry = entry;
|
|
2354
|
+
}
|
|
2355
|
+
if (inference.addJest) {
|
|
2356
|
+
const entry = buildEntry$1({
|
|
2357
|
+
files: [{ value: defaultJestGlob }],
|
|
2358
|
+
scope: "jest",
|
|
2359
|
+
blockGlobals: input.blockGlobals,
|
|
2360
|
+
blockGlobalsTokens: input.blockGlobalsTokens,
|
|
2361
|
+
topLevelGlobals: context.topLevelGlobals,
|
|
2362
|
+
topLevelGlobalsTokens: context.topLevelGlobalsTokens
|
|
2363
|
+
});
|
|
2364
|
+
appendScope$1(context.config, "jest", entry);
|
|
2365
|
+
if (!firstEntry) firstEntry = entry;
|
|
2366
|
+
}
|
|
2367
|
+
return firstEntry;
|
|
2368
|
+
}
|
|
2369
|
+
function recordDroppedRules(input, context) {
|
|
2370
|
+
if (input.rulesCount > 0) {
|
|
2371
|
+
context.droppedRules += input.rulesCount;
|
|
2372
|
+
context.droppedBlocks += 1;
|
|
2373
|
+
}
|
|
2374
|
+
input.unresolvedSpreads.forEach((name) => {
|
|
2375
|
+
context.warnings.push({
|
|
2376
|
+
kind: "unresolved-rules-spread",
|
|
2377
|
+
line: input.line,
|
|
2378
|
+
message: `Could not resolve identifier '${name}' inside rules; dropping`
|
|
2379
|
+
});
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
function applySpellChecker(input, context) {
|
|
2383
|
+
if (!input.spellCheckerOff) return;
|
|
2384
|
+
input.files.forEach((file) => {
|
|
2385
|
+
if (!context.config.spellChecker.ignoredPaths.some((existing) => existing.value === file.value)) context.config.spellChecker.ignoredPaths.push({ ...file });
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
function recordDroppedPlugins(input, context) {
|
|
2389
|
+
if (!input.customPluginNames || input.customPluginNames.length === 0) return;
|
|
2390
|
+
input.customPluginNames.forEach((name) => context.droppedPlugins.add(name));
|
|
2391
|
+
context.droppedPluginEntries += 1;
|
|
2392
|
+
}
|
|
2393
|
+
function processBlock(input, context) {
|
|
2394
|
+
recordDroppedRules(input, context);
|
|
2395
|
+
recordDroppedPlugins(input, context);
|
|
2396
|
+
applySpellChecker(input, context);
|
|
2397
|
+
const insiderPresets = collectInsiderPresets(input.extendsList);
|
|
2398
|
+
const firstEntry = insiderPresets.length > 0 ? emitInsiderScopes(insiderPresets, input, context) : emitInferredScopes(input, context);
|
|
2399
|
+
if (firstEntry && input.leadingComment !== void 0) firstEntry.leadingComment = input.leadingComment;
|
|
2400
|
+
return firstEntry !== null;
|
|
2401
|
+
}
|
|
2402
|
+
function blockInputFromRoot(root, defaultFiles) {
|
|
2403
|
+
return {
|
|
2404
|
+
line: root.line,
|
|
2405
|
+
files: defaultFiles,
|
|
2406
|
+
extendsList: root.extends,
|
|
2407
|
+
env: root.env,
|
|
2408
|
+
parserMarker: root.parserMarker,
|
|
2409
|
+
perBlockProject: root.parserOptions.project,
|
|
2410
|
+
globalComponents: root.rulesBlock.globalComponents,
|
|
2411
|
+
spellCheckerOff: root.rulesBlock.spellCheckerOff,
|
|
2412
|
+
blockGlobals: void 0,
|
|
2413
|
+
blockGlobalsTokens: void 0,
|
|
2414
|
+
customPluginNames: root.customPluginNames,
|
|
2415
|
+
rulesCount: root.rulesBlock.customRuleCount,
|
|
2416
|
+
unresolvedSpreads: root.rulesBlock.unresolvedSpreads,
|
|
2417
|
+
fieldComments: root.fieldComments
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function isBareOverride(override) {
|
|
2421
|
+
const noExtends = override.extends.length === 0;
|
|
2422
|
+
const noEnv = !override.env.browser && !override.env.node && !override.env.jest && !override.env.jestGlobals;
|
|
2423
|
+
const noParserMarker = !override.parserMarker;
|
|
2424
|
+
return noExtends && noEnv && noParserMarker;
|
|
2425
|
+
}
|
|
2426
|
+
function blockInputFromOverride(override, root) {
|
|
2427
|
+
const inherit = isBareOverride(override);
|
|
2428
|
+
return {
|
|
2429
|
+
line: override.line,
|
|
2430
|
+
files: override.files,
|
|
2431
|
+
extendsList: inherit ? root.extends : override.extends,
|
|
2432
|
+
env: inherit ? root.env : override.env,
|
|
2433
|
+
parserMarker: inherit ? root.parserMarker : override.parserMarker,
|
|
2434
|
+
perBlockProject: override.parserOptions.project,
|
|
2435
|
+
globalComponents: override.rulesBlock.globalComponents,
|
|
2436
|
+
spellCheckerOff: override.rulesBlock.spellCheckerOff,
|
|
2437
|
+
blockGlobals: override.globals,
|
|
2438
|
+
blockGlobalsTokens: override.globalsTokens,
|
|
2439
|
+
customPluginNames: override.customPluginNames,
|
|
2440
|
+
rulesCount: override.rulesBlock.customRuleCount,
|
|
2441
|
+
unresolvedSpreads: override.rulesBlock.unresolvedSpreads,
|
|
2442
|
+
leadingComment: override.leadingComment,
|
|
2443
|
+
fieldComments: override.fieldComments
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
function defaultRootFiles(root) {
|
|
2447
|
+
if (root.env.browser && hasVueExtend(root.extends)) return toEntries(["**/*.{js,jsx,ts,tsx,vue}"]);
|
|
2448
|
+
if (root.parserMarker) return toEntries(["**/*.{ts,tsx}"]);
|
|
2449
|
+
return toEntries(["**/*.{js,jsx,cjs,mjs}"]);
|
|
2450
|
+
}
|
|
2451
|
+
function applyIgnorePatterns(root, config) {
|
|
2452
|
+
root.ignorePatterns.forEach((ignore) => {
|
|
2453
|
+
if (!config.ignores.some((existing) => existing.value === ignore.value)) config.ignores.push({ ...ignore });
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
function recordSettingsWarning(root, warnings) {
|
|
2457
|
+
if (root.hasSettings) warnings.push({
|
|
2458
|
+
kind: "dropped-settings",
|
|
2459
|
+
line: root.line,
|
|
2460
|
+
message: "Dropped legacy `settings` block — flat config does not support this surface"
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
function consolidateExtendsWarning(extendKinds, rawDropped, warnings) {
|
|
2464
|
+
if (rawDropped.size === 0) return;
|
|
2465
|
+
const list = [...rawDropped].sort((left, right) => left.localeCompare(right)).join(", ");
|
|
2466
|
+
warnings.push({
|
|
2467
|
+
kind: "dropped-extends",
|
|
2468
|
+
message: `Dropped non-@useinsider extends: ${list}`
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
function recordNoInsiderWarning(hasInsiderExtend, warnings) {
|
|
2472
|
+
if (hasInsiderExtend) return;
|
|
2473
|
+
warnings.push({
|
|
2474
|
+
kind: "no-insider-extends",
|
|
2475
|
+
message: "Source did not extend any @useinsider/eslint-config preset — output is a best-effort inference. Custom rules were dropped; review the generated config and adjust scopes as needed."
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
function collectExtendKinds(root) {
|
|
2479
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
2480
|
+
const rawDropped = /* @__PURE__ */ new Set();
|
|
2481
|
+
let hasInsider = false;
|
|
2482
|
+
const visit = (list) => {
|
|
2483
|
+
list.forEach((extend) => {
|
|
2484
|
+
kinds.add(extend.kind);
|
|
2485
|
+
if (extend.kind === "insider") {
|
|
2486
|
+
hasInsider = true;
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
rawDropped.add(extend.raw);
|
|
2490
|
+
});
|
|
2491
|
+
};
|
|
2492
|
+
visit(root.extends);
|
|
2493
|
+
root.overrides.forEach((override) => visit(override.extends));
|
|
2494
|
+
return {
|
|
2495
|
+
kinds,
|
|
2496
|
+
rawDropped,
|
|
2497
|
+
hasInsider
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
function formatDroppedRulesMessage$1(count, blocks) {
|
|
2501
|
+
return `Dropped ${count} custom ${count === 1 ? "rule override" : "rule overrides"} across ${blocks} ${blocks === 1 ? "block" : "blocks"}`;
|
|
2502
|
+
}
|
|
2503
|
+
function tokensFromRecord$2(globals) {
|
|
2504
|
+
return Object.entries(globals).map(([key, value]) => ({
|
|
2505
|
+
kind: "named",
|
|
2506
|
+
key,
|
|
2507
|
+
value
|
|
2508
|
+
}));
|
|
2509
|
+
}
|
|
2510
|
+
function resolveBaseTokens$1(entry) {
|
|
2511
|
+
if (entry.globalsTokens && entry.globalsTokens.length > 0) return entry.globalsTokens;
|
|
2512
|
+
if (entry.globals && Object.keys(entry.globals).length > 0) return tokensFromRecord$2(entry.globals);
|
|
2513
|
+
}
|
|
2514
|
+
function applyGlobalsFolding$1(config) {
|
|
2515
|
+
let anySyntheticEmitted = false;
|
|
2516
|
+
config.scopes.forEach((entries) => {
|
|
2517
|
+
entries.forEach((entry) => {
|
|
2518
|
+
const baseTokens = resolveBaseTokens$1(entry);
|
|
2519
|
+
if (!baseTokens) return;
|
|
2520
|
+
const { tokens, addedSpreadEnvs } = foldGlobalsTokens(baseTokens);
|
|
2521
|
+
if (addedSpreadEnvs.length > 0) anySyntheticEmitted = true;
|
|
2522
|
+
entry.globalsTokens = tokens;
|
|
2523
|
+
const namedRecord = {};
|
|
2524
|
+
tokens.forEach((token) => {
|
|
2525
|
+
if (token.kind === "named") namedRecord[token.key] = token.value;
|
|
2526
|
+
});
|
|
2527
|
+
entry.globals = Object.keys(namedRecord).length > 0 ? namedRecord : void 0;
|
|
2528
|
+
});
|
|
2529
|
+
});
|
|
2530
|
+
return anySyntheticEmitted;
|
|
2531
|
+
}
|
|
2532
|
+
function buildPreservedImports(emittedSyntheticSpread) {
|
|
2533
|
+
if (!emittedSyntheticSpread) return [];
|
|
2534
|
+
return [{
|
|
2535
|
+
raw: "import globals from 'globals';",
|
|
2536
|
+
moduleSpecifier: "globals"
|
|
2537
|
+
}];
|
|
2538
|
+
}
|
|
2539
|
+
function transformEslintrcToV2(parseResult) {
|
|
2540
|
+
const config = freshConfig$1();
|
|
2541
|
+
const warnings = [...parseResult.warnings];
|
|
2542
|
+
if (!parseResult.root) return {
|
|
2543
|
+
config,
|
|
2544
|
+
silenceRulesCalls: [],
|
|
2545
|
+
needsSilenceRulesImport: false,
|
|
2546
|
+
preservedImports: [],
|
|
2547
|
+
warnings,
|
|
2548
|
+
droppedRuleCount: 0,
|
|
2549
|
+
droppedRuleBlockCount: 0
|
|
2550
|
+
};
|
|
2551
|
+
const { root } = parseResult;
|
|
2552
|
+
const context = {
|
|
2553
|
+
config,
|
|
2554
|
+
warnings,
|
|
2555
|
+
topLevelGlobals: root.globals,
|
|
2556
|
+
topLevelGlobalsTokens: root.globalsTokens,
|
|
2557
|
+
topLevelProject: root.parserOptions.project,
|
|
2558
|
+
droppedRules: 0,
|
|
2559
|
+
droppedBlocks: 0,
|
|
2560
|
+
droppedPlugins: /* @__PURE__ */ new Set(),
|
|
2561
|
+
droppedPluginEntries: 0
|
|
2562
|
+
};
|
|
2563
|
+
applyIgnorePatterns(root, config);
|
|
2564
|
+
recordSettingsWarning(root, warnings);
|
|
2565
|
+
if (root.overrides.length > 0) {
|
|
2566
|
+
root.overrides.forEach((override) => {
|
|
2567
|
+
processBlock(blockInputFromOverride(override, root), context);
|
|
2568
|
+
});
|
|
2569
|
+
if (root.rulesBlock.customRuleCount > 0 || root.rulesBlock.unresolvedSpreads.length > 0) recordDroppedRules(blockInputFromRoot(root, defaultRootFiles(root)), context);
|
|
2570
|
+
} else processBlock(blockInputFromRoot(root, defaultRootFiles(root)), context);
|
|
2571
|
+
if (context.droppedRules > 0) warnings.push({
|
|
2572
|
+
kind: "dropped-rules",
|
|
2573
|
+
message: formatDroppedRulesMessage$1(context.droppedRules, context.droppedBlocks)
|
|
2574
|
+
});
|
|
2575
|
+
const { kinds, rawDropped, hasInsider } = collectExtendKinds(root);
|
|
2576
|
+
consolidateExtendsWarning(kinds, rawDropped, warnings);
|
|
2577
|
+
recordNoInsiderWarning(hasInsider, warnings);
|
|
2578
|
+
if (context.droppedPluginEntries > 0 && context.droppedPlugins.size > 0) {
|
|
2579
|
+
const sorted = [...context.droppedPlugins].sort((left, right) => left.localeCompare(right));
|
|
2580
|
+
warnings.push({
|
|
2581
|
+
kind: "dropped-plugins",
|
|
2582
|
+
message: `Dropped custom plugins on ${context.droppedPluginEntries} entries: ${sorted.join(", ")}`
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
return {
|
|
2586
|
+
config,
|
|
2587
|
+
silenceRulesCalls: [],
|
|
2588
|
+
needsSilenceRulesImport: false,
|
|
2589
|
+
preservedImports: buildPreservedImports(applyGlobalsFolding$1(config)),
|
|
2590
|
+
warnings,
|
|
2591
|
+
droppedRuleCount: context.droppedRules,
|
|
2592
|
+
droppedRuleBlockCount: context.droppedBlocks
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
//#endregion
|
|
2596
|
+
//#region src/cli/migrate/parse.ts
|
|
2597
|
+
var knownPresets = new Set([
|
|
2598
|
+
"config",
|
|
2599
|
+
"javascript",
|
|
2600
|
+
"javascript-dom",
|
|
2601
|
+
"javascript-node",
|
|
2602
|
+
"typescript",
|
|
2603
|
+
"typescript-dom",
|
|
2604
|
+
"typescript-node",
|
|
2605
|
+
"vue2",
|
|
2606
|
+
"vue2-typescript",
|
|
2607
|
+
"vue3",
|
|
2608
|
+
"vue3-typescript",
|
|
2609
|
+
"jest",
|
|
2610
|
+
"vitest",
|
|
2611
|
+
"jsdoc-based-ts",
|
|
2612
|
+
"strict",
|
|
2613
|
+
"dom",
|
|
2614
|
+
"node"
|
|
2615
|
+
]);
|
|
2616
|
+
function lineOf(sourceFile, node) {
|
|
2617
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
2618
|
+
return line + 1;
|
|
2619
|
+
}
|
|
2620
|
+
function unwrapTypeCasts(expression) {
|
|
2621
|
+
let current = expression;
|
|
2622
|
+
while (typescript.isParenthesizedExpression(current) || typescript.isAsExpression(current) || typescript.isTypeAssertionExpression(current) || typescript.isSatisfiesExpression(current)) current = current.expression;
|
|
2623
|
+
return current;
|
|
2624
|
+
}
|
|
2625
|
+
function unwrapAwait(expression) {
|
|
2626
|
+
const peeled = unwrapTypeCasts(expression);
|
|
2627
|
+
if (typescript.isAwaitExpression(peeled)) return unwrapTypeCasts(peeled.expression);
|
|
2628
|
+
return peeled;
|
|
2629
|
+
}
|
|
2630
|
+
function isStringLiteral(node) {
|
|
2631
|
+
return typescript.isStringLiteral(node) || typescript.isNoSubstitutionTemplateLiteral(node);
|
|
2632
|
+
}
|
|
2633
|
+
function readStringLiteralArray(node) {
|
|
2634
|
+
if (!node || !typescript.isArrayLiteralExpression(node)) return;
|
|
2635
|
+
const items = node.elements.map((element) => isStringLiteral(element) ? element.text : null);
|
|
2636
|
+
if (items.some((item) => item === null)) return;
|
|
2637
|
+
return items;
|
|
2638
|
+
}
|
|
2639
|
+
function readStringLiteralEntries(node, sourceFile) {
|
|
2640
|
+
if (!node || !typescript.isArrayLiteralExpression(node)) return;
|
|
2641
|
+
const entries = node.elements.map((element) => {
|
|
2642
|
+
if (!isStringLiteral(element)) return null;
|
|
2643
|
+
const leadingComment = leadingCommentTextFor(sourceFile, element);
|
|
2644
|
+
return leadingComment === void 0 ? { value: element.text } : {
|
|
2645
|
+
value: element.text,
|
|
2646
|
+
leadingComment
|
|
2647
|
+
};
|
|
2648
|
+
});
|
|
2649
|
+
if (entries.some((entry) => entry === null)) return;
|
|
2650
|
+
return entries;
|
|
2651
|
+
}
|
|
2652
|
+
function propertyKeyName(property) {
|
|
2653
|
+
if (!typescript.isPropertyAssignment(property)) return null;
|
|
2654
|
+
const key = property.name;
|
|
2655
|
+
if (typescript.isIdentifier(key) || typescript.isStringLiteral(key)) return key.text;
|
|
2656
|
+
return null;
|
|
2657
|
+
}
|
|
2658
|
+
function findProperty(object, name) {
|
|
2659
|
+
return object.properties.filter((property) => typescript.isPropertyAssignment(property)).find((property) => propertyKeyName(property) === name);
|
|
2660
|
+
}
|
|
2661
|
+
function readGlobalValue(initializer) {
|
|
2662
|
+
if (isStringLiteral(initializer)) return { value: initializer.text };
|
|
2663
|
+
if (initializer.kind === typescript.SyntaxKind.TrueKeyword) return { value: true };
|
|
2664
|
+
if (initializer.kind === typescript.SyntaxKind.FalseKeyword) return { value: false };
|
|
2665
|
+
if (typescript.isNumericLiteral(initializer)) return { value: Number(initializer.text) };
|
|
2666
|
+
return null;
|
|
2667
|
+
}
|
|
2668
|
+
function readGlobalsObject(node) {
|
|
2669
|
+
if (!node || !typescript.isObjectLiteralExpression(node)) return;
|
|
2670
|
+
const entries = node.properties.map((property) => {
|
|
2671
|
+
if (!typescript.isPropertyAssignment(property)) return null;
|
|
2672
|
+
const name = propertyKeyName(property);
|
|
2673
|
+
if (name === null) return null;
|
|
2674
|
+
const valueWrapper = readGlobalValue(property.initializer);
|
|
2675
|
+
if (valueWrapper === null) return null;
|
|
2676
|
+
return [name, valueWrapper.value];
|
|
2677
|
+
});
|
|
2678
|
+
if (entries.some((entry) => entry === null)) return;
|
|
2679
|
+
return Object.fromEntries(entries);
|
|
2680
|
+
}
|
|
2681
|
+
function readGlobalsTokens(node, sourceFile) {
|
|
2682
|
+
if (!node || !typescript.isObjectLiteralExpression(node)) return;
|
|
2683
|
+
const tokens = [];
|
|
2684
|
+
node.properties.forEach((property) => {
|
|
2685
|
+
if (typescript.isSpreadAssignment(property)) {
|
|
2686
|
+
tokens.push({
|
|
2687
|
+
kind: "spread",
|
|
2688
|
+
raw: property.expression.getText(sourceFile)
|
|
2689
|
+
});
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
if (!typescript.isPropertyAssignment(property)) return;
|
|
2693
|
+
const name = propertyKeyName(property);
|
|
2694
|
+
if (name === null) return;
|
|
2695
|
+
const valueWrapper = readGlobalValue(property.initializer);
|
|
2696
|
+
if (valueWrapper === null) return;
|
|
2697
|
+
tokens.push({
|
|
2698
|
+
kind: "named",
|
|
2699
|
+
key: name,
|
|
2700
|
+
value: valueWrapper.value
|
|
2701
|
+
});
|
|
2702
|
+
});
|
|
2703
|
+
return tokens.length > 0 ? tokens : void 0;
|
|
2704
|
+
}
|
|
2705
|
+
function namedTokensToRecord(tokens) {
|
|
2706
|
+
if (!tokens) return;
|
|
2707
|
+
const entries = tokens.filter((token) => token.kind === "named").map((token) => [token.key, token.value]);
|
|
2708
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
2709
|
+
}
|
|
2710
|
+
function extractGlobalComponents(rulesNode, sourceFile) {
|
|
2711
|
+
const rule = findProperty(rulesNode, "vue/no-undef-components");
|
|
2712
|
+
if (!rule || !typescript.isArrayLiteralExpression(rule.initializer)) return;
|
|
2713
|
+
const [, optionsNode] = rule.initializer.elements;
|
|
2714
|
+
if (!optionsNode || !typescript.isObjectLiteralExpression(optionsNode)) return;
|
|
2715
|
+
const ignorePatterns = findProperty(optionsNode, "ignorePatterns");
|
|
2716
|
+
if (!ignorePatterns) return;
|
|
2717
|
+
return readStringLiteralEntries(ignorePatterns.initializer, sourceFile);
|
|
2718
|
+
}
|
|
2719
|
+
function assignFieldComment(data, field, comment) {
|
|
2720
|
+
if (comment === void 0) return;
|
|
2721
|
+
data.fieldComments = {
|
|
2722
|
+
...data.fieldComments,
|
|
2723
|
+
[field]: comment
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
function countExtraRules(rulesNode) {
|
|
2727
|
+
return rulesNode.properties.filter((property) => {
|
|
2728
|
+
const name = propertyKeyName(property);
|
|
2729
|
+
return name !== null && name !== "vue/no-undef-components";
|
|
2730
|
+
}).length;
|
|
2731
|
+
}
|
|
2732
|
+
function readParserProject(parserOptions, sourceFile) {
|
|
2733
|
+
const projectProperty = findProperty(parserOptions, "project");
|
|
2734
|
+
if (!projectProperty) return;
|
|
2735
|
+
const { initializer } = projectProperty;
|
|
2736
|
+
if (isStringLiteral(initializer)) return [{ value: initializer.text }];
|
|
2737
|
+
return readStringLiteralEntries(initializer, sourceFile);
|
|
2738
|
+
}
|
|
2739
|
+
function readParserProjectService(parserOptions) {
|
|
2740
|
+
if (findProperty(parserOptions, "projectService")?.initializer.kind === typescript.SyntaxKind.TrueKeyword) return true;
|
|
2741
|
+
}
|
|
2742
|
+
function expressionMentionsTsParser(expression, sourceFile) {
|
|
2743
|
+
return expression.getText(sourceFile).includes("@typescript-eslint/parser");
|
|
2744
|
+
}
|
|
2745
|
+
function readLanguageOptions(languageOptions, data, sourceFile) {
|
|
2746
|
+
const parserOptionsProperty = findProperty(languageOptions, "parserOptions");
|
|
2747
|
+
if (parserOptionsProperty && typescript.isObjectLiteralExpression(parserOptionsProperty.initializer)) {
|
|
2748
|
+
const parserOptions = parserOptionsProperty.initializer;
|
|
2749
|
+
data.parserProject = readParserProject(parserOptions, sourceFile);
|
|
2750
|
+
data.parserProjectService = readParserProjectService(parserOptions);
|
|
2751
|
+
}
|
|
2752
|
+
const parserProperty = findProperty(languageOptions, "parser");
|
|
2753
|
+
if (parserProperty && expressionMentionsTsParser(parserProperty.initializer, sourceFile)) data.parserIsTypescript = true;
|
|
2754
|
+
const globalsProperty = findProperty(languageOptions, "globals");
|
|
2755
|
+
if (globalsProperty) {
|
|
2756
|
+
data.globalsTokens = readGlobalsTokens(globalsProperty.initializer, sourceFile);
|
|
2757
|
+
data.globals = namedTokensToRecord(data.globalsTokens) ?? readGlobalsObject(globalsProperty.initializer);
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
function readPluginNames(pluginsProperty) {
|
|
2761
|
+
if (!typescript.isObjectLiteralExpression(pluginsProperty.initializer)) return [];
|
|
2762
|
+
return pluginsProperty.initializer.properties.map(propertyKeyName).filter((name) => name !== null);
|
|
2763
|
+
}
|
|
2764
|
+
function readRulesBlock(rulesProperty, data, sourceFile) {
|
|
2765
|
+
if (!typescript.isObjectLiteralExpression(rulesProperty.initializer)) return;
|
|
2766
|
+
const rulesObject = rulesProperty.initializer;
|
|
2767
|
+
data.globalComponents = extractGlobalComponents(rulesObject, sourceFile);
|
|
2768
|
+
data.extraRuleCount = countExtraRules(rulesObject);
|
|
2769
|
+
}
|
|
2770
|
+
function captureLanguageOptionsComments(languageOptionsObject, data, sourceFile) {
|
|
2771
|
+
const parserOptionsProperty = findProperty(languageOptionsObject, "parserOptions");
|
|
2772
|
+
if (parserOptionsProperty && typescript.isObjectLiteralExpression(parserOptionsProperty.initializer)) {
|
|
2773
|
+
const projectProperty = findProperty(parserOptionsProperty.initializer, "project");
|
|
2774
|
+
if (projectProperty) assignFieldComment(data, "parserProject", leadingCommentTextFor(sourceFile, projectProperty));
|
|
2775
|
+
}
|
|
2776
|
+
const globalsProperty = findProperty(languageOptionsObject, "globals");
|
|
2777
|
+
if (globalsProperty) assignFieldComment(data, "globals", leadingCommentTextFor(sourceFile, globalsProperty));
|
|
2778
|
+
}
|
|
2779
|
+
function processFilesProperty(configNode, data, sourceFile) {
|
|
2780
|
+
const filesProperty = findProperty(configNode, "files");
|
|
2781
|
+
if (!filesProperty) return;
|
|
2782
|
+
data.files = readStringLiteralEntries(filesProperty.initializer, sourceFile);
|
|
2783
|
+
assignFieldComment(data, "files", leadingCommentTextFor(sourceFile, filesProperty));
|
|
2784
|
+
}
|
|
2785
|
+
function processIgnoresProperty(configNode, data, sourceFile) {
|
|
2786
|
+
const ignoresProperty = findProperty(configNode, "ignores");
|
|
2787
|
+
if (!ignoresProperty) return;
|
|
2788
|
+
data.ignores = readStringLiteralEntries(ignoresProperty.initializer, sourceFile);
|
|
2789
|
+
assignFieldComment(data, "ignores", leadingCommentTextFor(sourceFile, ignoresProperty));
|
|
2790
|
+
}
|
|
2791
|
+
function processPluginsProperty(configNode, data) {
|
|
2792
|
+
const pluginsProperty = findProperty(configNode, "plugins");
|
|
2793
|
+
if (!pluginsProperty) return;
|
|
2794
|
+
const pluginNames = readPluginNames(pluginsProperty);
|
|
2795
|
+
if (pluginNames.length > 0) data.customPluginNames = pluginNames;
|
|
2796
|
+
}
|
|
2797
|
+
function processRulesProperty(configNode, data, sourceFile) {
|
|
2798
|
+
const rulesProperty = findProperty(configNode, "rules");
|
|
2799
|
+
if (!rulesProperty) return;
|
|
2800
|
+
readRulesBlock(rulesProperty, data, sourceFile);
|
|
2801
|
+
if (data.globalComponents !== void 0) assignFieldComment(data, "globalComponents", leadingCommentTextFor(sourceFile, rulesProperty));
|
|
2802
|
+
}
|
|
2803
|
+
function parseConfigObject(configNode, sourceFile) {
|
|
2804
|
+
const data = { extraRuleCount: 0 };
|
|
2805
|
+
processFilesProperty(configNode, data, sourceFile);
|
|
2806
|
+
processIgnoresProperty(configNode, data, sourceFile);
|
|
2807
|
+
const languageOptionsProperty = findProperty(configNode, "languageOptions");
|
|
2808
|
+
if (languageOptionsProperty && typescript.isObjectLiteralExpression(languageOptionsProperty.initializer)) {
|
|
2809
|
+
readLanguageOptions(languageOptionsProperty.initializer, data, sourceFile);
|
|
2810
|
+
captureLanguageOptionsComments(languageOptionsProperty.initializer, data, sourceFile);
|
|
2811
|
+
}
|
|
2812
|
+
processPluginsProperty(configNode, data);
|
|
2813
|
+
processRulesProperty(configNode, data, sourceFile);
|
|
2814
|
+
return data;
|
|
2815
|
+
}
|
|
2816
|
+
function mergeRescuedGlobalsTokens(innerTokens, siblingTokens) {
|
|
2817
|
+
if (!innerTokens || innerTokens.length === 0) return siblingTokens.map((token) => ({ ...token }));
|
|
2818
|
+
const innerNamedKeys = /* @__PURE__ */ new Set();
|
|
2819
|
+
innerTokens.forEach((token) => {
|
|
2820
|
+
if (token.kind === "named") innerNamedKeys.add(token.key);
|
|
2821
|
+
});
|
|
2822
|
+
const merged = innerTokens.map((token) => ({ ...token }));
|
|
2823
|
+
siblingTokens.forEach((token) => {
|
|
2824
|
+
if (token.kind === "spread") {
|
|
2825
|
+
merged.push({ ...token });
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
if (innerNamedKeys.has(token.key)) return;
|
|
2829
|
+
merged.push({ ...token });
|
|
2830
|
+
innerNamedKeys.add(token.key);
|
|
2831
|
+
});
|
|
2832
|
+
return merged;
|
|
2833
|
+
}
|
|
2834
|
+
function applyRescuedGlobals(data, rescued) {
|
|
2835
|
+
if (rescued.globalsTokens && rescued.globalsTokens.length > 0) {
|
|
2836
|
+
data.globalsTokens = mergeRescuedGlobalsTokens(data.globalsTokens, rescued.globalsTokens);
|
|
2837
|
+
data.globals = namedTokensToRecord(data.globalsTokens) ?? data.globals;
|
|
2838
|
+
return true;
|
|
2839
|
+
}
|
|
2840
|
+
if (rescued.globals && Object.keys(rescued.globals).length > 0) {
|
|
2841
|
+
const existingGlobals = data.globals ?? {};
|
|
2842
|
+
data.globals = {
|
|
2843
|
+
...rescued.globals,
|
|
2844
|
+
...existingGlobals
|
|
2845
|
+
};
|
|
2846
|
+
return true;
|
|
2847
|
+
}
|
|
2848
|
+
return false;
|
|
2849
|
+
}
|
|
2850
|
+
function applyRescuedParserProject(data, rescued) {
|
|
2851
|
+
if (!rescued.parserProject || rescued.parserProject.length === 0) return false;
|
|
2852
|
+
if (data.parserProject && data.parserProject.length > 0) return false;
|
|
2853
|
+
data.parserProject = rescued.parserProject;
|
|
2854
|
+
return true;
|
|
2855
|
+
}
|
|
2856
|
+
function applyRescuedLanguageOptions(data, rescued) {
|
|
2857
|
+
let rescuedAny = applyRescuedGlobals(data, rescued);
|
|
2858
|
+
rescuedAny = applyRescuedParserProject(data, rescued) || rescuedAny;
|
|
2859
|
+
if (rescued.parserIsTypescript && !data.parserIsTypescript) {
|
|
2860
|
+
data.parserIsTypescript = true;
|
|
2861
|
+
rescuedAny = true;
|
|
2862
|
+
}
|
|
2863
|
+
if (rescued.parserProjectService && data.parserProjectService === void 0) {
|
|
2864
|
+
data.parserProjectService = true;
|
|
2865
|
+
rescuedAny = true;
|
|
2866
|
+
}
|
|
2867
|
+
return rescuedAny;
|
|
2868
|
+
}
|
|
2869
|
+
function parseSiblingLanguageOptions(callArgument, sourceFile) {
|
|
2870
|
+
const languageOptionsProperty = findProperty(callArgument, "languageOptions");
|
|
2871
|
+
if (!languageOptionsProperty || !typescript.isObjectLiteralExpression(languageOptionsProperty.initializer)) return null;
|
|
2872
|
+
const rescued = { extraRuleCount: 0 };
|
|
2873
|
+
readLanguageOptions(languageOptionsProperty.initializer, rescued, sourceFile);
|
|
2874
|
+
return rescued;
|
|
2875
|
+
}
|
|
2876
|
+
function mergeSiblingIgnoresIntoData(callArgument, data, sourceFile) {
|
|
2877
|
+
const ignoresProperty = findProperty(callArgument, "ignores");
|
|
2878
|
+
if (!ignoresProperty) return false;
|
|
2879
|
+
const rescuedIgnores = readStringLiteralEntries(ignoresProperty.initializer, sourceFile);
|
|
2880
|
+
if (!rescuedIgnores || rescuedIgnores.length === 0) return false;
|
|
2881
|
+
const existing = data.ignores ?? [];
|
|
2882
|
+
const seen = new Set(existing.map((entry) => entry.value));
|
|
2883
|
+
const additions = [];
|
|
2884
|
+
rescuedIgnores.forEach((entry) => {
|
|
2885
|
+
if (seen.has(entry.value)) return;
|
|
2886
|
+
seen.add(entry.value);
|
|
2887
|
+
additions.push(entry);
|
|
2888
|
+
});
|
|
2889
|
+
if (additions.length === 0) return false;
|
|
2890
|
+
data.ignores = [...existing, ...additions];
|
|
2891
|
+
return true;
|
|
2892
|
+
}
|
|
2893
|
+
function applySiblingRescues(callArgument, data, sourceFile) {
|
|
2894
|
+
const rescuedSibling = parseSiblingLanguageOptions(callArgument, sourceFile);
|
|
2895
|
+
const rescuedLanguageOptions = rescuedSibling ? applyRescuedLanguageOptions(data, rescuedSibling) : false;
|
|
2896
|
+
const rescuedIgnores = mergeSiblingIgnoresIntoData(callArgument, data, sourceFile);
|
|
2897
|
+
return rescuedLanguageOptions || rescuedIgnores;
|
|
2898
|
+
}
|
|
2899
|
+
function classifyPresets(presetList, line, warnings) {
|
|
2900
|
+
const presets = [];
|
|
2901
|
+
const unknownPresets = [];
|
|
2902
|
+
presetList.forEach((presetName) => {
|
|
2903
|
+
if (knownPresets.has(presetName)) presets.push(presetName);
|
|
2904
|
+
else {
|
|
2905
|
+
unknownPresets.push(presetName);
|
|
2906
|
+
warnings.push({
|
|
2907
|
+
kind: "unknown-preset",
|
|
2908
|
+
line,
|
|
2909
|
+
message: `Unknown v1 preset '${presetName}' — dropped`
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
});
|
|
2913
|
+
return {
|
|
2914
|
+
presets,
|
|
2915
|
+
unknownPresets
|
|
2916
|
+
};
|
|
2917
|
+
}
|
|
2918
|
+
function parseUseInsiderCall(callExpression, sourceFile, warnings) {
|
|
2919
|
+
const line = lineOf(sourceFile, callExpression);
|
|
2920
|
+
const [firstArgument] = callExpression.arguments;
|
|
2921
|
+
if (!firstArgument || !typescript.isObjectLiteralExpression(firstArgument)) {
|
|
2922
|
+
warnings.push({
|
|
2923
|
+
kind: "unrecognized-block",
|
|
2924
|
+
line,
|
|
2925
|
+
message: "useInsider call without object literal argument; skipping"
|
|
2926
|
+
});
|
|
2927
|
+
return null;
|
|
2928
|
+
}
|
|
2929
|
+
const presetProperty = findProperty(firstArgument, "preset");
|
|
2930
|
+
const configProperty = findProperty(firstArgument, "config");
|
|
2931
|
+
if (!presetProperty) {
|
|
2932
|
+
warnings.push({
|
|
2933
|
+
kind: "unrecognized-block",
|
|
2934
|
+
line,
|
|
2935
|
+
message: "useInsider call missing preset; skipping"
|
|
2936
|
+
});
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
const presetList = readStringLiteralArray(presetProperty.initializer);
|
|
2940
|
+
if (!presetList) {
|
|
2941
|
+
warnings.push({
|
|
2942
|
+
kind: "unrecognized-block",
|
|
2943
|
+
line,
|
|
2944
|
+
message: "useInsider preset is not a string-literal array; skipping"
|
|
2945
|
+
});
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
const { presets, unknownPresets } = classifyPresets(presetList, line, warnings);
|
|
2949
|
+
const configData = configProperty && typescript.isObjectLiteralExpression(configProperty.initializer) ? parseConfigObject(configProperty.initializer, sourceFile) : { extraRuleCount: 0 };
|
|
2950
|
+
const rescuedAny = applySiblingRescues(firstArgument, configData, sourceFile);
|
|
2951
|
+
const call = {
|
|
2952
|
+
kind: "useInsiderCall",
|
|
2953
|
+
line,
|
|
2954
|
+
presets,
|
|
2955
|
+
unknownPresets,
|
|
2956
|
+
files: configData.files,
|
|
2957
|
+
ignores: configData.ignores,
|
|
2958
|
+
parserProject: configData.parserProject,
|
|
2959
|
+
parserProjectService: configData.parserProjectService,
|
|
2960
|
+
globals: configData.globals,
|
|
2961
|
+
globalsTokens: configData.globalsTokens,
|
|
2962
|
+
globalComponents: configData.globalComponents,
|
|
2963
|
+
parserIsTypescript: configData.parserIsTypescript,
|
|
2964
|
+
customPluginNames: configData.customPluginNames,
|
|
2965
|
+
extraRuleCount: configData.extraRuleCount,
|
|
2966
|
+
fieldComments: configData.fieldComments
|
|
2967
|
+
};
|
|
2968
|
+
if (rescuedAny) call.rescuedSiblingLanguageOptions = true;
|
|
2969
|
+
return call;
|
|
2970
|
+
}
|
|
2971
|
+
function tryClassifyIgnoresBlock(objectLiteral, keys, line, sourceFile) {
|
|
2972
|
+
if (keys.length !== 1 || keys[0] !== "ignores") return null;
|
|
2973
|
+
const ignoresProperty = findProperty(objectLiteral, "ignores");
|
|
2974
|
+
const ignores = ignoresProperty ? readStringLiteralEntries(ignoresProperty.initializer, sourceFile) : void 0;
|
|
2975
|
+
if (!ignores) return null;
|
|
2976
|
+
return {
|
|
2977
|
+
kind: "ignoresBlock",
|
|
2978
|
+
line,
|
|
2979
|
+
ignores
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
function isSpellCheckerOffRule(rules) {
|
|
2983
|
+
if (rules.length !== 1) return false;
|
|
2984
|
+
const [onlyRule] = rules;
|
|
2985
|
+
if (!typescript.isPropertyAssignment(onlyRule) || propertyKeyName(onlyRule) !== "@cspell/spellchecker") return false;
|
|
2986
|
+
return isStringLiteral(onlyRule.initializer) && onlyRule.initializer.text === "off";
|
|
2987
|
+
}
|
|
2988
|
+
function tryClassifySpellCheckerOff(objectLiteral, keys, line, sourceFile) {
|
|
2989
|
+
if (keys.length !== 2 || !keys.includes("files") || !keys.includes("rules")) return null;
|
|
2990
|
+
const filesProperty = findProperty(objectLiteral, "files");
|
|
2991
|
+
const rulesProperty = findProperty(objectLiteral, "rules");
|
|
2992
|
+
const files = filesProperty ? readStringLiteralEntries(filesProperty.initializer, sourceFile) : void 0;
|
|
2993
|
+
if (!files || !rulesProperty || !typescript.isObjectLiteralExpression(rulesProperty.initializer)) return null;
|
|
2994
|
+
if (!isSpellCheckerOffRule(rulesProperty.initializer.properties)) return null;
|
|
2995
|
+
return {
|
|
2996
|
+
kind: "spellCheckerOff",
|
|
2997
|
+
line,
|
|
2998
|
+
files
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
function tryClassifyStandaloneGlobals(objectLiteral, keys, line, sourceFile) {
|
|
3002
|
+
if (!keys.includes("files") || !keys.includes("languageOptions")) return null;
|
|
3003
|
+
const filesProperty = findProperty(objectLiteral, "files");
|
|
3004
|
+
const languageOptionsProperty = findProperty(objectLiteral, "languageOptions");
|
|
3005
|
+
const files = filesProperty ? readStringLiteralEntries(filesProperty.initializer, sourceFile) : void 0;
|
|
3006
|
+
if (!files || !languageOptionsProperty || !typescript.isObjectLiteralExpression(languageOptionsProperty.initializer)) return null;
|
|
3007
|
+
const globalsProperty = findProperty(languageOptionsProperty.initializer, "globals");
|
|
3008
|
+
if (!globalsProperty) return null;
|
|
3009
|
+
const tokens = readGlobalsTokens(globalsProperty.initializer, sourceFile);
|
|
3010
|
+
if (!tokens || tokens.length === 0) return null;
|
|
3011
|
+
return {
|
|
3012
|
+
kind: "standaloneGlobals",
|
|
3013
|
+
line,
|
|
3014
|
+
files,
|
|
3015
|
+
globalsTokens: tokens
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
function classifyObjectElement(objectLiteral, sourceFile, warnings) {
|
|
3019
|
+
const line = lineOf(sourceFile, objectLiteral);
|
|
3020
|
+
const keys = objectLiteral.properties.map(propertyKeyName).filter((name) => name !== null);
|
|
3021
|
+
const ignoresBlock = tryClassifyIgnoresBlock(objectLiteral, keys, line, sourceFile);
|
|
3022
|
+
if (ignoresBlock) return ignoresBlock;
|
|
3023
|
+
const spellCheckerOff = tryClassifySpellCheckerOff(objectLiteral, keys, line, sourceFile);
|
|
3024
|
+
if (spellCheckerOff) return spellCheckerOff;
|
|
3025
|
+
const standaloneGlobals = tryClassifyStandaloneGlobals(objectLiteral, keys, line, sourceFile);
|
|
3026
|
+
if (standaloneGlobals) return standaloneGlobals;
|
|
3027
|
+
warnings.push({
|
|
3028
|
+
kind: "unrecognized-block",
|
|
3029
|
+
line,
|
|
3030
|
+
message: "Unrecognized array element; dropping"
|
|
3031
|
+
});
|
|
3032
|
+
return {
|
|
3033
|
+
kind: "unrecognized",
|
|
3034
|
+
line,
|
|
3035
|
+
snippet: objectLiteral.getText(sourceFile)
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
function tryClassifyUseInsider(unwrapped, sourceFile, warnings, leadingComment) {
|
|
3039
|
+
if (!typescript.isCallExpression(unwrapped)) return;
|
|
3040
|
+
const callee = unwrapped.expression;
|
|
3041
|
+
if (!typescript.isIdentifier(callee) || callee.text !== "useInsider") return;
|
|
3042
|
+
const parsed = parseUseInsiderCall(unwrapped, sourceFile, warnings);
|
|
3043
|
+
if (parsed && leadingComment !== void 0) parsed.leadingComment = leadingComment;
|
|
3044
|
+
return parsed;
|
|
3045
|
+
}
|
|
3046
|
+
function classifyArrayElement(element, sourceFile, warnings) {
|
|
3047
|
+
const leadingComment = leadingCommentTextFor(sourceFile, element);
|
|
3048
|
+
const unwrapped = unwrapAwait(element);
|
|
3049
|
+
const useInsiderResult = tryClassifyUseInsider(unwrapped, sourceFile, warnings, leadingComment);
|
|
3050
|
+
if (useInsiderResult !== void 0) return useInsiderResult;
|
|
3051
|
+
if (typescript.isObjectLiteralExpression(unwrapped)) {
|
|
3052
|
+
const parsed = classifyObjectElement(unwrapped, sourceFile, warnings);
|
|
3053
|
+
if (leadingComment !== void 0) parsed.leadingComment = leadingComment;
|
|
3054
|
+
return parsed;
|
|
3055
|
+
}
|
|
3056
|
+
const line = lineOf(sourceFile, element);
|
|
3057
|
+
warnings.push({
|
|
3058
|
+
kind: "unrecognized-block",
|
|
3059
|
+
line,
|
|
3060
|
+
message: "Unrecognized array element; dropping"
|
|
3061
|
+
});
|
|
3062
|
+
const unrecognized = {
|
|
3063
|
+
kind: "unrecognized",
|
|
3064
|
+
line,
|
|
3065
|
+
snippet: element.getText(sourceFile)
|
|
3066
|
+
};
|
|
3067
|
+
if (leadingComment !== void 0) unrecognized.leadingComment = leadingComment;
|
|
3068
|
+
return unrecognized;
|
|
3069
|
+
}
|
|
3070
|
+
function isDefineConfigCall(expression) {
|
|
3071
|
+
return typescript.isCallExpression(expression) && typescript.isIdentifier(expression.expression) && expression.expression.text === "defineConfig";
|
|
3072
|
+
}
|
|
3073
|
+
function findDefaultExportArray(sourceFile) {
|
|
3074
|
+
const exportExpressions = sourceFile.statements.filter(typescript.isExportAssignment).map((statement) => unwrapTypeCasts(statement.expression));
|
|
3075
|
+
const directArray = exportExpressions.find(typescript.isArrayLiteralExpression);
|
|
3076
|
+
if (directArray) return directArray;
|
|
3077
|
+
const defineConfigCall = exportExpressions.find(isDefineConfigCall);
|
|
3078
|
+
if (!defineConfigCall) return null;
|
|
3079
|
+
const [first] = defineConfigCall.arguments;
|
|
3080
|
+
if (!first) return null;
|
|
3081
|
+
const unwrappedFirst = unwrapTypeCasts(first);
|
|
3082
|
+
if (typescript.isArrayLiteralExpression(unwrappedFirst)) return unwrappedFirst;
|
|
3083
|
+
return null;
|
|
3084
|
+
}
|
|
3085
|
+
function checkDefineConfigWrap(sourceFile) {
|
|
3086
|
+
return sourceFile.statements.some((statement) => {
|
|
3087
|
+
if (!typescript.isExportAssignment(statement)) return false;
|
|
3088
|
+
const expression = unwrapTypeCasts(statement.expression);
|
|
3089
|
+
return typescript.isCallExpression(expression) && typescript.isIdentifier(expression.expression) && expression.expression.text === "defineConfig";
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
function getModuleSpecifierText(statement) {
|
|
3093
|
+
const { moduleSpecifier } = statement;
|
|
3094
|
+
if (!isStringLiteral(moduleSpecifier)) return null;
|
|
3095
|
+
return moduleSpecifier.text;
|
|
3096
|
+
}
|
|
3097
|
+
function collectNamedImports(importDeclaration) {
|
|
3098
|
+
const namedBindings = importDeclaration.importClause?.namedBindings;
|
|
3099
|
+
if (!namedBindings || !typescript.isNamedImports(namedBindings)) return [];
|
|
3100
|
+
return namedBindings.elements.map((specifier) => specifier.propertyName?.text ?? specifier.name.text);
|
|
3101
|
+
}
|
|
3102
|
+
function hasDefaultBinding(importDeclaration) {
|
|
3103
|
+
return Boolean(importDeclaration.importClause?.name);
|
|
3104
|
+
}
|
|
3105
|
+
var insiderDefaultRewriteTargets = new Set([
|
|
3106
|
+
"useInsider",
|
|
3107
|
+
"silenceRules",
|
|
3108
|
+
"silenceDependencyWarning"
|
|
3109
|
+
]);
|
|
3110
|
+
function classifyInsiderImport(statement, sourceFile, flags) {
|
|
3111
|
+
collectNamedImports(statement).forEach((name) => {
|
|
3112
|
+
if (name === "useInsider") flags.hasUseInsider = true;
|
|
3113
|
+
if (name === "silenceRules") flags.hasSilenceRules = true;
|
|
3114
|
+
});
|
|
3115
|
+
if (hasDefaultBinding(statement)) {
|
|
3116
|
+
const defaultLocal = statement.importClause?.name?.text ?? "";
|
|
3117
|
+
if (insiderDefaultRewriteTargets.has(defaultLocal)) {
|
|
3118
|
+
if (defaultLocal === "useInsider") flags.hasUseInsider = true;
|
|
3119
|
+
if (defaultLocal === "silenceRules") flags.hasSilenceRules = true;
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
flags.hasUseInsider = true;
|
|
3123
|
+
const line = lineOf(sourceFile, statement);
|
|
3124
|
+
flags.preserved.push({
|
|
3125
|
+
raw: statement.getText(sourceFile),
|
|
3126
|
+
moduleSpecifier: "@useinsider/eslint-config"
|
|
3127
|
+
});
|
|
3128
|
+
flags.warnings.push({
|
|
3129
|
+
kind: "unrecognized-block",
|
|
3130
|
+
line,
|
|
3131
|
+
message: `Unexpected default import '${defaultLocal}' from '@useinsider/eslint-config'; kept literally — review before committing`
|
|
3132
|
+
});
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
function inspectImports(sourceFile) {
|
|
3136
|
+
const flags = {
|
|
3137
|
+
hasUseInsider: false,
|
|
3138
|
+
hasSilenceRules: false,
|
|
3139
|
+
preserved: [],
|
|
3140
|
+
warnings: []
|
|
3141
|
+
};
|
|
3142
|
+
sourceFile.statements.forEach((statement) => {
|
|
3143
|
+
if (!typescript.isImportDeclaration(statement)) return;
|
|
3144
|
+
const moduleSpecifier = getModuleSpecifierText(statement);
|
|
3145
|
+
if (moduleSpecifier === null) return;
|
|
3146
|
+
if (moduleSpecifier === "@useinsider/eslint-config") {
|
|
3147
|
+
classifyInsiderImport(statement, sourceFile, flags);
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
if (moduleSpecifier === "eslint/config") return;
|
|
3151
|
+
flags.preserved.push({
|
|
3152
|
+
raw: statement.getText(sourceFile),
|
|
3153
|
+
moduleSpecifier
|
|
3154
|
+
});
|
|
3155
|
+
});
|
|
3156
|
+
return flags;
|
|
3157
|
+
}
|
|
3158
|
+
function collectSilenceRulesCalls(sourceFile) {
|
|
3159
|
+
return sourceFile.statements.filter(typescript.isExpressionStatement).map((statement) => {
|
|
3160
|
+
const { expression } = statement;
|
|
3161
|
+
if (!typescript.isCallExpression(expression) || !typescript.isIdentifier(expression.expression)) return null;
|
|
3162
|
+
if (expression.expression.text !== "silenceRules") return null;
|
|
3163
|
+
const [argument] = expression.arguments;
|
|
3164
|
+
const rules = readStringLiteralEntries(argument, sourceFile);
|
|
3165
|
+
if (!rules) return null;
|
|
3166
|
+
const leadingComment = leadingCommentTextFor(sourceFile, statement);
|
|
3167
|
+
const call = {
|
|
3168
|
+
line: lineOf(sourceFile, statement),
|
|
3169
|
+
rules
|
|
3170
|
+
};
|
|
3171
|
+
if (leadingComment !== void 0) call.leadingComment = leadingComment;
|
|
3172
|
+
return call;
|
|
3173
|
+
}).filter((call) => call !== null);
|
|
3174
|
+
}
|
|
3175
|
+
function parseV1Config(source, filePath) {
|
|
3176
|
+
const sourceFile = typescript.createSourceFile(filePath, source, typescript.ScriptTarget.Latest, true);
|
|
3177
|
+
const warnings = [];
|
|
3178
|
+
const imports = inspectImports(sourceFile);
|
|
3179
|
+
warnings.push(...imports.warnings);
|
|
3180
|
+
const silenceRulesCalls = collectSilenceRulesCalls(sourceFile);
|
|
3181
|
+
const arrayLiteral = findDefaultExportArray(sourceFile);
|
|
3182
|
+
const arrayElements = arrayLiteral ? arrayLiteral.elements.map((element) => classifyArrayElement(element, sourceFile, warnings)).filter((entry) => entry !== null) : [];
|
|
3183
|
+
return {
|
|
3184
|
+
hasUseInsiderImport: imports.hasUseInsider,
|
|
3185
|
+
hasSilenceRulesImport: imports.hasSilenceRules,
|
|
3186
|
+
hasDefineConfigWrap: checkDefineConfigWrap(sourceFile),
|
|
3187
|
+
silenceRulesCalls,
|
|
3188
|
+
arrayElements,
|
|
3189
|
+
preservedImports: imports.preserved,
|
|
3190
|
+
warnings
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
//#endregion
|
|
3194
|
+
//#region src/cli/migrate/pruneUnusedImports.ts
|
|
3195
|
+
var INSIDER_MODULE_SPECIFIER = "@useinsider/eslint-config";
|
|
3196
|
+
function readModuleSpecifier(statement) {
|
|
3197
|
+
const { moduleSpecifier } = statement;
|
|
3198
|
+
if (!typescript.isStringLiteralLike(moduleSpecifier)) return null;
|
|
3199
|
+
return moduleSpecifier.text;
|
|
3200
|
+
}
|
|
3201
|
+
function readImportBindings(statement) {
|
|
3202
|
+
const moduleSpecifier = readModuleSpecifier(statement);
|
|
3203
|
+
if (moduleSpecifier === null) return null;
|
|
3204
|
+
const { importClause } = statement;
|
|
3205
|
+
if (!importClause) return {
|
|
3206
|
+
statement,
|
|
3207
|
+
moduleSpecifier,
|
|
3208
|
+
namedBindings: [],
|
|
3209
|
+
isSideEffect: true
|
|
3210
|
+
};
|
|
3211
|
+
const bindings = {
|
|
3212
|
+
statement,
|
|
3213
|
+
moduleSpecifier,
|
|
3214
|
+
namedBindings: [],
|
|
3215
|
+
isSideEffect: false
|
|
3216
|
+
};
|
|
3217
|
+
if (importClause.name) bindings.defaultBinding = importClause.name.text;
|
|
3218
|
+
const { namedBindings } = importClause;
|
|
3219
|
+
if (namedBindings) if (typescript.isNamespaceImport(namedBindings)) bindings.namespaceBinding = namedBindings.name.text;
|
|
3220
|
+
else namedBindings.elements.forEach((element) => {
|
|
3221
|
+
bindings.namedBindings.push({
|
|
3222
|
+
localName: element.name.text,
|
|
3223
|
+
importedName: element.propertyName?.text ?? element.name.text
|
|
3224
|
+
});
|
|
3225
|
+
});
|
|
3226
|
+
return bindings;
|
|
3227
|
+
}
|
|
3228
|
+
function isPropertyKeyPosition(node) {
|
|
3229
|
+
const { parent } = node;
|
|
3230
|
+
if (!parent) return false;
|
|
3231
|
+
if (typescript.isPropertyAssignment(parent) && parent.name === node) return true;
|
|
3232
|
+
if (typescript.isPropertyAccessExpression(parent) && parent.name === node) return true;
|
|
3233
|
+
if (typescript.isQualifiedName(parent) && parent.right === node) return true;
|
|
3234
|
+
return false;
|
|
3235
|
+
}
|
|
3236
|
+
function classifyIdentifier(node, referenced) {
|
|
3237
|
+
const { parent } = node;
|
|
3238
|
+
if (parent && typescript.isShorthandPropertyAssignment(parent) && parent.name === node) {
|
|
3239
|
+
referenced.add(node.text);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
if (isPropertyKeyPosition(node)) return;
|
|
3243
|
+
referenced.add(node.text);
|
|
3244
|
+
}
|
|
3245
|
+
function collectReferencedIdentifiers(sourceFile) {
|
|
3246
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
3247
|
+
function visit(node) {
|
|
3248
|
+
if (typescript.isImportDeclaration(node)) return;
|
|
3249
|
+
if (typescript.isIdentifier(node)) classifyIdentifier(node, referenced);
|
|
3250
|
+
typescript.forEachChild(node, visit);
|
|
3251
|
+
}
|
|
3252
|
+
sourceFile.statements.forEach((statement) => {
|
|
3253
|
+
if (typescript.isImportDeclaration(statement)) return;
|
|
3254
|
+
visit(statement);
|
|
3255
|
+
});
|
|
3256
|
+
return referenced;
|
|
3257
|
+
}
|
|
3258
|
+
function quoteModuleSpecifier(statement, sourceFile) {
|
|
3259
|
+
return statement.moduleSpecifier.getText(sourceFile);
|
|
3260
|
+
}
|
|
3261
|
+
function renderNamedSpecifier(binding) {
|
|
3262
|
+
if (binding.localName === binding.importedName) return binding.localName;
|
|
3263
|
+
return `${binding.importedName} as ${binding.localName}`;
|
|
3264
|
+
}
|
|
3265
|
+
function rebuildImportStatement(bindings, keptNamed, sourceFile) {
|
|
3266
|
+
const clauseParts = [];
|
|
3267
|
+
if (bindings.defaultBinding) clauseParts.push(bindings.defaultBinding);
|
|
3268
|
+
if (bindings.namespaceBinding) clauseParts.push(`* as ${bindings.namespaceBinding}`);
|
|
3269
|
+
else if (keptNamed.length > 0) {
|
|
3270
|
+
const list = keptNamed.map(renderNamedSpecifier).join(", ");
|
|
3271
|
+
clauseParts.push(`{ ${list} }`);
|
|
3272
|
+
}
|
|
3273
|
+
const moduleSpecifier = quoteModuleSpecifier(bindings.statement, sourceFile);
|
|
3274
|
+
if (clauseParts.length === 0) return `import ${moduleSpecifier};`;
|
|
3275
|
+
return `import ${clauseParts.join(", ")} from ${moduleSpecifier};`;
|
|
3276
|
+
}
|
|
3277
|
+
function summarizeUsage(bindings, referenced) {
|
|
3278
|
+
const defaultStays = bindings.defaultBinding ? referenced.has(bindings.defaultBinding) : false;
|
|
3279
|
+
const namespaceStays = bindings.namespaceBinding ? referenced.has(bindings.namespaceBinding) : false;
|
|
3280
|
+
const keptNamed = bindings.namedBindings.filter((named) => referenced.has(named.localName));
|
|
3281
|
+
return {
|
|
3282
|
+
hasDefault: Boolean(bindings.defaultBinding),
|
|
3283
|
+
hasNamespace: Boolean(bindings.namespaceBinding),
|
|
3284
|
+
hasNamed: bindings.namedBindings.length > 0,
|
|
3285
|
+
defaultStays,
|
|
3286
|
+
namespaceStays,
|
|
3287
|
+
keptNamed
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
function isFullyUnchanged(bindings, usage) {
|
|
3291
|
+
if (usage.hasDefault && !usage.defaultStays) return false;
|
|
3292
|
+
if (usage.hasNamespace && !usage.namespaceStays) return false;
|
|
3293
|
+
if (usage.hasNamed && usage.keptNamed.length !== bindings.namedBindings.length) return false;
|
|
3294
|
+
return true;
|
|
3295
|
+
}
|
|
3296
|
+
function hasAnyBinding(usage) {
|
|
3297
|
+
return usage.hasDefault || usage.hasNamespace || usage.hasNamed;
|
|
3298
|
+
}
|
|
3299
|
+
function hasAnySurvivingBinding(usage) {
|
|
3300
|
+
const namedStays = usage.hasNamed && usage.keptNamed.length > 0;
|
|
3301
|
+
return usage.defaultStays || usage.namespaceStays || namedStays;
|
|
3302
|
+
}
|
|
3303
|
+
function buildRebuiltBindings(bindings, usage) {
|
|
3304
|
+
return {
|
|
3305
|
+
...bindings,
|
|
3306
|
+
defaultBinding: usage.defaultStays ? bindings.defaultBinding : void 0,
|
|
3307
|
+
namespaceBinding: usage.namespaceStays ? bindings.namespaceBinding : void 0,
|
|
3308
|
+
namedBindings: usage.keptNamed
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
function decideRewrite(bindings, referenced, sourceFile) {
|
|
3312
|
+
if (bindings.moduleSpecifier === INSIDER_MODULE_SPECIFIER || bindings.isSideEffect) return { keep: true };
|
|
3313
|
+
const usage = summarizeUsage(bindings, referenced);
|
|
3314
|
+
if (!hasAnyBinding(usage)) return { keep: true };
|
|
3315
|
+
if (!hasAnySurvivingBinding(usage)) return { keep: false };
|
|
3316
|
+
if (isFullyUnchanged(bindings, usage)) return { keep: true };
|
|
3317
|
+
return {
|
|
3318
|
+
keep: true,
|
|
3319
|
+
replacement: rebuildImportStatement(buildRebuiltBindings(bindings, usage), usage.keptNamed, sourceFile)
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
function lineRangeForStatement(statement, sourceFile) {
|
|
3323
|
+
return {
|
|
3324
|
+
start: sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile)).line,
|
|
3325
|
+
end: sourceFile.getLineAndCharacterOfPosition(statement.getEnd()).line
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
function applyReplacements(source, replacements) {
|
|
3329
|
+
if (replacements.length === 0) return source;
|
|
3330
|
+
const lines = source.split("\n");
|
|
3331
|
+
replacements.slice().sort((left, right) => right.startLine - left.startLine).forEach((replacement) => {
|
|
3332
|
+
const removeCount = replacement.endLine - replacement.startLine + 1;
|
|
3333
|
+
if (replacement.text === null) lines.splice(replacement.startLine, removeCount);
|
|
3334
|
+
else lines.splice(replacement.startLine, removeCount, replacement.text);
|
|
3335
|
+
});
|
|
3336
|
+
return lines.join("\n");
|
|
3337
|
+
}
|
|
3338
|
+
function pruneUnusedImports(source) {
|
|
3339
|
+
const sourceFile = typescript.createSourceFile("migrated.mjs", source, typescript.ScriptTarget.Latest, true);
|
|
3340
|
+
const importStatements = sourceFile.statements.filter(typescript.isImportDeclaration);
|
|
3341
|
+
if (importStatements.length === 0) return source;
|
|
3342
|
+
const referenced = collectReferencedIdentifiers(sourceFile);
|
|
3343
|
+
const replacements = [];
|
|
3344
|
+
importStatements.forEach((statement) => {
|
|
3345
|
+
const bindings = readImportBindings(statement);
|
|
3346
|
+
if (!bindings) return;
|
|
3347
|
+
const decision = decideRewrite(bindings, referenced, sourceFile);
|
|
3348
|
+
if (decision.keep && decision.replacement === void 0) return;
|
|
3349
|
+
const range = lineRangeForStatement(statement, sourceFile);
|
|
3350
|
+
if (!decision.keep) {
|
|
3351
|
+
replacements.push({
|
|
3352
|
+
startLine: range.start,
|
|
3353
|
+
endLine: range.end,
|
|
3354
|
+
text: null
|
|
3355
|
+
});
|
|
3356
|
+
return;
|
|
3357
|
+
}
|
|
3358
|
+
if (decision.replacement !== void 0) replacements.push({
|
|
3359
|
+
startLine: range.start,
|
|
3360
|
+
endLine: range.end,
|
|
3361
|
+
text: decision.replacement
|
|
3362
|
+
});
|
|
3363
|
+
});
|
|
3364
|
+
return applyReplacements(source, replacements);
|
|
3365
|
+
}
|
|
3366
|
+
/* v8 ignore stop */
|
|
3367
|
+
//#endregion
|
|
3368
|
+
//#region src/cli/migrate/render.ts
|
|
3369
|
+
var indent = " ";
|
|
3370
|
+
var MAX_LINE_LENGTH = 120;
|
|
3371
|
+
function quote(value) {
|
|
3372
|
+
return `'${value.replace(/'/g, "\\'")}'`;
|
|
3373
|
+
}
|
|
3374
|
+
function anyEntryHasComment(entries) {
|
|
3375
|
+
return entries.some((entry) => entry.leadingComment !== void 0);
|
|
3376
|
+
}
|
|
3377
|
+
function indentCommentBlock(raw, baseIndent) {
|
|
3378
|
+
return raw.split("\n").map((line) => `${baseIndent}${line}`).join("\n");
|
|
3379
|
+
}
|
|
3380
|
+
function formatStringArrayInline(entries) {
|
|
3381
|
+
return `[${entries.map((entry) => quote(entry.value)).join(", ")}]`;
|
|
3382
|
+
}
|
|
3383
|
+
function formatStringArrayMultiline(entries, baseIndent) {
|
|
3384
|
+
const childIndent = `${baseIndent}${indent}`;
|
|
3385
|
+
const lines = ["["];
|
|
3386
|
+
entries.forEach((entry) => {
|
|
3387
|
+
if (entry.leadingComment !== void 0) lines.push(indentCommentBlock(entry.leadingComment, childIndent));
|
|
3388
|
+
lines.push(`${childIndent}${quote(entry.value)},`);
|
|
3389
|
+
});
|
|
3390
|
+
lines.push(`${baseIndent}]`);
|
|
3391
|
+
return lines.join("\n");
|
|
3392
|
+
}
|
|
3393
|
+
function renderStringArrayField(label, entries, baseIndent) {
|
|
3394
|
+
if (!anyEntryHasComment(entries)) {
|
|
3395
|
+
const inline = `${baseIndent}${label}: ${formatStringArrayInline(entries)},`;
|
|
3396
|
+
if (inline.length <= MAX_LINE_LENGTH) return inline;
|
|
3397
|
+
}
|
|
3398
|
+
return `${baseIndent}${label}: ${formatStringArrayMultiline(entries, baseIndent)},`;
|
|
3399
|
+
}
|
|
3400
|
+
function formatGlobalValue(value) {
|
|
3401
|
+
if (typeof value === "string") return quote(value);
|
|
3402
|
+
return String(value);
|
|
3403
|
+
}
|
|
3404
|
+
function tokensFromRecord$1(globals) {
|
|
3405
|
+
return Object.entries(globals).map(([key, value]) => ({
|
|
3406
|
+
kind: "named",
|
|
3407
|
+
key,
|
|
3408
|
+
value
|
|
3409
|
+
}));
|
|
3410
|
+
}
|
|
3411
|
+
function formatGlobalTokenInline(token) {
|
|
3412
|
+
if (token.kind === "spread") return `...${token.raw}`;
|
|
3413
|
+
return `${token.key}: ${formatGlobalValue(token.value)}`;
|
|
3414
|
+
}
|
|
3415
|
+
function formatGlobalTokenLine(token, childIndent) {
|
|
3416
|
+
if (token.kind === "spread") return `${childIndent}...${token.raw},`;
|
|
3417
|
+
return `${childIndent}${token.key}: ${formatGlobalValue(token.value)},`;
|
|
3418
|
+
}
|
|
3419
|
+
function formatGlobalsTokensInline(tokens) {
|
|
3420
|
+
return `{ ${tokens.map(formatGlobalTokenInline).join(", ")} }`;
|
|
3421
|
+
}
|
|
3422
|
+
function formatGlobalsTokensMultiline(tokens, baseIndent) {
|
|
3423
|
+
const childIndent = `${baseIndent}${indent}`;
|
|
3424
|
+
return [
|
|
3425
|
+
"{",
|
|
3426
|
+
...tokens.map((token) => formatGlobalTokenLine(token, childIndent)),
|
|
3427
|
+
`${baseIndent}}`
|
|
3428
|
+
].join("\n");
|
|
3429
|
+
}
|
|
3430
|
+
function renderGlobalsField(tokens, baseIndent) {
|
|
3431
|
+
const inline = `${baseIndent}globals: ${formatGlobalsTokensInline(tokens)},`;
|
|
3432
|
+
if (inline.length <= MAX_LINE_LENGTH) return inline;
|
|
3433
|
+
return `${baseIndent}globals: ${formatGlobalsTokensMultiline(tokens, baseIndent)},`;
|
|
3434
|
+
}
|
|
3435
|
+
function resolveEntryGlobalsTokens(entry) {
|
|
3436
|
+
if (entry.globalsTokens && entry.globalsTokens.length > 0) return entry.globalsTokens;
|
|
3437
|
+
if (entry.globals && Object.keys(entry.globals).length > 0) return tokensFromRecord$1(entry.globals);
|
|
3438
|
+
}
|
|
3439
|
+
function pushFieldComment(lines, comment, fieldIndent) {
|
|
3440
|
+
if (comment === void 0) return;
|
|
3441
|
+
lines.push(indentCommentBlock(comment, fieldIndent));
|
|
3442
|
+
}
|
|
3443
|
+
function pushArrayField(lines, label, entries, fieldComment, fieldIndent) {
|
|
3444
|
+
if (!entries || entries.length === 0) return;
|
|
3445
|
+
pushFieldComment(lines, fieldComment, fieldIndent);
|
|
3446
|
+
lines.push(renderStringArrayField(label, entries, fieldIndent));
|
|
3447
|
+
}
|
|
3448
|
+
function formatEntry(entry, baseIndent) {
|
|
3449
|
+
const fieldIndent = `${baseIndent}${indent}`;
|
|
3450
|
+
const lines = [`${baseIndent}{`];
|
|
3451
|
+
pushFieldComment(lines, entry.fieldComments?.files, fieldIndent);
|
|
3452
|
+
lines.push(renderStringArrayField("files", entry.files, fieldIndent));
|
|
3453
|
+
pushArrayField(lines, "tsconfigPaths", entry.tsconfigPaths, entry.fieldComments?.tsconfigPaths, fieldIndent);
|
|
3454
|
+
pushArrayField(lines, "globalComponents", entry.globalComponents, entry.fieldComments?.globalComponents, fieldIndent);
|
|
3455
|
+
pushArrayField(lines, "ignores", entry.ignores, entry.fieldComments?.ignores, fieldIndent);
|
|
3456
|
+
const tokens = resolveEntryGlobalsTokens(entry);
|
|
3457
|
+
if (tokens) {
|
|
3458
|
+
pushFieldComment(lines, entry.fieldComments?.globals, fieldIndent);
|
|
3459
|
+
lines.push(renderGlobalsField(tokens, fieldIndent));
|
|
3460
|
+
}
|
|
3461
|
+
lines.push(`${baseIndent}},`);
|
|
3462
|
+
return lines.join("\n");
|
|
3463
|
+
}
|
|
3464
|
+
function formatScope(scope, entries, baseIndent) {
|
|
3465
|
+
const childIndent = `${baseIndent}${indent}`;
|
|
3466
|
+
const renderedEntries = [];
|
|
3467
|
+
entries.forEach((entry) => {
|
|
3468
|
+
if (entry.leadingComment !== void 0) renderedEntries.push(indentCommentBlock(entry.leadingComment, childIndent));
|
|
3469
|
+
renderedEntries.push(formatEntry(entry, childIndent));
|
|
3470
|
+
});
|
|
3471
|
+
return [
|
|
3472
|
+
`${baseIndent}${scope}: [`,
|
|
3473
|
+
...renderedEntries,
|
|
3474
|
+
`${baseIndent}],`
|
|
3475
|
+
].join("\n");
|
|
3476
|
+
}
|
|
3477
|
+
function renderInsiderImport(plan) {
|
|
3478
|
+
return `import { ${(plan.needsSilenceRulesImport ? ["silenceRules", "useInsider"] : ["useInsider"]).slice().sort((left, right) => left.localeCompare(right)).join(", ")} } from '@useinsider/eslint-config';`;
|
|
3479
|
+
}
|
|
3480
|
+
function renderPreservedImports(preserved) {
|
|
3481
|
+
return preserved.map((item) => item.raw);
|
|
3482
|
+
}
|
|
3483
|
+
function renderImports(plan) {
|
|
3484
|
+
return [...renderPreservedImports(plan.preservedImports ?? []), renderInsiderImport(plan)].join("\n");
|
|
3485
|
+
}
|
|
3486
|
+
function renderSilenceRulesCall(rules, leadingComment) {
|
|
3487
|
+
const hasComment = anyEntryHasComment(rules);
|
|
3488
|
+
const prefix = leadingComment === void 0 ? "" : `${leadingComment}\n`;
|
|
3489
|
+
if (!hasComment) {
|
|
3490
|
+
const inline = `${prefix}silenceRules(${formatStringArrayInline(rules)});`;
|
|
3491
|
+
if (inline.length - prefix.length <= MAX_LINE_LENGTH) return inline;
|
|
3492
|
+
}
|
|
3493
|
+
const lines = ["silenceRules(["];
|
|
3494
|
+
rules.forEach((rule) => {
|
|
3495
|
+
if (rule.leadingComment !== void 0) lines.push(indentCommentBlock(rule.leadingComment, indent));
|
|
3496
|
+
lines.push(`${indent}${quote(rule.value)},`);
|
|
3497
|
+
});
|
|
3498
|
+
lines.push("]);");
|
|
3499
|
+
return `${prefix}${lines.join("\n")}`;
|
|
3500
|
+
}
|
|
3501
|
+
function renderSilenceRulesCalls(plan) {
|
|
3502
|
+
if (plan.silenceRulesCalls.length === 0) return "";
|
|
3503
|
+
return plan.silenceRulesCalls.map((call) => renderSilenceRulesCall(call.rules, call.leadingComment)).join("\n\n");
|
|
3504
|
+
}
|
|
3505
|
+
function hasAnyContent(plan) {
|
|
3506
|
+
const { config } = plan;
|
|
3507
|
+
return config.scopes.size > 0 || config.ignores.length > 0 || config.strictGlobs.length > 0 || config.strictWholeFile || config.spellChecker.ignoredPaths.length > 0 || config.configFiles.length > 0;
|
|
3508
|
+
}
|
|
3509
|
+
function renderStrict(plan) {
|
|
3510
|
+
if (plan.config.strictWholeFile) return `${indent}strict: true,`;
|
|
3511
|
+
if (plan.config.strictGlobs.length > 0) return renderStringArrayField("strict", plan.config.strictGlobs, indent);
|
|
3512
|
+
return null;
|
|
3513
|
+
}
|
|
3514
|
+
function renderSpellChecker(plan) {
|
|
3515
|
+
if (plan.config.spellChecker.ignoredPaths.length === 0) return null;
|
|
3516
|
+
const innerIndent = `${indent}${indent}`;
|
|
3517
|
+
return [
|
|
3518
|
+
`${indent}spellChecker: {`,
|
|
3519
|
+
renderStringArrayField("ignoredPaths", plan.config.spellChecker.ignoredPaths, innerIndent),
|
|
3520
|
+
`${indent}},`
|
|
3521
|
+
].join("\n");
|
|
3522
|
+
}
|
|
3523
|
+
function renderScopes(plan) {
|
|
3524
|
+
return [...plan.config.scopes.entries()].map(([scope, entries]) => formatScope(scope, entries, indent));
|
|
3525
|
+
}
|
|
3526
|
+
function renderConfigFiles(plan) {
|
|
3527
|
+
if (plan.config.configFiles.length === 0) return null;
|
|
3528
|
+
return renderStringArrayField("configFiles", plan.config.configFiles, indent);
|
|
3529
|
+
}
|
|
3530
|
+
function renderConfigBody(plan) {
|
|
3531
|
+
const lines = [];
|
|
3532
|
+
if (plan.config.ignores.length > 0) lines.push(renderStringArrayField("ignores", plan.config.ignores, indent));
|
|
3533
|
+
lines.push(...renderScopes(plan));
|
|
3534
|
+
const configFilesLine = renderConfigFiles(plan);
|
|
3535
|
+
if (configFilesLine) lines.push(configFilesLine);
|
|
3536
|
+
const strictLine = renderStrict(plan);
|
|
3537
|
+
if (strictLine) lines.push(strictLine);
|
|
3538
|
+
const spellCheckerSection = renderSpellChecker(plan);
|
|
3539
|
+
if (spellCheckerSection) lines.push(spellCheckerSection);
|
|
3540
|
+
return lines.join("\n");
|
|
3541
|
+
}
|
|
3542
|
+
function renderV2(plan) {
|
|
3543
|
+
const parts = [renderImports(plan)];
|
|
3544
|
+
const silenceRulesSection = renderSilenceRulesCalls(plan);
|
|
3545
|
+
if (silenceRulesSection) {
|
|
3546
|
+
parts.push("");
|
|
3547
|
+
parts.push(silenceRulesSection);
|
|
3548
|
+
}
|
|
3549
|
+
parts.push("");
|
|
3550
|
+
if (plan.useInsiderLeadingComment !== void 0) parts.push(plan.useInsiderLeadingComment);
|
|
3551
|
+
if (!hasAnyContent(plan)) {
|
|
3552
|
+
parts.push("export default useInsider({});");
|
|
3553
|
+
parts.push("");
|
|
3554
|
+
return pruneUnusedImports(parts.join("\n"));
|
|
3555
|
+
}
|
|
3556
|
+
parts.push("export default useInsider({");
|
|
3557
|
+
parts.push(renderConfigBody(plan));
|
|
3558
|
+
parts.push("});");
|
|
3559
|
+
parts.push("");
|
|
3560
|
+
return pruneUnusedImports(parts.join("\n"));
|
|
3561
|
+
}
|
|
3562
|
+
//#endregion
|
|
3563
|
+
//#region src/cli/migrate/transform.ts
|
|
3564
|
+
function appendUniqueEntry(target, incoming) {
|
|
3565
|
+
if (hasValue(target, incoming.value)) return;
|
|
3566
|
+
target.push(cloneEntry(incoming));
|
|
3567
|
+
}
|
|
3568
|
+
var presetToScope = {
|
|
3569
|
+
javascript: "javascript",
|
|
3570
|
+
"javascript-dom": "javascriptDom",
|
|
3571
|
+
"javascript-node": "javascriptNode",
|
|
3572
|
+
typescript: "typescript",
|
|
3573
|
+
"typescript-dom": "typescriptDom",
|
|
3574
|
+
"typescript-node": "typescriptNode",
|
|
3575
|
+
vue2: "vue2",
|
|
3576
|
+
vue3: "vue3",
|
|
3577
|
+
"vue2-typescript": "vue2Typescript",
|
|
3578
|
+
"vue3-typescript": "vue3Typescript",
|
|
3579
|
+
jest: "jest",
|
|
3580
|
+
vitest: "vitest",
|
|
3581
|
+
"jsdoc-based-ts": "jsdocBasedTs"
|
|
3582
|
+
};
|
|
3583
|
+
function isTypescriptContext(call) {
|
|
3584
|
+
if (call.parserIsTypescript) return true;
|
|
3585
|
+
return Boolean(call.parserProject && call.parserProject.length > 0);
|
|
3586
|
+
}
|
|
3587
|
+
function resolveAliasPreset(preset, call) {
|
|
3588
|
+
if (preset === "dom") return isTypescriptContext(call) ? "typescript-dom" : "javascript-dom";
|
|
3589
|
+
if (preset === "node") return isTypescriptContext(call) ? "typescript-node" : "javascript-node";
|
|
3590
|
+
return preset;
|
|
3591
|
+
}
|
|
3592
|
+
function promoteScopeForParser(scope, call) {
|
|
3593
|
+
if (!call.parserIsTypescript) return scope;
|
|
3594
|
+
if (scope === "javascript") return "typescript";
|
|
3595
|
+
if (scope === "javascriptDom") return "typescriptDom";
|
|
3596
|
+
if (scope === "javascriptNode") return "typescriptNode";
|
|
3597
|
+
if (scope === "vue2") return "vue2Typescript";
|
|
3598
|
+
if (scope === "vue3") return "vue3Typescript";
|
|
3599
|
+
return scope;
|
|
3600
|
+
}
|
|
3601
|
+
var tsScopes = new Set([
|
|
3602
|
+
"typescript",
|
|
3603
|
+
"typescriptDom",
|
|
3604
|
+
"typescriptNode",
|
|
3605
|
+
"vue2Typescript",
|
|
3606
|
+
"vue3Typescript",
|
|
3607
|
+
"jsdocBasedTs"
|
|
3608
|
+
]);
|
|
3609
|
+
var vueScopes = new Set([
|
|
3610
|
+
"vue2",
|
|
3611
|
+
"vue3",
|
|
3612
|
+
"vue2Typescript",
|
|
3613
|
+
"vue3Typescript"
|
|
3614
|
+
]);
|
|
3615
|
+
function freshConfig() {
|
|
3616
|
+
return {
|
|
3617
|
+
ignores: [],
|
|
3618
|
+
scopes: /* @__PURE__ */ new Map(),
|
|
3619
|
+
strictGlobs: [],
|
|
3620
|
+
strictWholeFile: false,
|
|
3621
|
+
spellChecker: { ignoredPaths: [] },
|
|
3622
|
+
configFiles: []
|
|
3623
|
+
};
|
|
3624
|
+
}
|
|
3625
|
+
function appendScope(config, scope, entry) {
|
|
3626
|
+
const existing = config.scopes.get(scope);
|
|
3627
|
+
if (existing) existing.push(entry);
|
|
3628
|
+
else config.scopes.set(scope, [entry]);
|
|
3629
|
+
}
|
|
3630
|
+
function applyTypescriptFields(entry, scope, call, warnings) {
|
|
3631
|
+
if (!tsScopes.has(scope)) return;
|
|
3632
|
+
if (call.parserProject && call.parserProject.length > 0) {
|
|
3633
|
+
entry.tsconfigPaths = cloneEntries(call.parserProject);
|
|
3634
|
+
return;
|
|
3635
|
+
}
|
|
3636
|
+
if (call.parserProjectService) warnings.push({
|
|
3637
|
+
kind: "project-service-dropped",
|
|
3638
|
+
line: call.line,
|
|
3639
|
+
message: "'projectService: true' is not supported in v2 — dropped"
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
function applyVueFields(entry, scope, call) {
|
|
3643
|
+
if (vueScopes.has(scope) && call.globalComponents && call.globalComponents.length > 0) entry.globalComponents = cloneEntries(call.globalComponents);
|
|
3644
|
+
}
|
|
3645
|
+
function mapV1FieldComments(call, scope) {
|
|
3646
|
+
const incoming = call.fieldComments;
|
|
3647
|
+
if (!incoming) return;
|
|
3648
|
+
const result = {};
|
|
3649
|
+
if (incoming.files !== void 0) result.files = incoming.files;
|
|
3650
|
+
if (incoming.ignores !== void 0) result.ignores = incoming.ignores;
|
|
3651
|
+
if (incoming.globals !== void 0) result.globals = incoming.globals;
|
|
3652
|
+
if (incoming.parserProject !== void 0 && tsScopes.has(scope)) result.tsconfigPaths = incoming.parserProject;
|
|
3653
|
+
if (incoming.globalComponents !== void 0 && vueScopes.has(scope)) result.globalComponents = incoming.globalComponents;
|
|
3654
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
3655
|
+
}
|
|
3656
|
+
function buildEntry(call, scope, warnings) {
|
|
3657
|
+
if (!call.files || call.files.length === 0) {
|
|
3658
|
+
warnings.push({
|
|
3659
|
+
kind: "unrecognized-block",
|
|
3660
|
+
line: call.line,
|
|
3661
|
+
message: `useInsider preset '${scope}' has no files glob; skipping`
|
|
3662
|
+
});
|
|
3663
|
+
return null;
|
|
3664
|
+
}
|
|
3665
|
+
const entry = { files: cloneEntries(call.files) };
|
|
3666
|
+
if (call.ignores && call.ignores.length > 0) entry.ignores = cloneEntries(call.ignores);
|
|
3667
|
+
applyTypescriptFields(entry, scope, call, warnings);
|
|
3668
|
+
applyVueFields(entry, scope, call);
|
|
3669
|
+
if (call.globalsTokens && call.globalsTokens.length > 0) entry.globalsTokens = call.globalsTokens.map((token) => ({ ...token }));
|
|
3670
|
+
if (call.globals && Object.keys(call.globals).length > 0) entry.globals = { ...call.globals };
|
|
3671
|
+
const fieldComments = mapV1FieldComments(call, scope);
|
|
3672
|
+
if (fieldComments) entry.fieldComments = fieldComments;
|
|
3673
|
+
return entry;
|
|
3674
|
+
}
|
|
3675
|
+
function handleStrictPreset(call, config) {
|
|
3676
|
+
if (!call.files || call.files.length === 0) {
|
|
3677
|
+
config.strictWholeFile = true;
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3680
|
+
call.files.forEach((file) => appendUniqueEntry(config.strictGlobs, file));
|
|
3681
|
+
}
|
|
3682
|
+
function handlePreset(preset, call, config, warnings) {
|
|
3683
|
+
if (preset === "strict") {
|
|
3684
|
+
handleStrictPreset(call, config);
|
|
3685
|
+
return null;
|
|
3686
|
+
}
|
|
3687
|
+
const baseScope = presetToScope[resolveAliasPreset(preset, call)];
|
|
3688
|
+
if (!baseScope) {
|
|
3689
|
+
warnings.push({
|
|
3690
|
+
kind: "unknown-preset",
|
|
3691
|
+
line: call.line,
|
|
3692
|
+
message: `Preset '${preset}' has no v2 mapping — dropped`
|
|
3693
|
+
});
|
|
3694
|
+
return null;
|
|
3695
|
+
}
|
|
3696
|
+
const scope = promoteScopeForParser(baseScope, call);
|
|
3697
|
+
const entry = buildEntry(call, scope, warnings);
|
|
3698
|
+
if (entry) {
|
|
3699
|
+
appendScope(config, scope, entry);
|
|
3700
|
+
return entry;
|
|
3701
|
+
}
|
|
3702
|
+
return null;
|
|
3703
|
+
}
|
|
3704
|
+
function emptyStats() {
|
|
3705
|
+
return {
|
|
3706
|
+
droppedRules: 0,
|
|
3707
|
+
droppedRuleBlocks: 0,
|
|
3708
|
+
droppedPlugins: /* @__PURE__ */ new Set(),
|
|
3709
|
+
droppedPluginEntries: 0
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
function captureConfigPresetFiles(call, config) {
|
|
3713
|
+
if (!call.presets.includes("config") || !call.files) return;
|
|
3714
|
+
call.files.forEach((file) => appendUniqueEntry(config.configFiles, file));
|
|
3715
|
+
}
|
|
3716
|
+
function processUseInsiderCall(call, config, warnings) {
|
|
3717
|
+
captureConfigPresetFiles(call, config);
|
|
3718
|
+
const filteredPresets = call.presets.filter((preset) => preset !== "config");
|
|
3719
|
+
const stats = emptyStats();
|
|
3720
|
+
stats.droppedRules = call.extraRuleCount > 0 ? call.extraRuleCount : 0;
|
|
3721
|
+
stats.droppedRuleBlocks = call.extraRuleCount > 0 ? 1 : 0;
|
|
3722
|
+
if (call.customPluginNames && call.customPluginNames.length > 0) {
|
|
3723
|
+
call.customPluginNames.forEach((name) => stats.droppedPlugins.add(name));
|
|
3724
|
+
stats.droppedPluginEntries = 1;
|
|
3725
|
+
}
|
|
3726
|
+
let firstEmitted = null;
|
|
3727
|
+
filteredPresets.forEach((preset) => {
|
|
3728
|
+
const emitted = handlePreset(preset, call, config, warnings);
|
|
3729
|
+
if (emitted && !firstEmitted) firstEmitted = emitted;
|
|
3730
|
+
});
|
|
3731
|
+
if (firstEmitted && call.leadingComment !== void 0) firstEmitted.leadingComment = call.leadingComment;
|
|
3732
|
+
return stats;
|
|
3733
|
+
}
|
|
3734
|
+
function applyIgnoresBlock(element, config) {
|
|
3735
|
+
if (element.leadingComment !== void 0 && element.ignores.length > 0 && config.ignores.length === 0 && element.ignores[0].leadingComment === void 0) {
|
|
3736
|
+
const first = cloneEntry(element.ignores[0]);
|
|
3737
|
+
first.leadingComment = element.leadingComment;
|
|
3738
|
+
config.ignores.push(first);
|
|
3739
|
+
element.ignores.slice(1).forEach((ignore) => appendUniqueEntry(config.ignores, ignore));
|
|
3740
|
+
return;
|
|
3741
|
+
}
|
|
3742
|
+
element.ignores.forEach((ignore) => appendUniqueEntry(config.ignores, ignore));
|
|
3743
|
+
}
|
|
3744
|
+
function applySpellCheckerOff(element, config) {
|
|
3745
|
+
element.files.forEach((file) => appendUniqueEntry(config.spellChecker.ignoredPaths, file));
|
|
3746
|
+
}
|
|
3747
|
+
function filesOverlap(left, right) {
|
|
3748
|
+
const leftValues = entryValues(left);
|
|
3749
|
+
const rightValues = entryValues(right);
|
|
3750
|
+
if (leftValues.length === rightValues.length && leftValues.every((value, index) => value === rightValues[index])) return true;
|
|
3751
|
+
return leftValues.some((value) => rightValues.includes(value));
|
|
3752
|
+
}
|
|
3753
|
+
function findEntryForStandalone(config, files) {
|
|
3754
|
+
return [...config.scopes.values()].flat().find((entry) => filesOverlap(entry.files, files)) ?? null;
|
|
3755
|
+
}
|
|
3756
|
+
function mergeStandaloneTokensIntoEntry(entry, incoming) {
|
|
3757
|
+
const existing = entry.globalsTokens ?? [];
|
|
3758
|
+
const namedSeen = /* @__PURE__ */ new Map();
|
|
3759
|
+
existing.forEach((token, index) => {
|
|
3760
|
+
if (token.kind === "named") namedSeen.set(token.key, index);
|
|
3761
|
+
});
|
|
3762
|
+
const merged = existing.slice();
|
|
3763
|
+
incoming.forEach((token) => {
|
|
3764
|
+
if (token.kind === "spread") {
|
|
3765
|
+
merged.push({ ...token });
|
|
3766
|
+
return;
|
|
3767
|
+
}
|
|
3768
|
+
const seenIndex = namedSeen.get(token.key);
|
|
3769
|
+
if (seenIndex === void 0) {
|
|
3770
|
+
namedSeen.set(token.key, merged.length);
|
|
3771
|
+
merged.push({ ...token });
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
merged[seenIndex] = { ...token };
|
|
3775
|
+
});
|
|
3776
|
+
entry.globalsTokens = merged;
|
|
3777
|
+
const namedRecord = {};
|
|
3778
|
+
merged.forEach((token) => {
|
|
3779
|
+
if (token.kind === "named") namedRecord[token.key] = token.value;
|
|
3780
|
+
});
|
|
3781
|
+
entry.globals = Object.keys(namedRecord).length > 0 ? namedRecord : void 0;
|
|
3782
|
+
}
|
|
3783
|
+
function inferScopeForStandalone(files) {
|
|
3784
|
+
const values = entryValues(files);
|
|
3785
|
+
if (values.some((file) => file.includes(".vue"))) return "vue3";
|
|
3786
|
+
if (values.some((file) => file.includes(".ts") || file.includes(".tsx"))) return "typescript";
|
|
3787
|
+
return "javascript";
|
|
3788
|
+
}
|
|
3789
|
+
function applyStandaloneGlobals(element, config) {
|
|
3790
|
+
const existing = findEntryForStandalone(config, element.files);
|
|
3791
|
+
if (existing) {
|
|
3792
|
+
mergeStandaloneTokensIntoEntry(existing, element.globalsTokens);
|
|
3793
|
+
if (existing.leadingComment === void 0 && element.leadingComment !== void 0) existing.leadingComment = element.leadingComment;
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
const scope = inferScopeForStandalone(element.files);
|
|
3797
|
+
const entry = { files: cloneEntries(element.files) };
|
|
3798
|
+
mergeStandaloneTokensIntoEntry(entry, element.globalsTokens);
|
|
3799
|
+
if (element.leadingComment !== void 0) entry.leadingComment = element.leadingComment;
|
|
3800
|
+
appendScope(config, scope, entry);
|
|
3801
|
+
}
|
|
3802
|
+
function processElement(element, config, warnings) {
|
|
3803
|
+
if (element.kind === "ignoresBlock") {
|
|
3804
|
+
applyIgnoresBlock(element, config);
|
|
3805
|
+
return emptyStats();
|
|
3806
|
+
}
|
|
3807
|
+
if (element.kind === "spellCheckerOff") {
|
|
3808
|
+
applySpellCheckerOff(element, config);
|
|
3809
|
+
return emptyStats();
|
|
3810
|
+
}
|
|
3811
|
+
if (element.kind === "standaloneGlobals") {
|
|
3812
|
+
applyStandaloneGlobals(element, config);
|
|
3813
|
+
return emptyStats();
|
|
3814
|
+
}
|
|
3815
|
+
if (element.kind === "useInsiderCall") return processUseInsiderCall(element, config, warnings);
|
|
3816
|
+
return emptyStats();
|
|
3817
|
+
}
|
|
3818
|
+
function formatDroppedRulesMessage(count, blocks) {
|
|
3819
|
+
return `Dropped ${count} custom ${count === 1 ? "rule override" : "rule overrides"} across ${blocks} ${blocks === 1 ? "block" : "blocks"}`;
|
|
3820
|
+
}
|
|
3821
|
+
function formatDroppedPluginsMessage(pluginNames, entries) {
|
|
3822
|
+
return `Dropped custom plugins on ${entries} entries: ${pluginNames.slice().sort((left, right) => left.localeCompare(right)).join(", ")}`;
|
|
3823
|
+
}
|
|
3824
|
+
function mergeStats(left, right) {
|
|
3825
|
+
const merged = emptyStats();
|
|
3826
|
+
merged.droppedRules = left.droppedRules + right.droppedRules;
|
|
3827
|
+
merged.droppedRuleBlocks = left.droppedRuleBlocks + right.droppedRuleBlocks;
|
|
3828
|
+
merged.droppedPluginEntries = left.droppedPluginEntries + right.droppedPluginEntries;
|
|
3829
|
+
left.droppedPlugins.forEach((name) => merged.droppedPlugins.add(name));
|
|
3830
|
+
right.droppedPlugins.forEach((name) => merged.droppedPlugins.add(name));
|
|
3831
|
+
return merged;
|
|
3832
|
+
}
|
|
3833
|
+
function tokensFromRecord(globals) {
|
|
3834
|
+
return Object.entries(globals).map(([key, value]) => ({
|
|
3835
|
+
kind: "named",
|
|
3836
|
+
key,
|
|
3837
|
+
value
|
|
3838
|
+
}));
|
|
3839
|
+
}
|
|
3840
|
+
function resolveBaseTokens(entry) {
|
|
3841
|
+
if (entry.globalsTokens && entry.globalsTokens.length > 0) return entry.globalsTokens;
|
|
3842
|
+
if (entry.globals && Object.keys(entry.globals).length > 0) return tokensFromRecord(entry.globals);
|
|
3843
|
+
}
|
|
3844
|
+
function applyGlobalsFolding(config) {
|
|
3845
|
+
let anySyntheticEmitted = false;
|
|
3846
|
+
config.scopes.forEach((entries) => {
|
|
3847
|
+
entries.forEach((entry) => {
|
|
3848
|
+
const baseTokens = resolveBaseTokens(entry);
|
|
3849
|
+
if (!baseTokens) return;
|
|
3850
|
+
const { tokens, addedSpreadEnvs } = foldGlobalsTokens(baseTokens);
|
|
3851
|
+
if (addedSpreadEnvs.length > 0) anySyntheticEmitted = true;
|
|
3852
|
+
entry.globalsTokens = tokens;
|
|
3853
|
+
const namedRecord = {};
|
|
3854
|
+
tokens.forEach((token) => {
|
|
3855
|
+
if (token.kind === "named") namedRecord[token.key] = token.value;
|
|
3856
|
+
});
|
|
3857
|
+
entry.globals = Object.keys(namedRecord).length > 0 ? namedRecord : void 0;
|
|
3858
|
+
});
|
|
3859
|
+
});
|
|
3860
|
+
return anySyntheticEmitted;
|
|
3861
|
+
}
|
|
3862
|
+
function ensureGlobalsImport(preserved) {
|
|
3863
|
+
if (preserved.some((item) => item.moduleSpecifier === "globals")) return preserved;
|
|
3864
|
+
return [{
|
|
3865
|
+
raw: "import globals from 'globals';",
|
|
3866
|
+
moduleSpecifier: "globals"
|
|
3867
|
+
}, ...preserved];
|
|
3868
|
+
}
|
|
3869
|
+
function countRescuedSiblingCalls(parseResult) {
|
|
3870
|
+
return parseResult.arrayElements.reduce((accumulator, element) => {
|
|
3871
|
+
if (element.kind === "useInsiderCall" && element.rescuedSiblingLanguageOptions === true) return accumulator + 1;
|
|
3872
|
+
return accumulator;
|
|
3873
|
+
}, 0);
|
|
3874
|
+
}
|
|
3875
|
+
function formatRescuedSiblingMessage(count) {
|
|
3876
|
+
return `Rescued misplaced \`languageOptions\` from ${count} \`useInsider()\` ${count === 1 ? "call" : "calls"} — these were source-level mistakes that v1 ignored silently`;
|
|
3877
|
+
}
|
|
3878
|
+
function transformToV2(parseResult) {
|
|
3879
|
+
const config = freshConfig();
|
|
3880
|
+
const warnings = [...parseResult.warnings];
|
|
3881
|
+
const stats = parseResult.arrayElements.reduce((accumulator, element) => mergeStats(accumulator, processElement(element, config, warnings)), emptyStats());
|
|
3882
|
+
if (stats.droppedRules > 0) warnings.push({
|
|
3883
|
+
kind: "dropped-rules",
|
|
3884
|
+
message: formatDroppedRulesMessage(stats.droppedRules, stats.droppedRuleBlocks)
|
|
3885
|
+
});
|
|
3886
|
+
if (stats.droppedPluginEntries > 0 && stats.droppedPlugins.size > 0) warnings.push({
|
|
3887
|
+
kind: "dropped-plugins",
|
|
3888
|
+
message: formatDroppedPluginsMessage([...stats.droppedPlugins], stats.droppedPluginEntries)
|
|
3889
|
+
});
|
|
3890
|
+
const rescuedSiblingCount = countRescuedSiblingCalls(parseResult);
|
|
3891
|
+
if (rescuedSiblingCount > 0) warnings.push({
|
|
3892
|
+
kind: "rescued-sibling-language-options",
|
|
3893
|
+
message: formatRescuedSiblingMessage(rescuedSiblingCount)
|
|
3894
|
+
});
|
|
3895
|
+
const preservedImports = applyGlobalsFolding(config) ? ensureGlobalsImport(parseResult.preservedImports) : parseResult.preservedImports;
|
|
3896
|
+
return {
|
|
3897
|
+
config,
|
|
3898
|
+
silenceRulesCalls: parseResult.silenceRulesCalls,
|
|
3899
|
+
needsSilenceRulesImport: parseResult.silenceRulesCalls.length > 0,
|
|
3900
|
+
preservedImports,
|
|
3901
|
+
warnings,
|
|
3902
|
+
droppedRuleCount: stats.droppedRules,
|
|
3903
|
+
droppedRuleBlockCount: stats.droppedRuleBlocks
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
//#endregion
|
|
3907
|
+
//#region src/cli/migrate/warnings.ts
|
|
3908
|
+
function formatWarnings(warnings) {
|
|
3909
|
+
if (warnings.length === 0) return "";
|
|
3910
|
+
return warnings.map((warning) => {
|
|
3911
|
+
return ` - ${warning.line === void 0 ? "" : `line ${warning.line}: `}${warning.message}`;
|
|
3912
|
+
}).join("\n");
|
|
3913
|
+
}
|
|
3914
|
+
//#endregion
|
|
3915
|
+
//#region src/cli/migrate/writer.ts
|
|
3916
|
+
function writeMigration(targetPath, newSource) {
|
|
3917
|
+
(0, node_fs.writeFileSync)(targetPath, newSource, "utf8");
|
|
3918
|
+
return { targetPath };
|
|
3919
|
+
}
|
|
3920
|
+
function writeMigrationLegacy(sourcePath, writePath, newSource) {
|
|
3921
|
+
(0, node_fs.writeFileSync)(writePath, newSource, "utf8");
|
|
3922
|
+
return {
|
|
3923
|
+
targetPath: writePath,
|
|
3924
|
+
deletedSourceCandidate: (0, node_path.basename)(sourcePath) === (0, node_path.basename)(writePath) ? void 0 : sourcePath
|
|
3925
|
+
};
|
|
3926
|
+
}
|
|
3927
|
+
//#endregion
|
|
3928
|
+
//#region src/cli/migrate/index.ts
|
|
3929
|
+
var flatConfigCandidates = [
|
|
3930
|
+
"eslint.config.mjs",
|
|
3931
|
+
"eslint.config.js",
|
|
3932
|
+
"eslint.config.cjs"
|
|
3933
|
+
];
|
|
3934
|
+
var eslintrcCandidates = [
|
|
3935
|
+
".eslintrc.cjs",
|
|
3936
|
+
".eslintrc.js",
|
|
3937
|
+
".eslintrc.mjs",
|
|
3938
|
+
".eslintrc.json"
|
|
3939
|
+
];
|
|
3940
|
+
var unsupportedEslintrcExtensions = [
|
|
3941
|
+
".json",
|
|
3942
|
+
".yml",
|
|
3943
|
+
".yaml"
|
|
3944
|
+
];
|
|
3945
|
+
function flatLooksLikeV1(filePath) {
|
|
3946
|
+
try {
|
|
3947
|
+
const source = (0, node_fs.readFileSync)(filePath, "utf8");
|
|
3948
|
+
return source.includes("useInsider") && /\b(import|require)\b/.test(source);
|
|
3949
|
+
} catch {
|
|
3950
|
+
return false;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
function resolveTargetPath(input) {
|
|
3954
|
+
if (input.target) {
|
|
3955
|
+
const absolute = (0, node_path.isAbsolute)(input.target) ? input.target : (0, node_path.resolve)(input.cwd, input.target);
|
|
3956
|
+
if ((0, node_fs.existsSync)(absolute)) return absolute;
|
|
3957
|
+
return null;
|
|
3958
|
+
}
|
|
3959
|
+
const flatFound = flatConfigCandidates.map((name) => (0, node_path.join)(input.cwd, name)).find((candidate) => (0, node_fs.existsSync)(candidate));
|
|
3960
|
+
const legacyFound = eslintrcCandidates.map((name) => (0, node_path.join)(input.cwd, name)).find((candidate) => (0, node_fs.existsSync)(candidate));
|
|
3961
|
+
if (flatFound && legacyFound) return flatLooksLikeV1(flatFound) ? flatFound : legacyFound;
|
|
3962
|
+
return flatFound ?? legacyFound ?? null;
|
|
3963
|
+
}
|
|
3964
|
+
function detectFormatByName(filePath) {
|
|
3965
|
+
const name = (0, node_path.basename)(filePath);
|
|
3966
|
+
if (name.startsWith("eslint.config.")) return "flat";
|
|
3967
|
+
if (!name.startsWith(".eslintrc")) return null;
|
|
3968
|
+
if (unsupportedEslintrcExtensions.find((extension) => name.endsWith(extension))) return "eslintrc-unsupported";
|
|
3969
|
+
return "eslintrc";
|
|
3970
|
+
}
|
|
3971
|
+
function detectFormatByContent(source) {
|
|
3972
|
+
if (source.includes("export default") && source.includes("useInsider")) return "flat";
|
|
3973
|
+
if (/\bmodule\.exports\s*=/.test(source)) return "eslintrc";
|
|
3974
|
+
return "flat";
|
|
3975
|
+
}
|
|
3976
|
+
function detectFormat(filePath, source) {
|
|
3977
|
+
const byName = detectFormatByName(filePath);
|
|
3978
|
+
if (byName) return byName;
|
|
3979
|
+
return detectFormatByContent(source);
|
|
3980
|
+
}
|
|
3981
|
+
function isV1Shape(parseResult) {
|
|
3982
|
+
if (!parseResult.hasUseInsiderImport) return false;
|
|
3983
|
+
return parseResult.arrayElements.some((element) => element.kind === "useInsiderCall");
|
|
3984
|
+
}
|
|
3985
|
+
function describeScopes(plan) {
|
|
3986
|
+
if (plan.config.scopes.size === 0) return "Scopes detected: (none)";
|
|
3987
|
+
return `Scopes detected: ${[...plan.config.scopes.entries()].map(([scope, entries]) => `${scope} (${entries.length})`).join(", ")}`;
|
|
3988
|
+
}
|
|
3989
|
+
function describeStrict(plan) {
|
|
3990
|
+
if (plan.config.strictWholeFile) return "Strict scopes: global (true)";
|
|
3991
|
+
if (plan.config.strictGlobs.length > 0) return `Strict scopes: 1 (${plan.config.strictGlobs.length} globs)`;
|
|
3992
|
+
return "Strict scopes: 0";
|
|
3993
|
+
}
|
|
3994
|
+
function summary(plan, format) {
|
|
3995
|
+
const lines = [];
|
|
3996
|
+
if (format === "eslintrc") lines.push("Source: legacy .eslintrc");
|
|
3997
|
+
else lines.push("Source: flat eslint.config.*");
|
|
3998
|
+
lines.push(describeScopes(plan));
|
|
3999
|
+
lines.push(describeStrict(plan));
|
|
4000
|
+
lines.push(`spellChecker.ignoredPaths: ${plan.config.spellChecker.ignoredPaths.length}`);
|
|
4001
|
+
const blocksLabel = plan.droppedRuleBlockCount === 1 ? "block" : "blocks";
|
|
4002
|
+
lines.push(`Dropped custom rules: ${plan.droppedRuleCount} across ${plan.droppedRuleBlockCount} ${blocksLabel}`);
|
|
4003
|
+
lines.push(`Warnings: ${plan.warnings.length}`);
|
|
4004
|
+
return lines.join("\n");
|
|
4005
|
+
}
|
|
4006
|
+
function planFromSource(source, filePath, format) {
|
|
4007
|
+
if (format === "flat") {
|
|
4008
|
+
const parseResult = parseV1Config(source, filePath);
|
|
4009
|
+
if (!isV1Shape(parseResult)) return null;
|
|
4010
|
+
const plan = transformToV2(parseResult);
|
|
4011
|
+
return {
|
|
4012
|
+
plan,
|
|
4013
|
+
rendered: renderV2(plan)
|
|
4014
|
+
};
|
|
4015
|
+
}
|
|
4016
|
+
const parseResult = parseEslintrc(source, filePath);
|
|
4017
|
+
if (!parseResult.found) return null;
|
|
4018
|
+
const plan = transformEslintrcToV2(parseResult);
|
|
4019
|
+
return {
|
|
4020
|
+
plan,
|
|
4021
|
+
rendered: renderV2(plan)
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
4024
|
+
function targetWritePath(cwd, sourcePath, format) {
|
|
4025
|
+
if (format !== "eslintrc") return sourcePath;
|
|
4026
|
+
const base = (0, node_path.basename)(sourcePath);
|
|
4027
|
+
const dir = sourcePath.slice(0, sourcePath.length - base.length);
|
|
4028
|
+
return (0, node_path.join)(dir.length > 0 ? dir : cwd, "eslint.config.mjs");
|
|
4029
|
+
}
|
|
4030
|
+
function parseEslintIgnoreContents(raw) {
|
|
4031
|
+
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4032
|
+
}
|
|
4033
|
+
function findEslintIgnore(sourcePath) {
|
|
4034
|
+
const candidate = (0, node_path.join)((0, node_path.dirname)(sourcePath), ".eslintignore");
|
|
4035
|
+
if (!(0, node_fs.existsSync)(candidate)) return null;
|
|
4036
|
+
return {
|
|
4037
|
+
path: candidate,
|
|
4038
|
+
patterns: parseEslintIgnoreContents((0, node_fs.readFileSync)(candidate, "utf8"))
|
|
4039
|
+
};
|
|
4040
|
+
}
|
|
4041
|
+
function mergeIgnorePatterns(plan, extra) {
|
|
4042
|
+
extra.forEach((pattern) => {
|
|
4043
|
+
if (!plan.config.ignores.some((existing) => existing.value === pattern)) plan.config.ignores.push({ value: pattern });
|
|
4044
|
+
});
|
|
4045
|
+
}
|
|
4046
|
+
function safeUnlink(filePath, clack, label) {
|
|
4047
|
+
try {
|
|
4048
|
+
(0, node_fs.unlinkSync)(filePath);
|
|
4049
|
+
} catch (error) {
|
|
4050
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
4051
|
+
if (clack.note) clack.note(`Could not delete ${label} at ${filePath}: ${reason}`, "Warning");
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
function describeDepChanges(depChanges) {
|
|
4055
|
+
if (!depChanges || !depChanges.packageJsonPath) return null;
|
|
4056
|
+
const lines = ["The following dependency changes will be applied to package.json:"];
|
|
4057
|
+
if (depChanges.removed.length > 0) lines.push(`- Remove (${depChanges.removed.length}): ${depChanges.removed.join(", ")}`);
|
|
4058
|
+
else lines.push("- Remove (0): (none)");
|
|
4059
|
+
lines.push(`- Install (${depChanges.install.specs.length}): ${depChanges.install.specs.join(", ")}`);
|
|
4060
|
+
return lines.join("\n");
|
|
4061
|
+
}
|
|
4062
|
+
async function confirmWrite(clack, writePath, initial = true) {
|
|
4063
|
+
const message = `Write migrated config to ${writePath}?`;
|
|
4064
|
+
const result = await clack.confirm({
|
|
4065
|
+
message,
|
|
4066
|
+
initialValue: initial
|
|
4067
|
+
});
|
|
4068
|
+
return !clack.isCancel(result) && result === true;
|
|
4069
|
+
}
|
|
4070
|
+
async function confirmInstall(clack, depSummary, initial = true) {
|
|
4071
|
+
const head = describeDepChanges(depSummary) ?? "";
|
|
4072
|
+
const message = head ? `${head}\nInstall dependencies now?` : "Install dependencies now?";
|
|
4073
|
+
const result = await clack.confirm({
|
|
4074
|
+
message,
|
|
4075
|
+
initialValue: initial
|
|
4076
|
+
});
|
|
4077
|
+
return !clack.isCancel(result) && result === true;
|
|
4078
|
+
}
|
|
4079
|
+
function renderNextStepsMessage(input) {
|
|
4080
|
+
const hasRemove = input.removed.length > 0;
|
|
4081
|
+
const hasInstall = input.installSpecs.length > 0;
|
|
4082
|
+
if (!hasRemove && !hasInstall) return null;
|
|
4083
|
+
const blocks = ["Skipped dependency installation. Run these to apply manually:"];
|
|
4084
|
+
if (hasRemove) blocks.push(formatNextStepsRemoveCommand(input.manager, input.removed));
|
|
4085
|
+
if (hasInstall) blocks.push(formatNextStepsAddCommand(input.manager, input.installSpecs));
|
|
4086
|
+
return blocks.join("\n\n");
|
|
4087
|
+
}
|
|
4088
|
+
function emitInstallNextSteps(clack, depSummary, cwd) {
|
|
4089
|
+
if (!clack.note) return;
|
|
4090
|
+
const message = renderNextStepsMessage({
|
|
4091
|
+
manager: detectPackageManager(cwd),
|
|
4092
|
+
removed: depSummary.removed,
|
|
4093
|
+
installSpecs: depSummary.install.specs
|
|
4094
|
+
});
|
|
4095
|
+
if (message === null) return;
|
|
4096
|
+
clack.note(message, "Next steps");
|
|
4097
|
+
}
|
|
4098
|
+
function emitSummary(clack, plan, format) {
|
|
4099
|
+
if (clack.note) clack.note(summary(plan, format), "Migration summary");
|
|
4100
|
+
if (plan.warnings.length > 0 && clack.note) clack.note(formatWarnings(plan.warnings), "Warnings");
|
|
4101
|
+
}
|
|
4102
|
+
function writeAndAnnounce(clack, targetPath, writePath, rendered, format) {
|
|
4103
|
+
if (format === "eslintrc") {
|
|
4104
|
+
const result = writeMigrationLegacy(targetPath, writePath, rendered);
|
|
4105
|
+
clack.outro(`Wrote ${writePath}`);
|
|
4106
|
+
return { deletedSourceCandidate: result.deletedSourceCandidate };
|
|
4107
|
+
}
|
|
4108
|
+
writeMigration(targetPath, rendered);
|
|
4109
|
+
clack.outro(`Wrote ${targetPath}`);
|
|
4110
|
+
return {};
|
|
4111
|
+
}
|
|
4112
|
+
async function confirmOverwriteExistingOutput(clack, writePath, sourcePath, initial = false) {
|
|
4113
|
+
const message = `An \`${(0, node_path.basename)(writePath)}\` already exists. Overwrite + delete \`${(0, node_path.basename)(sourcePath)}\`?`;
|
|
4114
|
+
const result = await clack.confirm({
|
|
4115
|
+
message,
|
|
4116
|
+
initialValue: initial
|
|
4117
|
+
});
|
|
4118
|
+
return !clack.isCancel(result) && result === true;
|
|
4119
|
+
}
|
|
4120
|
+
function resolveSource(input, clack) {
|
|
4121
|
+
const targetPath = resolveTargetPath(input);
|
|
4122
|
+
if (!targetPath) {
|
|
4123
|
+
clack.cancel("No eslint.config.{mjs,js,cjs} or .eslintrc.{cjs,js,mjs} found.");
|
|
4124
|
+
return {
|
|
4125
|
+
ok: false,
|
|
4126
|
+
exitCode: 1
|
|
4127
|
+
};
|
|
4128
|
+
}
|
|
4129
|
+
const source = (0, node_fs.readFileSync)(targetPath, "utf8");
|
|
4130
|
+
const format = detectFormat(targetPath, source);
|
|
4131
|
+
if (format === "eslintrc-unsupported") {
|
|
4132
|
+
const message = `Legacy ${(0, node_path.basename)(targetPath)} migration not supported in this release; convert manually first.`;
|
|
4133
|
+
clack.cancel(message);
|
|
4134
|
+
return {
|
|
4135
|
+
ok: false,
|
|
4136
|
+
exitCode: 1
|
|
4137
|
+
};
|
|
4138
|
+
}
|
|
4139
|
+
return {
|
|
4140
|
+
ok: true,
|
|
4141
|
+
resolved: {
|
|
4142
|
+
targetPath,
|
|
4143
|
+
source,
|
|
4144
|
+
format
|
|
4145
|
+
}
|
|
4146
|
+
};
|
|
4147
|
+
}
|
|
4148
|
+
function prepareMigration(resolved, input) {
|
|
4149
|
+
const planResult = planFromSource(resolved.source, resolved.targetPath, resolved.format);
|
|
4150
|
+
if (!planResult) return null;
|
|
4151
|
+
const { plan } = planResult;
|
|
4152
|
+
const writePath = targetWritePath(input.cwd, resolved.targetPath, resolved.format);
|
|
4153
|
+
const eslintIgnore = findEslintIgnore(resolved.targetPath);
|
|
4154
|
+
if (eslintIgnore && eslintIgnore.patterns.length > 0) mergeIgnorePatterns(plan, eslintIgnore.patterns);
|
|
4155
|
+
return {
|
|
4156
|
+
plan,
|
|
4157
|
+
rendered: renderV2(plan),
|
|
4158
|
+
writePath,
|
|
4159
|
+
eslintIgnore
|
|
4160
|
+
};
|
|
4161
|
+
}
|
|
4162
|
+
async function runConfirmations(clack, input, targetPath, writePath, previous) {
|
|
4163
|
+
if (writePath !== targetPath && (0, node_fs.existsSync)(writePath)) {
|
|
4164
|
+
if (!await confirmOverwriteExistingOutput(clack, writePath, targetPath, previous?.overwriteExisting ?? false)) {
|
|
4165
|
+
clack.outro("Aborted: existing output kept.");
|
|
4166
|
+
return false;
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
if (!input.yes) {
|
|
4170
|
+
if (!await confirmWrite(clack, writePath, previous?.proceed ?? true)) {
|
|
4171
|
+
clack.outro("Aborted: original config kept.");
|
|
4172
|
+
return false;
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
return true;
|
|
4176
|
+
}
|
|
4177
|
+
function consumerPackageJsonPath(writePath) {
|
|
4178
|
+
return (0, node_path.join)((0, node_path.dirname)(writePath), "package.json");
|
|
4179
|
+
}
|
|
4180
|
+
function tryReadConsumerManifest(path) {
|
|
4181
|
+
try {
|
|
4182
|
+
const raw = (0, node_fs.readFileSync)(path, "utf8");
|
|
4183
|
+
return JSON.parse(raw);
|
|
4184
|
+
} catch {
|
|
4185
|
+
return null;
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
function computeDepSummary(plan, writePath, overrides) {
|
|
4189
|
+
const pkgPath = consumerPackageJsonPath(writePath);
|
|
4190
|
+
const manifest = tryReadConsumerManifest(pkgPath);
|
|
4191
|
+
if (!manifest) return {
|
|
4192
|
+
removed: [],
|
|
4193
|
+
install: {
|
|
4194
|
+
specs: [],
|
|
4195
|
+
fallbacks: []
|
|
4196
|
+
},
|
|
4197
|
+
packageJsonPath: null
|
|
4198
|
+
};
|
|
4199
|
+
const pruneOutcome = pruneEslintDependencies(manifest);
|
|
4200
|
+
const ownManifest = (overrides?.readEslintConfigManifest ?? readOwnEslintConfigManifest)();
|
|
4201
|
+
const install = buildInstallList(new Set(plan.config.scopes.keys()), ownManifest);
|
|
4202
|
+
return {
|
|
4203
|
+
removed: pruneOutcome.removed,
|
|
4204
|
+
install,
|
|
4205
|
+
packageJsonPath: pkgPath
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
function persistPrunedManifest(depSummary) {
|
|
4209
|
+
if (!depSummary.packageJsonPath) return;
|
|
4210
|
+
pruneConsumerPackageJson((0, node_path.dirname)(depSummary.packageJsonPath));
|
|
4211
|
+
}
|
|
4212
|
+
function noteFallbacks(clack, depSummary) {
|
|
4213
|
+
if (depSummary.install.fallbacks.length === 0 || !clack.note) return;
|
|
4214
|
+
const list = depSummary.install.fallbacks.join(", ");
|
|
4215
|
+
clack.note(`Falling back to @latest for: ${list}`, "Warning");
|
|
4216
|
+
}
|
|
4217
|
+
function resolveInstallCwd(depSummary, input) {
|
|
4218
|
+
if (depSummary.packageJsonPath) return (0, node_path.dirname)(depSummary.packageJsonPath);
|
|
4219
|
+
return input.cwd;
|
|
4220
|
+
}
|
|
4221
|
+
async function runInstaller(clack, depSummary, input) {
|
|
4222
|
+
await installWithRetry({
|
|
4223
|
+
deps: depSummary.install.specs,
|
|
4224
|
+
cwd: resolveInstallCwd(depSummary, input),
|
|
4225
|
+
autoConfirm: Boolean(input.yes),
|
|
4226
|
+
clack,
|
|
4227
|
+
fsOps: input.overrides?.wipeFs,
|
|
4228
|
+
installer: input.overrides?.installer
|
|
4229
|
+
});
|
|
4230
|
+
}
|
|
4231
|
+
async function applyDepChanges(clack, depSummary, input) {
|
|
4232
|
+
if (!depSummary.packageJsonPath) {
|
|
4233
|
+
if (clack.note) clack.note("No package.json found alongside the migrated config — skipping dependency updates.", "Notice");
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4236
|
+
persistPrunedManifest(depSummary);
|
|
4237
|
+
if (input.noInstall) {
|
|
4238
|
+
emitInstallNextSteps(clack, depSummary, input.cwd);
|
|
4239
|
+
return;
|
|
4240
|
+
}
|
|
4241
|
+
if (!input.yes) {
|
|
4242
|
+
if (!await confirmInstall(clack, depSummary)) {
|
|
4243
|
+
emitInstallNextSteps(clack, depSummary, input.cwd);
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
noteFallbacks(clack, depSummary);
|
|
4248
|
+
await runInstaller(clack, depSummary, input);
|
|
4249
|
+
}
|
|
4250
|
+
function performWriteAndCleanup(clack, resolved, prepared) {
|
|
4251
|
+
const outcome = writeAndAnnounce(clack, resolved.targetPath, prepared.writePath, prepared.rendered, resolved.format);
|
|
4252
|
+
if (prepared.eslintIgnore) safeUnlink(prepared.eslintIgnore.path, clack, ".eslintignore");
|
|
4253
|
+
if (outcome.deletedSourceCandidate) safeUnlink(outcome.deletedSourceCandidate, clack, "source config");
|
|
4254
|
+
}
|
|
4255
|
+
function loadPreviousMigrate(clack, cwd) {
|
|
4256
|
+
const restored = loadSession({ cwd });
|
|
4257
|
+
const previousMigrate = restored?.command === "migrate" ? restored.migrate : void 0;
|
|
4258
|
+
if (previousMigrate && clack.note) clack.note("Restored your previous choices. Press Enter on each prompt to confirm, or arrow keys to change.", "Resuming previous session");
|
|
4259
|
+
return previousMigrate;
|
|
4260
|
+
}
|
|
4261
|
+
function emitNothingToMigrate(clack, format) {
|
|
4262
|
+
clack.outro(format === "eslintrc" ? "No legacy `.eslintrc` shape found — nothing to migrate." : "No v1 useInsider patterns found — nothing to migrate.");
|
|
4263
|
+
}
|
|
4264
|
+
function persistMigrateSession(input, prepared, resolved) {
|
|
4265
|
+
const overwriteExisting = prepared.writePath !== resolved.targetPath && (0, node_fs.existsSync)(prepared.writePath);
|
|
4266
|
+
saveSession({
|
|
4267
|
+
command: "migrate",
|
|
4268
|
+
migrate: {
|
|
4269
|
+
target: input.target,
|
|
4270
|
+
yes: input.yes,
|
|
4271
|
+
noInstall: input.noInstall,
|
|
4272
|
+
overwriteExisting,
|
|
4273
|
+
proceed: true
|
|
4274
|
+
}
|
|
4275
|
+
}, { cwd: input.cwd });
|
|
4276
|
+
}
|
|
4277
|
+
async function runMigrate(input, clackOverride) {
|
|
4278
|
+
const clack = clackOverride ?? await loadClack();
|
|
4279
|
+
const sourceResult = resolveSource(input, clack);
|
|
4280
|
+
if (!sourceResult.ok) return sourceResult.exitCode;
|
|
4281
|
+
const { resolved } = sourceResult;
|
|
4282
|
+
clack.intro(`Migrating ${resolved.targetPath}`);
|
|
4283
|
+
const previousMigrate = loadPreviousMigrate(clack, input.cwd);
|
|
4284
|
+
const prepared = prepareMigration(resolved, input);
|
|
4285
|
+
if (!prepared) {
|
|
4286
|
+
emitNothingToMigrate(clack, resolved.format);
|
|
4287
|
+
return 0;
|
|
4288
|
+
}
|
|
4289
|
+
emitSummary(clack, prepared.plan, resolved.format);
|
|
4290
|
+
const depSummary = computeDepSummary(prepared.plan, prepared.writePath, input.overrides);
|
|
4291
|
+
if (!await runConfirmations(clack, input, resolved.targetPath, prepared.writePath, previousMigrate)) return 0;
|
|
4292
|
+
persistMigrateSession(input, prepared, resolved);
|
|
4293
|
+
performWriteAndCleanup(clack, resolved, prepared);
|
|
4294
|
+
if (depSummary) await applyDepChanges(clack, depSummary, input);
|
|
4295
|
+
clearSession({ cwd: input.cwd });
|
|
4296
|
+
return 0;
|
|
4297
|
+
}
|
|
4298
|
+
//#endregion
|
|
4299
|
+
//#region src/cli/index.ts
|
|
4300
|
+
function resolveTsconfigInput(cwd) {
|
|
4301
|
+
const referencesResult = resolveTsconfigReferences(cwd);
|
|
4302
|
+
if (referencesResult) return {
|
|
4303
|
+
input: {
|
|
4304
|
+
referencesOnly: true,
|
|
4305
|
+
rootPath: referencesResult.rootPath,
|
|
4306
|
+
refs: referencesResult.refs
|
|
4307
|
+
},
|
|
4308
|
+
missing: false
|
|
4309
|
+
};
|
|
4310
|
+
const rootPath = (0, node_path.join)(cwd, "tsconfig.json");
|
|
4311
|
+
if ((0, node_fs.existsSync)(rootPath)) return {
|
|
4312
|
+
input: {
|
|
4313
|
+
referencesOnly: false,
|
|
4314
|
+
rootPath,
|
|
4315
|
+
refs: []
|
|
4316
|
+
},
|
|
4317
|
+
missing: false
|
|
4318
|
+
};
|
|
4319
|
+
return {
|
|
4320
|
+
input: null,
|
|
4321
|
+
missing: true
|
|
4322
|
+
};
|
|
4323
|
+
}
|
|
4324
|
+
function decisionToChoice(decision) {
|
|
4325
|
+
return decision;
|
|
4326
|
+
}
|
|
4327
|
+
function isPackageJsonSource(existing) {
|
|
4328
|
+
return existing.path.endsWith("package.json");
|
|
4329
|
+
}
|
|
4330
|
+
async function handleExistingConfig(clack, input, existing, initial) {
|
|
4331
|
+
const decision = await askExistingConfigDecision(clack, existing.path, initial);
|
|
4332
|
+
saveSession({
|
|
4333
|
+
command: "scaffold",
|
|
4334
|
+
scaffold: { existingConfigChoice: decisionToChoice(decision) }
|
|
4335
|
+
}, { cwd: input.cwd });
|
|
4336
|
+
if (decision === "exit") {
|
|
4337
|
+
clearSession({ cwd: input.cwd });
|
|
4338
|
+
clack.outro("Exited: existing config kept.");
|
|
4339
|
+
return {
|
|
4340
|
+
continueScaffold: false,
|
|
4341
|
+
exitCode: 0
|
|
4342
|
+
};
|
|
4343
|
+
}
|
|
4344
|
+
if (decision === "migrate") {
|
|
4345
|
+
clearSession({ cwd: input.cwd });
|
|
4346
|
+
if (isPackageJsonSource(existing)) {
|
|
4347
|
+
clack.cancel("Migration from package.json `eslintConfig` is not supported; extract it to .eslintrc.cjs first, or pick Override.");
|
|
4348
|
+
return {
|
|
4349
|
+
continueScaffold: false,
|
|
4350
|
+
exitCode: 1
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
return {
|
|
4354
|
+
continueScaffold: false,
|
|
4355
|
+
exitCode: await runMigrate({
|
|
4356
|
+
cwd: input.cwd,
|
|
4357
|
+
target: existing.path,
|
|
4358
|
+
yes: input.yes
|
|
4359
|
+
}, clack)
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
return {
|
|
4363
|
+
continueScaffold: true,
|
|
4364
|
+
viaOverride: true
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
function isScaffoldChoice(value) {
|
|
4368
|
+
return value === "override" || value === "migrate" || value === "exit";
|
|
4369
|
+
}
|
|
4370
|
+
function decisionInitial(persistedChoice) {
|
|
4371
|
+
if (persistedChoice && isScaffoldChoice(persistedChoice)) return persistedChoice;
|
|
4372
|
+
return "migrate";
|
|
4373
|
+
}
|
|
4374
|
+
function buildQuestionsContext(input, restoredAnswers) {
|
|
4375
|
+
const { input: tsconfigInput, missing } = resolveTsconfigInput(input.cwd);
|
|
4376
|
+
return {
|
|
4377
|
+
tsconfigInput,
|
|
4378
|
+
tsconfigMissing: missing,
|
|
4379
|
+
previousAnswers: restoredAnswers
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
function noteOverridePrune(clack, removed) {
|
|
4383
|
+
if (removed.length === 0 || !clack.note) return;
|
|
4384
|
+
const list = removed.join(", ");
|
|
4385
|
+
clack.note(`Removed stale ESLint-related entries from package.json: ${list}`, "Override cleanup");
|
|
4386
|
+
}
|
|
4387
|
+
function describeHybridPattern(detection) {
|
|
4388
|
+
if (detection.kind === "suffix") return `Detected suffix convention: Options API files match \`${detection.optionsPattern}\`.`;
|
|
4389
|
+
if (detection.kind === "directory") return `Detected directory convention: \`${detection.optionsDir}/\` vs \`${detection.compositionDir}/\`.`;
|
|
4390
|
+
return "";
|
|
4391
|
+
}
|
|
4392
|
+
function noteHybridFound(clack, detection) {
|
|
4393
|
+
if (!clack.note) return;
|
|
4394
|
+
if (detection.kind !== "suffix" && detection.kind !== "directory") return;
|
|
4395
|
+
clack.note(describeHybridPattern(detection), "Hybrid Vue pattern detected");
|
|
4396
|
+
}
|
|
4397
|
+
function noteHybridMissing(clack) {
|
|
4398
|
+
if (!clack.note) return;
|
|
4399
|
+
clack.note("Hybrid Vue selected, but your `.vue` files do not follow a distinguishing naming pattern (e.g. `*.options.vue` vs `*.vue`). The generated config will treat every `.vue` file with one preset (composition by default). To get proper per-file linting, rename your Options-API components to `*.options.vue` (or similar) and re-run this CLI.", "No hybrid pattern detected");
|
|
4400
|
+
}
|
|
4401
|
+
function noteHybridSuffixScaffold(clack, suffix) {
|
|
4402
|
+
if (!clack.note) return;
|
|
4403
|
+
clack.note(`Rename your Options-API files to end with \`${suffix}\` and they will be picked up by the Options preset automatically. Composition components keep the plain \`.vue\` extension.`, "Suffix convention scaffolded");
|
|
4404
|
+
}
|
|
4405
|
+
async function resolveVueHybridPlan(clack, cwd, yes, previous) {
|
|
4406
|
+
const detection = detectVueApiPattern({ cwd });
|
|
4407
|
+
if (detection.kind === "suffix") {
|
|
4408
|
+
noteHybridFound(clack, detection);
|
|
4409
|
+
const detectedDefault = detection.targetApi === "options" ? "composition" : "options";
|
|
4410
|
+
return {
|
|
4411
|
+
suffix: detection.suffix,
|
|
4412
|
+
defaultApi: detectedDefault,
|
|
4413
|
+
fallbackToComposition: false
|
|
4414
|
+
};
|
|
4415
|
+
}
|
|
4416
|
+
if (detection.kind === "directory") {
|
|
4417
|
+
noteHybridFound(clack, detection);
|
|
4418
|
+
return {
|
|
4419
|
+
suffix: void 0,
|
|
4420
|
+
defaultApi: void 0,
|
|
4421
|
+
fallbackToComposition: false
|
|
4422
|
+
};
|
|
4423
|
+
}
|
|
4424
|
+
noteHybridMissing(clack);
|
|
4425
|
+
if (yes) return {
|
|
4426
|
+
suffix: void 0,
|
|
4427
|
+
defaultApi: void 0,
|
|
4428
|
+
fallbackToComposition: true
|
|
4429
|
+
};
|
|
4430
|
+
if (!await askHybridProceedWithSuffix(clack)) return {
|
|
4431
|
+
suffix: void 0,
|
|
4432
|
+
defaultApi: void 0,
|
|
4433
|
+
fallbackToComposition: true
|
|
4434
|
+
};
|
|
4435
|
+
const defaultApi = await askVueHybridDefaultApi(clack, previous?.defaultApi);
|
|
4436
|
+
const suffix = await askHybridSuffix(clack, defaultApi, previous?.suffix);
|
|
4437
|
+
noteHybridSuffixScaffold(clack, suffix);
|
|
4438
|
+
return {
|
|
4439
|
+
suffix,
|
|
4440
|
+
defaultApi,
|
|
4441
|
+
fallbackToComposition: false
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
async function resolveHybridAnswers(clack, input, answers) {
|
|
4445
|
+
if (answers.framework !== "vue" || answers.vueApiStyle !== "hybrid") return answers;
|
|
4446
|
+
const plan = await resolveVueHybridPlan(clack, input.cwd, Boolean(input.yes), answers.vueHybridSuffix);
|
|
4447
|
+
if (plan.fallbackToComposition) return {
|
|
4448
|
+
...answers,
|
|
4449
|
+
vueApiStyle: "composition",
|
|
4450
|
+
vueHybridSuffix: void 0
|
|
4451
|
+
};
|
|
4452
|
+
if (plan.suffix && plan.defaultApi) return {
|
|
4453
|
+
...answers,
|
|
4454
|
+
vueHybridSuffix: {
|
|
4455
|
+
defaultApi: plan.defaultApi,
|
|
4456
|
+
suffix: plan.suffix
|
|
4457
|
+
}
|
|
4458
|
+
};
|
|
4459
|
+
return answers;
|
|
4460
|
+
}
|
|
4461
|
+
async function installAndWrite(clack, input, answers, options) {
|
|
4462
|
+
if (options.pruneBeforeInstall) noteOverridePrune(clack, pruneConsumerPackageJson(input.cwd).removed);
|
|
4463
|
+
const deps = buildDependencyList(answers);
|
|
4464
|
+
if (clack.note) clack.note(deps.map((dep) => ` - ${dep}`).join("\n"), "Installing dev dependencies");
|
|
4465
|
+
await installWithRetry({
|
|
4466
|
+
deps,
|
|
4467
|
+
cwd: input.cwd,
|
|
4468
|
+
autoConfirm: Boolean(input.yes),
|
|
4469
|
+
clack,
|
|
4470
|
+
installer: input.overrides?.installer ?? installDependencies,
|
|
4471
|
+
fsOps: input.overrides?.wipeFs
|
|
4472
|
+
});
|
|
4473
|
+
const targetPath = (0, node_path.join)(input.cwd, "eslint.config.mjs");
|
|
4474
|
+
(0, node_fs.writeFileSync)(targetPath, renderConfigFile(answers), "utf8");
|
|
4475
|
+
return targetPath;
|
|
4476
|
+
}
|
|
4477
|
+
async function runScaffold(input, clackOverride) {
|
|
4478
|
+
const clack = clackOverride ?? await loadClack();
|
|
4479
|
+
const existing = detectExistingEslintConfig(input.cwd);
|
|
4480
|
+
const restored = loadSession({ cwd: input.cwd });
|
|
4481
|
+
const restoredScaffold = restored?.command === "scaffold" ? restored.scaffold : void 0;
|
|
4482
|
+
let viaOverride = false;
|
|
4483
|
+
if (existing) {
|
|
4484
|
+
const outcome = await handleExistingConfig(clack, input, existing, decisionInitial(restoredScaffold?.existingConfigChoice));
|
|
4485
|
+
if (!outcome.continueScaffold) return outcome.exitCode;
|
|
4486
|
+
({viaOverride} = outcome);
|
|
4487
|
+
}
|
|
4488
|
+
const resolvedAnswers = await resolveHybridAnswers(clack, input, await runQuestions(buildQuestionsContext(input, restoredScaffold?.answers), clack));
|
|
4489
|
+
saveSession({
|
|
4490
|
+
command: "scaffold",
|
|
4491
|
+
scaffold: {
|
|
4492
|
+
answers: resolvedAnswers,
|
|
4493
|
+
existingConfigChoice: "override"
|
|
4494
|
+
}
|
|
4495
|
+
}, { cwd: input.cwd });
|
|
4496
|
+
const targetPath = await installAndWrite(clack, input, resolvedAnswers, { pruneBeforeInstall: viaOverride });
|
|
4497
|
+
clearSession({ cwd: input.cwd });
|
|
4498
|
+
clack.outro(`Wrote ${targetPath}`);
|
|
4499
|
+
return 0;
|
|
4500
|
+
}
|
|
4501
|
+
async function runScaffoldSubcommand(scaffoldInput) {
|
|
4502
|
+
return runScaffold(scaffoldInput);
|
|
4503
|
+
}
|
|
4504
|
+
async function runMigrateSubcommand(input) {
|
|
4505
|
+
return runMigrate({
|
|
4506
|
+
cwd: input.cwd,
|
|
4507
|
+
target: input.target,
|
|
4508
|
+
yes: input.yes,
|
|
4509
|
+
noInstall: input.noInstall
|
|
4510
|
+
});
|
|
4511
|
+
}
|
|
4512
|
+
function writeStderr(line) {
|
|
4513
|
+
console.error(line);
|
|
4514
|
+
}
|
|
4515
|
+
async function main() {
|
|
4516
|
+
return dispatch(process.argv.slice(2), process.cwd(), {
|
|
4517
|
+
scaffold: runScaffoldSubcommand,
|
|
4518
|
+
migrate: runMigrateSubcommand,
|
|
4519
|
+
stderr: writeStderr
|
|
4520
|
+
});
|
|
4521
|
+
}
|
|
4522
|
+
function exitCleanly(code) {
|
|
4523
|
+
process.exit(code);
|
|
4524
|
+
}
|
|
4525
|
+
function isUnderTest() {
|
|
4526
|
+
return Boolean(process.env.VITEST) || process.env.NODE_ENV === "test";
|
|
4527
|
+
}
|
|
4528
|
+
if (!isUnderTest()) main().then(exitCleanly).catch((error) => {
|
|
4529
|
+
if (!(error instanceof CliCancelledError)) {
|
|
4530
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4531
|
+
console.error(`[useinsider/eslint-config] ${message}`);
|
|
4532
|
+
}
|
|
4533
|
+
exitCleanly(1);
|
|
4534
|
+
});
|
|
4535
|
+
//#endregion
|
|
4536
|
+
exports.runScaffold = runScaffold;
|
|
4537
|
+
|
|
4538
|
+
//# sourceMappingURL=cli.cjs.map
|