eslint-config-decent 1.1.0 → 1.2.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 +29 -0
- package/dist/index.cjs +127 -19
- package/dist/index.d.cts +6 -2
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.mjs +127 -19
- package/package.json +15 -15
- package/src/extension.ts +30 -0
- package/src/index.ts +12 -3
- package/src/rules/requireExtensionRule.ts +54 -0
- package/src/rules/requireIndexRule.ts +51 -0
- package/src/typescriptEslint.ts +1 -8
package/README.md
CHANGED
|
@@ -15,6 +15,35 @@ import tsEslint from 'typescript-eslint';
|
|
|
15
15
|
export default tsEslint.config(...defaultConfig());
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Override parserOptions
|
|
19
|
+
|
|
20
|
+
````mjs
|
|
21
|
+
// eslint.config.mjs
|
|
22
|
+
|
|
23
|
+
import { defaultConfig } from 'eslint-config-decent';
|
|
24
|
+
import tsEslint from 'typescript-eslint';
|
|
25
|
+
|
|
26
|
+
export default tsEslint.config(...defaultConfig({
|
|
27
|
+
projectService: {
|
|
28
|
+
allowedDefaultProject: ['./*.js', './*.cjs', './*.mjs', './tests/**/*.ts', './tests/**/*.js', './tests/**/*.cjs', './tests/**/*.mjs'],
|
|
29
|
+
defaultProject: 'tsconfig.json',
|
|
30
|
+
},
|
|
31
|
+
tsconfigRootDir: import.meta.dirname,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
## Disable require-extensions rules
|
|
35
|
+
|
|
36
|
+
```mjs
|
|
37
|
+
// eslint.config.mjs
|
|
38
|
+
|
|
39
|
+
import { defaultConfig } from 'eslint-config-decent';
|
|
40
|
+
import tsEslint from 'typescript-eslint';
|
|
41
|
+
|
|
42
|
+
export default tsEslint.config(...defaultConfig({
|
|
43
|
+
enableRequireExtensions: false,
|
|
44
|
+
}));
|
|
45
|
+
````
|
|
46
|
+
|
|
18
47
|
## License
|
|
19
48
|
|
|
20
49
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,8 @@ const eslint = require('@eslint/js');
|
|
|
4
4
|
const globals = require('globals');
|
|
5
5
|
const tsEslint = require('typescript-eslint');
|
|
6
6
|
const prettier = require('eslint-plugin-prettier/recommended');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
7
9
|
const jsdoc = require('eslint-plugin-jsdoc');
|
|
8
10
|
const mocha = require('eslint-plugin-mocha');
|
|
9
11
|
const promise = require('eslint-plugin-promise');
|
|
@@ -30,7 +32,7 @@ const testingLibrary__default = /*#__PURE__*/_interopDefaultCompat(testingLibrar
|
|
|
30
32
|
const security__default = /*#__PURE__*/_interopDefaultCompat(security);
|
|
31
33
|
const unicorn__default = /*#__PURE__*/_interopDefaultCompat(unicorn);
|
|
32
34
|
|
|
33
|
-
const base$
|
|
35
|
+
const base$8 = {
|
|
34
36
|
rules: {
|
|
35
37
|
"array-callback-return": ["error", { allowImplicit: true }],
|
|
36
38
|
"block-scoped-var": "error",
|
|
@@ -277,12 +279,122 @@ const cjs = {
|
|
|
277
279
|
strict: ["error", "global"]
|
|
278
280
|
}
|
|
279
281
|
};
|
|
280
|
-
const configs$
|
|
281
|
-
base: base$
|
|
282
|
+
const configs$8 = {
|
|
283
|
+
base: base$8,
|
|
282
284
|
cjsAndEsm,
|
|
283
285
|
cjs
|
|
284
286
|
};
|
|
285
287
|
|
|
288
|
+
const requireExtensionRule = {
|
|
289
|
+
meta: {
|
|
290
|
+
type: "suggestion",
|
|
291
|
+
docs: {
|
|
292
|
+
description: "Ensure import and export statements include a file extension",
|
|
293
|
+
category: "Best Practices",
|
|
294
|
+
recommended: true
|
|
295
|
+
},
|
|
296
|
+
fixable: "code",
|
|
297
|
+
schema: []
|
|
298
|
+
},
|
|
299
|
+
create(context) {
|
|
300
|
+
function checkSource(source) {
|
|
301
|
+
const importPath = source.value;
|
|
302
|
+
if (!importPath || !importPath.startsWith(".") || importPath.endsWith(".js")) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const resolvedPath = path.resolve(path.dirname(context.filename), importPath);
|
|
306
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
307
|
+
context.report({
|
|
308
|
+
node: source,
|
|
309
|
+
message: "Relative imports and exports must include a file extension.",
|
|
310
|
+
fix(fixer) {
|
|
311
|
+
const fixedPath = `${importPath}.js`;
|
|
312
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
ImportDeclaration(node) {
|
|
319
|
+
checkSource(node.source);
|
|
320
|
+
},
|
|
321
|
+
ExportNamedDeclaration(node) {
|
|
322
|
+
if (node.source) {
|
|
323
|
+
checkSource(node.source);
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
ExportAllDeclaration(node) {
|
|
327
|
+
checkSource(node.source);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const requireIndexRule = {
|
|
334
|
+
meta: {
|
|
335
|
+
type: "suggestion",
|
|
336
|
+
docs: {
|
|
337
|
+
description: "Ensure directory import and export statements use index.js",
|
|
338
|
+
category: "Best Practices",
|
|
339
|
+
recommended: true
|
|
340
|
+
},
|
|
341
|
+
fixable: "code",
|
|
342
|
+
schema: []
|
|
343
|
+
},
|
|
344
|
+
create(context) {
|
|
345
|
+
function checkSource(source) {
|
|
346
|
+
const importPath = source.value;
|
|
347
|
+
const resolvedPath = path.resolve(path.dirname(context.filename), importPath);
|
|
348
|
+
const isDirectory = fs.existsSync(resolvedPath) && fs.lstatSync(resolvedPath).isDirectory();
|
|
349
|
+
if (isDirectory) {
|
|
350
|
+
context.report({
|
|
351
|
+
node: source,
|
|
352
|
+
message: "Directory imports and exports must use index.js.",
|
|
353
|
+
fix(fixer) {
|
|
354
|
+
const fixedPath = importPath.replace(/\/?$/, "/index.js");
|
|
355
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
ImportDeclaration(node) {
|
|
362
|
+
checkSource(node.source);
|
|
363
|
+
},
|
|
364
|
+
ExportNamedDeclaration(node) {
|
|
365
|
+
if (node.source) {
|
|
366
|
+
checkSource(node.source);
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
ExportAllDeclaration(node) {
|
|
370
|
+
checkSource(node.source);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const base$7 = {
|
|
377
|
+
plugins: {
|
|
378
|
+
"decent-extension": {
|
|
379
|
+
meta: {
|
|
380
|
+
name: "decent-extension",
|
|
381
|
+
version: "1.0.0"
|
|
382
|
+
},
|
|
383
|
+
rules: {
|
|
384
|
+
"require-extension": requireExtensionRule,
|
|
385
|
+
"require-index": requireIndexRule
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
rules: {
|
|
390
|
+
"decent-extension/require-extension": "error",
|
|
391
|
+
"decent-extension/require-index": "error"
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const configs$7 = {
|
|
395
|
+
base: base$7
|
|
396
|
+
};
|
|
397
|
+
|
|
286
398
|
const base$6 = {
|
|
287
399
|
settings: {
|
|
288
400
|
jsdoc: {
|
|
@@ -492,14 +604,6 @@ const base$1 = {
|
|
|
492
604
|
"@typescript-eslint/no-empty-interface": "error",
|
|
493
605
|
"@typescript-eslint/no-extra-semi": "error",
|
|
494
606
|
"@typescript-eslint/no-shadow": "error",
|
|
495
|
-
"@typescript-eslint/no-use-before-define": [
|
|
496
|
-
"error",
|
|
497
|
-
{
|
|
498
|
-
functions: true,
|
|
499
|
-
classes: true,
|
|
500
|
-
variables: true
|
|
501
|
-
}
|
|
502
|
-
],
|
|
503
607
|
"@typescript-eslint/parameter-properties": [
|
|
504
608
|
"error",
|
|
505
609
|
{
|
|
@@ -508,7 +612,8 @@ const base$1 = {
|
|
|
508
612
|
],
|
|
509
613
|
"@typescript-eslint/restrict-template-expressions": ["error", { allowNumber: true }],
|
|
510
614
|
"@typescript-eslint/return-await": "error",
|
|
511
|
-
"@typescript-eslint/sort-type-constituents": "error"
|
|
615
|
+
"@typescript-eslint/sort-type-constituents": "error",
|
|
616
|
+
"@typescript-eslint/use-unknown-in-catch-callback-variable": "off"
|
|
512
617
|
}
|
|
513
618
|
};
|
|
514
619
|
const configs$1 = {
|
|
@@ -533,7 +638,8 @@ const configs = {
|
|
|
533
638
|
base
|
|
534
639
|
};
|
|
535
640
|
|
|
536
|
-
function defaultConfig(
|
|
641
|
+
function defaultConfig(options) {
|
|
642
|
+
const enableRequireExtensionRule = options?.enableRequireExtensionRule ?? true;
|
|
537
643
|
const languageOptions = {
|
|
538
644
|
globals: {
|
|
539
645
|
...globals__default.node
|
|
@@ -541,11 +647,11 @@ function defaultConfig(parserOptions) {
|
|
|
541
647
|
parserOptions: {
|
|
542
648
|
// @ts-expect-error - This is a valid option
|
|
543
649
|
projectService: {
|
|
544
|
-
allowedDefaultProject: ["./*.js
|
|
650
|
+
allowedDefaultProject: ["./*.{js,cjs,mjs}", "./tests/**/*.{ts,js,cjs,mjs}"],
|
|
545
651
|
defaultProject: "tsconfig.json"
|
|
546
652
|
},
|
|
547
653
|
tsconfigRootDir: undefined,
|
|
548
|
-
...parserOptions
|
|
654
|
+
...options?.parserOptions
|
|
549
655
|
}
|
|
550
656
|
};
|
|
551
657
|
return [
|
|
@@ -564,13 +670,15 @@ function defaultConfig(parserOptions) {
|
|
|
564
670
|
{
|
|
565
671
|
files: ["**/*.ts", "**/*.js", "**/*.cjs", "**/*.mjs", "**/*.tsx"],
|
|
566
672
|
plugins: {
|
|
673
|
+
...configs$7.base.plugins,
|
|
567
674
|
...configs$6.base.plugins,
|
|
568
675
|
...configs$4.base.plugins,
|
|
569
676
|
...configs$2.base.plugins,
|
|
570
677
|
...configs.base.plugins
|
|
571
678
|
},
|
|
572
679
|
rules: {
|
|
573
|
-
...configs$
|
|
680
|
+
...configs$8.base.rules,
|
|
681
|
+
...enableRequireExtensionRule ? configs$7.base.rules : {},
|
|
574
682
|
...configs$6.base.rules,
|
|
575
683
|
...configs$4.base.rules,
|
|
576
684
|
...configs$2.base.rules,
|
|
@@ -582,14 +690,14 @@ function defaultConfig(parserOptions) {
|
|
|
582
690
|
languageOptions: {
|
|
583
691
|
sourceType: "script"
|
|
584
692
|
},
|
|
585
|
-
...configs$
|
|
693
|
+
...configs$8.cjsAndEsm
|
|
586
694
|
},
|
|
587
695
|
{
|
|
588
696
|
files: ["**/*.js", "**/*.cjs"],
|
|
589
697
|
languageOptions: {
|
|
590
698
|
sourceType: "script"
|
|
591
699
|
},
|
|
592
|
-
...configs$
|
|
700
|
+
...configs$8.cjs
|
|
593
701
|
},
|
|
594
702
|
{
|
|
595
703
|
files: ["**/*.ts", "**/*.tsx"],
|
|
@@ -608,7 +716,7 @@ function defaultConfig(parserOptions) {
|
|
|
608
716
|
}
|
|
609
717
|
|
|
610
718
|
exports.defaultConfig = defaultConfig;
|
|
611
|
-
exports.eslintConfigs = configs$
|
|
719
|
+
exports.eslintConfigs = configs$8;
|
|
612
720
|
exports.jsdocConfigs = configs$6;
|
|
613
721
|
exports.promiseConfigs = configs$4;
|
|
614
722
|
exports.reactConfigs = configs$3;
|
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,10 @@ declare const configs: {
|
|
|
30
30
|
base: ConfigWithExtends;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
interface DefaultConfigOptions {
|
|
34
|
+
parserOptions?: NonNullable<ConfigWithExtends['languageOptions']>['parserOptions'];
|
|
35
|
+
enableRequireExtensionRule?: boolean;
|
|
36
|
+
}
|
|
37
|
+
declare function defaultConfig(options?: DefaultConfigOptions): ConfigWithExtends[];
|
|
34
38
|
|
|
35
|
-
export { defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
|
39
|
+
export { type DefaultConfigOptions, defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
package/dist/index.d.mts
CHANGED
|
@@ -30,6 +30,10 @@ declare const configs: {
|
|
|
30
30
|
base: ConfigWithExtends;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
interface DefaultConfigOptions {
|
|
34
|
+
parserOptions?: NonNullable<ConfigWithExtends['languageOptions']>['parserOptions'];
|
|
35
|
+
enableRequireExtensionRule?: boolean;
|
|
36
|
+
}
|
|
37
|
+
declare function defaultConfig(options?: DefaultConfigOptions): ConfigWithExtends[];
|
|
34
38
|
|
|
35
|
-
export { defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
|
39
|
+
export { type DefaultConfigOptions, defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,10 @@ declare const configs: {
|
|
|
30
30
|
base: ConfigWithExtends;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
interface DefaultConfigOptions {
|
|
34
|
+
parserOptions?: NonNullable<ConfigWithExtends['languageOptions']>['parserOptions'];
|
|
35
|
+
enableRequireExtensionRule?: boolean;
|
|
36
|
+
}
|
|
37
|
+
declare function defaultConfig(options?: DefaultConfigOptions): ConfigWithExtends[];
|
|
34
38
|
|
|
35
|
-
export { defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
|
39
|
+
export { type DefaultConfigOptions, defaultConfig, configs$6 as eslintConfigs, configs$5 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,8 @@ import eslint from '@eslint/js';
|
|
|
2
2
|
import globals from 'globals';
|
|
3
3
|
import tsEslint from 'typescript-eslint';
|
|
4
4
|
import prettier from 'eslint-plugin-prettier/recommended';
|
|
5
|
+
import { existsSync, lstatSync } from 'fs';
|
|
6
|
+
import { resolve, dirname } from 'path';
|
|
5
7
|
import jsdoc from 'eslint-plugin-jsdoc';
|
|
6
8
|
import mocha from 'eslint-plugin-mocha';
|
|
7
9
|
import promise from 'eslint-plugin-promise';
|
|
@@ -12,7 +14,7 @@ import testingLibrary from 'eslint-plugin-testing-library';
|
|
|
12
14
|
import security from 'eslint-plugin-security';
|
|
13
15
|
import unicorn from 'eslint-plugin-unicorn';
|
|
14
16
|
|
|
15
|
-
const base$
|
|
17
|
+
const base$8 = {
|
|
16
18
|
rules: {
|
|
17
19
|
"array-callback-return": ["error", { allowImplicit: true }],
|
|
18
20
|
"block-scoped-var": "error",
|
|
@@ -259,12 +261,122 @@ const cjs = {
|
|
|
259
261
|
strict: ["error", "global"]
|
|
260
262
|
}
|
|
261
263
|
};
|
|
262
|
-
const configs$
|
|
263
|
-
base: base$
|
|
264
|
+
const configs$8 = {
|
|
265
|
+
base: base$8,
|
|
264
266
|
cjsAndEsm,
|
|
265
267
|
cjs
|
|
266
268
|
};
|
|
267
269
|
|
|
270
|
+
const requireExtensionRule = {
|
|
271
|
+
meta: {
|
|
272
|
+
type: "suggestion",
|
|
273
|
+
docs: {
|
|
274
|
+
description: "Ensure import and export statements include a file extension",
|
|
275
|
+
category: "Best Practices",
|
|
276
|
+
recommended: true
|
|
277
|
+
},
|
|
278
|
+
fixable: "code",
|
|
279
|
+
schema: []
|
|
280
|
+
},
|
|
281
|
+
create(context) {
|
|
282
|
+
function checkSource(source) {
|
|
283
|
+
const importPath = source.value;
|
|
284
|
+
if (!importPath || !importPath.startsWith(".") || importPath.endsWith(".js")) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const resolvedPath = resolve(dirname(context.filename), importPath);
|
|
288
|
+
if (!existsSync(resolvedPath)) {
|
|
289
|
+
context.report({
|
|
290
|
+
node: source,
|
|
291
|
+
message: "Relative imports and exports must include a file extension.",
|
|
292
|
+
fix(fixer) {
|
|
293
|
+
const fixedPath = `${importPath}.js`;
|
|
294
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
ImportDeclaration(node) {
|
|
301
|
+
checkSource(node.source);
|
|
302
|
+
},
|
|
303
|
+
ExportNamedDeclaration(node) {
|
|
304
|
+
if (node.source) {
|
|
305
|
+
checkSource(node.source);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
ExportAllDeclaration(node) {
|
|
309
|
+
checkSource(node.source);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const requireIndexRule = {
|
|
316
|
+
meta: {
|
|
317
|
+
type: "suggestion",
|
|
318
|
+
docs: {
|
|
319
|
+
description: "Ensure directory import and export statements use index.js",
|
|
320
|
+
category: "Best Practices",
|
|
321
|
+
recommended: true
|
|
322
|
+
},
|
|
323
|
+
fixable: "code",
|
|
324
|
+
schema: []
|
|
325
|
+
},
|
|
326
|
+
create(context) {
|
|
327
|
+
function checkSource(source) {
|
|
328
|
+
const importPath = source.value;
|
|
329
|
+
const resolvedPath = resolve(dirname(context.filename), importPath);
|
|
330
|
+
const isDirectory = existsSync(resolvedPath) && lstatSync(resolvedPath).isDirectory();
|
|
331
|
+
if (isDirectory) {
|
|
332
|
+
context.report({
|
|
333
|
+
node: source,
|
|
334
|
+
message: "Directory imports and exports must use index.js.",
|
|
335
|
+
fix(fixer) {
|
|
336
|
+
const fixedPath = importPath.replace(/\/?$/, "/index.js");
|
|
337
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
ImportDeclaration(node) {
|
|
344
|
+
checkSource(node.source);
|
|
345
|
+
},
|
|
346
|
+
ExportNamedDeclaration(node) {
|
|
347
|
+
if (node.source) {
|
|
348
|
+
checkSource(node.source);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
ExportAllDeclaration(node) {
|
|
352
|
+
checkSource(node.source);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const base$7 = {
|
|
359
|
+
plugins: {
|
|
360
|
+
"decent-extension": {
|
|
361
|
+
meta: {
|
|
362
|
+
name: "decent-extension",
|
|
363
|
+
version: "1.0.0"
|
|
364
|
+
},
|
|
365
|
+
rules: {
|
|
366
|
+
"require-extension": requireExtensionRule,
|
|
367
|
+
"require-index": requireIndexRule
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
rules: {
|
|
372
|
+
"decent-extension/require-extension": "error",
|
|
373
|
+
"decent-extension/require-index": "error"
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
const configs$7 = {
|
|
377
|
+
base: base$7
|
|
378
|
+
};
|
|
379
|
+
|
|
268
380
|
const base$6 = {
|
|
269
381
|
settings: {
|
|
270
382
|
jsdoc: {
|
|
@@ -474,14 +586,6 @@ const base$1 = {
|
|
|
474
586
|
"@typescript-eslint/no-empty-interface": "error",
|
|
475
587
|
"@typescript-eslint/no-extra-semi": "error",
|
|
476
588
|
"@typescript-eslint/no-shadow": "error",
|
|
477
|
-
"@typescript-eslint/no-use-before-define": [
|
|
478
|
-
"error",
|
|
479
|
-
{
|
|
480
|
-
functions: true,
|
|
481
|
-
classes: true,
|
|
482
|
-
variables: true
|
|
483
|
-
}
|
|
484
|
-
],
|
|
485
589
|
"@typescript-eslint/parameter-properties": [
|
|
486
590
|
"error",
|
|
487
591
|
{
|
|
@@ -490,7 +594,8 @@ const base$1 = {
|
|
|
490
594
|
],
|
|
491
595
|
"@typescript-eslint/restrict-template-expressions": ["error", { allowNumber: true }],
|
|
492
596
|
"@typescript-eslint/return-await": "error",
|
|
493
|
-
"@typescript-eslint/sort-type-constituents": "error"
|
|
597
|
+
"@typescript-eslint/sort-type-constituents": "error",
|
|
598
|
+
"@typescript-eslint/use-unknown-in-catch-callback-variable": "off"
|
|
494
599
|
}
|
|
495
600
|
};
|
|
496
601
|
const configs$1 = {
|
|
@@ -515,7 +620,8 @@ const configs = {
|
|
|
515
620
|
base
|
|
516
621
|
};
|
|
517
622
|
|
|
518
|
-
function defaultConfig(
|
|
623
|
+
function defaultConfig(options) {
|
|
624
|
+
const enableRequireExtensionRule = options?.enableRequireExtensionRule ?? true;
|
|
519
625
|
const languageOptions = {
|
|
520
626
|
globals: {
|
|
521
627
|
...globals.node
|
|
@@ -523,11 +629,11 @@ function defaultConfig(parserOptions) {
|
|
|
523
629
|
parserOptions: {
|
|
524
630
|
// @ts-expect-error - This is a valid option
|
|
525
631
|
projectService: {
|
|
526
|
-
allowedDefaultProject: ["./*.js
|
|
632
|
+
allowedDefaultProject: ["./*.{js,cjs,mjs}", "./tests/**/*.{ts,js,cjs,mjs}"],
|
|
527
633
|
defaultProject: "tsconfig.json"
|
|
528
634
|
},
|
|
529
635
|
tsconfigRootDir: import.meta.dirname,
|
|
530
|
-
...parserOptions
|
|
636
|
+
...options?.parserOptions
|
|
531
637
|
}
|
|
532
638
|
};
|
|
533
639
|
return [
|
|
@@ -546,13 +652,15 @@ function defaultConfig(parserOptions) {
|
|
|
546
652
|
{
|
|
547
653
|
files: ["**/*.ts", "**/*.js", "**/*.cjs", "**/*.mjs", "**/*.tsx"],
|
|
548
654
|
plugins: {
|
|
655
|
+
...configs$7.base.plugins,
|
|
549
656
|
...configs$6.base.plugins,
|
|
550
657
|
...configs$4.base.plugins,
|
|
551
658
|
...configs$2.base.plugins,
|
|
552
659
|
...configs.base.plugins
|
|
553
660
|
},
|
|
554
661
|
rules: {
|
|
555
|
-
...configs$
|
|
662
|
+
...configs$8.base.rules,
|
|
663
|
+
...enableRequireExtensionRule ? configs$7.base.rules : {},
|
|
556
664
|
...configs$6.base.rules,
|
|
557
665
|
...configs$4.base.rules,
|
|
558
666
|
...configs$2.base.rules,
|
|
@@ -564,14 +672,14 @@ function defaultConfig(parserOptions) {
|
|
|
564
672
|
languageOptions: {
|
|
565
673
|
sourceType: "script"
|
|
566
674
|
},
|
|
567
|
-
...configs$
|
|
675
|
+
...configs$8.cjsAndEsm
|
|
568
676
|
},
|
|
569
677
|
{
|
|
570
678
|
files: ["**/*.js", "**/*.cjs"],
|
|
571
679
|
languageOptions: {
|
|
572
680
|
sourceType: "script"
|
|
573
681
|
},
|
|
574
|
-
...configs$
|
|
682
|
+
...configs$8.cjs
|
|
575
683
|
},
|
|
576
684
|
{
|
|
577
685
|
files: ["**/*.ts", "**/*.tsx"],
|
|
@@ -589,4 +697,4 @@ function defaultConfig(parserOptions) {
|
|
|
589
697
|
];
|
|
590
698
|
}
|
|
591
699
|
|
|
592
|
-
export { defaultConfig, configs$
|
|
700
|
+
export { defaultConfig, configs$8 as eslintConfigs, configs$6 as jsdocConfigs, configs$4 as promiseConfigs, configs$3 as reactConfigs, configs$2 as securityConfigs, configs$1 as typescriptEslintConfigs, configs as unicornConfigs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-config-decent",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A decent ESLint configuration",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -72,33 +72,33 @@
|
|
|
72
72
|
"node": ">=20.11.0"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@eslint/js": "^9.
|
|
75
|
+
"@eslint/js": "^9.6.0",
|
|
76
76
|
"eslint-config-prettier": "^9.1.0",
|
|
77
|
-
"eslint-plugin-jsdoc": "^48.
|
|
77
|
+
"eslint-plugin-jsdoc": "^48.5.0",
|
|
78
78
|
"eslint-plugin-mocha": "^10.4.3",
|
|
79
79
|
"eslint-plugin-prettier": "^5.1.3",
|
|
80
|
-
"eslint-plugin-promise": "^6.
|
|
81
|
-
"eslint-plugin-jsx-a11y": "^6.
|
|
82
|
-
"eslint-plugin-react": "^7.34.
|
|
80
|
+
"eslint-plugin-promise": "^6.4.0",
|
|
81
|
+
"eslint-plugin-jsx-a11y": "^6.9.0",
|
|
82
|
+
"eslint-plugin-react": "^7.34.3",
|
|
83
83
|
"eslint-plugin-react-hooks": "^4.6.2",
|
|
84
|
-
"eslint-plugin-security": "^3.0.
|
|
84
|
+
"eslint-plugin-security": "^3.0.1",
|
|
85
85
|
"eslint-plugin-testing-library": "^6.2.2",
|
|
86
|
-
"eslint-plugin-unicorn": "^
|
|
87
|
-
"globals": "^15.
|
|
88
|
-
"typescript-eslint": "
|
|
86
|
+
"eslint-plugin-unicorn": "^54.0.0",
|
|
87
|
+
"globals": "^15.7.0",
|
|
88
|
+
"typescript-eslint": "8.0.0-alpha.29"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@swc/core": "1.
|
|
91
|
+
"@swc/core": "1.6.6",
|
|
92
92
|
"@types/node": ">=20",
|
|
93
|
-
"eslint": "^9.
|
|
93
|
+
"eslint": "^9.6.0",
|
|
94
94
|
"husky": "^9.0.11",
|
|
95
|
-
"lint-staged": "^15.2.
|
|
95
|
+
"lint-staged": "^15.2.7",
|
|
96
96
|
"markdownlint-cli": "^0.41.0",
|
|
97
97
|
"npm-run-all": "^4.1.5",
|
|
98
98
|
"pinst": "^3.0.0",
|
|
99
|
-
"prettier": "^3.3.
|
|
99
|
+
"prettier": "^3.3.2",
|
|
100
100
|
"rimraf": "^5.0.7",
|
|
101
|
-
"typescript": "^5.
|
|
101
|
+
"typescript": "^5.5.3",
|
|
102
102
|
"unbuild": "2.0.0"
|
|
103
103
|
},
|
|
104
104
|
"overrides": {
|
package/src/extension.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConfigWithExtends } from 'typescript-eslint';
|
|
2
|
+
import { requireExtensionRule } from './rules/requireExtensionRule.js';
|
|
3
|
+
import { requireIndexRule } from './rules/requireIndexRule.js';
|
|
4
|
+
|
|
5
|
+
const base: ConfigWithExtends = {
|
|
6
|
+
plugins: {
|
|
7
|
+
'decent-extension': {
|
|
8
|
+
meta: {
|
|
9
|
+
name: 'decent-extension',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
},
|
|
12
|
+
rules: {
|
|
13
|
+
'require-extension': requireExtensionRule,
|
|
14
|
+
'require-index': requireIndexRule,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
rules: {
|
|
19
|
+
'decent-extension/require-extension': 'error',
|
|
20
|
+
'decent-extension/require-index': 'error',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const configs = {
|
|
25
|
+
base,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
configs,
|
|
30
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import globals from 'globals';
|
|
|
3
3
|
import tsEslint, { type ConfigWithExtends } from 'typescript-eslint';
|
|
4
4
|
import prettier from 'eslint-plugin-prettier/recommended';
|
|
5
5
|
import { configs as eslintConfigs } from './eslint.js';
|
|
6
|
+
import { configs as extensionConfigs } from './extension.js';
|
|
6
7
|
import { configs as jsdocConfigs } from './jsdoc.js';
|
|
7
8
|
import { configs as mochaConfigs } from './mocha.js';
|
|
8
9
|
import { configs as promiseConfigs } from './promise.js';
|
|
@@ -21,7 +22,13 @@ export {
|
|
|
21
22
|
unicornConfigs,
|
|
22
23
|
};
|
|
23
24
|
|
|
24
|
-
export
|
|
25
|
+
export interface DefaultConfigOptions {
|
|
26
|
+
parserOptions?: NonNullable<ConfigWithExtends['languageOptions']>['parserOptions'];
|
|
27
|
+
enableRequireExtensionRule?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function defaultConfig(options?: DefaultConfigOptions): ConfigWithExtends[] {
|
|
31
|
+
const enableRequireExtensionRule = options?.enableRequireExtensionRule ?? true;
|
|
25
32
|
const languageOptions: ConfigWithExtends['languageOptions'] = {
|
|
26
33
|
globals: {
|
|
27
34
|
...globals.node,
|
|
@@ -29,11 +36,11 @@ export function defaultConfig(parserOptions?: NonNullable<ConfigWithExtends['lan
|
|
|
29
36
|
parserOptions: {
|
|
30
37
|
// @ts-expect-error - This is a valid option
|
|
31
38
|
projectService: {
|
|
32
|
-
allowedDefaultProject: ['./*.js
|
|
39
|
+
allowedDefaultProject: ['./*.{js,cjs,mjs}', './tests/**/*.{ts,js,cjs,mjs}'],
|
|
33
40
|
defaultProject: 'tsconfig.json',
|
|
34
41
|
},
|
|
35
42
|
tsconfigRootDir: import.meta.dirname,
|
|
36
|
-
...parserOptions,
|
|
43
|
+
...options?.parserOptions,
|
|
37
44
|
},
|
|
38
45
|
};
|
|
39
46
|
|
|
@@ -53,6 +60,7 @@ export function defaultConfig(parserOptions?: NonNullable<ConfigWithExtends['lan
|
|
|
53
60
|
{
|
|
54
61
|
files: ['**/*.ts', '**/*.js', '**/*.cjs', '**/*.mjs', '**/*.tsx'],
|
|
55
62
|
plugins: {
|
|
63
|
+
...extensionConfigs.base.plugins,
|
|
56
64
|
...jsdocConfigs.base.plugins,
|
|
57
65
|
...promiseConfigs.base.plugins,
|
|
58
66
|
...securityConfigs.base.plugins,
|
|
@@ -60,6 +68,7 @@ export function defaultConfig(parserOptions?: NonNullable<ConfigWithExtends['lan
|
|
|
60
68
|
},
|
|
61
69
|
rules: {
|
|
62
70
|
...eslintConfigs.base.rules,
|
|
71
|
+
...(enableRequireExtensionRule ? extensionConfigs.base.rules : {}),
|
|
63
72
|
...jsdocConfigs.base.rules,
|
|
64
73
|
...promiseConfigs.base.rules,
|
|
65
74
|
...securityConfigs.base.rules,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import type { Rule } from 'eslint';
|
|
4
|
+
|
|
5
|
+
export const requireExtensionRule: Rule.RuleModule = {
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'suggestion',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Ensure import and export statements include a file extension',
|
|
10
|
+
category: 'Best Practices',
|
|
11
|
+
recommended: true,
|
|
12
|
+
},
|
|
13
|
+
fixable: 'code',
|
|
14
|
+
schema: [],
|
|
15
|
+
},
|
|
16
|
+
create(context: Rule.RuleContext) {
|
|
17
|
+
function checkSource(source: Parameters<NonNullable<Rule.NodeListener['ImportDeclaration']>>[0]['source']): void {
|
|
18
|
+
const importPath = source.value as string;
|
|
19
|
+
|
|
20
|
+
if (!importPath || !importPath.startsWith('.') || importPath.endsWith('.js')) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const resolvedPath = resolve(dirname(context.filename), importPath);
|
|
25
|
+
|
|
26
|
+
// If the import/export path doesn't end with a file extension, report an error
|
|
27
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
28
|
+
if (!existsSync(resolvedPath)) {
|
|
29
|
+
context.report({
|
|
30
|
+
node: source,
|
|
31
|
+
message: 'Relative imports and exports must include a file extension.',
|
|
32
|
+
fix(fixer) {
|
|
33
|
+
const fixedPath = `${importPath}.js`;
|
|
34
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
ImportDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ImportDeclaration']>>[0]): void {
|
|
42
|
+
checkSource(node.source);
|
|
43
|
+
},
|
|
44
|
+
ExportNamedDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ExportNamedDeclaration']>>[0]): void {
|
|
45
|
+
if (node.source) {
|
|
46
|
+
checkSource(node.source);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
ExportAllDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ExportAllDeclaration']>>[0]): void {
|
|
50
|
+
checkSource(node.source);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync, lstatSync } from 'fs';
|
|
2
|
+
import { resolve, dirname } from 'path';
|
|
3
|
+
import type { Rule } from 'eslint';
|
|
4
|
+
|
|
5
|
+
export const requireIndexRule: Rule.RuleModule = {
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'suggestion',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Ensure directory import and export statements use index.js',
|
|
10
|
+
category: 'Best Practices',
|
|
11
|
+
recommended: true,
|
|
12
|
+
},
|
|
13
|
+
fixable: 'code',
|
|
14
|
+
schema: [],
|
|
15
|
+
},
|
|
16
|
+
create(context: Rule.RuleContext) {
|
|
17
|
+
function checkSource(source: Parameters<NonNullable<Rule.NodeListener['ImportDeclaration']>>[0]['source']): void {
|
|
18
|
+
const importPath = source.value as string;
|
|
19
|
+
|
|
20
|
+
// Resolve the path relative to the file being linted
|
|
21
|
+
const resolvedPath = resolve(dirname(context.filename), importPath);
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
24
|
+
const isDirectory = existsSync(resolvedPath) && lstatSync(resolvedPath).isDirectory();
|
|
25
|
+
if (isDirectory) {
|
|
26
|
+
context.report({
|
|
27
|
+
node: source,
|
|
28
|
+
message: 'Directory imports and exports must use index.js.',
|
|
29
|
+
fix(fixer) {
|
|
30
|
+
const fixedPath = importPath.replace(/\/?$/, '/index.js');
|
|
31
|
+
return fixer.replaceText(source, `'${fixedPath}'`);
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
ImportDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ImportDeclaration']>>[0]): void {
|
|
39
|
+
checkSource(node.source);
|
|
40
|
+
},
|
|
41
|
+
ExportNamedDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ExportNamedDeclaration']>>[0]): void {
|
|
42
|
+
if (node.source) {
|
|
43
|
+
checkSource(node.source);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
ExportAllDeclaration(node: Parameters<NonNullable<Rule.NodeListener['ExportAllDeclaration']>>[0]): void {
|
|
47
|
+
checkSource(node.source);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
package/src/typescriptEslint.ts
CHANGED
|
@@ -50,14 +50,6 @@ const base: ConfigWithExtends = {
|
|
|
50
50
|
'@typescript-eslint/no-empty-interface': 'error',
|
|
51
51
|
'@typescript-eslint/no-extra-semi': 'error',
|
|
52
52
|
'@typescript-eslint/no-shadow': 'error',
|
|
53
|
-
'@typescript-eslint/no-use-before-define': [
|
|
54
|
-
'error',
|
|
55
|
-
{
|
|
56
|
-
functions: true,
|
|
57
|
-
classes: true,
|
|
58
|
-
variables: true,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
53
|
'@typescript-eslint/parameter-properties': [
|
|
62
54
|
'error',
|
|
63
55
|
{
|
|
@@ -67,6 +59,7 @@ const base: ConfigWithExtends = {
|
|
|
67
59
|
'@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true }],
|
|
68
60
|
'@typescript-eslint/return-await': 'error',
|
|
69
61
|
'@typescript-eslint/sort-type-constituents': 'error',
|
|
62
|
+
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
|
70
63
|
},
|
|
71
64
|
};
|
|
72
65
|
|