@whoj/eslint-config 7.5.2 → 9.2.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 CHANGED
@@ -167,6 +167,72 @@ Add the following settings to your `.vscode/settings.json`:
167
167
 
168
168
  </details>
169
169
 
170
+ <details>
171
+ <summary>🔲 Zed support</summary>
172
+
173
+ <br>
174
+
175
+ Add the following settings to your `.zed/settings.json`:
176
+
177
+ ```jsonc
178
+ {
179
+ // Use ESLint's --fix:
180
+ "code_actions_on_format": {
181
+ "source.fixAll.eslint": true
182
+ },
183
+ "formatter": [],
184
+ // Enable eslint for all supported languages
185
+ // Defaults only include https://github.com/search?q=repo%3Azed-industries%2Fzed%20eslint_languages&type=code
186
+ "languages": {
187
+ "HTML": {
188
+ "language_servers": ["...", "eslint"]
189
+ },
190
+ "Markdown": {
191
+ "language_servers": ["...", "eslint"]
192
+ },
193
+ "Markdown-Inline": {
194
+ "language_servers": ["...", "eslint"]
195
+ },
196
+ "JSON": {
197
+ "language_servers": ["...", "eslint"]
198
+ },
199
+ "JSONC": {
200
+ "language_servers": ["...", "eslint"]
201
+ },
202
+ "YAML": {
203
+ "language_servers": ["...", "eslint"]
204
+ },
205
+ "CSS": {
206
+ "language_servers": ["...", "eslint"]
207
+ }
208
+ // Add other languages as needed
209
+ },
210
+ "lsp": {
211
+ "eslint": {
212
+ "settings": {
213
+ "workingDirectories": ["./"],
214
+
215
+ // Silent the stylistic rules in your IDE, but still auto fix them
216
+ "rulesCustomizations": [
217
+ { "rule": "style/*", "severity": "off", "fixable": true },
218
+ { "rule": "format/*", "severity": "off", "fixable": true },
219
+ { "rule": "*-indent", "severity": "off", "fixable": true },
220
+ { "rule": "*-spacing", "severity": "off", "fixable": true },
221
+ { "rule": "*-spaces", "severity": "off", "fixable": true },
222
+ { "rule": "*-order", "severity": "off", "fixable": true },
223
+ { "rule": "*-dangle", "severity": "off", "fixable": true },
224
+ { "rule": "*-newline", "severity": "off", "fixable": true },
225
+ { "rule": "*quotes", "severity": "off", "fixable": true },
226
+ { "rule": "*semi", "severity": "off", "fixable": true }
227
+ ]
228
+ }
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ </details>
235
+
170
236
  <details>
171
237
  <summary>🟩 Neovim Support</summary>
172
238
 
@@ -294,9 +360,9 @@ export default whoj.clone().overrides({
294
360
  },
295
361
  },
296
362
  }).disableRulesFix([
297
- 'unused-imports/no-unused-imports',
298
- 'test/no-only-tests',
299
363
  'prefer-const',
364
+ 'test/no-only-tests',
365
+ 'unused-imports/no-unused-imports',
300
366
  ], {
301
367
  builtinRules: () => import(['eslint', 'use-at-your-own-risk'].join('/')).then(r => r.builtinRules),
302
368
  });
@@ -346,6 +412,7 @@ export default whoj({
346
412
 
347
413
  // Or customize the stylistic rules
348
414
  stylistic: {
415
+ braceStyle: 'stroustrup', // '1tbs', or 'allman'
349
416
  indent: 2, // 4, or 'tab'
350
417
  quotes: 'single', // or 'double'
351
418
  },
@@ -510,7 +577,7 @@ export default whoj(
510
577
  },
511
578
  },
512
579
  {
513
- // Without `files`, they are general rules for all files
580
+ // Without `files`, they are general rules for all files (Markdown excluded — see note below)
514
581
  rules: {
515
582
  'style/semi': ['error', 'never'],
516
583
  },
@@ -518,6 +585,9 @@ export default whoj(
518
585
  );
519
586
  ```
520
587
 
588
+ > [!NOTE]
589
+ > Rule overrides without an explicit `files` constraint are automatically excluded from Markdown files, via [`composer.setDefaultIgnores`](https://github.com/antfu/eslint-flat-config-utils#composersetdefaultignores). This prevents JS-only rules (e.g. `no-irregular-whitespace`, `perfectionist/sort-imports`) from crashing on `@eslint/markdown`'s `SourceCode`, which doesn't expose JS-specific methods like `getAllComments()`. If you want a rule to apply to Markdown, scope it explicitly with `files: ['**/*.md']`.
590
+
521
591
  We also provided the `overrides` options in each integration to make it easier:
522
592
 
523
593
  ```js
@@ -529,6 +599,10 @@ export default whoj({
529
599
  overrides: {
530
600
  'ts/consistent-type-definitions': ['error', 'interface'],
531
601
  },
602
+ // type aware rules overrides should write here
603
+ overridesTypeAware: {
604
+ 'ts/no-unsafe-assignment': ['warn'],
605
+ }
532
606
  },
533
607
  vue: {
534
608
  overrides: {
@@ -679,7 +753,7 @@ export default whoj({
679
753
  Running `npx eslint` should prompt you to install the required dependencies, otherwise, you can install them manually:
680
754
 
681
755
  ```bash
682
- npm i -D @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh
756
+ npm i -D @eslint-react/eslint-plugin eslint-plugin-react-refresh
683
757
  ```
684
758
 
685
759
  #### Next.js
@@ -849,6 +923,21 @@ export default whoj({
849
923
  });
850
924
  ```
851
925
 
926
+ ### Prettier
927
+
928
+ If you're using prettier outside eslint, you can disable the config via etc:
929
+
930
+ ```js
931
+ import antfu from '@antfu/eslint-config';
932
+ import prettierConflicts from 'eslint-config-prettier';
933
+
934
+ export default antfu({
935
+ rules: {
936
+ 'some-rule': 'off'
937
+ }
938
+ }, prettierConflicts);
939
+ ```
940
+
852
941
  ### Editor Specific Disables
853
942
 
854
943
  Auto-fixing for the following rules are disabled when ESLint is running in a code editor:
package/dist/cli.mjs CHANGED
@@ -1,15 +1,14 @@
1
- import process from "node:process";
2
- import fs from "node:fs/promises";
3
- import fs$1 from "node:fs";
4
- import path from "node:path";
5
1
  import c, { green } from "ansis";
6
2
  import { cac } from "cac";
3
+ import process from "node:process";
7
4
  import * as p from "@clack/prompts";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
8
7
  import { execSync } from "node:child_process";
8
+ import fsp from "node:fs/promises";
9
9
  import parse from "parse-gitignore";
10
10
  import { deepMerge } from "@whoj/utils-core";
11
11
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
12
-
13
12
  //#region src/cli/utils.ts
14
13
  function isGitClean() {
15
14
  try {
@@ -54,20 +53,21 @@ export default whoj.clone().overrides({
54
53
  })
55
54
  `.trimStart();
56
55
  }
57
-
58
56
  //#endregion
59
57
  //#region src/cli/stages/update-eslint-files.ts
58
+ const ESLINT_OR_PRETTIER = /eslint|prettier/;
59
+ const ESLINT_CONFIG = /eslint\.config\./;
60
60
  async function updateEslintFiles(result) {
61
61
  const cwd = process.cwd();
62
62
  const pathESLintIgnore = path.join(cwd, ".eslintignore");
63
63
  const pathPackageJSON = path.join(cwd, "package.json");
64
- const pkgContent = await fs.readFile(pathPackageJSON, "utf-8");
64
+ const pkgContent = await fsp.readFile(pathPackageJSON, "utf-8");
65
65
  const configFileName = JSON.parse(pkgContent).type === "module" ? "eslint.config.js" : "eslint.config.mjs";
66
66
  const pathFlatConfig = path.join(cwd, configFileName);
67
67
  const eslintIgnores = [];
68
- if (fs$1.existsSync(pathESLintIgnore)) {
68
+ if (fs.existsSync(pathESLintIgnore)) {
69
69
  p.log.step(c.cyan`Migrating existing .eslintignore`);
70
- const globs = parse(await fs.readFile(pathESLintIgnore, "utf-8")).globs();
70
+ const globs = parse(await fsp.readFile(pathESLintIgnore, "utf-8")).globs();
71
71
  for (const glob of globs) if (glob.type === "ignore") eslintIgnores.push(...glob.patterns);
72
72
  else if (glob.type === "unignore") eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`));
73
73
  }
@@ -77,17 +77,16 @@ async function updateEslintFiles(result) {
77
77
  if (result.extra.includes("unocss")) configLines.push("unocss: true,");
78
78
  for (const framework of result.frameworks) configLines.push(`${framework}: true,`);
79
79
  const eslintConfigContent = getEslintConfigContent(configLines.map((i) => ` ${i}`).join("\n"), []);
80
- await fs.writeFile(pathFlatConfig, eslintConfigContent);
80
+ await fsp.writeFile(pathFlatConfig, eslintConfigContent);
81
81
  p.log.success(c.green`Created ${configFileName}`);
82
- const files = fs$1.readdirSync(cwd);
82
+ const files = fs.readdirSync(cwd);
83
83
  const legacyConfig = [];
84
84
  files.forEach((file) => {
85
- if (/eslint|prettier/.test(file) && !/eslint\.config\./.test(file)) legacyConfig.push(file);
85
+ if (ESLINT_OR_PRETTIER.test(file) && !ESLINT_CONFIG.test(file)) legacyConfig.push(file);
86
86
  });
87
87
  if (legacyConfig.length) p.note(c.dim(legacyConfig.join(", ")), "You can now remove those files manually");
88
88
  return configFileName;
89
89
  }
90
-
91
90
  //#endregion
92
91
  //#region src/cli/constants.ts
93
92
  const vscodeSettingsString = `
@@ -150,31 +149,31 @@ const frameworkOptions = [
150
149
  value: "react",
151
150
  label: c.cyan("React")
152
151
  },
153
- {
154
- value: "svelte",
155
- label: c.red("Svelte")
156
- },
157
- {
158
- value: "astro",
159
- label: c.magenta("Astro")
160
- },
161
152
  {
162
153
  value: "solid",
163
154
  label: c.cyan("Solid")
164
155
  },
156
+ {
157
+ value: "svelte",
158
+ label: c.red("Svelte")
159
+ },
165
160
  {
166
161
  value: "slidev",
167
162
  label: c.blue("Slidev")
163
+ },
164
+ {
165
+ value: "astro",
166
+ label: c.magenta("Astro")
168
167
  }
169
168
  ];
170
169
  const frameworks = frameworkOptions.map(({ value }) => value);
171
170
  const extraOptions = [{
171
+ value: "unocss",
172
+ label: c.cyan("UnoCSS")
173
+ }, {
172
174
  value: "formatter",
173
175
  label: c.red("Formatter"),
174
176
  hint: "Use external formatters (Prettier and/or dprint) to format files that ESLint cannot handle yet (.css, .html, etc)"
175
- }, {
176
- value: "unocss",
177
- label: c.cyan("UnoCSS")
178
177
  }];
179
178
  const extra = extraOptions.map(({ value }) => value);
180
179
  const dependenciesMap = {
@@ -187,11 +186,7 @@ const dependenciesMap = {
187
186
  formatterAstro: ["prettier-plugin-astro"],
188
187
  astro: ["eslint-plugin-astro", "astro-eslint-parser"],
189
188
  svelte: ["eslint-plugin-svelte", "svelte-eslint-parser"],
190
- react: [
191
- "@eslint-react/eslint-plugin",
192
- "eslint-plugin-react-hooks",
193
- "eslint-plugin-react-refresh"
194
- ]
189
+ react: ["@eslint-react/eslint-plugin", "eslint-plugin-react-refresh"]
195
190
  };
196
191
  const jetbrainsSettingsObj = {
197
192
  "?xml": {
@@ -214,37 +209,33 @@ const jetbrainsSettingsObj = {
214
209
  }
215
210
  }
216
211
  };
217
-
218
212
  //#endregion
219
213
  //#region package.json
220
- var version = "7.5.2";
221
-
214
+ var version = "9.2.0";
222
215
  //#endregion
223
216
  //#region src/cli/constants-generated.ts
224
217
  const versionsMap = {
225
- "@eslint-react/eslint-plugin": "^2.12.4",
226
- "@next/eslint-plugin-next": "^16.1.6",
227
- "@unocss/eslint-plugin": "^66.6.0",
228
- "astro-eslint-parser": "^1.2.2",
229
- "eslint": "^9.39.2||^10.0.0",
230
- "eslint-plugin-astro": "^1.5.0",
231
- "eslint-plugin-format": "^1.4.0",
232
- "eslint-plugin-react-hooks": "^7.0.1",
233
- "eslint-plugin-react-refresh": "^0.5.0",
218
+ "@eslint-react/eslint-plugin": "^5.14.7",
219
+ "@next/eslint-plugin-next": "^16.2.10",
220
+ "@unocss/eslint-plugin": "^66.7.5",
221
+ "astro-eslint-parser": "^2.1 || ^3.0",
222
+ "eslint": "^9.0.0 || ^10.7.0",
223
+ "eslint-plugin-astro": "^2.1 || ^3.0",
224
+ "eslint-plugin-format": "^2.0.1",
225
+ "eslint-plugin-react-refresh": "^0.5.3",
234
226
  "eslint-plugin-solid": "^0.14.5",
235
- "eslint-plugin-svelte": "^3.15.0",
227
+ "eslint-plugin-svelte": "^3.20.0",
236
228
  "prettier-plugin-astro": "^0.14.1",
237
229
  "prettier-plugin-slidev": "^1.0.5",
238
- "svelte-eslint-parser": "^1.4.1"
230
+ "svelte-eslint-parser": "^1.8.0"
239
231
  };
240
-
241
232
  //#endregion
242
233
  //#region src/cli/stages/update-package-json.ts
243
234
  async function updatePackageJson(result) {
244
235
  const cwd = process.cwd();
245
236
  const pathPackageJSON = path.join(cwd, "package.json");
246
237
  p.log.step(c.cyan`Bumping @whoj/eslint-config to v${version}`);
247
- const pkgContent = await fs.readFile(pathPackageJSON, "utf-8");
238
+ const pkgContent = await fsp.readFile(pathPackageJSON, "utf-8");
248
239
  const pkg = JSON.parse(pkgContent);
249
240
  pkg.devDependencies ??= {};
250
241
  pkg.devDependencies["@whoj/eslint-config"] = `^${version}`;
@@ -275,10 +266,9 @@ async function updatePackageJson(result) {
275
266
  });
276
267
  }
277
268
  if (addedPackages.length) p.note(c.dim(addedPackages.join(", ")), "Added packages");
278
- await fs.writeFile(pathPackageJSON, JSON.stringify(pkg, null, 2));
269
+ await fsp.writeFile(pathPackageJSON, JSON.stringify(pkg, null, 2));
279
270
  p.log.success(c.green`Changes wrote to package.json`);
280
271
  }
281
-
282
272
  //#endregion
283
273
  //#region src/cli/stages/update-jetbrains-idea.ts
284
274
  async function updateJetbrainsIdea(result, configFileName) {
@@ -296,12 +286,12 @@ async function updateJetbrainsIdea(result, configFileName) {
296
286
  const cwd = process.cwd();
297
287
  const ideaDir = path.join(cwd, ".idea");
298
288
  const jsLintersDir = path.join(ideaDir, "jsLinters");
299
- if (!fs$1.existsSync(ideaDir)) fs$1.mkdirSync(ideaDir, { recursive: true });
300
- if (!fs$1.existsSync(jsLintersDir)) fs$1.mkdirSync(jsLintersDir, { recursive: true });
289
+ if (!fs.existsSync(ideaDir)) fs.mkdirSync(ideaDir, { recursive: true });
290
+ if (!fs.existsSync(jsLintersDir)) fs.mkdirSync(jsLintersDir, { recursive: true });
301
291
  const eslintXmlPath = path.join(jsLintersDir, "eslint.xml");
302
292
  let settings;
303
- if (fs$1.existsSync(eslintXmlPath)) {
304
- const content = fs$1.readFileSync(eslintXmlPath, "utf-8");
293
+ if (fs.existsSync(eslintXmlPath)) {
294
+ const content = fs.readFileSync(eslintXmlPath, "utf-8");
305
295
  settings = deepMerge(xmlParser.parse(content), jetbrainsSettingsObj);
306
296
  p.log.step(c.cyan`Updating existing JetBrains ESLint configuration`);
307
297
  } else {
@@ -313,41 +303,40 @@ async function updateJetbrainsIdea(result, configFileName) {
313
303
  path: `$PROJECT_DIR$/.idea/jsLinters/${configFileName}`
314
304
  };
315
305
  const xml = xmlBuilder.build(settings);
316
- fs$1.writeFileSync(eslintXmlPath, xml, "utf-8");
306
+ fs.writeFileSync(eslintXmlPath, xml, "utf-8");
317
307
  const jetbrainsConfigPath = path.join(jsLintersDir, configFileName);
318
308
  const configContent = getJetbrainsEslintConfigContent(path.relative(jsLintersDir, path.join(cwd, configFileName)));
319
- fs$1.writeFileSync(jetbrainsConfigPath, configContent, "utf-8");
309
+ fs.writeFileSync(jetbrainsConfigPath, configContent, "utf-8");
320
310
  p.log.success(c.green`JetBrains ESLint configuration updated`);
321
311
  }
322
-
323
312
  //#endregion
324
313
  //#region src/cli/stages/update-vscode-settings.ts
314
+ const LAST_LINE_PATTERN = /\s*\}$/;
325
315
  async function updateVscodeSettings(result) {
326
316
  const cwd = process.cwd();
327
317
  if (!result.updateVscodeSettings) return;
328
318
  const dotVscodePath = path.join(cwd, ".vscode");
329
319
  const settingsPath = path.join(dotVscodePath, "settings.json");
330
- if (!fs$1.existsSync(dotVscodePath)) await fs.mkdir(dotVscodePath, { recursive: true });
331
- if (!fs$1.existsSync(settingsPath)) {
332
- await fs.writeFile(settingsPath, `{${vscodeSettingsString}}\n`, "utf-8");
320
+ if (!fs.existsSync(dotVscodePath)) await fsp.mkdir(dotVscodePath, { recursive: true });
321
+ if (!fs.existsSync(settingsPath)) {
322
+ await fsp.writeFile(settingsPath, `{${vscodeSettingsString}}\n`, "utf-8");
333
323
  p.log.success(green`Created .vscode/settings.json`);
334
324
  } else {
335
- let settingsContent = await fs.readFile(settingsPath, "utf8");
336
- settingsContent = settingsContent.trim().replace(/\s*\}$/, "");
325
+ let settingsContent = await fsp.readFile(settingsPath, "utf8");
326
+ settingsContent = settingsContent.trim().replace(LAST_LINE_PATTERN, "");
337
327
  settingsContent += settingsContent.endsWith(",") || settingsContent.endsWith("{") ? "" : ",";
338
328
  settingsContent += `${vscodeSettingsString}}\n`;
339
- await fs.writeFile(settingsPath, settingsContent, "utf-8");
329
+ await fsp.writeFile(settingsPath, settingsContent, "utf-8");
340
330
  p.log.success(green`Updated .vscode/settings.json`);
341
331
  }
342
332
  }
343
-
344
333
  //#endregion
345
334
  //#region src/cli/run.ts
346
335
  async function run({ jetbrains, ...options } = {}) {
347
336
  const argSkipPrompt = !!process.env.SKIP_PROMPT || options.yes;
348
337
  const argTemplate = options.frameworks?.map((m) => m?.trim()).filter(Boolean);
349
338
  const argExtra = options.extra?.map((m) => m?.trim()).filter(Boolean);
350
- if (fs$1.existsSync(path.join(process.cwd(), "eslint.config.js"))) {
339
+ if (fs.existsSync(path.join(process.cwd(), "eslint.config.js"))) {
351
340
  p.log.warn(c.yellow`eslint.config.js already exists, migration wizard exited.`);
352
341
  return process.exit(1);
353
342
  }
@@ -378,7 +367,7 @@ async function run({ jetbrains, ...options } = {}) {
378
367
  });
379
368
  },
380
369
  extra: ({ results }) => {
381
- const isArgExtraValid = argExtra?.length && !argExtra.filter((element) => !extra.includes(element)).length;
370
+ const isArgExtraValid = argExtra?.length && argExtra.every((element) => extra.includes(element));
382
371
  if (!results.uncommittedConfirmed || isArgExtraValid) return;
383
372
  const message = !isArgExtraValid && argExtra ? `"${argExtra}" isn't a valid extra util. Please choose from below: ` : "Select a extra utils:";
384
373
  return p.multiselect({
@@ -414,7 +403,6 @@ async function run({ jetbrains, ...options } = {}) {
414
403
  p.log.success(c.green`Setup completed`);
415
404
  p.outro(`Now you can update the dependencies by run ${c.blue("pnpm install")} and run ${c.blue("eslint --fix")}\n`);
416
405
  }
417
-
418
406
  //#endregion
419
407
  //#region src/cli/index.ts
420
408
  function header() {
@@ -422,11 +410,7 @@ function header() {
422
410
  p.intro(`${c.green`@whoj/eslint-config `}${c.dim`v${version}`}`);
423
411
  }
424
412
  const cli = cac("@whoj/eslint-config");
425
- cli.command("", "Run the initialization or migration").option("--yes, -y", "Skip prompts and use default values", { default: false }).option("--template, -t <template>", "Use the framework template for optimal customization: vue / react / svelte / astro", { type: [] }).option("--extra, -e <extra>", "Use the extra utils: formatter / perfectionist / unocss", { type: [] }).option("--jetbrains", "Configure eslint settings for better Jetbrains IDE. (WebStorm / PhpStorm) experience.", {
426
- default: true,
427
- type: "boolean",
428
- alias: ["idea", "j"]
429
- }).action(async (args) => {
413
+ cli.command("", "Run the initialization or migration").option("--yes, -y", "Skip prompts and use default values", { default: false }).option("--template, -t <template>", "Use the framework template for optimal customization: vue / react / svelte / astro", { type: [] }).option("--extra, -e <extra>", "Use the extra utils: formatter / perfectionist / unocss", { type: [] }).option("--jetbrains, --idea, -j", "Configure eslint settings for better Jetbrains IDE. (WebStorm / PhpStorm) experience.", { default: true }).action(async (args) => {
430
414
  header();
431
415
  try {
432
416
  await run(args);
@@ -439,6 +423,5 @@ cli.command("", "Run the initialization or migration").option("--yes, -y", "Skip
439
423
  cli.help();
440
424
  cli.version(version);
441
425
  cli.parse();
442
-
443
426
  //#endregion
444
- export { };
427
+ export {};