@favorodera/eslint-config 0.1.4 → 1.0.1
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 +4 -6
- package/dist/index.d.mts +288 -183
- package/dist/index.mjs +625 -541
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlatConfigComposer
|
|
1
|
+
import { FlatConfigComposer } from "eslint-flat-config-utils";
|
|
2
2
|
import { defu } from "defu";
|
|
3
3
|
import globals from "globals";
|
|
4
4
|
import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
|
|
@@ -15,7 +15,7 @@ const mdGlob = "**/*.md";
|
|
|
15
15
|
/** Glob pattern for matching virtual files extracted from Markdown */
|
|
16
16
|
const mdInMdGlob = "**/*.md/*.md";
|
|
17
17
|
/** Glob pattern for matching code blocks embedded in Markdown files */
|
|
18
|
-
const codeInMdGlob = "**/*.md
|
|
18
|
+
const codeInMdGlob = "**/*.md/**/*.{js,cjs,mjs,ts,cts,mts,vue}";
|
|
19
19
|
/** Glob pattern for matching scripts files */
|
|
20
20
|
const scriptsGlob = "**/*.{js,cjs,mjs,ts,cts,mts}";
|
|
21
21
|
/** Glob pattern for matching test files */
|
|
@@ -76,6 +76,99 @@ const ignoresGlob = [
|
|
|
76
76
|
"**/.*/skills"
|
|
77
77
|
];
|
|
78
78
|
//#endregion
|
|
79
|
+
//#region src/configs/disables.ts
|
|
80
|
+
/**
|
|
81
|
+
* Centralized disable configs applied **last** in the composer chain
|
|
82
|
+
* so they always override any rule enabled by preceding configs.
|
|
83
|
+
* @returns Config items that turn off specific rules for targeted file globs.
|
|
84
|
+
*/
|
|
85
|
+
function disables() {
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
files: [
|
|
89
|
+
json5Glob,
|
|
90
|
+
jsoncGlob,
|
|
91
|
+
jsonGlob
|
|
92
|
+
],
|
|
93
|
+
name: "favorodera/disables/jsonc",
|
|
94
|
+
rules: { "no-irregular-whitespace": "off" }
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
files: [mdGlob],
|
|
98
|
+
ignores: [mdInMdGlob],
|
|
99
|
+
languageOptions: { frontmatter: "yaml" },
|
|
100
|
+
name: "favorodera/disables/markdown",
|
|
101
|
+
rules: { "md/no-missing-atx-heading-space": "off" }
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
files: [codeInMdGlob],
|
|
105
|
+
languageOptions: { parserOptions: { ecmaFeatures: { impliedStrict: true } } },
|
|
106
|
+
name: "favorodera/disables/code-in-markdown",
|
|
107
|
+
rules: {
|
|
108
|
+
"no-alert": "off",
|
|
109
|
+
"no-console": "off",
|
|
110
|
+
"no-labels": "off",
|
|
111
|
+
"no-lone-blocks": "off",
|
|
112
|
+
"no-restricted-syntax": "off",
|
|
113
|
+
"no-undef": "off",
|
|
114
|
+
"no-unused-expressions": "off",
|
|
115
|
+
"no-unused-labels": "off",
|
|
116
|
+
"no-unused-vars": "off",
|
|
117
|
+
"unicode-bom": "off",
|
|
118
|
+
"node/prefer-global/process": "off",
|
|
119
|
+
"style/comma-dangle": "off",
|
|
120
|
+
"style/eol-last": "off",
|
|
121
|
+
"style/line-comment-position": "off",
|
|
122
|
+
"style/padding-line-between-statements": "off",
|
|
123
|
+
"ts/consistent-type-imports": "off",
|
|
124
|
+
"ts/explicit-function-return-type": "off",
|
|
125
|
+
"ts/no-namespace": "off",
|
|
126
|
+
"ts/no-redeclare": "off",
|
|
127
|
+
"ts/no-require-imports": "off",
|
|
128
|
+
"ts/no-unused-expressions": "off",
|
|
129
|
+
"ts/no-unused-vars": "off",
|
|
130
|
+
"ts/no-use-before-define": "off",
|
|
131
|
+
"unused-imports/no-unused-imports": "off",
|
|
132
|
+
"unused-imports/no-unused-vars": "off",
|
|
133
|
+
"vue/no-unused-vars": "off",
|
|
134
|
+
"jsdoc/require-jsdoc": "off"
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
files: [
|
|
139
|
+
jsGlob,
|
|
140
|
+
tsGlob,
|
|
141
|
+
vueGlob
|
|
142
|
+
],
|
|
143
|
+
name: "favorodera/disables/js-ts-vue",
|
|
144
|
+
rules: {
|
|
145
|
+
"jsdoc/require-throws-type": "off",
|
|
146
|
+
"node/no-missing-import": "off",
|
|
147
|
+
"node/no-missing-require": "off",
|
|
148
|
+
"node/no-process-exit": "off",
|
|
149
|
+
"tailwind/enforce-consistent-important-position": "off",
|
|
150
|
+
"tailwind/enforce-consistent-variable-syntax": "off",
|
|
151
|
+
"tailwind/enforce-shorthand-classes": "off",
|
|
152
|
+
"unicorn/filename-case": "off",
|
|
153
|
+
"unicorn/no-process-exit": "off",
|
|
154
|
+
"unicorn/prevent-abbreviations": "off",
|
|
155
|
+
"no-unused-vars": "off",
|
|
156
|
+
"ts/no-unused-vars": "off"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
files: [vueGlob],
|
|
161
|
+
name: "favorodera/disables/vue",
|
|
162
|
+
rules: { "vue/multi-word-component-names": "off" }
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
files: [testGlob],
|
|
166
|
+
name: "favorodera/disables/test",
|
|
167
|
+
rules: { "no-unused-expressions": "off" }
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
79
172
|
//#region src/configs/ignores.ts
|
|
80
173
|
const defaultPatterns = ignoresGlob;
|
|
81
174
|
/**
|
|
@@ -126,57 +219,70 @@ function resolveOptions(value, defaults) {
|
|
|
126
219
|
if (!value) return false;
|
|
127
220
|
return defu(value === true ? {} : value, defaults);
|
|
128
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Creates a new object that omits the specified keys from the target object.
|
|
224
|
+
* @template TTarget The type of the target object.
|
|
225
|
+
* @template TTargetKeys The type of the keys to omit from the target object.
|
|
226
|
+
* @param target The target object from which to omit the specified keys.
|
|
227
|
+
* @param keys An array of keys to omit from the target object.
|
|
228
|
+
* @returns A new object that contains all properties of the target object except for the specified keys.
|
|
229
|
+
*/
|
|
230
|
+
function omit(target, keys) {
|
|
231
|
+
const targetClone = { ...target };
|
|
232
|
+
for (const key of keys) Reflect.deleteProperty(targetClone, key);
|
|
233
|
+
return targetClone;
|
|
234
|
+
}
|
|
129
235
|
//#endregion
|
|
130
236
|
//#region src/configs/imports.ts
|
|
131
|
-
const importsDefaults = { files: [
|
|
132
|
-
jsGlob,
|
|
133
|
-
tsGlob,
|
|
134
|
-
vueGlob
|
|
135
|
-
] };
|
|
136
237
|
/**
|
|
137
238
|
* Constructs the flat config items for imports linting, providing plugin setup and
|
|
138
239
|
* specific rules to enforce consistent import ordering and quality.
|
|
139
|
-
* @param options Configuration options for imports linting.
|
|
140
240
|
* @returns Promise resolving to imports ESLint config items.
|
|
141
241
|
*/
|
|
142
|
-
async function imports(
|
|
143
|
-
const resolved = defu(options, importsDefaults);
|
|
242
|
+
async function imports() {
|
|
144
243
|
const importPlugin = await importModule(import("eslint-plugin-import-lite"));
|
|
145
|
-
const
|
|
244
|
+
const files = [
|
|
245
|
+
jsGlob,
|
|
246
|
+
tsGlob,
|
|
247
|
+
vueGlob
|
|
248
|
+
];
|
|
249
|
+
const recommendedConfig = importPlugin.configs.recommended;
|
|
250
|
+
const { rules = {} } = recommendedConfig;
|
|
146
251
|
return [{
|
|
147
|
-
|
|
148
|
-
|
|
252
|
+
...omit(recommendedConfig, [
|
|
253
|
+
"rules",
|
|
254
|
+
"files",
|
|
255
|
+
"name"
|
|
256
|
+
]),
|
|
257
|
+
name: "favorodera/imports/setup"
|
|
149
258
|
}, {
|
|
150
|
-
files
|
|
259
|
+
files,
|
|
151
260
|
name: "favorodera/imports/rules",
|
|
152
261
|
rules: {
|
|
153
|
-
...
|
|
262
|
+
...rules,
|
|
154
263
|
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
|
155
264
|
"import/first": "error",
|
|
156
265
|
"import/newline-after-import": ["error", { count: 1 }],
|
|
157
266
|
"import/no-duplicates": ["error", { "prefer-inline": true }],
|
|
158
267
|
"import/no-mutable-exports": "error",
|
|
159
|
-
"import/no-named-default": "error"
|
|
160
|
-
...resolved.overrides
|
|
268
|
+
"import/no-named-default": "error"
|
|
161
269
|
}
|
|
162
270
|
}];
|
|
163
271
|
}
|
|
164
272
|
//#endregion
|
|
165
273
|
//#region src/configs/javascript.ts
|
|
166
|
-
const javascriptDefaults = { files: [
|
|
167
|
-
jsGlob,
|
|
168
|
-
tsGlob,
|
|
169
|
-
vueGlob
|
|
170
|
-
] };
|
|
171
274
|
/**
|
|
172
275
|
* Constructs the flat config items for core JavaScript linting, setting up the required
|
|
173
276
|
* language options, globals, and recommended baseline rules.
|
|
174
|
-
* @param options Javascript configuration options.
|
|
175
277
|
* @returns Promise resolving to javascript ESLint config items.
|
|
176
278
|
*/
|
|
177
|
-
async function javascript(
|
|
178
|
-
const
|
|
179
|
-
const
|
|
279
|
+
async function javascript() {
|
|
280
|
+
const jsPlugin = await importModule(import("@eslint/js"));
|
|
281
|
+
const files = [
|
|
282
|
+
jsGlob,
|
|
283
|
+
tsGlob,
|
|
284
|
+
vueGlob
|
|
285
|
+
];
|
|
180
286
|
return [{
|
|
181
287
|
languageOptions: {
|
|
182
288
|
ecmaVersion: "latest",
|
|
@@ -193,15 +299,16 @@ async function javascript(options) {
|
|
|
193
299
|
linterOptions: { reportUnusedDisableDirectives: true },
|
|
194
300
|
name: "favorodera/javascript/setup"
|
|
195
301
|
}, {
|
|
196
|
-
files
|
|
302
|
+
files,
|
|
197
303
|
name: "favorodera/javascript/rules",
|
|
198
304
|
rules: {
|
|
199
|
-
...
|
|
200
|
-
"array-callback-return": "error",
|
|
305
|
+
...jsPlugin.configs.recommended.rules,
|
|
306
|
+
"array-callback-return": ["error", { allowImplicit: true }],
|
|
201
307
|
"block-scoped-var": "error",
|
|
202
308
|
"default-case-last": "error",
|
|
203
309
|
"dot-notation": "error",
|
|
204
310
|
"eqeqeq": "error",
|
|
311
|
+
"getter-return": ["error", { allowImplicit: true }],
|
|
205
312
|
"new-cap": "error",
|
|
206
313
|
"no-alert": "error",
|
|
207
314
|
"no-array-constructor": "error",
|
|
@@ -284,75 +391,88 @@ async function javascript(options) {
|
|
|
284
391
|
"unicode-bom": "error",
|
|
285
392
|
"valid-typeof": ["error", { requireStringLiterals: true }],
|
|
286
393
|
"vars-on-top": "error",
|
|
287
|
-
"yoda": "error"
|
|
288
|
-
...resolved.overrides
|
|
394
|
+
"yoda": "error"
|
|
289
395
|
}
|
|
290
396
|
}];
|
|
291
397
|
}
|
|
292
398
|
//#endregion
|
|
293
399
|
//#region src/configs/jsdoc.ts
|
|
294
|
-
const jsdocDefaults = { files: [
|
|
295
|
-
jsGlob,
|
|
296
|
-
tsGlob,
|
|
297
|
-
vueGlob
|
|
298
|
-
] };
|
|
299
400
|
/**
|
|
300
401
|
* Constructs the flat config items for JSDoc linting, ensuring comments follow
|
|
301
402
|
* proper styling, parameter checks, and types alignment.
|
|
302
|
-
* @param options JSDoc configuration options.
|
|
303
403
|
* @returns Promise resolving to JSDoc ESLint config items.
|
|
304
404
|
*/
|
|
305
|
-
async function jsdoc(
|
|
306
|
-
const resolved = defu(options, jsdocDefaults);
|
|
405
|
+
async function jsdoc() {
|
|
307
406
|
const jsdocPlugin = await importModule(import("eslint-plugin-jsdoc"));
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
407
|
+
const files = [
|
|
408
|
+
jsGlob,
|
|
409
|
+
tsGlob,
|
|
410
|
+
vueGlob
|
|
411
|
+
];
|
|
412
|
+
const recommendedConfig = jsdocPlugin.configs["flat/recommended-typescript-error"];
|
|
413
|
+
const stylisticConfig = jsdocPlugin.configs["flat/stylistic-typescript-error"];
|
|
414
|
+
const { rules: recommendedRules = {} } = recommendedConfig;
|
|
415
|
+
const recommendedRest = omit(recommendedConfig, [
|
|
416
|
+
"rules",
|
|
417
|
+
"files",
|
|
418
|
+
"name"
|
|
419
|
+
]);
|
|
420
|
+
const { rules: stylisticRules = {} } = stylisticConfig;
|
|
421
|
+
const stylisticRest = omit(stylisticConfig, [
|
|
422
|
+
"rules",
|
|
423
|
+
"files",
|
|
424
|
+
"name"
|
|
425
|
+
]);
|
|
426
|
+
const rules = {
|
|
427
|
+
...recommendedRules,
|
|
428
|
+
...stylisticRules
|
|
311
429
|
};
|
|
312
|
-
return [
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
"jsdoc/
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
430
|
+
return [
|
|
431
|
+
{
|
|
432
|
+
...recommendedRest,
|
|
433
|
+
name: "favorodera/jsdoc/recommended/setup"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
...stylisticRest,
|
|
437
|
+
name: "favorodera/jsdoc/stylistic/setup"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
files,
|
|
441
|
+
name: "favorodera/jsdoc/rules",
|
|
442
|
+
rules: {
|
|
443
|
+
...rules,
|
|
444
|
+
"jsdoc/check-indentation": "error",
|
|
445
|
+
"jsdoc/check-template-names": "error",
|
|
446
|
+
"jsdoc/imports-as-dependencies": "error",
|
|
447
|
+
"jsdoc/lines-before-block": ["error", { ignoreSingleLines: false }],
|
|
448
|
+
"jsdoc/multiline-blocks": "error",
|
|
449
|
+
"jsdoc/no-bad-blocks": "error",
|
|
450
|
+
"jsdoc/no-blank-block-descriptions": "error",
|
|
451
|
+
"jsdoc/no-blank-blocks": "error",
|
|
452
|
+
"jsdoc/sort-tags": "error"
|
|
453
|
+
}
|
|
331
454
|
}
|
|
332
|
-
|
|
455
|
+
];
|
|
333
456
|
}
|
|
334
457
|
//#endregion
|
|
335
458
|
//#region src/configs/jsonc.ts
|
|
336
|
-
const jsoncDefaults = { files: [
|
|
337
|
-
json5Glob,
|
|
338
|
-
jsoncGlob,
|
|
339
|
-
jsonGlob
|
|
340
|
-
] };
|
|
341
459
|
/**
|
|
342
460
|
* Constructs the flat config items for JSON, JSON5, and JSONC linting, setting up
|
|
343
461
|
* the custom parser, rule validations, and sorting rules for package.json/tsconfig.json.
|
|
344
|
-
* @param options JSONC configuration options.
|
|
345
462
|
* @returns Promise resolving to JSONC ESLint config items.
|
|
346
463
|
*/
|
|
347
|
-
async function jsonc(
|
|
348
|
-
const resolved = defu(options, jsoncDefaults);
|
|
464
|
+
async function jsonc() {
|
|
349
465
|
return [
|
|
350
466
|
{
|
|
351
467
|
name: "favorodera/jsonc/setup",
|
|
352
468
|
plugins: { jsonc: await importModule(import("eslint-plugin-jsonc")) }
|
|
353
469
|
},
|
|
354
470
|
{
|
|
355
|
-
files:
|
|
471
|
+
files: [
|
|
472
|
+
json5Glob,
|
|
473
|
+
jsoncGlob,
|
|
474
|
+
jsonGlob
|
|
475
|
+
],
|
|
356
476
|
language: "jsonc/x",
|
|
357
477
|
name: "favorodera/jsonc/rules",
|
|
358
478
|
rules: {
|
|
@@ -400,8 +520,7 @@ async function jsonc(options) {
|
|
|
400
520
|
"jsonc/quotes": "error",
|
|
401
521
|
"jsonc/space-unary-ops": "error",
|
|
402
522
|
"jsonc/valid-json-number": "error",
|
|
403
|
-
"jsonc/vue-custom-block/no-parsing-error": "error"
|
|
404
|
-
...resolved.overrides
|
|
523
|
+
"jsonc/vue-custom-block/no-parsing-error": "error"
|
|
405
524
|
}
|
|
406
525
|
},
|
|
407
526
|
{
|
|
@@ -620,21 +739,12 @@ async function jsonc(options) {
|
|
|
620
739
|
pathPattern: "^compilerOptions$"
|
|
621
740
|
}
|
|
622
741
|
] }
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
files: resolved.files,
|
|
626
|
-
language: "jsonc/x",
|
|
627
|
-
name: "favorodera/jsonc/disables",
|
|
628
|
-
rules: { "no-irregular-whitespace": "off" }
|
|
629
742
|
}
|
|
630
743
|
];
|
|
631
744
|
}
|
|
632
745
|
//#endregion
|
|
633
746
|
//#region src/configs/markdown.ts
|
|
634
|
-
const markdownDefaults = {
|
|
635
|
-
files: [mdGlob],
|
|
636
|
-
gfm: true
|
|
637
|
-
};
|
|
747
|
+
const markdownDefaults = { gfm: true };
|
|
638
748
|
/**
|
|
639
749
|
* Constructs the flat config items for Markdown linting, extracting and linting
|
|
640
750
|
* embedded code blocks within the document according to specified rules.
|
|
@@ -644,139 +754,87 @@ const markdownDefaults = {
|
|
|
644
754
|
async function markdown(options) {
|
|
645
755
|
const resolved = defu(options, markdownDefaults);
|
|
646
756
|
const markdownPlugin = await importModule(import("@eslint/markdown"));
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
files: [codeInMdGlob],
|
|
669
|
-
languageOptions: { parserOptions: { ecmaFeatures: { impliedStrict: true } } },
|
|
670
|
-
name: "favorodera/markdown/code-in-md/disables",
|
|
671
|
-
rules: {
|
|
672
|
-
"no-alert": "off",
|
|
673
|
-
"no-console": "off",
|
|
674
|
-
"no-labels": "off",
|
|
675
|
-
"no-lone-blocks": "off",
|
|
676
|
-
"no-restricted-syntax": "off",
|
|
677
|
-
"no-undef": "off",
|
|
678
|
-
"no-unused-expressions": "off",
|
|
679
|
-
"no-unused-labels": "off",
|
|
680
|
-
"no-unused-vars": "off",
|
|
681
|
-
"unicode-bom": "off",
|
|
682
|
-
"node/prefer-global/process": "off",
|
|
683
|
-
"style/comma-dangle": "off",
|
|
684
|
-
"style/eol-last": "off",
|
|
685
|
-
"style/padding-line-between-statements": "off",
|
|
686
|
-
"ts/consistent-type-imports": "off",
|
|
687
|
-
"ts/explicit-function-return-type": "off",
|
|
688
|
-
"ts/no-namespace": "off",
|
|
689
|
-
"ts/no-redeclare": "off",
|
|
690
|
-
"ts/no-require-imports": "off",
|
|
691
|
-
"ts/no-unused-expressions": "off",
|
|
692
|
-
"ts/no-unused-vars": "off",
|
|
693
|
-
"ts/no-use-before-define": "off",
|
|
694
|
-
"unused-imports/no-unused-imports": "off",
|
|
695
|
-
"unused-imports/no-unused-vars": "off",
|
|
696
|
-
"vue/no-unused-vars": "off",
|
|
697
|
-
"jsdoc/require-jsdoc": "off"
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
];
|
|
757
|
+
const [recommendedConfig] = markdownPlugin.configs.recommended;
|
|
758
|
+
const { rules = {} } = recommendedConfig;
|
|
759
|
+
return [{
|
|
760
|
+
...omit(recommendedConfig, [
|
|
761
|
+
"rules",
|
|
762
|
+
"files",
|
|
763
|
+
"name",
|
|
764
|
+
"language"
|
|
765
|
+
]),
|
|
766
|
+
name: "favorodera/markdown/setup"
|
|
767
|
+
}, {
|
|
768
|
+
files: [mdGlob],
|
|
769
|
+
ignores: [mdInMdGlob],
|
|
770
|
+
language: resolved.gfm ? "md/gfm" : "md/commonmark",
|
|
771
|
+
languageOptions: { frontmatter: "yaml" },
|
|
772
|
+
name: "favorodera/markdown/rules",
|
|
773
|
+
processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
|
|
774
|
+
rules
|
|
775
|
+
}];
|
|
701
776
|
}
|
|
702
777
|
//#endregion
|
|
703
778
|
//#region src/configs/node.ts
|
|
704
|
-
const nodeDefaults = { files: [
|
|
705
|
-
jsGlob,
|
|
706
|
-
tsGlob,
|
|
707
|
-
vueGlob
|
|
708
|
-
] };
|
|
709
779
|
/**
|
|
710
780
|
* Constructs the flat config items for Node.js linting, providing rules
|
|
711
781
|
* to enforce best practices for Node.js environments.
|
|
712
|
-
* @param options Node configuration options.
|
|
713
782
|
* @returns Promise resolving to Node ESLint config items.
|
|
714
783
|
*/
|
|
715
|
-
async function node(
|
|
716
|
-
const resolved = defu(options, nodeDefaults);
|
|
784
|
+
async function node() {
|
|
717
785
|
const nodePlugin = await importModule(import("eslint-plugin-n"));
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
plugins: { node: nodePlugin }
|
|
723
|
-
},
|
|
724
|
-
{
|
|
725
|
-
files: resolved.files,
|
|
726
|
-
name: "favorodera/node/rules",
|
|
727
|
-
rules: {
|
|
728
|
-
...renamePluginsInRules(baseRules, { n: "node" }),
|
|
729
|
-
"node/callback-return": "error",
|
|
730
|
-
"node/handle-callback-err": ["error", "^(err|error)$"],
|
|
731
|
-
"node/no-callback-literal": "error",
|
|
732
|
-
"node/no-new-require": "error",
|
|
733
|
-
"node/no-path-concat": "error",
|
|
734
|
-
"node/no-unpublished-import": "error",
|
|
735
|
-
"node/prefer-global/buffer": "error",
|
|
736
|
-
"node/prefer-global/console": "error",
|
|
737
|
-
"node/prefer-global/crypto": "error",
|
|
738
|
-
"node/prefer-global/process": "error",
|
|
739
|
-
"node/prefer-global/text-decoder": "error",
|
|
740
|
-
"node/prefer-global/text-encoder": "error",
|
|
741
|
-
"node/prefer-global/timers": "error",
|
|
742
|
-
"node/prefer-global/url": "error",
|
|
743
|
-
"node/prefer-global/url-search-params": "error",
|
|
744
|
-
"node/prefer-node-protocol": "error",
|
|
745
|
-
"node/prefer-promises/dns": "error",
|
|
746
|
-
"node/prefer-promises/fs": "error",
|
|
747
|
-
"node/no-process-exit": "off",
|
|
748
|
-
...resolved.overrides
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
|
-
{
|
|
752
|
-
files: [tsGlob, vueGlob],
|
|
753
|
-
name: "favorodera/node/disables",
|
|
754
|
-
rules: {
|
|
755
|
-
"node/no-missing-import": "off",
|
|
756
|
-
"node/no-missing-require": "off"
|
|
757
|
-
}
|
|
758
|
-
}
|
|
786
|
+
const files = [
|
|
787
|
+
jsGlob,
|
|
788
|
+
tsGlob,
|
|
789
|
+
vueGlob
|
|
759
790
|
];
|
|
791
|
+
const recommendedConfig = nodePlugin.configs["flat/recommended-module"];
|
|
792
|
+
const { rules = {} } = recommendedConfig;
|
|
793
|
+
return [{
|
|
794
|
+
...omit(recommendedConfig, [
|
|
795
|
+
"rules",
|
|
796
|
+
"files",
|
|
797
|
+
"name"
|
|
798
|
+
]),
|
|
799
|
+
name: "favorodera/node/setup"
|
|
800
|
+
}, {
|
|
801
|
+
files,
|
|
802
|
+
name: "favorodera/node/rules",
|
|
803
|
+
rules: {
|
|
804
|
+
...rules,
|
|
805
|
+
"node/callback-return": "error",
|
|
806
|
+
"node/handle-callback-err": ["error", "^(err|error)$"],
|
|
807
|
+
"node/no-callback-literal": "error",
|
|
808
|
+
"node/no-new-require": "error",
|
|
809
|
+
"node/no-path-concat": "error",
|
|
810
|
+
"node/no-unpublished-import": "error",
|
|
811
|
+
"node/prefer-global/buffer": "error",
|
|
812
|
+
"node/prefer-global/console": "error",
|
|
813
|
+
"node/prefer-global/crypto": "error",
|
|
814
|
+
"node/prefer-global/process": "error",
|
|
815
|
+
"node/prefer-global/text-decoder": "error",
|
|
816
|
+
"node/prefer-global/text-encoder": "error",
|
|
817
|
+
"node/prefer-global/timers": "error",
|
|
818
|
+
"node/prefer-global/url": "error",
|
|
819
|
+
"node/prefer-global/url-search-params": "error",
|
|
820
|
+
"node/prefer-node-protocol": "error",
|
|
821
|
+
"node/prefer-promises/dns": "error",
|
|
822
|
+
"node/prefer-promises/fs": "error"
|
|
823
|
+
}
|
|
824
|
+
}];
|
|
760
825
|
}
|
|
761
826
|
//#endregion
|
|
762
827
|
//#region src/configs/perfectionist.ts
|
|
763
828
|
const perfectionistDefaults = {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
newlinesInside: "ignore",
|
|
774
|
-
order: "asc",
|
|
775
|
-
partitionByComment: true,
|
|
776
|
-
partitionByNewLine: true,
|
|
777
|
-
specialCharacters: "keep",
|
|
778
|
-
type: "natural"
|
|
779
|
-
}
|
|
829
|
+
ignoreCase: true,
|
|
830
|
+
locales: "en-US",
|
|
831
|
+
newlinesBetween: "ignore",
|
|
832
|
+
newlinesInside: "ignore",
|
|
833
|
+
order: "asc",
|
|
834
|
+
partitionByComment: true,
|
|
835
|
+
partitionByNewLine: true,
|
|
836
|
+
specialCharacters: "keep",
|
|
837
|
+
type: "natural"
|
|
780
838
|
};
|
|
781
839
|
/**
|
|
782
840
|
* Constructs the flat config items for Perfectionist linting, providing rules
|
|
@@ -787,19 +845,27 @@ const perfectionistDefaults = {
|
|
|
787
845
|
async function perfectionist(options) {
|
|
788
846
|
const resolved = defu(options, perfectionistDefaults);
|
|
789
847
|
const perfectionistPlugin = await importModule(import("eslint-plugin-perfectionist"));
|
|
790
|
-
const
|
|
791
|
-
|
|
848
|
+
const files = [
|
|
849
|
+
jsGlob,
|
|
850
|
+
tsGlob,
|
|
851
|
+
vueGlob
|
|
852
|
+
];
|
|
853
|
+
const safeType = resolved.type ?? "natural";
|
|
854
|
+
const presetConfig = perfectionistPlugin.configs[`recommended-${safeType}`];
|
|
855
|
+
const { rules = {} } = presetConfig;
|
|
792
856
|
return [{
|
|
857
|
+
...omit(presetConfig, [
|
|
858
|
+
"rules",
|
|
859
|
+
"settings",
|
|
860
|
+
"files",
|
|
861
|
+
"name"
|
|
862
|
+
]),
|
|
793
863
|
name: "favorodera/perfectionist/setup",
|
|
794
|
-
|
|
795
|
-
settings: { perfectionist: resolved.settings }
|
|
864
|
+
settings: { perfectionist: resolved }
|
|
796
865
|
}, {
|
|
797
|
-
files
|
|
866
|
+
files,
|
|
798
867
|
name: "favorodera/perfectionist/rules",
|
|
799
|
-
rules
|
|
800
|
-
...baseRules,
|
|
801
|
-
...resolved.overrides
|
|
802
|
-
}
|
|
868
|
+
rules
|
|
803
869
|
}];
|
|
804
870
|
}
|
|
805
871
|
//#endregion
|
|
@@ -840,6 +906,15 @@ async function pnpm() {
|
|
|
840
906
|
name: "favorodera/pnpm/pnpm-workspace-yaml",
|
|
841
907
|
rules: {
|
|
842
908
|
"pnpm/yaml-enforce-settings": ["error", { settings: {
|
|
909
|
+
allowBuilds: {
|
|
910
|
+
"@parcel/watcher": true,
|
|
911
|
+
"@tailwindcss/oxide": true,
|
|
912
|
+
"better-sqlite3": true,
|
|
913
|
+
"esbuild": true,
|
|
914
|
+
"sharp": true,
|
|
915
|
+
"unrs-resolver": true,
|
|
916
|
+
"vue-demi": true
|
|
917
|
+
},
|
|
843
918
|
shellEmulator: true,
|
|
844
919
|
trustPolicy: "no-downgrade"
|
|
845
920
|
} }],
|
|
@@ -853,19 +928,12 @@ async function pnpm() {
|
|
|
853
928
|
//#endregion
|
|
854
929
|
//#region src/configs/stylistic.ts
|
|
855
930
|
const stylisticDefaults = {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
braceStyle: "1tbs",
|
|
863
|
-
experimental: false,
|
|
864
|
-
indent: 2,
|
|
865
|
-
jsx: false,
|
|
866
|
-
quotes: "single",
|
|
867
|
-
semi: false
|
|
868
|
-
}
|
|
931
|
+
braceStyle: "1tbs",
|
|
932
|
+
experimental: false,
|
|
933
|
+
indent: 2,
|
|
934
|
+
jsx: false,
|
|
935
|
+
quotes: "single",
|
|
936
|
+
semi: false
|
|
869
937
|
};
|
|
870
938
|
/**
|
|
871
939
|
* Constructs the flat config items for Stylistic linting, applying customized
|
|
@@ -876,18 +944,28 @@ const stylisticDefaults = {
|
|
|
876
944
|
async function stylistic(options) {
|
|
877
945
|
const resolved = defu(options, stylisticDefaults);
|
|
878
946
|
const stylePlugin = await importModule(import("@stylistic/eslint-plugin"));
|
|
879
|
-
const
|
|
947
|
+
const files = [
|
|
948
|
+
jsGlob,
|
|
949
|
+
tsGlob,
|
|
950
|
+
vueGlob
|
|
951
|
+
];
|
|
952
|
+
const recommendedConfig = stylePlugin.configs.customize({
|
|
880
953
|
pluginName: "style",
|
|
881
|
-
...resolved
|
|
882
|
-
})
|
|
954
|
+
...resolved
|
|
955
|
+
});
|
|
956
|
+
const { rules = {} } = recommendedConfig;
|
|
883
957
|
return [{
|
|
884
|
-
|
|
885
|
-
|
|
958
|
+
...omit(recommendedConfig, [
|
|
959
|
+
"rules",
|
|
960
|
+
"files",
|
|
961
|
+
"name"
|
|
962
|
+
]),
|
|
963
|
+
name: "favorodera/stylistic/setup"
|
|
886
964
|
}, {
|
|
887
|
-
files
|
|
965
|
+
files,
|
|
888
966
|
name: "favorodera/stylistic/rules",
|
|
889
967
|
rules: {
|
|
890
|
-
...
|
|
968
|
+
...rules,
|
|
891
969
|
"style/array-bracket-newline": "error",
|
|
892
970
|
"style/array-element-newline": "error",
|
|
893
971
|
"style/curly-newline": ["error", "always"],
|
|
@@ -905,21 +983,13 @@ async function stylistic(options) {
|
|
|
905
983
|
"style/object-curly-newline": "error",
|
|
906
984
|
"style/semi-style": "error",
|
|
907
985
|
"style/switch-colon-spacing": "error",
|
|
908
|
-
"style/wrap-regex": "error"
|
|
909
|
-
...resolved.overrides
|
|
986
|
+
"style/wrap-regex": "error"
|
|
910
987
|
}
|
|
911
988
|
}];
|
|
912
989
|
}
|
|
913
990
|
//#endregion
|
|
914
991
|
//#region src/configs/tailwind.ts
|
|
915
|
-
const tailwindDefaults = {
|
|
916
|
-
files: [
|
|
917
|
-
jsGlob,
|
|
918
|
-
tsGlob,
|
|
919
|
-
vueGlob
|
|
920
|
-
],
|
|
921
|
-
settings: { detectComponentClasses: true }
|
|
922
|
-
};
|
|
992
|
+
const tailwindDefaults = { detectComponentClasses: true };
|
|
923
993
|
/**
|
|
924
994
|
* Constructs the flat config items for Tailwind CSS linting, providing custom settings
|
|
925
995
|
* to parse and lint utility classes, and enforcing consistent ordering.
|
|
@@ -929,147 +999,152 @@ const tailwindDefaults = {
|
|
|
929
999
|
async function tailwind(options) {
|
|
930
1000
|
const resolved = defu(options, tailwindDefaults);
|
|
931
1001
|
const tailwindPlugin = await importModule(import("eslint-plugin-better-tailwindcss"));
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
1002
|
+
const files = [
|
|
1003
|
+
jsGlob,
|
|
1004
|
+
tsGlob,
|
|
1005
|
+
vueGlob
|
|
1006
|
+
];
|
|
1007
|
+
const recommendedConfig = tailwindPlugin.configs["recommended-error"];
|
|
1008
|
+
const stylisticConfig = tailwindPlugin.configs["stylistic-error"];
|
|
1009
|
+
const { rules: recommendedRules } = recommendedConfig;
|
|
1010
|
+
const recommendedRest = omit(recommendedConfig, ["rules"]);
|
|
1011
|
+
const { rules: stylisticRules } = stylisticConfig;
|
|
1012
|
+
const stylisticRest = omit(stylisticConfig, ["rules"]);
|
|
1013
|
+
const rules = {
|
|
1014
|
+
...recommendedRules,
|
|
1015
|
+
...stylisticRules
|
|
935
1016
|
};
|
|
936
|
-
return [
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1017
|
+
return [
|
|
1018
|
+
{
|
|
1019
|
+
...recommendedRest,
|
|
1020
|
+
name: "favorodera/tailwind/recommended/setup"
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
...stylisticRest,
|
|
1024
|
+
name: "favorodera/tailwind/stylistic/setup"
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
files,
|
|
1028
|
+
name: "favorodera/tailwind/rules",
|
|
1029
|
+
rules: {
|
|
1030
|
+
...rules,
|
|
1031
|
+
"tailwind/enforce-consistent-class-order": ["error", {
|
|
1032
|
+
componentClassOrder: "asc",
|
|
1033
|
+
componentClassPosition: "start",
|
|
1034
|
+
order: "strict",
|
|
1035
|
+
unknownClassOrder: "asc",
|
|
1036
|
+
unknownClassPosition: "start"
|
|
1037
|
+
}],
|
|
1038
|
+
"tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
|
|
1039
|
+
"tailwind/enforce-consistent-variant-order": "error",
|
|
1040
|
+
"tailwind/enforce-logical-properties": "error"
|
|
1041
|
+
},
|
|
1042
|
+
settings: { "better-tailwindcss": resolved }
|
|
959
1043
|
}
|
|
960
|
-
|
|
1044
|
+
];
|
|
961
1045
|
}
|
|
962
1046
|
//#endregion
|
|
963
1047
|
//#region src/configs/test.ts
|
|
964
|
-
const testDefaults = { files: [testGlob] };
|
|
965
1048
|
/**
|
|
966
1049
|
* Constructs the flat config items for test linting, extending
|
|
967
1050
|
* the recommended Vitest rule sets.
|
|
968
|
-
* @param options Test configuration options.
|
|
969
1051
|
* @returns Promise resolving to test ESLint config items.
|
|
970
1052
|
*/
|
|
971
|
-
async function test(
|
|
972
|
-
const resolved = defu(options, testDefaults);
|
|
1053
|
+
async function test() {
|
|
973
1054
|
const testPlugin = await importModule(import("@vitest/eslint-plugin"));
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
...resolved.overrides
|
|
1039
|
-
}
|
|
1040
|
-
},
|
|
1041
|
-
{
|
|
1042
|
-
files: resolved.files,
|
|
1043
|
-
name: "favorodera/test/disables",
|
|
1044
|
-
rules: { "no-unused-expressions": "off" }
|
|
1055
|
+
const files = [testGlob];
|
|
1056
|
+
const recommendedConfig = testPlugin.configs.recommended;
|
|
1057
|
+
const { rules } = recommendedConfig;
|
|
1058
|
+
return [{
|
|
1059
|
+
...omit(recommendedConfig, ["rules", "name"]),
|
|
1060
|
+
name: "favorodera/test/setup"
|
|
1061
|
+
}, {
|
|
1062
|
+
files,
|
|
1063
|
+
name: "favorodera/test/rules",
|
|
1064
|
+
rules: {
|
|
1065
|
+
...rules,
|
|
1066
|
+
"test/consistent-each-for": ["error", {
|
|
1067
|
+
describe: "for",
|
|
1068
|
+
it: "for",
|
|
1069
|
+
suite: "for",
|
|
1070
|
+
test: "for"
|
|
1071
|
+
}],
|
|
1072
|
+
"test/consistent-test-it": ["error", {
|
|
1073
|
+
fn: "it",
|
|
1074
|
+
withinDescribe: "it"
|
|
1075
|
+
}],
|
|
1076
|
+
"test/consistent-vitest-vi": "error",
|
|
1077
|
+
"test/hoisted-apis-on-top": "error",
|
|
1078
|
+
"test/max-expects": "error",
|
|
1079
|
+
"test/max-nested-describe": "error",
|
|
1080
|
+
"test/no-alias-methods": "error",
|
|
1081
|
+
"test/no-conditional-in-test": "error",
|
|
1082
|
+
"test/no-conditional-tests": "error",
|
|
1083
|
+
"test/no-duplicate-hooks": "error",
|
|
1084
|
+
"test/no-hooks": "error",
|
|
1085
|
+
"test/no-large-snapshots": "warn",
|
|
1086
|
+
"test/no-test-prefixes": "error",
|
|
1087
|
+
"test/no-test-return-statement": "error",
|
|
1088
|
+
"test/padding-around-all": "error",
|
|
1089
|
+
"test/prefer-called-times": "error",
|
|
1090
|
+
"test/prefer-called-with": "error",
|
|
1091
|
+
"test/prefer-comparison-matcher": "error",
|
|
1092
|
+
"test/prefer-each": "error",
|
|
1093
|
+
"test/prefer-equality-matcher": "error",
|
|
1094
|
+
"test/prefer-expect-resolves": "error",
|
|
1095
|
+
"test/prefer-expect-type-of": "error",
|
|
1096
|
+
"test/prefer-hooks-in-order": "error",
|
|
1097
|
+
"test/prefer-hooks-on-top": "error",
|
|
1098
|
+
"test/prefer-import-in-mock": "error",
|
|
1099
|
+
"test/prefer-importing-vitest-globals": "error",
|
|
1100
|
+
"test/prefer-lowercase-title": "error",
|
|
1101
|
+
"test/prefer-mock-promise-shorthand": "error",
|
|
1102
|
+
"test/prefer-mock-return-shorthand": "error",
|
|
1103
|
+
"test/prefer-snapshot-hint": "error",
|
|
1104
|
+
"test/prefer-spy-on": "error",
|
|
1105
|
+
"test/prefer-strict-boolean-matchers": "error",
|
|
1106
|
+
"test/prefer-strict-equal": "error",
|
|
1107
|
+
"test/prefer-to-be": "error",
|
|
1108
|
+
"test/prefer-to-be-object": "error",
|
|
1109
|
+
"test/prefer-to-contain": "error",
|
|
1110
|
+
"test/prefer-to-have-been-called-times": "error",
|
|
1111
|
+
"test/prefer-to-have-length": "error",
|
|
1112
|
+
"test/prefer-todo": "error",
|
|
1113
|
+
"test/prefer-vi-mocked": "error",
|
|
1114
|
+
"test/require-awaited-expect-poll": "error",
|
|
1115
|
+
"test/require-hook": "error",
|
|
1116
|
+
"test/require-to-throw-message": "error",
|
|
1117
|
+
"test/require-top-level-describe": "error",
|
|
1118
|
+
"test/warn-todo": "warn"
|
|
1045
1119
|
}
|
|
1046
|
-
];
|
|
1120
|
+
}];
|
|
1047
1121
|
}
|
|
1048
1122
|
//#endregion
|
|
1049
1123
|
//#region src/configs/typescript.ts
|
|
1050
|
-
const typescriptDefaults = { files: [tsGlob] };
|
|
1051
1124
|
/**
|
|
1052
1125
|
* Constructs the flat config items for TypeScript linting, initializing the parser
|
|
1053
1126
|
* and extending the recommended and strict type-aware rule sets.
|
|
1054
|
-
* @param options TypeScript configuration options.
|
|
1055
1127
|
* @returns Promise resolving to TypeScript ESLint config items.
|
|
1056
1128
|
*/
|
|
1057
|
-
async function typescript(
|
|
1058
|
-
const resolved = defu(options, typescriptDefaults);
|
|
1129
|
+
async function typescript() {
|
|
1059
1130
|
const tsEsLint = await importModule(import("typescript-eslint"));
|
|
1060
|
-
const
|
|
1131
|
+
const files = [tsGlob];
|
|
1132
|
+
const [baseConfig, eslintRecommended, strictConfig] = tsEsLint.configs.strict;
|
|
1133
|
+
const stylisticConfig = tsEsLint.configs.stylistic[2];
|
|
1134
|
+
const baseRest = omit(baseConfig, ["rules", "name"]);
|
|
1135
|
+
const rules = {
|
|
1136
|
+
...eslintRecommended?.rules,
|
|
1137
|
+
...strictConfig?.rules,
|
|
1138
|
+
...stylisticConfig?.rules
|
|
1139
|
+
};
|
|
1061
1140
|
return [{
|
|
1062
|
-
|
|
1063
|
-
|
|
1141
|
+
...baseRest,
|
|
1142
|
+
name: "favorodera/typescript/setup"
|
|
1064
1143
|
}, {
|
|
1065
|
-
files
|
|
1066
|
-
languageOptions: {
|
|
1067
|
-
parser: tsEsLint.parser,
|
|
1068
|
-
parserOptions: { sourceType: "module" }
|
|
1069
|
-
},
|
|
1144
|
+
files,
|
|
1070
1145
|
name: "favorodera/typescript/rules",
|
|
1071
1146
|
rules: {
|
|
1072
|
-
...
|
|
1147
|
+
...rules,
|
|
1073
1148
|
"ts/array-type": ["error", {
|
|
1074
1149
|
default: "generic",
|
|
1075
1150
|
readonly: "generic"
|
|
@@ -1079,88 +1154,68 @@ async function typescript(options) {
|
|
|
1079
1154
|
"ts/method-signature-style": "error",
|
|
1080
1155
|
"ts/no-import-type-side-effects": "error",
|
|
1081
1156
|
"ts/no-loop-func": "error",
|
|
1082
|
-
"ts/no-redeclare": "error"
|
|
1083
|
-
...resolved.overrides
|
|
1157
|
+
"ts/no-redeclare": "error"
|
|
1084
1158
|
}
|
|
1085
1159
|
}];
|
|
1086
1160
|
}
|
|
1087
1161
|
//#endregion
|
|
1088
1162
|
//#region src/configs/unicorn.ts
|
|
1089
|
-
const unicornDefaults = { files: [
|
|
1090
|
-
jsGlob,
|
|
1091
|
-
tsGlob,
|
|
1092
|
-
vueGlob
|
|
1093
|
-
] };
|
|
1094
1163
|
/**
|
|
1095
1164
|
* Constructs the flat config items for Unicorn linting, providing a collection of
|
|
1096
1165
|
* awesome ESLint rules to improve code quality and enforce best practices.
|
|
1097
|
-
* @param options Unicorn configuration options.
|
|
1098
1166
|
* @returns Promise resolving to Unicorn ESLint config items.
|
|
1099
1167
|
*/
|
|
1100
|
-
async function unicorn(
|
|
1101
|
-
const resolved = defu(options, unicornDefaults);
|
|
1168
|
+
async function unicorn() {
|
|
1102
1169
|
const unicornPlugin = await importModule(import("eslint-plugin-unicorn"));
|
|
1103
|
-
const
|
|
1170
|
+
const files = [
|
|
1171
|
+
jsGlob,
|
|
1172
|
+
tsGlob,
|
|
1173
|
+
vueGlob
|
|
1174
|
+
];
|
|
1175
|
+
const recommendedConfig = unicornPlugin.configs.recommended;
|
|
1176
|
+
const { rules = {} } = recommendedConfig;
|
|
1104
1177
|
return [{
|
|
1105
|
-
|
|
1106
|
-
|
|
1178
|
+
...omit(recommendedConfig, [
|
|
1179
|
+
"rules",
|
|
1180
|
+
"files",
|
|
1181
|
+
"name"
|
|
1182
|
+
]),
|
|
1183
|
+
name: "favorodera/unicorn/setup"
|
|
1107
1184
|
}, {
|
|
1108
|
-
files
|
|
1109
|
-
languageOptions: { globals: globals.builtin },
|
|
1185
|
+
files,
|
|
1110
1186
|
name: "favorodera/unicorn/rules",
|
|
1111
|
-
rules
|
|
1112
|
-
...baseRules,
|
|
1113
|
-
"unicorn/filename-case": "off",
|
|
1114
|
-
"unicorn/no-process-exit": "off",
|
|
1115
|
-
"unicorn/prevent-abbreviations": "off",
|
|
1116
|
-
...resolved.overrides
|
|
1117
|
-
}
|
|
1187
|
+
rules
|
|
1118
1188
|
}];
|
|
1119
1189
|
}
|
|
1120
1190
|
//#endregion
|
|
1121
1191
|
//#region src/configs/unused-imports.ts
|
|
1122
|
-
const unusedImportsDefaults = { files: [
|
|
1123
|
-
jsGlob,
|
|
1124
|
-
tsGlob,
|
|
1125
|
-
vueGlob
|
|
1126
|
-
] };
|
|
1127
1192
|
/**
|
|
1128
1193
|
* Constructs the flat config items for unused imports linting, providing plugin setup and
|
|
1129
1194
|
* specific rules to detect and remove unused imports and variables.
|
|
1130
|
-
* @param options Configuration options for unused imports linting.
|
|
1131
1195
|
* @returns Promise resolving to unused imports ESLint config items.
|
|
1132
1196
|
*/
|
|
1133
|
-
async function unusedImports(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
{
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
}
|
|
1154
|
-
},
|
|
1155
|
-
{
|
|
1156
|
-
files: resolved.files,
|
|
1157
|
-
name: "favorodera/unused-imports/disables",
|
|
1158
|
-
rules: {
|
|
1159
|
-
"no-unused-vars": "off",
|
|
1160
|
-
"ts/no-unused-vars": "off"
|
|
1161
|
-
}
|
|
1197
|
+
async function unusedImports() {
|
|
1198
|
+
return [{
|
|
1199
|
+
name: "favorodera/unused-imports/setup",
|
|
1200
|
+
plugins: { "unused-imports": await importModule(import("eslint-plugin-unused-imports")) }
|
|
1201
|
+
}, {
|
|
1202
|
+
files: [
|
|
1203
|
+
jsGlob,
|
|
1204
|
+
tsGlob,
|
|
1205
|
+
vueGlob
|
|
1206
|
+
],
|
|
1207
|
+
name: "favorodera/unused-imports/rules",
|
|
1208
|
+
rules: {
|
|
1209
|
+
"unused-imports/no-unused-imports": "error",
|
|
1210
|
+
"unused-imports/no-unused-vars": ["error", {
|
|
1211
|
+
args: "after-used",
|
|
1212
|
+
argsIgnorePattern: "^_",
|
|
1213
|
+
ignoreRestSiblings: true,
|
|
1214
|
+
vars: "all",
|
|
1215
|
+
varsIgnorePattern: "^_"
|
|
1216
|
+
}]
|
|
1162
1217
|
}
|
|
1163
|
-
];
|
|
1218
|
+
}];
|
|
1164
1219
|
}
|
|
1165
1220
|
//#endregion
|
|
1166
1221
|
//#region src/configs/vue.ts
|
|
@@ -1169,167 +1224,195 @@ const sfcBlocksDefaults = { blocks: {
|
|
|
1169
1224
|
styles: true,
|
|
1170
1225
|
template: false
|
|
1171
1226
|
} };
|
|
1172
|
-
const vueDefaults = {
|
|
1173
|
-
files: [vueGlob],
|
|
1174
|
-
sfcBlocks: sfcBlocksDefaults
|
|
1175
|
-
};
|
|
1227
|
+
const vueDefaults = { sfcBlocks: sfcBlocksDefaults };
|
|
1176
1228
|
/**
|
|
1177
|
-
* Constructs the flat config items for Vue linting, setting up the custom template parser,
|
|
1229
|
+
* Constructs the flat config items for Vue and accessibility linting, setting up the custom template parser,
|
|
1178
1230
|
* Vue block processors, and rules to enforce recommended component syntax.
|
|
1179
1231
|
* @param options Vue configuration options.
|
|
1180
1232
|
* @returns Promise resolving to Vue ESLint config items.
|
|
1181
1233
|
*/
|
|
1182
1234
|
async function vue(options) {
|
|
1183
|
-
const
|
|
1184
|
-
const
|
|
1185
|
-
const [vuePlugin, vueParser, tsEsLint] = await Promise.all([
|
|
1235
|
+
const sfcBlocks = resolveOptions(defu(options, vueDefaults).sfcBlocks, sfcBlocksDefaults);
|
|
1236
|
+
const [vuePlugin, vueParser, tsEsLint, a11yPlugin] = await Promise.all([
|
|
1186
1237
|
importModule(import("eslint-plugin-vue")),
|
|
1187
1238
|
importModule(import("vue-eslint-parser")),
|
|
1188
|
-
importModule(import("typescript-eslint"))
|
|
1239
|
+
importModule(import("typescript-eslint")),
|
|
1240
|
+
importModule(import("eslint-plugin-vuejs-accessibility"))
|
|
1241
|
+
]);
|
|
1242
|
+
const vueRecommendedConfig = vuePlugin.configs["flat/recommended-error"];
|
|
1243
|
+
const a11yRecommendedConfig = a11yPlugin.configs["flat/recommended"];
|
|
1244
|
+
const [vueBase] = vueRecommendedConfig;
|
|
1245
|
+
const vueBaseRest = omit(vueBase, [
|
|
1246
|
+
"rules",
|
|
1247
|
+
"files",
|
|
1248
|
+
"name"
|
|
1189
1249
|
]);
|
|
1190
|
-
const
|
|
1250
|
+
const vueRules = extractRules(vueRecommendedConfig);
|
|
1251
|
+
const [a11yBase] = a11yRecommendedConfig;
|
|
1252
|
+
const a11yBaseRest = omit(a11yBase, ["name"]);
|
|
1253
|
+
const a11yRules = extractRules(a11yRecommendedConfig);
|
|
1191
1254
|
const processor = sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]);
|
|
1192
|
-
return [
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
files: resolved.files,
|
|
1213
|
-
languageOptions: {
|
|
1214
|
-
parser: vueParser,
|
|
1215
|
-
parserOptions: {
|
|
1216
|
-
extraFileExtensions: [".vue"],
|
|
1217
|
-
parser: tsEsLint.parser,
|
|
1218
|
-
sourceType: "module"
|
|
1219
|
-
}
|
|
1255
|
+
return [
|
|
1256
|
+
{
|
|
1257
|
+
...vueBaseRest,
|
|
1258
|
+
languageOptions: { globals: {
|
|
1259
|
+
computed: "readonly",
|
|
1260
|
+
defineEmits: "readonly",
|
|
1261
|
+
defineExpose: "readonly",
|
|
1262
|
+
defineProps: "readonly",
|
|
1263
|
+
onMounted: "readonly",
|
|
1264
|
+
onUnmounted: "readonly",
|
|
1265
|
+
reactive: "readonly",
|
|
1266
|
+
ref: "readonly",
|
|
1267
|
+
shallowReactive: "readonly",
|
|
1268
|
+
shallowRef: "readonly",
|
|
1269
|
+
toRef: "readonly",
|
|
1270
|
+
toRefs: "readonly",
|
|
1271
|
+
watch: "readonly",
|
|
1272
|
+
watchEffect: "readonly"
|
|
1273
|
+
} },
|
|
1274
|
+
name: "favorodera/vue/setup"
|
|
1220
1275
|
},
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
singleline: "always"
|
|
1234
|
-
}],
|
|
1235
|
-
"vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
|
|
1236
|
-
"vue/define-macros-order": ["error", {
|
|
1237
|
-
defineExposeLast: true,
|
|
1238
|
-
order: [
|
|
1239
|
-
"defineOptions",
|
|
1240
|
-
"definePage",
|
|
1241
|
-
"defineSlots",
|
|
1242
|
-
"defineEmits",
|
|
1243
|
-
"defineProps",
|
|
1244
|
-
"defineModel"
|
|
1245
|
-
]
|
|
1246
|
-
}],
|
|
1247
|
-
"vue/define-props-declaration": ["error", "type-based"],
|
|
1248
|
-
"vue/define-props-destructuring": ["error", { destructure: "never" }],
|
|
1249
|
-
"vue/multi-word-component-names": "off",
|
|
1250
|
-
"vue/next-tick-style": ["error", "promise"],
|
|
1251
|
-
"vue/no-import-compiler-macros": "error",
|
|
1252
|
-
"vue/no-negated-v-if-condition": "error",
|
|
1253
|
-
"vue/no-reserved-component-names": ["error", {
|
|
1254
|
-
disallowVue3BuiltInComponents: true,
|
|
1255
|
-
disallowVueBuiltInComponents: true,
|
|
1256
|
-
htmlElementCaseSensitive: false
|
|
1257
|
-
}],
|
|
1258
|
-
"vue/no-root-v-if": "error",
|
|
1259
|
-
"vue/no-template-target-blank": "error",
|
|
1260
|
-
"vue/no-unused-emit-declarations": "error",
|
|
1261
|
-
"vue/no-unused-properties": "error",
|
|
1262
|
-
"vue/no-unused-refs": "error",
|
|
1263
|
-
"vue/no-use-v-else-with-v-for": "error",
|
|
1264
|
-
"vue/no-useless-mustaches": "error",
|
|
1265
|
-
"vue/no-useless-v-bind": "error",
|
|
1266
|
-
"vue/padding-line-between-blocks": "error",
|
|
1267
|
-
"vue/padding-line-between-tags": ["error", [
|
|
1268
|
-
{
|
|
1269
|
-
blankLine: "always",
|
|
1270
|
-
next: "*:multi-line",
|
|
1271
|
-
prev: "*:single-line"
|
|
1272
|
-
},
|
|
1273
|
-
{
|
|
1274
|
-
blankLine: "always",
|
|
1275
|
-
next: "*:single-line",
|
|
1276
|
-
prev: "*:multi-line"
|
|
1277
|
-
},
|
|
1278
|
-
{
|
|
1279
|
-
blankLine: "always",
|
|
1280
|
-
next: "*:multi-line",
|
|
1281
|
-
prev: "*:multi-line"
|
|
1282
|
-
},
|
|
1283
|
-
{
|
|
1284
|
-
blankLine: "never",
|
|
1285
|
-
next: "*:single-line",
|
|
1286
|
-
prev: "*:single-line"
|
|
1276
|
+
{
|
|
1277
|
+
...a11yBaseRest,
|
|
1278
|
+
name: "favorodera/vue/a11y/setup"
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
files: [vueGlob],
|
|
1282
|
+
languageOptions: {
|
|
1283
|
+
parser: vueParser,
|
|
1284
|
+
parserOptions: {
|
|
1285
|
+
extraFileExtensions: [".vue"],
|
|
1286
|
+
parser: tsEsLint.parser,
|
|
1287
|
+
sourceType: "module"
|
|
1287
1288
|
}
|
|
1288
|
-
|
|
1289
|
-
"vue/
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1289
|
+
},
|
|
1290
|
+
name: "favorodera/vue/rules",
|
|
1291
|
+
processor,
|
|
1292
|
+
rules: {
|
|
1293
|
+
...vueRules,
|
|
1294
|
+
"vue/block-lang": ["error", { script: { lang: "ts" } }],
|
|
1295
|
+
"vue/block-order": ["error", { order: [
|
|
1296
|
+
"script",
|
|
1297
|
+
"template",
|
|
1298
|
+
"style"
|
|
1299
|
+
] }],
|
|
1300
|
+
"vue/block-tag-newline": ["error", {
|
|
1301
|
+
multiline: "always",
|
|
1302
|
+
singleline: "always"
|
|
1303
|
+
}],
|
|
1304
|
+
"vue/comment-directive": ["error", { reportUnusedDisableDirectives: true }],
|
|
1305
|
+
"vue/define-macros-order": ["error", {
|
|
1306
|
+
defineExposeLast: true,
|
|
1307
|
+
order: [
|
|
1308
|
+
"defineOptions",
|
|
1309
|
+
"definePage",
|
|
1310
|
+
"defineSlots",
|
|
1311
|
+
"defineEmits",
|
|
1312
|
+
"defineProps",
|
|
1313
|
+
"defineModel"
|
|
1314
|
+
]
|
|
1315
|
+
}],
|
|
1316
|
+
"vue/define-props-declaration": ["error", "type-based"],
|
|
1317
|
+
"vue/define-props-destructuring": ["error", { destructure: "never" }],
|
|
1318
|
+
"vue/next-tick-style": ["error", "promise"],
|
|
1319
|
+
"vue/no-import-compiler-macros": "error",
|
|
1320
|
+
"vue/no-negated-v-if-condition": "error",
|
|
1321
|
+
"vue/no-reserved-component-names": ["error", {
|
|
1322
|
+
disallowVue3BuiltInComponents: true,
|
|
1323
|
+
disallowVueBuiltInComponents: true,
|
|
1324
|
+
htmlElementCaseSensitive: false
|
|
1325
|
+
}],
|
|
1326
|
+
"vue/no-root-v-if": "error",
|
|
1327
|
+
"vue/no-template-target-blank": "error",
|
|
1328
|
+
"vue/no-unused-emit-declarations": "error",
|
|
1329
|
+
"vue/no-unused-properties": "error",
|
|
1330
|
+
"vue/no-unused-refs": "error",
|
|
1331
|
+
"vue/no-use-v-else-with-v-for": "error",
|
|
1332
|
+
"vue/no-useless-mustaches": "error",
|
|
1333
|
+
"vue/no-useless-v-bind": "error",
|
|
1334
|
+
"vue/padding-line-between-blocks": "error",
|
|
1335
|
+
"vue/padding-line-between-tags": ["error", [
|
|
1336
|
+
{
|
|
1337
|
+
blankLine: "always",
|
|
1338
|
+
next: "*:multi-line",
|
|
1339
|
+
prev: "*:single-line"
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
blankLine: "always",
|
|
1343
|
+
next: "*:single-line",
|
|
1344
|
+
prev: "*:multi-line"
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
blankLine: "always",
|
|
1348
|
+
next: "*:multi-line",
|
|
1349
|
+
prev: "*:multi-line"
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
blankLine: "never",
|
|
1353
|
+
next: "*:single-line",
|
|
1354
|
+
prev: "*:single-line"
|
|
1355
|
+
}
|
|
1356
|
+
]],
|
|
1357
|
+
"vue/prefer-prop-type-boolean-first": "error",
|
|
1358
|
+
"vue/prefer-separate-static-class": "error",
|
|
1359
|
+
"vue/prefer-single-event-payload": "error",
|
|
1360
|
+
"vue/prefer-use-template-ref": "error",
|
|
1361
|
+
"vue/slot-name-casing": ["error", "kebab-case"],
|
|
1362
|
+
"vue/v-for-delimiter-style": ["error", "in"]
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
files: [vueGlob],
|
|
1367
|
+
name: "favorodera/vue/a11y/rules",
|
|
1368
|
+
rules: {
|
|
1369
|
+
...a11yRules,
|
|
1370
|
+
"vue-a11y/no-aria-hidden-on-focusable": "error",
|
|
1371
|
+
"vue-a11y/no-onchange": "error",
|
|
1372
|
+
"vue-a11y/no-role-presentation-on-focusable": "error"
|
|
1373
|
+
}
|
|
1296
1374
|
}
|
|
1297
|
-
|
|
1375
|
+
];
|
|
1298
1376
|
}
|
|
1299
1377
|
//#endregion
|
|
1300
1378
|
//#region src/configs/yaml.ts
|
|
1301
|
-
const yamlDefaults = { files: [yamlGlob] };
|
|
1302
1379
|
/**
|
|
1303
1380
|
* Constructs the flat config items for YAML linting, setting up
|
|
1304
1381
|
* the custom parser and rule validations.
|
|
1305
|
-
* @param options YAML configuration options.
|
|
1306
1382
|
* @returns Promise resolving to YAML ESLint config items.
|
|
1307
1383
|
*/
|
|
1308
|
-
async function yaml(
|
|
1309
|
-
const resolved = defu(options, yamlDefaults);
|
|
1384
|
+
async function yaml() {
|
|
1310
1385
|
const [yamlPlugin, yamlParser] = await Promise.all([importModule(import("eslint-plugin-yml")), importModule(import("yaml-eslint-parser"))]);
|
|
1311
|
-
const
|
|
1386
|
+
const standardConfig = yamlPlugin.configs.standard;
|
|
1387
|
+
const [pluginConfig] = standardConfig;
|
|
1388
|
+
const pluginRest = omit(pluginConfig, [
|
|
1389
|
+
"rules",
|
|
1390
|
+
"files",
|
|
1391
|
+
"name"
|
|
1392
|
+
]);
|
|
1393
|
+
const rules = extractRules(standardConfig);
|
|
1312
1394
|
return [
|
|
1313
1395
|
{
|
|
1314
|
-
|
|
1315
|
-
|
|
1396
|
+
...pluginRest,
|
|
1397
|
+
name: "favorodera/yaml/setup"
|
|
1316
1398
|
},
|
|
1317
1399
|
{
|
|
1318
|
-
files:
|
|
1400
|
+
files: [yamlGlob],
|
|
1401
|
+
language: "yaml/yaml",
|
|
1319
1402
|
languageOptions: { parser: yamlParser },
|
|
1320
1403
|
name: "favorodera/yaml/rules",
|
|
1321
1404
|
rules: {
|
|
1322
|
-
...
|
|
1405
|
+
...rules,
|
|
1323
1406
|
"yaml/quotes": ["error", {
|
|
1324
1407
|
avoidEscape: true,
|
|
1325
1408
|
prefer: "single"
|
|
1326
1409
|
}],
|
|
1327
|
-
"yaml/require-string-key": "error"
|
|
1328
|
-
...resolved.overrides
|
|
1410
|
+
"yaml/require-string-key": "error"
|
|
1329
1411
|
}
|
|
1330
1412
|
},
|
|
1331
1413
|
{
|
|
1332
1414
|
files: [pnpmWorkspaceGlob],
|
|
1415
|
+
language: "yaml/yaml",
|
|
1333
1416
|
languageOptions: { parser: yamlParser },
|
|
1334
1417
|
name: "favorodera/yaml/sort/pnpm-workspace-yaml",
|
|
1335
1418
|
rules: { "yaml/sort-keys": [
|
|
@@ -1437,16 +1520,17 @@ function factory(options = {}) {
|
|
|
1437
1520
|
if (resolved) configs.push(configFunction(resolved));
|
|
1438
1521
|
}
|
|
1439
1522
|
let composer = new FlatConfigComposer();
|
|
1440
|
-
composer = composer.append(...configs).renamePlugins({
|
|
1523
|
+
composer = composer.append(...configs).append(...disables()).renamePlugins({
|
|
1441
1524
|
"@typescript-eslint": "ts",
|
|
1442
1525
|
"better-tailwindcss": "tailwind",
|
|
1443
1526
|
"import-lite": "import",
|
|
1444
1527
|
"markdown": "md",
|
|
1445
1528
|
"n": "node",
|
|
1446
1529
|
"vitest": "test",
|
|
1530
|
+
"vuejs-accessibility": "vue-a11y",
|
|
1447
1531
|
"yml": "yaml"
|
|
1448
1532
|
});
|
|
1449
1533
|
return composer;
|
|
1450
1534
|
}
|
|
1451
1535
|
//#endregion
|
|
1452
|
-
export { codeInMdGlob, extractRules, factory, ignoresGlob, importModule, jsGlob, json5Glob, jsonGlob, jsoncGlob, mdGlob, mdInMdGlob, packageJsonGlob, pnpmWorkspaceGlob, scriptsGlob, testGlob, tsConfigGlob, tsGlob, vueGlob, yamlGlob };
|
|
1536
|
+
export { codeInMdGlob, extractRules, factory, ignoresGlob, importModule, jsGlob, json5Glob, jsonGlob, jsoncGlob, mdGlob, mdInMdGlob, omit, packageJsonGlob, pnpmWorkspaceGlob, scriptsGlob, testGlob, tsConfigGlob, tsGlob, vueGlob, yamlGlob };
|