@tb-dev/eslint-config 4.2.1 → 4.4.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/dist/index.cjs CHANGED
@@ -1,31 +1,12 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (let key of __getOwnPropNames(from))
11
- if (!__hasOwnProp.call(to, key) && key !== except)
12
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- // If the importer is in node compatibility mode or this is not an ESM
18
- // file that has been converted to a CommonJS file using a Babel-
19
- // compatible transform (i.e. "__esModule" has not been set), then set
20
- // "default" to the CommonJS "module.exports" for node compatibility.
21
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
- mod
23
- ));
24
- const process = require("node:process");
25
- const globals = require("globals");
1
+ 'use strict';
2
+
3
+ const process = require('node:process');
4
+ const globals = require('globals');
5
+
26
6
  var Glob = /* @__PURE__ */ ((Glob2) => {
27
7
  Glob2["All"] = "**/*.?([cm])[jt]s?(x)";
28
8
  Glob2["Javascript"] = "**/*.?([cm])js?(x)";
9
+ Glob2["Json"] = "**/*.json?([c5])";
29
10
  Glob2["Typescript"] = "**/*.?([cm])ts?(x)";
30
11
  Glob2["Vitest"] = "**/*.{test,spec}.[jt]s";
31
12
  Glob2["Vue"] = "**/*.vue";
@@ -41,16 +22,22 @@ var GlobIgnore = /* @__PURE__ */ ((GlobIgnore2) => {
41
22
  GlobIgnore2["Temp"] = "**/?(.)temp";
42
23
  return GlobIgnore2;
43
24
  })(GlobIgnore || {});
25
+
44
26
  async function interopDefault(promise) {
45
27
  const result = await promise;
46
28
  return result.default ?? result;
47
29
  }
30
+ async function json(path) {
31
+ return interopDefault(import(path));
32
+ }
48
33
  function getIgnores() {
49
34
  return Object.values(GlobIgnore);
50
35
  }
36
+
51
37
  const stylisticRules = {
52
38
  "stylistic/array-bracket-newline": ["error", "consistent"],
53
39
  "stylistic/array-bracket-spacing": ["error", "never"],
40
+ "stylistic/array-element-newline": ["error", "consistent"],
54
41
  "stylistic/arrow-parens": ["error", "always"],
55
42
  "stylistic/arrow-spacing": "error",
56
43
  "stylistic/block-spacing": "error",
@@ -71,7 +58,11 @@ const stylisticRules = {
71
58
  "stylistic/function-call-argument-newline": ["error", "consistent"],
72
59
  "stylistic/function-call-spacing": ["error", "never"],
73
60
  "stylistic/function-paren-newline": ["error", "never"],
74
- "stylistic/generator-star-spacing": ["error", { before: false, after: true }],
61
+ "stylistic/generator-star-spacing": ["error", {
62
+ before: false,
63
+ after: true,
64
+ method: { before: true, after: false }
65
+ }],
75
66
  "stylistic/implicit-arrow-linebreak": ["error", "beside"],
76
67
  "stylistic/indent": ["error", 2, { flatTernaryExpressions: true }],
77
68
  "stylistic/indent-binary-ops": ["error", 2],
@@ -155,12 +146,12 @@ const stylisticRules = {
155
146
  async function stylistic(options) {
156
147
  const { overrides, stylistic: enabled } = options;
157
148
  if (!enabled) return {};
158
- const plugin = await interopDefault(import("@stylistic/eslint-plugin"));
149
+ const plugin = await interopDefault(import('@stylistic/eslint-plugin'));
159
150
  const files = [Glob.All];
160
151
  if (options.vue) files.push(Glob.Vue);
161
152
  const rules = {
162
153
  ...stylisticRules,
163
- ...overrides == null ? void 0 : overrides.stylistic
154
+ ...overrides?.stylistic
164
155
  };
165
156
  return {
166
157
  files,
@@ -168,15 +159,17 @@ async function stylistic(options) {
168
159
  rules
169
160
  };
170
161
  }
162
+
171
163
  async function vue(options) {
172
164
  const { overrides, vue: enabled } = options;
173
165
  if (!enabled) return [];
174
166
  const [vuePlugin, vueParser, tsParser] = await Promise.all([
175
167
  // @ts-expect-error no types
176
- interopDefault(import("eslint-plugin-vue")),
177
- interopDefault(import("vue-eslint-parser")),
178
- interopDefault(import("@typescript-eslint/parser"))
168
+ interopDefault(import('eslint-plugin-vue')),
169
+ interopDefault(import('vue-eslint-parser')),
170
+ interopDefault(import('@typescript-eslint/parser'))
179
171
  ]);
172
+ const INLINE_ELEMENTS = await json("eslint-plugin-vue/lib/utils/inline-non-void-elements.json");
180
173
  const rules = {
181
174
  ...vuePlugin.configs.base.rules,
182
175
  "vue/attribute-hyphenation": ["error", "always"],
@@ -207,41 +200,17 @@ async function vue(options) {
207
200
  }],
208
201
  "vue/define-props-declaration": ["error", "type-based"],
209
202
  "vue/enforce-style-attribute": ["error", { allow: ["scoped"] }],
210
- "vue/first-attribute-linebreak": "off",
211
203
  "vue/html-button-has-type": ["error", {
212
204
  button: true,
213
205
  submit: true,
214
206
  reset: true
215
207
  }],
216
- "vue/html-closing-bracket-newline": ["error", {
217
- singleline: "never",
218
- multiline: "always",
219
- selfClosingTag: {
220
- singleline: "never",
221
- multiline: "always"
222
- }
223
- }],
224
- "vue/html-closing-bracket-spacing": ["error", {
225
- startTag: "never",
226
- endTag: "never",
227
- selfClosingTag: "always"
228
- }],
229
- "vue/html-self-closing": ["error", {
230
- html: {
231
- void: "never",
232
- normal: "always",
233
- component: "always"
234
- },
235
- svg: "always",
236
- math: "always"
237
- }],
238
208
  "vue/match-component-file-name": ["off", {
239
209
  extensions: ["tsx", "vue"],
240
210
  shouldMatchCase: false
241
211
  }],
242
212
  "vue/match-component-import-name": "error",
243
213
  "vue/multi-word-component-names": "off",
244
- "vue/mustache-interpolation-spacing": ["error", "always"],
245
214
  "vue/no-arrow-functions-in-watch": "off",
246
215
  "vue/no-async-in-computed-properties": "error",
247
216
  "vue/no-boolean-default": ["error", "no-default"],
@@ -256,7 +225,6 @@ async function vue(options) {
256
225
  "vue/no-expose-after-await": "error",
257
226
  "vue/no-lifecycle-after-await": "error",
258
227
  "vue/no-lone-template": "error",
259
- "vue/no-multi-spaces": "error",
260
228
  "vue/no-multiple-objects-in-class": "error",
261
229
  "vue/no-mutating-props": "error",
262
230
  "vue/no-parsing-error": "error",
@@ -270,7 +238,6 @@ async function vue(options) {
270
238
  "vue/no-setup-props-reactivity-loss": "error",
271
239
  "vue/no-shared-component-data": "error",
272
240
  "vue/no-side-effects-in-computed-properties": "error",
273
- "vue/no-spaces-around-equal-signs-in-attribute": "error",
274
241
  "vue/no-static-inline-styles": ["error", { allowBinding: false }],
275
242
  "vue/no-template-key": "error",
276
243
  "vue/no-template-shadow": "error",
@@ -356,12 +323,67 @@ async function vue(options) {
356
323
  "vue/valid-v-show": "error",
357
324
  "vue/valid-v-slot": "error",
358
325
  "vue/valid-v-text": "error",
359
- ...overrides == null ? void 0 : overrides.vue
326
+ ...overrides?.vue
360
327
  };
361
328
  if (options.stylistic) {
329
+ Object.assign(rules, {
330
+ "vue/first-attribute-linebreak": ["error", {
331
+ singleline: "beside",
332
+ multiline: "below"
333
+ }],
334
+ "vue/html-closing-bracket-newline": ["error", {
335
+ singleline: "never",
336
+ multiline: "always",
337
+ selfClosingTag: {
338
+ singleline: "never",
339
+ multiline: "always"
340
+ }
341
+ }],
342
+ "vue/html-closing-bracket-spacing": ["error", {
343
+ startTag: "never",
344
+ endTag: "never",
345
+ selfClosingTag: "always"
346
+ }],
347
+ "vue/html-indent": ["error", 2, {
348
+ attribute: 1,
349
+ baseIndent: 1,
350
+ closeBracket: 0,
351
+ alignAttributesVertically: true,
352
+ ignores: []
353
+ }],
354
+ "vue/html-quotes": ["error", "double", { avoidEscape: false }],
355
+ "vue/html-self-closing": ["error", {
356
+ html: {
357
+ void: "never",
358
+ normal: "always",
359
+ component: "always"
360
+ },
361
+ svg: "always",
362
+ math: "always"
363
+ }],
364
+ "vue/max-attributes-per-line": ["error", {
365
+ singleline: Number.MAX_SAFE_INTEGER,
366
+ multiline: 1
367
+ }],
368
+ "vue/multiline-html-element-content-newline": ["error", {
369
+ ignoreWhenEmpty: true,
370
+ ignores: ["pre", "textarea", ...INLINE_ELEMENTS],
371
+ allowEmptyLines: false
372
+ }],
373
+ "vue/mustache-interpolation-spacing": ["error", "always"],
374
+ "vue/no-multi-spaces": ["error", { ignoreProperties: false }],
375
+ "vue/no-spaces-around-equal-signs-in-attribute": "error",
376
+ "vue/singleline-html-element-content-newline": ["off", {
377
+ ignoreWhenNoAttributes: true,
378
+ ignoreWhenEmpty: true,
379
+ ignores: ["pre", "textarea", ...INLINE_ELEMENTS],
380
+ externalIgnores: []
381
+ }]
382
+ });
362
383
  const vueStylistic = [
363
384
  "vue/array-bracket-newline",
364
385
  "vue/array-bracket-spacing",
386
+ "vue/array-element-newline",
365
387
  "vue/key-spacing",
366
388
  "vue/keyword-spacing",
367
389
  "vue/max-len",
@@ -385,9 +407,7 @@ async function vue(options) {
385
407
  }, {}));
386
408
  }
387
409
  return [
388
- {
389
- plugins: { vue: vuePlugin }
390
- },
410
+ { plugins: { vue: vuePlugin } },
391
411
  {
392
412
  files: [Glob.Vue],
393
413
  languageOptions: {
@@ -409,10 +429,64 @@ async function vue(options) {
409
429
  }
410
430
  ];
411
431
  }
432
+
433
+ async function jsonc(options) {
434
+ const { overrides, jsonc: enabled } = options;
435
+ if (!enabled) return [];
436
+ const [jsoncPlugin, jsoncParser] = await Promise.all([
437
+ interopDefault(import('eslint-plugin-jsonc')),
438
+ interopDefault(import('jsonc-eslint-parser'))
439
+ ]);
440
+ const rules = {
441
+ "jsonc/no-bigint-literals": "error",
442
+ "jsonc/no-binary-expression": "error",
443
+ "jsonc/no-binary-numeric-literals": "error",
444
+ "jsonc/no-infinity": "error",
445
+ "jsonc/no-nan": "error",
446
+ "jsonc/no-number-props": "error",
447
+ "jsonc/no-undefined-value": "error",
448
+ "jsonc/valid-json-number": "error",
449
+ ...overrides?.jsonc
450
+ };
451
+ if (options.stylistic) {
452
+ Object.assign(rules, {
453
+ "jsonc/comma-dangle": ["error", "never"],
454
+ "jsonc/quotes": ["error", "double", { avoidEscape: false }],
455
+ "jsonc/space-unary-ops": "error"
456
+ });
457
+ const jsoncStylistic = [
458
+ "jsonc/array-bracket-newline",
459
+ "jsonc/array-bracket-spacing",
460
+ "jsonc/array-element-newline",
461
+ "jsonc/comma-style",
462
+ "jsonc/indent",
463
+ "jsonc/key-spacing",
464
+ "jsonc/object-curly-newline",
465
+ "jsonc/object-curly-spacing",
466
+ "jsonc/object-property-newline"
467
+ ];
468
+ Object.assign(rules, jsoncStylistic.reduce((acc, rule) => {
469
+ const name = rule.replace("jsonc/", "stylistic/");
470
+ if (Object.hasOwn(stylisticRules, name)) {
471
+ acc[rule] = stylisticRules[name];
472
+ }
473
+ return acc;
474
+ }, {}));
475
+ }
476
+ return [
477
+ { plugins: { jsonc: jsoncPlugin } },
478
+ {
479
+ files: [Glob.Json],
480
+ languageOptions: { parser: jsoncParser }
481
+ },
482
+ { rules }
483
+ ];
484
+ }
485
+
412
486
  async function vitest(options) {
413
487
  const { overrides, vitest: enabled } = options;
414
488
  if (!enabled) return {};
415
- const plugin = await interopDefault(import("eslint-plugin-vitest"));
489
+ const plugin = await interopDefault(import('eslint-plugin-vitest'));
416
490
  return {
417
491
  plugins: { vitest: plugin },
418
492
  files: [Glob.Vitest],
@@ -448,14 +522,15 @@ async function vitest(options) {
448
522
  "vitest/require-top-level-describe": ["error", { maxNumberOfTopLevelDescribes: 10 }],
449
523
  "vitest/valid-describe-callback": "error",
450
524
  "vitest/valid-expect": "error",
451
- ...overrides == null ? void 0 : overrides.vitest
525
+ ...overrides?.vitest
452
526
  }
453
527
  };
454
528
  }
529
+
455
530
  async function unicorn(options) {
456
531
  const { overrides, unicorn: enabled = true } = options;
457
532
  if (!enabled) return {};
458
- const plugin = await interopDefault(import("eslint-plugin-unicorn"));
533
+ const plugin = await interopDefault(import('eslint-plugin-unicorn'));
459
534
  return {
460
535
  plugins: { unicorn: plugin },
461
536
  rules: {
@@ -498,10 +573,27 @@ async function unicorn(options) {
498
573
  "unicorn/prefer-structured-clone": "error",
499
574
  "unicorn/prefer-type-error": "error",
500
575
  "unicorn/relative-url-style": ["error", "never"],
501
- ...overrides == null ? void 0 : overrides.unicorn
576
+ ...overrides?.unicorn
502
577
  }
503
578
  };
504
579
  }
580
+
581
+ async function tailwind(options) {
582
+ const { overrides, tailwind: enabled } = options;
583
+ if (!enabled) return {};
584
+ const plugin = await interopDefault(import('eslint-plugin-tailwindcss'));
585
+ const rules = {
586
+ "tailwindcss/classnames-order": "error",
587
+ "tailwindcss/enforces-shorthand": "error",
588
+ "tailwindcss/no-contradicting-classname": "error",
589
+ ...overrides?.tailwind
590
+ };
591
+ return {
592
+ plugins: { tailwindcss: plugin },
593
+ rules
594
+ };
595
+ }
596
+
505
597
  function javascript(options) {
506
598
  const { overrides } = options;
507
599
  const files = [Glob.All];
@@ -641,15 +733,16 @@ function javascript(options) {
641
733
  "use-isnan": "error",
642
734
  "valid-typeof": "error",
643
735
  yoda: ["error", "never"],
644
- ...overrides == null ? void 0 : overrides.javascript
736
+ ...overrides?.javascript
645
737
  }
646
738
  };
647
739
  }
740
+
648
741
  async function typescript(options) {
649
742
  const { project, overrides } = options;
650
743
  const [tsParser, tsPlugin] = await Promise.all([
651
- interopDefault(import("@typescript-eslint/parser")),
652
- interopDefault(import("@typescript-eslint/eslint-plugin"))
744
+ interopDefault(import('@typescript-eslint/parser')),
745
+ interopDefault(import('@typescript-eslint/eslint-plugin'))
653
746
  ]);
654
747
  const files = [Glob.Typescript];
655
748
  if (options.vue) files.push(Glob.Vue);
@@ -853,7 +946,7 @@ async function typescript(options) {
853
946
  "@typescript-eslint/unbound-method": "error",
854
947
  "@typescript-eslint/unified-signatures": ["error", { ignoreDifferentlyNamedParameters: true }],
855
948
  "@typescript-eslint/use-unknown-in-catch-callback-variable": "error",
856
- ...overrides == null ? void 0 : overrides.typescript
949
+ ...overrides?.typescript
857
950
  };
858
951
  return {
859
952
  files,
@@ -871,10 +964,11 @@ async function typescript(options) {
871
964
  rules
872
965
  };
873
966
  }
967
+
874
968
  async function perfectionist(options) {
875
969
  const { overrides, perfectionist: enabled = true } = options;
876
970
  if (!enabled) return {};
877
- const plugin = await interopDefault(import("eslint-plugin-perfectionist"));
971
+ const plugin = await interopDefault(import('eslint-plugin-perfectionist'));
878
972
  return {
879
973
  plugins: { perfectionist: plugin },
880
974
  rules: {
@@ -933,10 +1027,11 @@ async function perfectionist(options) {
933
1027
  order: "asc",
934
1028
  ignoreCase: true
935
1029
  }],
936
- ...overrides == null ? void 0 : overrides.perfectionist
1030
+ ...overrides?.perfectionist
937
1031
  }
938
1032
  };
939
1033
  }
1034
+
940
1035
  async function defineConfig(options) {
941
1036
  const ignores = {
942
1037
  ignores: [...getIgnores(), ...options.ignores ?? []]
@@ -945,12 +1040,15 @@ async function defineConfig(options) {
945
1040
  javascript(options),
946
1041
  typescript(options),
947
1042
  ...await vue(options),
1043
+ ...await jsonc(options),
1044
+ stylistic(options),
948
1045
  perfectionist(options),
949
1046
  unicorn(options),
950
- stylistic(options),
951
1047
  vitest(options),
1048
+ tailwind(options),
952
1049
  ignores
953
1050
  ]);
954
1051
  return objects;
955
1052
  }
1053
+
956
1054
  module.exports = defineConfig;
package/dist/index.d.ts CHANGED
@@ -15,10 +15,14 @@ declare interface ConfigObject {
15
15
  }
16
16
 
17
17
  declare interface ConfigOptions {
18
+ /** @default false */
19
+ jsonc?: boolean;
18
20
  /** @default true */
19
21
  perfectionist?: boolean;
20
22
  /** @default false */
21
23
  stylistic?: boolean;
24
+ /** @default false */
25
+ tailwind?: boolean;
22
26
  /** @default true */
23
27
  unicorn?: boolean;
24
28
  /** @default false */
@@ -30,8 +34,10 @@ declare interface ConfigOptions {
30
34
  ignores?: Ignores['ignores'];
31
35
  overrides?: {
32
36
  javascript?: Rules;
37
+ jsonc?: Rules;
33
38
  perfectionist?: Rules;
34
39
  stylistic?: Rules;
40
+ tailwind?: Rules;
35
41
  typescript?: Rules;
36
42
  unicorn?: Rules;
37
43
  vitest?: Rules;
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
- import process from "node:process";
2
- import globals from "globals";
1
+ import process from 'node:process';
2
+ import globals from 'globals';
3
+
3
4
  var Glob = /* @__PURE__ */ ((Glob2) => {
4
5
  Glob2["All"] = "**/*.?([cm])[jt]s?(x)";
5
6
  Glob2["Javascript"] = "**/*.?([cm])js?(x)";
7
+ Glob2["Json"] = "**/*.json?([c5])";
6
8
  Glob2["Typescript"] = "**/*.?([cm])ts?(x)";
7
9
  Glob2["Vitest"] = "**/*.{test,spec}.[jt]s";
8
10
  Glob2["Vue"] = "**/*.vue";
@@ -18,16 +20,22 @@ var GlobIgnore = /* @__PURE__ */ ((GlobIgnore2) => {
18
20
  GlobIgnore2["Temp"] = "**/?(.)temp";
19
21
  return GlobIgnore2;
20
22
  })(GlobIgnore || {});
23
+
21
24
  async function interopDefault(promise) {
22
25
  const result = await promise;
23
26
  return result.default ?? result;
24
27
  }
28
+ async function json(path) {
29
+ return interopDefault(import(path, { with: { type: "json" } }));
30
+ }
25
31
  function getIgnores() {
26
32
  return Object.values(GlobIgnore);
27
33
  }
34
+
28
35
  const stylisticRules = {
29
36
  "stylistic/array-bracket-newline": ["error", "consistent"],
30
37
  "stylistic/array-bracket-spacing": ["error", "never"],
38
+ "stylistic/array-element-newline": ["error", "consistent"],
31
39
  "stylistic/arrow-parens": ["error", "always"],
32
40
  "stylistic/arrow-spacing": "error",
33
41
  "stylistic/block-spacing": "error",
@@ -48,7 +56,11 @@ const stylisticRules = {
48
56
  "stylistic/function-call-argument-newline": ["error", "consistent"],
49
57
  "stylistic/function-call-spacing": ["error", "never"],
50
58
  "stylistic/function-paren-newline": ["error", "never"],
51
- "stylistic/generator-star-spacing": ["error", { before: false, after: true }],
59
+ "stylistic/generator-star-spacing": ["error", {
60
+ before: false,
61
+ after: true,
62
+ method: { before: true, after: false }
63
+ }],
52
64
  "stylistic/implicit-arrow-linebreak": ["error", "beside"],
53
65
  "stylistic/indent": ["error", 2, { flatTernaryExpressions: true }],
54
66
  "stylistic/indent-binary-ops": ["error", 2],
@@ -132,12 +144,12 @@ const stylisticRules = {
132
144
  async function stylistic(options) {
133
145
  const { overrides, stylistic: enabled } = options;
134
146
  if (!enabled) return {};
135
- const plugin = await interopDefault(import("@stylistic/eslint-plugin"));
147
+ const plugin = await interopDefault(import('@stylistic/eslint-plugin'));
136
148
  const files = [Glob.All];
137
149
  if (options.vue) files.push(Glob.Vue);
138
150
  const rules = {
139
151
  ...stylisticRules,
140
- ...overrides == null ? void 0 : overrides.stylistic
152
+ ...overrides?.stylistic
141
153
  };
142
154
  return {
143
155
  files,
@@ -145,15 +157,17 @@ async function stylistic(options) {
145
157
  rules
146
158
  };
147
159
  }
160
+
148
161
  async function vue(options) {
149
162
  const { overrides, vue: enabled } = options;
150
163
  if (!enabled) return [];
151
164
  const [vuePlugin, vueParser, tsParser] = await Promise.all([
152
165
  // @ts-expect-error no types
153
- interopDefault(import("eslint-plugin-vue")),
154
- interopDefault(import("vue-eslint-parser")),
155
- interopDefault(import("@typescript-eslint/parser"))
166
+ interopDefault(import('eslint-plugin-vue')),
167
+ interopDefault(import('vue-eslint-parser')),
168
+ interopDefault(import('@typescript-eslint/parser'))
156
169
  ]);
170
+ const INLINE_ELEMENTS = await json("eslint-plugin-vue/lib/utils/inline-non-void-elements.json");
157
171
  const rules = {
158
172
  ...vuePlugin.configs.base.rules,
159
173
  "vue/attribute-hyphenation": ["error", "always"],
@@ -184,41 +198,17 @@ async function vue(options) {
184
198
  }],
185
199
  "vue/define-props-declaration": ["error", "type-based"],
186
200
  "vue/enforce-style-attribute": ["error", { allow: ["scoped"] }],
187
- "vue/first-attribute-linebreak": "off",
188
201
  "vue/html-button-has-type": ["error", {
189
202
  button: true,
190
203
  submit: true,
191
204
  reset: true
192
205
  }],
193
- "vue/html-closing-bracket-newline": ["error", {
194
- singleline: "never",
195
- multiline: "always",
196
- selfClosingTag: {
197
- singleline: "never",
198
- multiline: "always"
199
- }
200
- }],
201
- "vue/html-closing-bracket-spacing": ["error", {
202
- startTag: "never",
203
- endTag: "never",
204
- selfClosingTag: "always"
205
- }],
206
- "vue/html-self-closing": ["error", {
207
- html: {
208
- void: "never",
209
- normal: "always",
210
- component: "always"
211
- },
212
- svg: "always",
213
- math: "always"
214
- }],
215
206
  "vue/match-component-file-name": ["off", {
216
207
  extensions: ["tsx", "vue"],
217
208
  shouldMatchCase: false
218
209
  }],
219
210
  "vue/match-component-import-name": "error",
220
211
  "vue/multi-word-component-names": "off",
221
- "vue/mustache-interpolation-spacing": ["error", "always"],
222
212
  "vue/no-arrow-functions-in-watch": "off",
223
213
  "vue/no-async-in-computed-properties": "error",
224
214
  "vue/no-boolean-default": ["error", "no-default"],
@@ -233,7 +223,6 @@ async function vue(options) {
233
223
  "vue/no-expose-after-await": "error",
234
224
  "vue/no-lifecycle-after-await": "error",
235
225
  "vue/no-lone-template": "error",
236
- "vue/no-multi-spaces": "error",
237
226
  "vue/no-multiple-objects-in-class": "error",
238
227
  "vue/no-mutating-props": "error",
239
228
  "vue/no-parsing-error": "error",
@@ -247,7 +236,6 @@ async function vue(options) {
247
236
  "vue/no-setup-props-reactivity-loss": "error",
248
237
  "vue/no-shared-component-data": "error",
249
238
  "vue/no-side-effects-in-computed-properties": "error",
250
- "vue/no-spaces-around-equal-signs-in-attribute": "error",
251
239
  "vue/no-static-inline-styles": ["error", { allowBinding: false }],
252
240
  "vue/no-template-key": "error",
253
241
  "vue/no-template-shadow": "error",
@@ -333,12 +321,67 @@ async function vue(options) {
333
321
  "vue/valid-v-show": "error",
334
322
  "vue/valid-v-slot": "error",
335
323
  "vue/valid-v-text": "error",
336
- ...overrides == null ? void 0 : overrides.vue
324
+ ...overrides?.vue
337
325
  };
338
326
  if (options.stylistic) {
327
+ Object.assign(rules, {
328
+ "vue/first-attribute-linebreak": ["error", {
329
+ singleline: "beside",
330
+ multiline: "below"
331
+ }],
332
+ "vue/html-closing-bracket-newline": ["error", {
333
+ singleline: "never",
334
+ multiline: "always",
335
+ selfClosingTag: {
336
+ singleline: "never",
337
+ multiline: "always"
338
+ }
339
+ }],
340
+ "vue/html-closing-bracket-spacing": ["error", {
341
+ startTag: "never",
342
+ endTag: "never",
343
+ selfClosingTag: "always"
344
+ }],
345
+ "vue/html-indent": ["error", 2, {
346
+ attribute: 1,
347
+ baseIndent: 1,
348
+ closeBracket: 0,
349
+ alignAttributesVertically: true,
350
+ ignores: []
351
+ }],
352
+ "vue/html-quotes": ["error", "double", { avoidEscape: false }],
353
+ "vue/html-self-closing": ["error", {
354
+ html: {
355
+ void: "never",
356
+ normal: "always",
357
+ component: "always"
358
+ },
359
+ svg: "always",
360
+ math: "always"
361
+ }],
362
+ "vue/max-attributes-per-line": ["error", {
363
+ singleline: Number.MAX_SAFE_INTEGER,
364
+ multiline: 1
365
+ }],
366
+ "vue/multiline-html-element-content-newline": ["error", {
367
+ ignoreWhenEmpty: true,
368
+ ignores: ["pre", "textarea", ...INLINE_ELEMENTS],
369
+ allowEmptyLines: false
370
+ }],
371
+ "vue/mustache-interpolation-spacing": ["error", "always"],
372
+ "vue/no-multi-spaces": ["error", { ignoreProperties: false }],
373
+ "vue/no-spaces-around-equal-signs-in-attribute": "error",
374
+ "vue/singleline-html-element-content-newline": ["off", {
375
+ ignoreWhenNoAttributes: true,
376
+ ignoreWhenEmpty: true,
377
+ ignores: ["pre", "textarea", ...INLINE_ELEMENTS],
378
+ externalIgnores: []
379
+ }]
380
+ });
339
381
  const vueStylistic = [
340
382
  "vue/array-bracket-newline",
341
383
  "vue/array-bracket-spacing",
384
+ "vue/array-element-newline",
342
385
  "vue/key-spacing",
343
386
  "vue/keyword-spacing",
344
387
  "vue/max-len",
@@ -362,9 +405,7 @@ async function vue(options) {
362
405
  }, {}));
363
406
  }
364
407
  return [
365
- {
366
- plugins: { vue: vuePlugin }
367
- },
408
+ { plugins: { vue: vuePlugin } },
368
409
  {
369
410
  files: [Glob.Vue],
370
411
  languageOptions: {
@@ -386,10 +427,64 @@ async function vue(options) {
386
427
  }
387
428
  ];
388
429
  }
430
+
431
+ async function jsonc(options) {
432
+ const { overrides, jsonc: enabled } = options;
433
+ if (!enabled) return [];
434
+ const [jsoncPlugin, jsoncParser] = await Promise.all([
435
+ interopDefault(import('eslint-plugin-jsonc')),
436
+ interopDefault(import('jsonc-eslint-parser'))
437
+ ]);
438
+ const rules = {
439
+ "jsonc/no-bigint-literals": "error",
440
+ "jsonc/no-binary-expression": "error",
441
+ "jsonc/no-binary-numeric-literals": "error",
442
+ "jsonc/no-infinity": "error",
443
+ "jsonc/no-nan": "error",
444
+ "jsonc/no-number-props": "error",
445
+ "jsonc/no-undefined-value": "error",
446
+ "jsonc/valid-json-number": "error",
447
+ ...overrides?.jsonc
448
+ };
449
+ if (options.stylistic) {
450
+ Object.assign(rules, {
451
+ "jsonc/comma-dangle": ["error", "never"],
452
+ "jsonc/quotes": ["error", "double", { avoidEscape: false }],
453
+ "jsonc/space-unary-ops": "error"
454
+ });
455
+ const jsoncStylistic = [
456
+ "jsonc/array-bracket-newline",
457
+ "jsonc/array-bracket-spacing",
458
+ "jsonc/array-element-newline",
459
+ "jsonc/comma-style",
460
+ "jsonc/indent",
461
+ "jsonc/key-spacing",
462
+ "jsonc/object-curly-newline",
463
+ "jsonc/object-curly-spacing",
464
+ "jsonc/object-property-newline"
465
+ ];
466
+ Object.assign(rules, jsoncStylistic.reduce((acc, rule) => {
467
+ const name = rule.replace("jsonc/", "stylistic/");
468
+ if (Object.hasOwn(stylisticRules, name)) {
469
+ acc[rule] = stylisticRules[name];
470
+ }
471
+ return acc;
472
+ }, {}));
473
+ }
474
+ return [
475
+ { plugins: { jsonc: jsoncPlugin } },
476
+ {
477
+ files: [Glob.Json],
478
+ languageOptions: { parser: jsoncParser }
479
+ },
480
+ { rules }
481
+ ];
482
+ }
483
+
389
484
  async function vitest(options) {
390
485
  const { overrides, vitest: enabled } = options;
391
486
  if (!enabled) return {};
392
- const plugin = await interopDefault(import("eslint-plugin-vitest"));
487
+ const plugin = await interopDefault(import('eslint-plugin-vitest'));
393
488
  return {
394
489
  plugins: { vitest: plugin },
395
490
  files: [Glob.Vitest],
@@ -425,14 +520,15 @@ async function vitest(options) {
425
520
  "vitest/require-top-level-describe": ["error", { maxNumberOfTopLevelDescribes: 10 }],
426
521
  "vitest/valid-describe-callback": "error",
427
522
  "vitest/valid-expect": "error",
428
- ...overrides == null ? void 0 : overrides.vitest
523
+ ...overrides?.vitest
429
524
  }
430
525
  };
431
526
  }
527
+
432
528
  async function unicorn(options) {
433
529
  const { overrides, unicorn: enabled = true } = options;
434
530
  if (!enabled) return {};
435
- const plugin = await interopDefault(import("eslint-plugin-unicorn"));
531
+ const plugin = await interopDefault(import('eslint-plugin-unicorn'));
436
532
  return {
437
533
  plugins: { unicorn: plugin },
438
534
  rules: {
@@ -475,10 +571,27 @@ async function unicorn(options) {
475
571
  "unicorn/prefer-structured-clone": "error",
476
572
  "unicorn/prefer-type-error": "error",
477
573
  "unicorn/relative-url-style": ["error", "never"],
478
- ...overrides == null ? void 0 : overrides.unicorn
574
+ ...overrides?.unicorn
479
575
  }
480
576
  };
481
577
  }
578
+
579
+ async function tailwind(options) {
580
+ const { overrides, tailwind: enabled } = options;
581
+ if (!enabled) return {};
582
+ const plugin = await interopDefault(import('eslint-plugin-tailwindcss'));
583
+ const rules = {
584
+ "tailwindcss/classnames-order": "error",
585
+ "tailwindcss/enforces-shorthand": "error",
586
+ "tailwindcss/no-contradicting-classname": "error",
587
+ ...overrides?.tailwind
588
+ };
589
+ return {
590
+ plugins: { tailwindcss: plugin },
591
+ rules
592
+ };
593
+ }
594
+
482
595
  function javascript(options) {
483
596
  const { overrides } = options;
484
597
  const files = [Glob.All];
@@ -618,15 +731,16 @@ function javascript(options) {
618
731
  "use-isnan": "error",
619
732
  "valid-typeof": "error",
620
733
  yoda: ["error", "never"],
621
- ...overrides == null ? void 0 : overrides.javascript
734
+ ...overrides?.javascript
622
735
  }
623
736
  };
624
737
  }
738
+
625
739
  async function typescript(options) {
626
740
  const { project, overrides } = options;
627
741
  const [tsParser, tsPlugin] = await Promise.all([
628
- interopDefault(import("@typescript-eslint/parser")),
629
- interopDefault(import("@typescript-eslint/eslint-plugin"))
742
+ interopDefault(import('@typescript-eslint/parser')),
743
+ interopDefault(import('@typescript-eslint/eslint-plugin'))
630
744
  ]);
631
745
  const files = [Glob.Typescript];
632
746
  if (options.vue) files.push(Glob.Vue);
@@ -830,7 +944,7 @@ async function typescript(options) {
830
944
  "@typescript-eslint/unbound-method": "error",
831
945
  "@typescript-eslint/unified-signatures": ["error", { ignoreDifferentlyNamedParameters: true }],
832
946
  "@typescript-eslint/use-unknown-in-catch-callback-variable": "error",
833
- ...overrides == null ? void 0 : overrides.typescript
947
+ ...overrides?.typescript
834
948
  };
835
949
  return {
836
950
  files,
@@ -848,10 +962,11 @@ async function typescript(options) {
848
962
  rules
849
963
  };
850
964
  }
965
+
851
966
  async function perfectionist(options) {
852
967
  const { overrides, perfectionist: enabled = true } = options;
853
968
  if (!enabled) return {};
854
- const plugin = await interopDefault(import("eslint-plugin-perfectionist"));
969
+ const plugin = await interopDefault(import('eslint-plugin-perfectionist'));
855
970
  return {
856
971
  plugins: { perfectionist: plugin },
857
972
  rules: {
@@ -910,10 +1025,11 @@ async function perfectionist(options) {
910
1025
  order: "asc",
911
1026
  ignoreCase: true
912
1027
  }],
913
- ...overrides == null ? void 0 : overrides.perfectionist
1028
+ ...overrides?.perfectionist
914
1029
  }
915
1030
  };
916
1031
  }
1032
+
917
1033
  async function defineConfig(options) {
918
1034
  const ignores = {
919
1035
  ignores: [...getIgnores(), ...options.ignores ?? []]
@@ -922,14 +1038,15 @@ async function defineConfig(options) {
922
1038
  javascript(options),
923
1039
  typescript(options),
924
1040
  ...await vue(options),
1041
+ ...await jsonc(options),
1042
+ stylistic(options),
925
1043
  perfectionist(options),
926
1044
  unicorn(options),
927
- stylistic(options),
928
1045
  vitest(options),
1046
+ tailwind(options),
929
1047
  ignores
930
1048
  ]);
931
1049
  return objects;
932
1050
  }
933
- export {
934
- defineConfig as default
935
- };
1051
+
1052
+ export { defineConfig as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tb-dev/eslint-config",
3
- "version": "4.2.1",
3
+ "version": "4.4.0",
4
4
  "description": "ESLint config",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,6 +13,13 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/ferreira-tb/eslint-config/issues"
15
15
  },
16
+ "keywords": [
17
+ "eslint",
18
+ "eslint-config",
19
+ "typescript",
20
+ "stylistic",
21
+ "vue"
22
+ ],
16
23
  "lint-staged": {
17
24
  "*.{?(c|m)@(j|t)s,css,vue,md,json}": "eslint --config eslint.config.js --fix"
18
25
  },
@@ -20,7 +27,9 @@
20
27
  "@stylistic/eslint-plugin": "^2.3.0",
21
28
  "@typescript-eslint/eslint-plugin": "^7.17.0",
22
29
  "@typescript-eslint/parser": "^7.17.0",
30
+ "eslint-plugin-jsonc": "^2.16.0",
23
31
  "eslint-plugin-perfectionist": "^3.0.0",
32
+ "eslint-plugin-tailwindcss": "^3.17.4",
24
33
  "eslint-plugin-unicorn": "^54.0.0",
25
34
  "eslint-plugin-vitest": "^0.5.4",
26
35
  "eslint-plugin-vue": "^9.27.0",
@@ -43,7 +52,7 @@
43
52
  "typescript": "^5.4.0"
44
53
  },
45
54
  "engines": {
46
- "node": ">=20"
55
+ "node": ">=22"
47
56
  },
48
57
  "files": [
49
58
  "dist/**/*"