@isentinel/eslint-config 6.0.0-beta.2 → 6.0.0-beta.21
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 +322 -3
- package/bin/index.js +1 -1
- package/bin/lint.js +3 -0
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +719 -0
- package/dist/formatter-agents.d.mts +22 -0
- package/dist/formatter-agents.mjs +69 -0
- package/dist/index.d.mts +19142 -11553
- package/dist/index.mjs +5394 -2590
- package/dist/lint-cli.d.mts +1 -0
- package/dist/lint-cli.mjs +4827 -0
- package/dist/lint-ignored.d.mts +1 -0
- package/dist/lint-ignored.mjs +280 -0
- package/dist/oxlint.d.mts +32373 -0
- package/dist/oxlint.mjs +4711 -0
- package/package.json +74 -41
package/dist/cli.mjs
CHANGED
|
@@ -1 +1,720 @@
|
|
|
1
|
+
import { cancel, confirm, group, intro, log, multiselect, note, outro } from "@clack/prompts";
|
|
2
|
+
import ansis from "ansis";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { hideBin } from "yargs/helpers";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import fsp from "node:fs/promises";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { existsSync, readFileSync } from "fs";
|
|
10
|
+
import { execSync } from "node:child_process";
|
|
11
|
+
//#region package.json
|
|
12
|
+
var version = "6.0.0-beta.21";
|
|
13
|
+
var package_default = {
|
|
14
|
+
name: "@isentinel/eslint-config",
|
|
15
|
+
version,
|
|
16
|
+
description: "iSentinel's ESLint config",
|
|
17
|
+
keywords: [
|
|
18
|
+
"eslint-config",
|
|
19
|
+
"roblox",
|
|
20
|
+
"roblox-ts",
|
|
21
|
+
"rbxts"
|
|
22
|
+
],
|
|
23
|
+
homepage: "https://github.com/christopher-buss/eslint-config",
|
|
24
|
+
bugs: { "url": "https://github.com/christopher-buss/eslint-config/issues" },
|
|
25
|
+
repository: {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/christopher-buss/eslint-config.git"
|
|
28
|
+
},
|
|
29
|
+
license: "MIT",
|
|
30
|
+
author: "Christopher Buss <christopher.buss@pm.me> (https://github.com/christopher-buss)",
|
|
31
|
+
contributors: [{
|
|
32
|
+
"name": "Anthony Fu",
|
|
33
|
+
"email": "anthonyfu117@hotmail.com",
|
|
34
|
+
"url": "https://github.com/antfu"
|
|
35
|
+
}],
|
|
36
|
+
sideEffects: false,
|
|
37
|
+
type: "module",
|
|
38
|
+
exports: {
|
|
39
|
+
".": "./dist/index.mjs",
|
|
40
|
+
"./cli": "./dist/cli.mjs",
|
|
41
|
+
"./eslint": {
|
|
42
|
+
"types": "./dist/index.d.mts",
|
|
43
|
+
"default": "./dist/index.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./formatter-agents": "./dist/formatter-agents.mjs",
|
|
46
|
+
"./oxlint": {
|
|
47
|
+
"types": "./dist/oxlint.d.mts",
|
|
48
|
+
"default": "./dist/oxlint.mjs"
|
|
49
|
+
},
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
main: "./dist/index.mjs",
|
|
53
|
+
module: "./dist/index.mjs",
|
|
54
|
+
types: "./dist/index.d.mts",
|
|
55
|
+
bin: {
|
|
56
|
+
"eslint-config": "./bin/index.js",
|
|
57
|
+
"isentinel-lint": "./bin/lint.js"
|
|
58
|
+
},
|
|
59
|
+
files: ["bin", "dist"],
|
|
60
|
+
scripts: {
|
|
61
|
+
"build": "nr gen && tsdown && node scripts/check-dist.ts",
|
|
62
|
+
"build:inspector": "pnpm build && pnpm dlx @eslint/config-inspector build --config eslint-inspector.config.ts --out-dir ./.eslint-config-inspector",
|
|
63
|
+
"dev": "npx @eslint/config-inspector --config eslint-inspector.config.ts",
|
|
64
|
+
"gen": "node scripts/typegen.ts && node scripts/typegen-oxlint.ts && node scripts/typegen-defaults.ts && node scripts/typegen-defaults-oxlint.ts && node scripts/versiongen.ts",
|
|
65
|
+
"postgen": "echo 'Generation complete!'",
|
|
66
|
+
"lint": "concurrently 'oxlint' 'eslint --cache'",
|
|
67
|
+
"lint:ci": "concurrently 'oxlint' 'eslint --cache --cache-strategy content'",
|
|
68
|
+
"oxlint": "oxlint",
|
|
69
|
+
"prepack": "nr build",
|
|
70
|
+
"release": "bumpp",
|
|
71
|
+
"stub": "tsdown",
|
|
72
|
+
"test": "nr gen && vitest run",
|
|
73
|
+
"test:watch": "vitest",
|
|
74
|
+
"typecheck": "tsc --noEmit",
|
|
75
|
+
"validate:oxlint": "node scripts/validate-oxlint.ts",
|
|
76
|
+
"watch": "tsdown --watch --dts"
|
|
77
|
+
},
|
|
78
|
+
dependencies: {
|
|
79
|
+
"@antfu/install-pkg": "catalog:prod",
|
|
80
|
+
"@clack/prompts": "catalog:prod",
|
|
81
|
+
"@cspell/eslint-plugin": "catalog:prod",
|
|
82
|
+
"@e18e/eslint-plugin": "catalog:prod",
|
|
83
|
+
"@eslint-community/eslint-plugin-eslint-comments": "catalog:prod",
|
|
84
|
+
"@eslint/markdown": "catalog:prod",
|
|
85
|
+
"@isentinel/dict-rbxts": "catalog:prod",
|
|
86
|
+
"@isentinel/dict-roblox": "catalog:prod",
|
|
87
|
+
"@oxlint/plugins": "catalog:prod",
|
|
88
|
+
"@pobammer-ts/small-rules": "catalog:prod",
|
|
89
|
+
"@stylistic/eslint-plugin": "catalog:prod",
|
|
90
|
+
"@typescript-eslint/eslint-plugin": "catalog:prod",
|
|
91
|
+
"@typescript-eslint/parser": "catalog:prod",
|
|
92
|
+
"ansis": "catalog:prod",
|
|
93
|
+
"concurrently": "catalog:prod",
|
|
94
|
+
"eslint-config-flat-gitignore": "catalog:prod",
|
|
95
|
+
"eslint-config-prettier": "catalog:prod",
|
|
96
|
+
"eslint-flat-config-utils": "catalog:prod",
|
|
97
|
+
"eslint-merge-processors": "catalog:prod",
|
|
98
|
+
"eslint-plugin-antfu": "catalog:prod",
|
|
99
|
+
"eslint-plugin-better-max-params": "catalog:prod",
|
|
100
|
+
"eslint-plugin-comment-length": "catalog:prod",
|
|
101
|
+
"eslint-plugin-de-morgan": "catalog:prod",
|
|
102
|
+
"eslint-plugin-flawless": "catalog:prod",
|
|
103
|
+
"eslint-plugin-format-lua": "catalog:prod",
|
|
104
|
+
"eslint-plugin-import-lite": "catalog:prod",
|
|
105
|
+
"eslint-plugin-jsdoc": "catalog:prod",
|
|
106
|
+
"eslint-plugin-jsonc": "catalog:prod",
|
|
107
|
+
"eslint-plugin-oxfmt": "catalog:prod",
|
|
108
|
+
"eslint-plugin-package-json": "catalog:prod",
|
|
109
|
+
"eslint-plugin-perfectionist": "catalog:prod",
|
|
110
|
+
"eslint-plugin-pnpm": "catalog:prod",
|
|
111
|
+
"eslint-plugin-promise": "catalog:prod",
|
|
112
|
+
"eslint-plugin-roblox-ts": "catalog:prod",
|
|
113
|
+
"eslint-plugin-sentinel": "catalog:prod",
|
|
114
|
+
"eslint-plugin-sonarjs": "catalog:prod",
|
|
115
|
+
"eslint-plugin-toml": "catalog:prod",
|
|
116
|
+
"eslint-plugin-unicorn": "catalog:prod",
|
|
117
|
+
"eslint-plugin-unused-imports": "catalog:prod",
|
|
118
|
+
"eslint-plugin-yml": "catalog:prod",
|
|
119
|
+
"file-entry-cache": "catalog:prod",
|
|
120
|
+
"find-up-simple": "catalog:prod",
|
|
121
|
+
"globals": "catalog:prod",
|
|
122
|
+
"jsonc-eslint-parser": "catalog:prod",
|
|
123
|
+
"local-pkg": "catalog:prod",
|
|
124
|
+
"oxfmt": "catalog:prod",
|
|
125
|
+
"oxlint-plugin-eslint": "catalog:prod",
|
|
126
|
+
"oxlint-plugin-oxlint-comments": "catalog:prod",
|
|
127
|
+
"prettier": "catalog:prod",
|
|
128
|
+
"semver": "catalog:prod",
|
|
129
|
+
"toml-eslint-parser": "catalog:prod",
|
|
130
|
+
"yaml-eslint-parser": "catalog:prod",
|
|
131
|
+
"yargs": "catalog:prod"
|
|
132
|
+
},
|
|
133
|
+
devDependencies: {
|
|
134
|
+
"@antfu/ni": "catalog:dev",
|
|
135
|
+
"@eslint-react/shared": "catalog:dev",
|
|
136
|
+
"@eslint/config-inspector": "catalog:dev",
|
|
137
|
+
"@isentinel/eslint-config": "workspace:*",
|
|
138
|
+
"@isentinel/tsconfig": "catalog:dev",
|
|
139
|
+
"@stylistic/eslint-plugin-migrate": "catalog:dev",
|
|
140
|
+
"@total-typescript/shoehorn": "catalog:dev",
|
|
141
|
+
"@types/node": "catalog:dev",
|
|
142
|
+
"@types/picomatch": "catalog:dev",
|
|
143
|
+
"@types/semver": "catalog:dev",
|
|
144
|
+
"@types/yargs": "catalog:dev",
|
|
145
|
+
"@vitest/eslint-plugin": "catalog:peer",
|
|
146
|
+
"bumpp": "catalog:dev",
|
|
147
|
+
"eslint": "catalog:peer",
|
|
148
|
+
"eslint-plugin-erasable-syntax-only": "catalog:peer",
|
|
149
|
+
"eslint-plugin-eslint-plugin": "catalog:peer",
|
|
150
|
+
"eslint-plugin-jest": "catalog:peer",
|
|
151
|
+
"eslint-plugin-jest-extended": "catalog:peer",
|
|
152
|
+
"eslint-plugin-n": "catalog:peer",
|
|
153
|
+
"eslint-plugin-react-jsx": "catalog:peer",
|
|
154
|
+
"eslint-plugin-react-naming-convention": "catalog:peer",
|
|
155
|
+
"eslint-plugin-react-x": "catalog:peer",
|
|
156
|
+
"eslint-typegen": "catalog:dev",
|
|
157
|
+
"jiti": "catalog:dev",
|
|
158
|
+
"oxlint": "catalog:peer",
|
|
159
|
+
"oxlint-tsgolint": "catalog:peer",
|
|
160
|
+
"parse-gitignore-ts": "catalog:dev",
|
|
161
|
+
"picomatch": "catalog:dev",
|
|
162
|
+
"pnpm-workspace-yaml": "catalog:dev",
|
|
163
|
+
"publint": "catalog:dev",
|
|
164
|
+
"tsdown": "catalog:dev",
|
|
165
|
+
"type-fest": "catalog:dev",
|
|
166
|
+
"typescript": "catalog:dev",
|
|
167
|
+
"unplugin-unused": "catalog:dev",
|
|
168
|
+
"vitest": "catalog:dev"
|
|
169
|
+
},
|
|
170
|
+
peerDependencies: {
|
|
171
|
+
"@vitest/eslint-plugin": "^1.6.4",
|
|
172
|
+
"eslint": "^10.4.0",
|
|
173
|
+
"eslint-plugin-eslint-plugin": "^7.5.0",
|
|
174
|
+
"eslint-plugin-jest": "^29.15.0",
|
|
175
|
+
"eslint-plugin-jest-extended": "^3.0.0",
|
|
176
|
+
"eslint-plugin-n": "^17.0.0 || ^18.0.0",
|
|
177
|
+
"eslint-plugin-react-jsx": "^5.10.0",
|
|
178
|
+
"eslint-plugin-react-naming-convention": "^5.10.0",
|
|
179
|
+
"eslint-plugin-react-x": "^5.10.0",
|
|
180
|
+
"oxlint": ">=1.73.0",
|
|
181
|
+
"oxlint-tsgolint": ">=0.24.0"
|
|
182
|
+
},
|
|
183
|
+
peerDependenciesMeta: {
|
|
184
|
+
"@vitest/eslint-plugin": { "optional": true },
|
|
185
|
+
"eslint-plugin-eslint-plugin": { "optional": true },
|
|
186
|
+
"eslint-plugin-jest": { "optional": true },
|
|
187
|
+
"eslint-plugin-jest-extended": { "optional": true },
|
|
188
|
+
"eslint-plugin-n": { "optional": true },
|
|
189
|
+
"eslint-plugin-react-jsx": { "optional": true },
|
|
190
|
+
"eslint-plugin-react-naming-convention": { "optional": true },
|
|
191
|
+
"eslint-plugin-react-x": { "optional": true },
|
|
192
|
+
"oxlint": { "optional": true },
|
|
193
|
+
"oxlint-tsgolint": { "optional": true }
|
|
194
|
+
},
|
|
195
|
+
packageManager: "pnpm@11.15.1+sha512.81350b07e53c9538a02f1f2303b4290fa2d7be04e56e2a970c4cc4b417dc761de196edabd49d55c7dc9580db81007c44143e4e3d7e462b3000d23c255122d065",
|
|
196
|
+
engines: { "node": ">=24.12.0" },
|
|
197
|
+
publishConfig: { "access": "public" }
|
|
198
|
+
};
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/cli/constants.ts
|
|
201
|
+
const vscodeSettingsString = `
|
|
202
|
+
// Disable the default formatter, use eslint instead
|
|
203
|
+
"prettier.enable": false,
|
|
204
|
+
"editor.formatOnSave": false,
|
|
205
|
+
|
|
206
|
+
// Auto fix
|
|
207
|
+
"editor.codeActionsOnSave": {
|
|
208
|
+
"source.fixAll.eslint": "explicit",
|
|
209
|
+
"source.organizeImports": "never"
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// Silent the stylistic rules in you IDE, but still auto fix them
|
|
213
|
+
"eslint.rules.customizations": [
|
|
214
|
+
{ "rule": "style/*", "severity": "off", "fixable": true },
|
|
215
|
+
{ "rule": "format/*", "severity": "off", "fixable": true },
|
|
216
|
+
{ "rule": "*fmt/*", "severity": "off", "fixable": true },
|
|
217
|
+
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
|
218
|
+
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
|
219
|
+
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
|
220
|
+
{ "rule": "*-order", "severity": "off", "fixable": true },
|
|
221
|
+
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
|
222
|
+
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
|
223
|
+
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
|
224
|
+
{ "rule": "*semi", "severity": "off", "fixable": true }
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
// Enable eslint for all supported languages
|
|
228
|
+
"eslint.validate": [
|
|
229
|
+
"typescript",
|
|
230
|
+
"typescriptreact",
|
|
231
|
+
"markdown",
|
|
232
|
+
"json",
|
|
233
|
+
"jsonc",
|
|
234
|
+
"yaml",
|
|
235
|
+
"toml",
|
|
236
|
+
"luau"
|
|
237
|
+
]
|
|
238
|
+
`;
|
|
239
|
+
const frameworkOptions = [{
|
|
240
|
+
label: ansis.red("Test"),
|
|
241
|
+
value: "test"
|
|
242
|
+
}, {
|
|
243
|
+
label: ansis.cyan("React"),
|
|
244
|
+
value: "react"
|
|
245
|
+
}];
|
|
246
|
+
const frameworks = frameworkOptions.map(({ value }) => value);
|
|
247
|
+
const dependenciesMap = {
|
|
248
|
+
react: [
|
|
249
|
+
"eslint-plugin-react-x",
|
|
250
|
+
"eslint-plugin-react-jsx",
|
|
251
|
+
"eslint-plugin-react-naming-convention"
|
|
252
|
+
],
|
|
253
|
+
test: ["eslint-plugin-jest"]
|
|
254
|
+
};
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/guards.ts
|
|
257
|
+
/**
|
|
258
|
+
* Whether a value is an array whose every element is a string.
|
|
259
|
+
*
|
|
260
|
+
* @param value - The value to test.
|
|
261
|
+
* @returns Whether the value is a `string` array.
|
|
262
|
+
*/
|
|
263
|
+
function isStringArray(value) {
|
|
264
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region node_modules/.pnpm/parse-gitignore-ts@1.0.0/node_modules/parse-gitignore-ts/dist/index.mjs
|
|
268
|
+
var isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
269
|
+
var INVALID_PATH_CHARS_REGEX = /[<>:"|?*\n\r\t\f\x00-\x1F]/;
|
|
270
|
+
var GLOBSTAR_REGEX = /(?:^|\/)[*]{2}($|\/)/;
|
|
271
|
+
var MAX_PATH_LENGTH = 248;
|
|
272
|
+
var isValidPath = (input) => {
|
|
273
|
+
if (typeof input === "string") return input.length <= MAX_PATH_LENGTH && !INVALID_PATH_CHARS_REGEX.test(input);
|
|
274
|
+
return false;
|
|
275
|
+
};
|
|
276
|
+
var split = (str) => String(str).split(/\r\n?|\n/);
|
|
277
|
+
var isComment = (str) => str.startsWith("#");
|
|
278
|
+
var isParsed = (input) => isObject(input) && "patterns" in input && "sections" in input;
|
|
279
|
+
var patterns = (input) => {
|
|
280
|
+
return split(input).map((l) => l.trim()).filter((line) => line !== "" && !isComment(line));
|
|
281
|
+
};
|
|
282
|
+
var parse = (input, options = {}) => {
|
|
283
|
+
let filepath = options.path;
|
|
284
|
+
if (isParsed(input)) return input;
|
|
285
|
+
if (isValidPath(input) && existsSync(input)) {
|
|
286
|
+
filepath = input;
|
|
287
|
+
input = readFileSync(input);
|
|
288
|
+
}
|
|
289
|
+
const lines = split(input);
|
|
290
|
+
const names = /* @__PURE__ */ new Map();
|
|
291
|
+
const parsed = {
|
|
292
|
+
sections: [],
|
|
293
|
+
patterns: []
|
|
294
|
+
};
|
|
295
|
+
let section = {
|
|
296
|
+
name: "default",
|
|
297
|
+
patterns: []
|
|
298
|
+
};
|
|
299
|
+
let prev = null;
|
|
300
|
+
for (const line of lines) {
|
|
301
|
+
const value = line.trim();
|
|
302
|
+
if (value.startsWith("#")) {
|
|
303
|
+
const match = /^#+\s*(.*)\s*$/.exec(value);
|
|
304
|
+
const name = match ? match[1] : "";
|
|
305
|
+
if (prev) {
|
|
306
|
+
names.delete(prev.name);
|
|
307
|
+
prev.comment = prev.comment ? `${prev.comment}
|
|
308
|
+
${value}` : value;
|
|
309
|
+
prev.name = name ? `${prev.name.trim()}
|
|
310
|
+
${name.trim()}` : prev.name.trim();
|
|
311
|
+
names.set(prev.name.toLowerCase().trim(), prev);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
section = {
|
|
315
|
+
name: name.trim(),
|
|
316
|
+
comment: value,
|
|
317
|
+
patterns: []
|
|
318
|
+
};
|
|
319
|
+
names.set(section.name.toLowerCase(), section);
|
|
320
|
+
parsed.sections.push(section);
|
|
321
|
+
prev = section;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (value !== "") {
|
|
325
|
+
section.patterns.push(value);
|
|
326
|
+
parsed.patterns.push(value);
|
|
327
|
+
}
|
|
328
|
+
prev = null;
|
|
329
|
+
}
|
|
330
|
+
if (options.dedupe === true || options.unique === true) return dedupe(parsed, {
|
|
331
|
+
...options,
|
|
332
|
+
format: false
|
|
333
|
+
});
|
|
334
|
+
parsed.path = filepath;
|
|
335
|
+
parsed.input = Buffer.from(input.toString());
|
|
336
|
+
const result = parsed;
|
|
337
|
+
result.format = (opts = {}) => format(result, {
|
|
338
|
+
...options,
|
|
339
|
+
...opts
|
|
340
|
+
});
|
|
341
|
+
result.dedupe = (opts = {}) => dedupe(result, {
|
|
342
|
+
...options,
|
|
343
|
+
...opts
|
|
344
|
+
});
|
|
345
|
+
result.globs = (opts = {}) => globs(result, {
|
|
346
|
+
path: filepath,
|
|
347
|
+
...options,
|
|
348
|
+
...opts
|
|
349
|
+
});
|
|
350
|
+
return result;
|
|
351
|
+
};
|
|
352
|
+
var parseFile = (filepath, options = {}) => {
|
|
353
|
+
return parse(readFileSync(filepath, "utf8"), options);
|
|
354
|
+
};
|
|
355
|
+
var dedupe = (input, options = {}) => {
|
|
356
|
+
const parsed = parse(input, {
|
|
357
|
+
...options,
|
|
358
|
+
dedupe: false
|
|
359
|
+
});
|
|
360
|
+
const names = /* @__PURE__ */ new Map();
|
|
361
|
+
const res = {
|
|
362
|
+
sections: [],
|
|
363
|
+
patterns: []
|
|
364
|
+
};
|
|
365
|
+
const patternsSet = /* @__PURE__ */ new Set();
|
|
366
|
+
let current;
|
|
367
|
+
for (const section of parsed.sections) {
|
|
368
|
+
const { name = "", comment, patterns: patterns2 } = section;
|
|
369
|
+
const key = name.trim().toLowerCase();
|
|
370
|
+
for (const pattern of patterns2) patternsSet.add(pattern);
|
|
371
|
+
if (name && names.has(key)) {
|
|
372
|
+
current = names.get(key);
|
|
373
|
+
current.patterns = [...current.patterns, ...patterns2];
|
|
374
|
+
} else {
|
|
375
|
+
current = {
|
|
376
|
+
name,
|
|
377
|
+
comment,
|
|
378
|
+
patterns: patterns2
|
|
379
|
+
};
|
|
380
|
+
res.sections.push(current);
|
|
381
|
+
names.set(key, current);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
for (const section of res.sections) section.patterns = [...new Set(section.patterns)];
|
|
385
|
+
res.patterns = [...patternsSet];
|
|
386
|
+
const result = res;
|
|
387
|
+
result.format = (opts = {}) => format(result, {
|
|
388
|
+
...options,
|
|
389
|
+
...opts
|
|
390
|
+
});
|
|
391
|
+
result.dedupe = (opts = {}) => dedupe(result, {
|
|
392
|
+
...options,
|
|
393
|
+
...opts
|
|
394
|
+
});
|
|
395
|
+
result.globs = (opts = {}) => globs(result, {
|
|
396
|
+
path: parsed.path,
|
|
397
|
+
...options,
|
|
398
|
+
...opts
|
|
399
|
+
});
|
|
400
|
+
return result;
|
|
401
|
+
};
|
|
402
|
+
var glob = (pattern, options) => {
|
|
403
|
+
if (GLOBSTAR_REGEX.test(pattern)) return pattern;
|
|
404
|
+
let relative = false;
|
|
405
|
+
if (pattern.startsWith("/")) {
|
|
406
|
+
pattern = pattern.slice(1);
|
|
407
|
+
relative = true;
|
|
408
|
+
} else if (pattern.slice(1, pattern.length - 1).includes("/")) relative = true;
|
|
409
|
+
pattern += pattern.endsWith("/") ? "**/" : "/**";
|
|
410
|
+
return relative ? pattern : `**/${pattern}`;
|
|
411
|
+
};
|
|
412
|
+
var globs = (input, options = {}) => {
|
|
413
|
+
const parsed = parse(input, options);
|
|
414
|
+
const result = [];
|
|
415
|
+
let index = 0;
|
|
416
|
+
const inputPatterns = parsed.patterns.concat(options.ignore || []).concat((options.unignore || []).map((p) => !p.startsWith("!") ? "!" + p : p));
|
|
417
|
+
const push = (prefix, pattern) => {
|
|
418
|
+
const prev = result[result.length - 1];
|
|
419
|
+
const type = prefix ? "unignore" : "ignore";
|
|
420
|
+
if (prev && prev.type === type) {
|
|
421
|
+
if (!prev.patterns.includes(pattern)) prev.patterns.push(pattern);
|
|
422
|
+
} else {
|
|
423
|
+
result.push({
|
|
424
|
+
type,
|
|
425
|
+
path: options.path || null,
|
|
426
|
+
patterns: [pattern],
|
|
427
|
+
index
|
|
428
|
+
});
|
|
429
|
+
index++;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
for (let pattern of inputPatterns) {
|
|
433
|
+
let prefix = "";
|
|
434
|
+
if (pattern.startsWith("!")) {
|
|
435
|
+
pattern = pattern.slice(1);
|
|
436
|
+
prefix = "!";
|
|
437
|
+
}
|
|
438
|
+
push(prefix, pattern.startsWith("/") ? pattern.slice(1) : pattern);
|
|
439
|
+
push(prefix, glob(pattern));
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
};
|
|
443
|
+
var formatSection = (section = {
|
|
444
|
+
name: "",
|
|
445
|
+
patterns: []
|
|
446
|
+
}) => {
|
|
447
|
+
const output = [section.comment || ""];
|
|
448
|
+
if (section.patterns?.length) {
|
|
449
|
+
output.push(section.patterns.join("\n"));
|
|
450
|
+
output.push("");
|
|
451
|
+
}
|
|
452
|
+
return output.join("\n");
|
|
453
|
+
};
|
|
454
|
+
var format = (input, options = {}) => {
|
|
455
|
+
const parsed = parse(input, options);
|
|
456
|
+
const fn = options.formatSection || formatSection;
|
|
457
|
+
const sections = parsed.sections || parsed;
|
|
458
|
+
const output = [];
|
|
459
|
+
for (const section of [].concat(sections)) output.push(fn(section));
|
|
460
|
+
return output.join("\n");
|
|
461
|
+
};
|
|
462
|
+
var parseGitignore = parse;
|
|
463
|
+
parseGitignore.file = parseFile;
|
|
464
|
+
parseGitignore.parse = parse;
|
|
465
|
+
parseGitignore.dedupe = dedupe;
|
|
466
|
+
parseGitignore.format = format;
|
|
467
|
+
parseGitignore.globs = globs;
|
|
468
|
+
parseGitignore.formatSection = formatSection;
|
|
469
|
+
parseGitignore.patterns = patterns;
|
|
470
|
+
var index_default = parseGitignore;
|
|
471
|
+
/*!
|
|
472
|
+
* parse-gitignore-ts
|
|
473
|
+
* TypeScript version of parse-gitignore <https://github.com/jonschlinkert/parse-gitignore>
|
|
474
|
+
* Original Copyright (c) 2015-present, Jon Schlinkert.
|
|
475
|
+
* Released under the MIT License.
|
|
476
|
+
*/
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/cli/utils.ts
|
|
479
|
+
function getEslintConfigContent(mainConfig, additionalConfigs) {
|
|
480
|
+
return `
|
|
481
|
+
import isentinel from '@isentinel/eslint-config'
|
|
482
|
+
|
|
483
|
+
export default isentinel({
|
|
484
|
+
${mainConfig}
|
|
485
|
+
}${additionalConfigs?.map((config) => `,{\n${config}\n}`).join(",") ?? ""})
|
|
486
|
+
`.trimStart();
|
|
487
|
+
}
|
|
488
|
+
function isGitClean() {
|
|
489
|
+
try {
|
|
490
|
+
execSync("git diff-index --quiet HEAD --");
|
|
491
|
+
return true;
|
|
492
|
+
} catch {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/cli/stages/update-eslint-files.ts
|
|
498
|
+
const LEGACY_CONFIG_REGEX = /eslint|prettier/;
|
|
499
|
+
async function updateEslintFiles(result) {
|
|
500
|
+
const cwd = process.cwd();
|
|
501
|
+
const pathESLintIgnore = path.join(cwd, ".eslintignore");
|
|
502
|
+
const pathPackageJSON = path.join(cwd, "package.json");
|
|
503
|
+
const packageContent = await fsp.readFile(pathPackageJSON, "utf-8");
|
|
504
|
+
const configFileName = JSON.parse(packageContent)["type"] === "module" ? "eslint.config.js" : "eslint.config.mjs";
|
|
505
|
+
const pathFlatConfig = path.join(cwd, configFileName);
|
|
506
|
+
const eslintIgnores = [];
|
|
507
|
+
if (fs.existsSync(pathESLintIgnore)) {
|
|
508
|
+
log.step(ansis.cyan("Migrating existing .eslintignore"));
|
|
509
|
+
const globs = index_default(await fsp.readFile(pathESLintIgnore, "utf-8")).globs();
|
|
510
|
+
for (const glob of globs) if (glob.type === "ignore") eslintIgnores.push(...glob.patterns);
|
|
511
|
+
else eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`));
|
|
512
|
+
}
|
|
513
|
+
const configLines = [];
|
|
514
|
+
if (eslintIgnores.length) configLines.push(`ignores: ${JSON.stringify(eslintIgnores)},`);
|
|
515
|
+
for (const framework of result.frameworks) configLines.push(`${framework}: true,`);
|
|
516
|
+
const eslintConfigContent = getEslintConfigContent(configLines.map((index) => ` ${index}`).join("\n"), []);
|
|
517
|
+
await fsp.writeFile(pathFlatConfig, eslintConfigContent);
|
|
518
|
+
log.success(ansis.green(`Created ${configFileName}`));
|
|
519
|
+
const files = fs.readdirSync(cwd);
|
|
520
|
+
const legacyConfig = [];
|
|
521
|
+
for (const file of files) if (LEGACY_CONFIG_REGEX.test(file) && !file.includes("eslint.config.")) legacyConfig.push(file);
|
|
522
|
+
if (legacyConfig.length) note(ansis.dim(legacyConfig.join(", ")), "You can now remove those files manually");
|
|
523
|
+
}
|
|
524
|
+
//#endregion
|
|
525
|
+
//#region src/cli/constants-generated.ts
|
|
526
|
+
const versionsMap = {
|
|
527
|
+
"eslint": "10.7.0",
|
|
528
|
+
"eslint-plugin-jest": "29.15.4",
|
|
529
|
+
"eslint-plugin-react-jsx": "5.17.2",
|
|
530
|
+
"eslint-plugin-react-naming-convention": "5.17.2",
|
|
531
|
+
"eslint-plugin-react-x": "5.17.2"
|
|
532
|
+
};
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/cli/stages/update-package-json.ts
|
|
535
|
+
/** Scripts the wizard wires up so `pnpm lint` drives the hybrid runner. */
|
|
536
|
+
const LINT_SCRIPTS = {
|
|
537
|
+
"lint": "isentinel-lint",
|
|
538
|
+
"lint:fix": "isentinel-lint --fix"
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Merge the lint scripts into a `scripts` block in place. Missing scripts are
|
|
542
|
+
* always added. An existing script with identical content is left untouched. An
|
|
543
|
+
* existing script with different content is preserved in skip-prompt mode, and
|
|
544
|
+
* otherwise only overwritten when {@link ConfirmOverwrite} resolves true.
|
|
545
|
+
*
|
|
546
|
+
* @param scripts - The consumer's `scripts` block, mutated in place.
|
|
547
|
+
* @param options - Skip-prompt flag and the overwrite confirmation callback.
|
|
548
|
+
* @returns Which scripts were added and which were overwritten.
|
|
549
|
+
*/
|
|
550
|
+
async function mergeLintScripts(scripts, options) {
|
|
551
|
+
const added = [];
|
|
552
|
+
const overwritten = [];
|
|
553
|
+
for (const [name, desired] of Object.entries(LINT_SCRIPTS)) {
|
|
554
|
+
const existing = scripts[name];
|
|
555
|
+
if (existing === void 0) {
|
|
556
|
+
scripts[name] = desired;
|
|
557
|
+
added.push(name);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (existing === desired || options.skipPrompt) continue;
|
|
561
|
+
if (await options.confirmOverwrite?.(name, existing, desired) === true) {
|
|
562
|
+
scripts[name] = desired;
|
|
563
|
+
overwritten.push(name);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
added,
|
|
568
|
+
overwritten
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
async function updatePackageJson(result, skipPrompt = false) {
|
|
572
|
+
const cwd = process.cwd();
|
|
573
|
+
const pathPackageJSON = path.join(cwd, "package.json");
|
|
574
|
+
log.step(ansis.cyan(`Bumping @isentinel/eslint-config to v${version}`));
|
|
575
|
+
const packageContent = await fsp.readFile(pathPackageJSON, "utf-8");
|
|
576
|
+
const parsedPackage = JSON.parse(packageContent);
|
|
577
|
+
parsedPackage.devDependencies ??= {};
|
|
578
|
+
parsedPackage.devDependencies["@isentinel/eslint-config"] = `^${version}`;
|
|
579
|
+
parsedPackage.devDependencies["eslint"] ??= versionsMap.eslint;
|
|
580
|
+
const addedPackages = [];
|
|
581
|
+
for (const framework of result.frameworks) {
|
|
582
|
+
if (!(framework in dependenciesMap)) continue;
|
|
583
|
+
const dependencies = dependenciesMap[framework];
|
|
584
|
+
for (const dependency of dependencies) {
|
|
585
|
+
parsedPackage.devDependencies[dependency] = versionsMap[dependency];
|
|
586
|
+
addedPackages.push(dependency);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (addedPackages.length) note(ansis.dim(addedPackages.join(", ")), "Added packages");
|
|
590
|
+
parsedPackage.scripts ??= {};
|
|
591
|
+
const { scripts = {} } = parsedPackage;
|
|
592
|
+
const { added, overwritten } = await mergeLintScripts(scripts, {
|
|
593
|
+
confirmOverwrite: async (name, existing, desired) => {
|
|
594
|
+
return await confirm({
|
|
595
|
+
initialValue: false,
|
|
596
|
+
message: `A "${name}" script already exists ("${existing}"). Overwrite it with "${desired}"?`
|
|
597
|
+
}) === true;
|
|
598
|
+
},
|
|
599
|
+
skipPrompt
|
|
600
|
+
});
|
|
601
|
+
const touched = [...added, ...overwritten];
|
|
602
|
+
if (touched.length) note(ansis.dim(touched.map((name) => `${name}: ${scripts[name]}`).join("\n")), "Lint scripts");
|
|
603
|
+
await fsp.writeFile(pathPackageJSON, JSON.stringify(parsedPackage, null, 2));
|
|
604
|
+
log.success(ansis.green("Changes wrote to package.json"));
|
|
605
|
+
}
|
|
606
|
+
//#endregion
|
|
607
|
+
//#region src/cli/stages/update-vscode-settings.ts
|
|
608
|
+
async function updateVscodeSettings(result) {
|
|
609
|
+
if (!result.updateVscodeSettings) return;
|
|
610
|
+
const cwd = process.cwd();
|
|
611
|
+
const dotVscodePath = path.join(cwd, ".vscode");
|
|
612
|
+
const settingsPath = path.join(dotVscodePath, "settings.json");
|
|
613
|
+
if (!fs.existsSync(dotVscodePath)) await fsp.mkdir(dotVscodePath, { recursive: true });
|
|
614
|
+
if (!fs.existsSync(settingsPath)) {
|
|
615
|
+
await fsp.writeFile(settingsPath, `{${vscodeSettingsString}}\n`, "utf-8");
|
|
616
|
+
log.success(ansis.green("Created .vscode/settings.json"));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
let settingsContent = await fsp.readFile(settingsPath, "utf8");
|
|
620
|
+
settingsContent = settingsContent.trim();
|
|
621
|
+
if (settingsContent.endsWith("}")) settingsContent = settingsContent.slice(0, -1).trimEnd();
|
|
622
|
+
settingsContent += settingsContent.endsWith(",") || settingsContent.endsWith("{") ? "" : ",";
|
|
623
|
+
settingsContent += `${vscodeSettingsString}}\n`;
|
|
624
|
+
await fsp.writeFile(settingsPath, settingsContent, "utf-8");
|
|
625
|
+
log.success(ansis.green("Updated .vscode/settings.json"));
|
|
626
|
+
}
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/cli/run.ts
|
|
629
|
+
async function run(options = {}) {
|
|
630
|
+
const argumentSkipPrompt = !!(process.env["SKIP_PROMPT"] ?? "") || options.yes;
|
|
631
|
+
const argumentTemplate = options.frameworks?.map((framework) => framework.trim()).filter((framework) => {
|
|
632
|
+
return frameworks.some((valid) => valid === framework);
|
|
633
|
+
});
|
|
634
|
+
const eslintConfigFiles = fs.readdirSync(process.cwd()).filter((file) => file.startsWith("eslint.config."));
|
|
635
|
+
if (eslintConfigFiles.length > 0) {
|
|
636
|
+
log.warn(ansis.yellow(`${eslintConfigFiles[0]} already exists, migration wizard exited.`));
|
|
637
|
+
return process.exit(1);
|
|
638
|
+
}
|
|
639
|
+
let result = {
|
|
640
|
+
frameworks: argumentTemplate ?? [],
|
|
641
|
+
uncommittedConfirmed: false,
|
|
642
|
+
updateVscodeSettings: true
|
|
643
|
+
};
|
|
644
|
+
if (argumentSkipPrompt !== true) {
|
|
645
|
+
const { frameworks: groupedFrameworks, uncommittedConfirmed, updateVscodeSettings: shouldUpdateVscode } = await group({
|
|
646
|
+
uncommittedConfirmed: async () => {
|
|
647
|
+
if (isGitClean()) return true;
|
|
648
|
+
return confirm({
|
|
649
|
+
initialValue: false,
|
|
650
|
+
message: "There are uncommitted changes in the current repository, are you sure to continue?"
|
|
651
|
+
});
|
|
652
|
+
},
|
|
653
|
+
frameworks: async ({ results }) => {
|
|
654
|
+
if ((argumentTemplate && argumentTemplate.length > 0 && argumentTemplate.every((template) => frameworks.includes(template))) === true || results.uncommittedConfirmed !== true) return;
|
|
655
|
+
const message = argumentTemplate && argumentTemplate.length > 0 ? `"${argumentTemplate.join(", ")}" isn't a valid template. Please choose from below: ` : "Select a framework:";
|
|
656
|
+
return multiselect({
|
|
657
|
+
message: ansis.reset(message),
|
|
658
|
+
options: frameworkOptions,
|
|
659
|
+
required: false
|
|
660
|
+
});
|
|
661
|
+
},
|
|
662
|
+
updateVscodeSettings: async ({ results }) => {
|
|
663
|
+
if (results.uncommittedConfirmed !== true) return;
|
|
664
|
+
return confirm({
|
|
665
|
+
initialValue: true,
|
|
666
|
+
message: "Update .vscode/settings.json for better VS Code experience?"
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}, { onCancel: () => {
|
|
670
|
+
cancel("Operation cancelled.");
|
|
671
|
+
process.exit(0);
|
|
672
|
+
} });
|
|
673
|
+
result = {
|
|
674
|
+
frameworks: isStringArray(groupedFrameworks) ? groupedFrameworks.filter((value) => {
|
|
675
|
+
return frameworks.some((option) => option === value);
|
|
676
|
+
}) : [],
|
|
677
|
+
uncommittedConfirmed: uncommittedConfirmed === true,
|
|
678
|
+
updateVscodeSettings: shouldUpdateVscode !== false
|
|
679
|
+
};
|
|
680
|
+
if (!result.uncommittedConfirmed) return process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
await updatePackageJson(result, argumentSkipPrompt === true);
|
|
683
|
+
await updateEslintFiles(result);
|
|
684
|
+
await updateVscodeSettings(result);
|
|
685
|
+
log.success(ansis.green("Setup completed"));
|
|
686
|
+
outro(`Now you can update the dependencies by running ${ansis.blue("pnpm install")} and lint by running ${ansis.blue("pnpm lint")} (or ${ansis.blue("pnpm lint:fix")})\n`);
|
|
687
|
+
}
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region src/cli/index.ts
|
|
690
|
+
function header() {
|
|
691
|
+
console.log("\n");
|
|
692
|
+
const introText = ansis.green("@isentinel/eslint-config");
|
|
693
|
+
const versionText = `v${package_default.version}`;
|
|
694
|
+
intro(introText + ansis.dim(versionText));
|
|
695
|
+
}
|
|
696
|
+
yargs(hideBin(process.argv)).scriptName("@isentinel/eslint-config").usage("").command("*", "Run the initialization or migration", (args) => {
|
|
697
|
+
return args.option("yes", {
|
|
698
|
+
alias: "y",
|
|
699
|
+
description: "Skip prompts and use default values",
|
|
700
|
+
type: "boolean"
|
|
701
|
+
}).option("template", {
|
|
702
|
+
alias: "t",
|
|
703
|
+
description: "Use the framework template for optimal customization: react, jest",
|
|
704
|
+
type: "string"
|
|
705
|
+
}).help();
|
|
706
|
+
}, async (args) => {
|
|
707
|
+
header();
|
|
708
|
+
try {
|
|
709
|
+
await run({
|
|
710
|
+
...args,
|
|
711
|
+
frameworks: args.template !== void 0 ? [args.template] : void 0
|
|
712
|
+
});
|
|
713
|
+
} catch (err) {
|
|
714
|
+
log.error(ansis.inverse(ansis.red(" Failed to migrate ")));
|
|
715
|
+
log.error(ansis.red(`✘ ${String(err)}`));
|
|
716
|
+
process.exit(1);
|
|
717
|
+
}
|
|
718
|
+
}).showHelpOnFail(false).alias("h", "help").version("version", package_default.version).alias("v", "version").help().argv;
|
|
719
|
+
//#endregion
|
|
1
720
|
export {};
|