@zayne-labs/eslint-config 0.13.13 → 0.13.15

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
@@ -1,11 +1,14 @@
1
1
  # @zayne-labs/eslint-config
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@zayne-labs/eslint-config?color=444&label=)](https://npmjs.com/package/@zayne-labs/eslint-config)
4
+
3
5
  Opinionated ESLint config with sensible defaults and zero-config setup.
4
6
 
5
7
  - One-line setup with reasonable defaults and best practices
6
8
  - Works out-of-the-box with TypeScript, JSX, Vue, JSON, YAML, TOML, Markdown, and more
7
9
  - [ESLint Flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new) for easy composition
8
- - Optional framework support: [Vue](#vue), [React](#react), [Svelte](#svelte), [Astro](#astro), [Solid](#solid)
10
+ - Optional framework support: [Vue](#vue), [React](#react), [Astro](#astro), [Solid](#solid), and [Expo](#expo-react-native)
11
+ - First-class integrations for [Better Tailwind CSS](#better-tailwind-css), [TanStack](#tanstack), [pnpm catalogs](#pnpm-workspaces-and-catalogs), and dependency checks
9
12
  - Respects `.gitignore` by default
10
13
  - Highly [customizable](#customization) when you need it
11
14
  - Requires ESLint v9.5.0+ and Node.js v20+
@@ -25,10 +28,18 @@ pnpx @zayne-labs/eslint-config@latest
25
28
 
26
29
  The CLI will guide you through:
27
30
 
28
- - Framework selection (React, Vue, Svelte, Astro)
29
- - Additional integrations (TailwindCSS, etc.)
31
+ - Framework selection (React, Vue, Solid, Astro)
32
+ - Additional integrations (Better TailwindCSS, etc.)
30
33
  - Automatic dependency installation
31
34
 
35
+ The wizard also supports non-interactive setup:
36
+
37
+ ```bash
38
+ pnpx @zayne-labs/eslint-config@latest --yes --template react --extra tailwindcss-better
39
+ ```
40
+
41
+ Run it in a project that does not already have an `eslint.config.*` file. Existing legacy ESLint files are left untouched and listed for manual review after migration.
42
+
32
43
  ### Manual Installation
33
44
 
34
45
  ```bash
@@ -121,7 +132,6 @@ Install the [ESLint extension](https://marketplace.visualstudio.com/items?itemNa
121
132
  "gql",
122
133
  "graphql",
123
134
  "astro",
124
- "svelte",
125
135
  "css",
126
136
  "less",
127
137
  "scss",
@@ -161,7 +171,6 @@ lspconfig.eslint.setup(
161
171
  "gql",
162
172
  "graphql",
163
173
  "astro",
164
- "svelte",
165
174
  "css",
166
175
  "less",
167
176
  "scss",
@@ -211,7 +220,7 @@ export default zayne({
211
220
  // Parse the `.gitignore` file to get the ignores, on by default
212
221
  gitignore: true,
213
222
 
214
- // Project type: 'app' (default) or 'lib'
223
+ // Project type: "app" (default), "app-strict", "lib", or "lib-strict"
215
224
  type: "app",
216
225
 
217
226
  // Disable all optional configs at once (keeps only essentials)
@@ -236,6 +245,30 @@ export default zayne({
236
245
  });
237
246
  ```
238
247
 
248
+ ### Configuration Reference
249
+
250
+ The factory accepts booleans for simple enable/disable cases and option objects when an integration needs files, rule overrides, or plugin-specific settings.
251
+
252
+ | Option | Default | Purpose |
253
+ | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------- |
254
+ | `type` | `"app"` | Selects `"app"`, `"app-strict"`, `"lib"`, or `"lib-strict"` rule behavior. |
255
+ | `withDefaults` | `true` | Controls the standard optional baseline without disabling essential JavaScript, ignore, JSX, or command support. |
256
+ | `gitignore` | `true` | Reads the nearest `.gitignore`; accepts the underlying flat-gitignore options. |
257
+ | `typescript` | Auto-detected | Enables TypeScript rules and optional type-aware or erasable-syntax rules. |
258
+ | `react` | Auto-detected | Enables the React integration when React is installed. |
259
+ | `pnpm` | Auto-detected | Enables workspace rules when a `pnpm-workspace.yaml` is found. |
260
+ | `baseline`, `comments`, `imports`, `jsdoc`, `jsonc`, `markdown`, `node`, `perfectionist`, `regexp`, `stylistic`, `toml`, `unicorn`, `yaml` | `withDefaults` | Controls the standard integrations; Baseline defaults to widely available JS/TS compatibility warnings. |
261
+ | `astro`, `vue`, `solid`, `expo` | `false` | Enables framework-specific parsing and rules. |
262
+ | `depend`, `tailwindcssBetter`, `tanstack` | `false` | Enables optional dependency, Tailwind CSS, and TanStack integrations. |
263
+
264
+ Most integrations accept an `overrides` object. Integrations that apply to special file types also accept `files`. TypeScript, Baseline, and React provide separate type-aware file and override options, while Solid provides a separate `filesTypeAware` scope.
265
+
266
+ [Baseline JS](https://baselinejs.vercel.app/docs/config) automatically uses type-aware checks for TypeScript files when type-aware linting is active.
267
+
268
+ ### Optional Dependencies
269
+
270
+ Optional framework and integration plugins are loaded only when their feature is enabled. In an interactive terminal outside CI, the config can offer to install a missing peer dependency. In CI and non-interactive environments, install the documented peers explicitly.
271
+
239
272
  ### Custom Rules
240
273
 
241
274
  Pass additional configs as extra arguments:
@@ -264,6 +297,65 @@ export default zayne(
264
297
  );
265
298
  ```
266
299
 
300
+ Rules from an integration can also be overridden alongside that integration. This keeps the correct file scope and parser configuration:
301
+
302
+ ```js
303
+ import { zayne } from "@zayne-labs/eslint-config";
304
+
305
+ export default zayne({
306
+ react: {
307
+ overrides: {
308
+ "react/no-array-index-key": "off",
309
+ },
310
+ },
311
+ typescript: {
312
+ overrides: {
313
+ "ts-eslint/consistent-type-definitions": ["error", "type"],
314
+ },
315
+ overridesTypeAware: {
316
+ "ts-eslint/no-unsafe-assignment": "warn",
317
+ },
318
+ tsconfigPath: true,
319
+ },
320
+ });
321
+ ```
322
+
323
+ ### Plugin Renaming
324
+
325
+ Plugin prefixes are normalized by default so rule names stay stable if the underlying implementation package changes:
326
+
327
+ | Prefix | Original plugin prefix |
328
+ | ---------------------- | ---------------------- |
329
+ | `react/*` | `@eslint-react/*` |
330
+ | `nextjs/*` | `@next/next/*` |
331
+ | `stylistic/*` | `@stylistic/*` |
332
+ | `tanstack-query/*` | `@tanstack/query/*` |
333
+ | `tanstack-router/*` | `@tanstack/router/*` |
334
+ | `ts-eslint/*` | `@typescript-eslint/*` |
335
+ | `tailwindcss-better/*` | `better-tailwindcss/*` |
336
+ | `import/*` | `import-x/*` |
337
+ | `node/*` | `n/*` |
338
+
339
+ The factory also renames matching rule prefixes in custom configs. Set `autoRenamePlugins: false` to opt out, or use the returned composer's `renamePlugins()` method to define another mapping.
340
+
341
+ ### Config Composer
342
+
343
+ `zayne()` returns a [`FlatConfigComposer`](https://github.com/antfu/eslint-flat-config-utils), so configurations can be inserted or overridden by their stable `zayne/*` names:
344
+
345
+ ```js
346
+ import { zayne } from "@zayne-labs/eslint-config";
347
+
348
+ export default zayne()
349
+ .prepend({
350
+ ignores: ["**/generated/**"],
351
+ })
352
+ .override("zayne/stylistic/rules", {
353
+ rules: {
354
+ "stylistic/quotes": ["error", "single"],
355
+ },
356
+ });
357
+ ```
358
+
267
359
  ### Advanced Composition
268
360
 
269
361
  Import and compose fine-grained configs directly:
@@ -276,6 +368,8 @@ Import and compose fine-grained configs directly:
276
368
  ```js
277
369
  import {
278
370
  astro,
371
+ baseline,
372
+ command,
279
373
  comments,
280
374
  depend,
281
375
  expo,
@@ -290,6 +384,7 @@ import {
290
384
  perfectionist,
291
385
  pnpm,
292
386
  react,
387
+ regexp,
293
388
  solid,
294
389
  sortPackageJson,
295
390
  sortTsconfig,
@@ -307,14 +402,17 @@ import { FlatConfigComposer } from "eslint-flat-config-utils";
307
402
  export default new FlatConfigComposer().append(
308
403
  ignores(),
309
404
  javascript(),
405
+ command(),
310
406
  typescript(),
311
407
  jsx(),
408
+ baseline(),
312
409
  comments(),
313
410
  node(),
314
411
  jsdoc(),
315
412
  imports(),
316
413
  unicorn(),
317
414
  perfectionist(),
415
+ regexp(),
318
416
  stylistic(),
319
417
  react(),
320
418
  vue(),
@@ -327,7 +425,7 @@ export default new FlatConfigComposer().append(
327
425
 
328
426
  </details>
329
427
 
330
- See [configs](https://github.com/zayne-labs/eslint-config/blob/main/src/configs) and [factory](https://github.com/zayne-labs/eslint-config/blob/main/src/factory.ts) for implementation details.
428
+ See [configs](https://github.com/zayne-labs/config/tree/main/packages/eslint-config/src/configs) and [factory](https://github.com/zayne-labs/config/blob/main/packages/eslint-config/src/factory.ts) for implementation details.
331
429
 
332
430
  > Thanks to [antfu/eslint-config](https://github.com/antfu/eslint-config) for the inspiration and reference.
333
431
 
@@ -345,10 +443,24 @@ export default zayne({
345
443
  });
346
444
  ```
347
445
 
446
+ Vue 3 and custom SFC block processing are enabled by default. Vue 2 can be selected explicitly, accessibility rules are opt-in, and SFC block processing can be disabled when it is not needed:
447
+
448
+ ```js
449
+ export default zayne({
450
+ vue: {
451
+ a11y: true,
452
+ sfcBlocks: false,
453
+ vueVersion: 2,
454
+ },
455
+ });
456
+ ```
457
+
348
458
  Install peer dependencies:
349
459
 
350
460
  ```bash
351
- pnpm i -D eslint-plugin-vue vue-eslint-parser
461
+ pnpm i -D eslint-plugin-vue vue-eslint-parser eslint-processor-vue-blocks
462
+ # When a11y is enabled:
463
+ pnpm i -D eslint-plugin-vuejs-accessibility
352
464
  ```
353
465
 
354
466
  ### React
@@ -359,31 +471,38 @@ Auto-detected in most cases, or enable explicitly:
359
471
  import { zayne } from "@zayne-labs/eslint-config";
360
472
 
361
473
  export default zayne({
362
- react: true,
474
+ react: {
475
+ compiler: true,
476
+ nextjs: true,
477
+ refresh: true,
478
+ youMightNotNeedAnEffect: true,
479
+ },
363
480
  });
364
481
  ```
365
482
 
483
+ The base React integration, React Compiler checks, refresh rules, and effect guidance can each be configured or disabled independently. Next.js rules are enabled only through `react.nextjs`.
484
+
366
485
  Install peer dependencies (prompted automatically when running ESLint):
367
486
 
368
487
  ```bash
369
- pnpm i -D @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh
488
+ pnpm i -D @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh eslint-plugin-react-you-might-not-need-an-effect
489
+ # When react.nextjs is enabled:
490
+ pnpm i -D @next/eslint-plugin-next
370
491
  ```
371
492
 
372
- ### Svelte
493
+ #### Next.js
373
494
 
374
- ```js
375
- import { zayne } from "@zayne-labs/eslint-config";
495
+ Next.js is supported but must be enabled explicitly. Set `nextjs: true` inside the React integration:
376
496
 
497
+ ```js
377
498
  export default zayne({
378
- svelte: true,
499
+ react: {
500
+ nextjs: true,
501
+ },
379
502
  });
380
503
  ```
381
504
 
382
- Install peer dependencies:
383
-
384
- ```bash
385
- pnpm i -D eslint-plugin-svelte
386
- ```
505
+ The integration combines the plugin's recommended and Core Web Vitals rules. Its refresh configuration also permits the standard Next.js route exports. Pass an object instead when custom `files` or `overrides` are needed.
387
506
 
388
507
  ### Astro
389
508
 
@@ -398,7 +517,7 @@ export default zayne({
398
517
  Install peer dependencies:
399
518
 
400
519
  ```bash
401
- pnpm i -D eslint-plugin-astro
520
+ pnpm i -D eslint-plugin-astro astro-eslint-parser
402
521
  ```
403
522
 
404
523
  ### Solid
@@ -417,7 +536,23 @@ Install peer dependencies:
417
536
  pnpm i -D eslint-plugin-solid
418
537
  ```
419
538
 
420
- ### TailwindCSS
539
+ ### JSX Accessibility
540
+
541
+ JSX syntax is enabled by default. Accessibility rules are opt-in and work independently of the React integration:
542
+
543
+ ```js
544
+ export default zayne({
545
+ jsx: {
546
+ a11y: true,
547
+ },
548
+ });
549
+ ```
550
+
551
+ ```bash
552
+ pnpm i -D eslint-plugin-jsx-a11y
553
+ ```
554
+
555
+ ### Better Tailwind CSS
421
556
 
422
557
  Uses the enhanced `eslint-plugin-better-tailwindcss` for improved class sorting and validation:
423
558
 
@@ -451,6 +586,18 @@ Install peer dependencies:
451
586
  pnpm i -D eslint-config-expo
452
587
  ```
453
588
 
589
+ ### Node Security
590
+
591
+ Node rules are enabled by default. The additional `eslint-plugin-security` rules are opt-in:
592
+
593
+ ```js
594
+ export default zayne({
595
+ node: {
596
+ security: true,
597
+ },
598
+ });
599
+ ```
600
+
454
601
  ### TanStack
455
602
 
456
603
  Support for TanStack Query and Router:
@@ -472,15 +619,20 @@ Install peer dependencies:
472
619
  pnpm i -D @tanstack/eslint-plugin-query @tanstack/eslint-plugin-router
473
620
  ```
474
621
 
475
- ### PNPM Catalogs
622
+ ### PNPM Workspaces and Catalogs
476
623
 
477
- Lint PNPM catalog protocol usage:
624
+ pnpm support is auto-detected from the nearest `pnpm-workspace.yaml`. It can validate package and workspace manifests, require catalog usage, and sort workspace YAML entries:
478
625
 
479
626
  ```js
480
627
  import { zayne } from "@zayne-labs/eslint-config";
481
628
 
482
629
  export default zayne({
483
- pnpm: true,
630
+ pnpm: {
631
+ catalogs: true,
632
+ json: true,
633
+ sort: true,
634
+ yaml: true,
635
+ },
484
636
  });
485
637
  ```
486
638
 
@@ -508,6 +660,19 @@ Install peer dependencies:
508
660
  pnpm i -D eslint-plugin-depend
509
661
  ```
510
662
 
663
+ ### Command Comments
664
+
665
+ [`eslint-plugin-command`](https://github.com/antfu/eslint-plugin-command) is always available for explicit, comment-driven transformations. Place a supported triple-slash command directly above the code and run ESLint with fixes:
666
+
667
+ ```ts
668
+ /// to-for-of
669
+ items.forEach((item) => {
670
+ console.log(item);
671
+ });
672
+ ```
673
+
674
+ Commands are intended as temporary codemods; their trigger comments are removed with the transformation.
675
+
511
676
  ## Type-Aware Rules
512
677
 
513
678
  Type-aware linting is automatically enabled when TypeScript is detected. It uses the nearest `tsconfig.json` by default.
@@ -541,27 +706,52 @@ export default zayne({
541
706
  });
542
707
  ```
543
708
 
709
+ Set `tsconfigPath: true` to use the nearest project automatically. Type-aware file globs and rules can be refined with `filesTypeAware`, `ignoresTypeAware`, and `overridesTypeAware`.
710
+
711
+ Projects targeting erasable TypeScript syntax can enable the corresponding optional rules:
712
+
713
+ ```js
714
+ export default zayne({
715
+ typescript: {
716
+ erasableOnly: true,
717
+ },
718
+ });
719
+ ```
720
+
544
721
  ### Editor Specific Disables
545
722
 
546
723
  Auto-fixing for the following rules are disabled when ESLint is running in a code editor:
547
724
 
548
725
  - [`prefer-const`](https://eslint.org/docs/rules/prefer-const)
549
- - [`test/no-only-tests`](https://github.com/levibuzolic/eslint-plugin-no-only-tests)
550
726
  - [`pnpm/json-enforce-catalog`](https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/eslint-plugin-pnpm#rules)
551
727
  - [`pnpm/json-prefer-workspace-settings`](https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/eslint-plugin-pnpm#rules)
552
728
  - [`pnpm/json-valid-catalog`](https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/eslint-plugin-pnpm#rules)
553
729
 
554
- This is to prevent unused imports from getting removed by the editor during refactoring to get a better developer experience. Those rules will be applied when you run ESLint in the terminal or [Lint Staged](#lint-staged). If you don't want this behavior, you can disable them:
730
+ This prevents editor autosave from applying potentially disruptive fixes while code is being refactored. Full autofix behavior is restored when ESLint runs in the terminal or through [lint-staged](#lint-staged). If you don't want this behavior, disable editor detection explicitly:
555
731
 
556
732
  ```js
557
733
  // eslint.config.js
558
- import { zayne } from "@antfu/eslint-config";
734
+ import { zayne } from "@zayne-labs/eslint-config";
559
735
 
560
736
  export default zayne({
561
737
  isInEditor: false,
562
738
  });
563
739
  ```
564
740
 
741
+ ### Lint Staged
742
+
743
+ To apply safe autofixes before each commit:
744
+
745
+ ```json
746
+ {
747
+ "lint-staged": {
748
+ "*": "eslint --fix"
749
+ }
750
+ }
751
+ ```
752
+
753
+ When ESLint runs through lint-staged or a Git hook, terminal rule behavior is preserved instead of the editor-specific non-fixable mode.
754
+
565
755
  ## Inspecting Config
566
756
 
567
757
  View active rules using the [ESLint Config Inspector](https://github.com/eslint/config-inspector):
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/cli/index.js CHANGED
@@ -10,14 +10,14 @@ import fsp from "node:fs/promises";
10
10
  import parse from "parse-gitignore";
11
11
  import { execSync } from "node:child_process";
12
12
  //#region package.json
13
- var version = "0.13.13";
13
+ var version = "0.13.15";
14
14
  //#endregion
15
15
  //#region src/cli/constants.ts
16
16
  const vscodeSettingsString = `
17
17
  // Auto fix
18
- // "editor.codeActionsOnSave": {
19
- // "source.fixAll.eslint": "explicit",
20
- // },
18
+ "editor.codeActionsOnSave": {
19
+ "source.fixAll.eslint": "explicit",
20
+ },
21
21
 
22
22
  // Enable eslint for all supported languages
23
23
  "eslint.validate": [
@@ -37,10 +37,10 @@ const vscodeSettingsString = `
37
37
  "gql",
38
38
  "graphql",
39
39
  "astro",
40
- "svelte",
41
40
  "css",
42
41
  "less",
43
42
  "scss",
43
+ "pcss",
44
44
  "postcss"
45
45
  ]
46
46
  `;
@@ -53,10 +53,6 @@ const frameworkOptions = [
53
53
  label: c.cyan("React"),
54
54
  value: "react"
55
55
  },
56
- {
57
- label: c.red("Svelte"),
58
- value: "svelte"
59
- },
60
56
  {
61
57
  label: c.magenta("Astro"),
62
58
  value: "astro"
@@ -81,7 +77,6 @@ const dependenciesMap = defineEnumDeep({
81
77
  "eslint-plugin-react-you-might-not-need-an-effect"
82
78
  ],
83
79
  solid: ["eslint-plugin-solid"],
84
- svelte: ["eslint-plugin-svelte", "svelte-eslint-parser"],
85
80
  "tailwindcss-better": ["eslint-plugin-better-tailwindcss"],
86
81
  vue: [
87
82
  "eslint-plugin-vue",
@@ -110,6 +105,8 @@ ${mainConfig}
110
105
  };
111
106
  //#endregion
112
107
  //#region src/cli/stages/update-eslint-files.ts
108
+ const ESLINT = "eslint";
109
+ const ESLINT_CONFIG = "eslint.config.";
113
110
  const updateEslintFiles = async (result) => {
114
111
  const cwd = process.cwd();
115
112
  const pathESLintIgnore = path.join(cwd, ".eslintignore");
@@ -120,37 +117,39 @@ const updateEslintFiles = async (result) => {
120
117
  const eslintIgnores = [];
121
118
  if (fs.existsSync(pathESLintIgnore)) {
122
119
  p.log.step(c.cyan`Migrating existing .eslintignore`);
123
- const globs = parse(await fsp.readFile(pathESLintIgnore, "utf8")).globs();
124
- for (const glob of globs) {
125
- if (glob.type === "ignore") eslintIgnores.push(...glob.patterns);
126
- if (glob.type === "unignore") eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`));
127
- }
120
+ const content = await fsp.readFile(pathESLintIgnore, "utf8");
121
+ const globs = parse(content).globs();
122
+ for (const glob of globs) if (glob.type === "ignore") eslintIgnores.push(...glob.patterns);
123
+ else if (glob.type === "unignore") eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`));
128
124
  }
129
125
  const configLines = [];
130
126
  if (eslintIgnores.length > 0) configLines.push(`ignores: ${JSON.stringify(eslintIgnores)},`);
131
- if (result.extra.includes("tailwindcssBetter")) configLines.push(`tailwindcssBetter: true,`);
127
+ if (result.extra.includes("tailwindcss-better")) configLines.push(`tailwindcssBetter: true,`);
132
128
  for (const framework of result.frameworks) configLines.push(`${framework}: true,`);
133
- const eslintConfigContent = getEslintConfigContent(configLines.map((line) => ` ${line}`).join("\n"), []);
129
+ const mainConfig = configLines.map((line) => ` ${line}`).join("\n");
130
+ const eslintConfigContent = getEslintConfigContent(mainConfig, []);
134
131
  await fsp.writeFile(pathFlatConfig, eslintConfigContent);
135
132
  p.log.success(c.green`Created ${configFileName}`);
133
+ const files = fs.readdirSync(cwd);
134
+ const legacyConfig = [];
135
+ for (const file of files) if (file.includes(ESLINT) && !file.includes(ESLINT_CONFIG)) legacyConfig.push(file);
136
+ if (legacyConfig.length > 0) p.note(c.dim(legacyConfig.join(", ")), "You can now remove those files manually");
136
137
  };
137
138
  //#endregion
138
139
  //#region src/cli/constants-generated.ts
139
140
  const versionsMap = defineEnum({
140
- "@eslint-react/eslint-plugin": "^5.8.1",
141
- "astro-eslint-parser": "^1.4.0",
142
- eslint: "^10.4.0",
143
- "eslint-plugin-astro": "^1.7.0",
144
- "eslint-plugin-better-tailwindcss": "^4.5.0",
141
+ "@eslint-react/eslint-plugin": "^5.19.0",
142
+ "astro-eslint-parser": "^3.1.0",
143
+ eslint: "^10.10.0",
144
+ "eslint-plugin-astro": "^3.1.0",
145
+ "eslint-plugin-better-tailwindcss": "^4.7.0",
145
146
  "eslint-plugin-react-hooks": "^7.1.1",
146
- "eslint-plugin-react-refresh": "^0.5.2",
147
- "eslint-plugin-react-you-might-not-need-an-effect": "^0.10.1",
148
- "eslint-plugin-solid": "^0.14.5",
149
- "eslint-plugin-svelte": "^3.17.1",
150
- "eslint-plugin-vue": "^10.9.1",
147
+ "eslint-plugin-react-refresh": "^0.5.6",
148
+ "eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
149
+ "eslint-plugin-solid": "^0.17.0",
150
+ "eslint-plugin-vue": "^10.11.0",
151
151
  "eslint-processor-vue-blocks": "^2.0.0",
152
- "svelte-eslint-parser": "^1.6.1",
153
- "vue-eslint-parser": "^10.4.0"
152
+ "vue-eslint-parser": "^10.4.1"
154
153
  });
155
154
  //#endregion
156
155
  //#region src/cli/stages/update-package-json.ts
@@ -164,23 +163,20 @@ const updatePackageJson = async (result) => {
164
163
  pkg.devDependencies["@zayne-labs/eslint-config"] = `^${version}`;
165
164
  pkg.devDependencies.eslint ??= versionsMap.eslint;
166
165
  const addedPackages = [];
167
- for (const item of result.extra) switch (item) {
168
- case "tailwindcss-better":
169
- dependenciesMap["tailwindcss-better"].forEach((depName) => {
170
- pkg.devDependencies && (pkg.devDependencies[depName] = versionsMap[depName]);
171
- addedPackages.push(depName);
172
- });
173
- break;
174
- default:
166
+ for (const item of result.extra) {
167
+ const dependencies = dependenciesMap[item];
168
+ for (const dependency of dependencies) {
169
+ pkg.devDependencies[dependency] = versionsMap[dependency];
170
+ addedPackages.push(dependency);
171
+ }
175
172
  }
176
173
  for (const framework of result.frameworks) {
177
174
  const dependencies = dependenciesMap[framework];
178
175
  if (!dependencies) continue;
179
- dependencies.forEach((dependency) => {
180
- if (!pkg.devDependencies) return;
176
+ for (const dependency of dependencies) {
181
177
  pkg.devDependencies[dependency] = versionsMap[dependency];
182
178
  addedPackages.push(dependency);
183
- });
179
+ }
184
180
  }
185
181
  if (addedPackages.length > 0) p.note(c.dim(addedPackages.join(", ")), "Added packages");
186
182
  await fsp.writeFile(pathPackageJSON, JSON.stringify(pkg, null, 2));
@@ -189,8 +185,8 @@ const updatePackageJson = async (result) => {
189
185
  //#endregion
190
186
  //#region src/cli/stages/update-vscode-settings.ts
191
187
  const updateVscodeSettings = async (result) => {
192
- const cwd = process.cwd();
193
188
  if (!result.updateVscodeSettings) return;
189
+ const cwd = process.cwd();
194
190
  const dotVscodePath = path.join(cwd, ".vscode");
195
191
  const settingsPath = path.join(dotVscodePath, "settings.json");
196
192
  if (!fs.existsSync(dotVscodePath)) await fsp.mkdir(dotVscodePath, { recursive: true });
@@ -208,12 +204,24 @@ const updateVscodeSettings = async (result) => {
208
204
  };
209
205
  //#endregion
210
206
  //#region src/cli/run.ts
207
+ const ESLINT_CONFIG_FILES = [
208
+ "eslint.config.js",
209
+ "eslint.config.mjs",
210
+ "eslint.config.cjs",
211
+ "eslint.config.ts",
212
+ "eslint.config.mts",
213
+ "eslint.config.cts"
214
+ ];
211
215
  const runCli = async (options = {}) => {
212
- const argSkipPrompt = Boolean(process.env.SKIP_PROMPT) || options.yes;
213
- const argTemplate = options.frameworks?.map((m) => m.trim()).filter(Boolean);
214
- const argExtra = options.extra?.map((m) => m.trim()).filter(Boolean);
215
- if (fs.existsSync(path.join(process.cwd(), "eslint.config.js"))) {
216
- p.log.warn(c.yellow`eslint.config.js already exists, migration wizard exited.`);
216
+ const isSkippingPrompts = Boolean(process.env.SKIP_PROMPT) || Boolean(options.yes);
217
+ const argTemplate = options.frameworks?.filter((framework) => typeof framework === "string").map((framework) => framework.trim()).filter(Boolean);
218
+ const argExtra = options.extra?.filter((extraOption) => typeof extraOption === "string").map((extraOption) => extraOption.trim()).filter(Boolean);
219
+ const invalidFrameworks = argTemplate?.filter((framework) => !frameworks.includes(framework));
220
+ const invalidExtras = argExtra?.filter((extraOption) => !extra.includes(extraOption));
221
+ if (isSkippingPrompts && ((invalidFrameworks?.length ?? 0) > 0 || (invalidExtras?.length ?? 0) > 0)) throw new Error(`Invalid CLI options.${invalidFrameworks?.length ? ` Frameworks: ${invalidFrameworks.join(", ")}.` : ""}${invalidExtras?.length ? ` Extras: ${invalidExtras.join(", ")}.` : ""}`);
222
+ const existingConfigFile = ESLINT_CONFIG_FILES.find((fileName) => fs.existsSync(path.join(process.cwd(), fileName)));
223
+ if (existingConfigFile) {
224
+ p.log.warn(c.yellow`${existingConfigFile} already exists, migration wizard exited.`);
217
225
  return process.exit(1);
218
226
  }
219
227
  let result = {
@@ -222,7 +230,7 @@ const runCli = async (options = {}) => {
222
230
  uncommittedConfirmed: false,
223
231
  updateVscodeSettings: true
224
232
  };
225
- if (!argSkipPrompt) {
233
+ if (!isSkippingPrompts) {
226
234
  result = await p.group({
227
235
  uncommittedConfirmed: () => {
228
236
  if (isGitClean()) return Promise.resolve(true);
@@ -232,8 +240,7 @@ const runCli = async (options = {}) => {
232
240
  });
233
241
  },
234
242
  frameworks: ({ results }) => {
235
- const isArgTemplateValid = (argTemplate?.length ?? 0) > 0 && (argTemplate ?? []).filter((element) => !frameworks.includes(element)).length === 0;
236
- if (!results.uncommittedConfirmed || isArgTemplateValid) return;
243
+ if ((argTemplate?.length ?? 0) > 0 && (argTemplate ?? []).filter((element) => !frameworks.includes(element)).length === 0 || !results.uncommittedConfirmed) return;
237
244
  const message = argTemplate ? `"${JSON.stringify(argTemplate)}" isn't a valid template. Please choose from below: ` : "Select a framework:";
238
245
  return p.multiselect({
239
246
  message: c.reset(message),
@@ -242,8 +249,7 @@ const runCli = async (options = {}) => {
242
249
  });
243
250
  },
244
251
  extra: ({ results }) => {
245
- const isArgExtraValid = (argExtra?.length ?? 0) > 0 && (argExtra ?? []).filter((element) => !extra.includes(element)).length === 0;
246
- if (!results.uncommittedConfirmed || isArgExtraValid) return;
252
+ if ((argExtra?.length ?? 0) > 0 && (argExtra ?? []).filter((element) => !extra.includes(element)).length === 0 || !results.uncommittedConfirmed) return;
247
253
  const message = argExtra ? `"${JSON.stringify(argExtra)}" isn't a valid extra util. Please choose from below: ` : "Select an extra util:";
248
254
  return p.multiselect({
249
255
  message: c.reset(message),
@@ -276,7 +282,7 @@ const header = () => {
276
282
  p.intro(`${c.green`@zayne-labs/eslint-config `}${c.dim`v${version}`}`);
277
283
  };
278
284
  const cli = cac("@zayne-labs/eslint-config");
279
- 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: tailwindcss", { type: [] }).action(async (options) => {
285
+ 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 / solid / astro", { type: [] }).option("--extra, -e <extra>", "Use the extra utils: tailwindcss-better", { type: [] }).action(async (options) => {
280
286
  header();
281
287
  try {
282
288
  await runCli(options);