@eslint-umbrella/presets 1.1.0 → 1.1.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/CHANGELOG.md +6 -0
- package/package.json +12 -9
- package/src/base-typechecked.js +148 -27
- package/src/base.js +119 -29
- package/src/express.js +46 -7
- package/src/index.js +39 -9
- package/src/nest-typechecked.js +49 -0
- package/src/nest.js +37 -8
- package/src/next.js +33 -1
- package/src/react-native.js +32 -4
- package/src/react-pdf.js +32 -4
- package/src/react.js +39 -12
- package/src/test.js +0 -11
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-umbrella/presets",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pinned ESLint flat config presets for React, Next.js, Express, NestJS, React Native, React-PDF, and base/test",
|
|
6
6
|
"publishConfig": {
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
11
|
"./base": "./src/base.js",
|
|
12
|
+
"./base-typechecked": "./src/base-typechecked.js",
|
|
12
13
|
"./react": "./src/react.js",
|
|
13
14
|
"./next": "./src/next.js",
|
|
14
15
|
"./express": "./src/express.js",
|
|
15
16
|
"./nest": "./src/nest.js",
|
|
16
17
|
"./react-native": "./src/react-native.js",
|
|
17
|
-
"./react-pdf": "./src/react-pdf.js"
|
|
18
|
-
"./test": "./src/test.js",
|
|
19
|
-
"./base-typechecked": "./src/base-typechecked.js"
|
|
18
|
+
"./react-pdf": "./src/react-pdf.js"
|
|
20
19
|
},
|
|
20
|
+
"sideEffects": false,
|
|
21
21
|
"scripts": {
|
|
22
22
|
"lint": "eslint .",
|
|
23
23
|
"lint:fix": "eslint . --fix",
|
|
@@ -28,21 +28,24 @@
|
|
|
28
28
|
},
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@eslint/js": "9.11.0",
|
|
32
31
|
"@next/eslint-plugin-next": "14.2.14",
|
|
33
|
-
"eslint": "9.11.0",
|
|
34
32
|
"eslint-plugin-import": "2.31.0",
|
|
35
|
-
"eslint-plugin-
|
|
33
|
+
"eslint-plugin-react": "7.37.1",
|
|
36
34
|
"eslint-plugin-react-hooks": "5.1.0",
|
|
37
35
|
"eslint-plugin-react-refresh": "0.4.12",
|
|
38
36
|
"eslint-plugin-security": "3.0.1",
|
|
39
37
|
"eslint-plugin-unused-imports": "4.1.4",
|
|
40
|
-
"
|
|
38
|
+
"globals": "^16.4.0",
|
|
41
39
|
"typescript": "5.6.3",
|
|
42
40
|
"typescript-eslint": "8.8.1"
|
|
43
41
|
},
|
|
44
42
|
"devDependencies": {
|
|
45
43
|
"@changesets/cli": "^2.29.7",
|
|
46
|
-
"eslint
|
|
44
|
+
"@eslint/js": "^9.35.0",
|
|
45
|
+
"@stylistic/eslint-plugin": "^5.3.1",
|
|
46
|
+
"eslint": "^9.35.0",
|
|
47
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
48
|
+
"eslint-plugin-react-native": "^5.0.0",
|
|
49
|
+
"nodemon": "^3.1.10"
|
|
47
50
|
}
|
|
48
51
|
}
|
package/src/base-typechecked.js
CHANGED
|
@@ -1,52 +1,173 @@
|
|
|
1
|
-
// src/base-typechecked.js
|
|
2
1
|
import js from "@eslint/js";
|
|
3
2
|
import importPlugin from "eslint-plugin-import";
|
|
3
|
+
import promise from "eslint-plugin-promise";
|
|
4
4
|
import unused from "eslint-plugin-unused-imports";
|
|
5
|
-
import tseslint from "typescript-eslint";
|
|
5
|
+
import tseslint from "typescript-eslint";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Type-aware base preset (requires
|
|
8
|
+
* Type-aware base preset (requires tsconfig).
|
|
9
9
|
* @param {string[]} project - tsconfig paths (default: ["tsconfig.json"])
|
|
10
10
|
* @returns {import('eslint').Linter.FlatConfig[]}
|
|
11
11
|
*/
|
|
12
12
|
export default function baseTypeChecked(project = ["tsconfig.json"]) {
|
|
13
13
|
return tseslint.config(
|
|
14
|
-
//
|
|
15
|
-
{ ignores: [
|
|
14
|
+
// 0) general ignores
|
|
15
|
+
{ ignores: [
|
|
16
|
+
// package managers
|
|
17
|
+
"node_modules",
|
|
18
|
+
".pnpm-store", ".turbo", ".npm", ".yarn", ".yarn/*", "pnpm-lock.yaml", "yarn.lock",
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
// builds / caches / coverage
|
|
21
|
+
"dist", "build", "coverage", ".cache", ".eslintcache",
|
|
22
|
+
|
|
23
|
+
// env / local
|
|
24
|
+
".env", ".env.*", "*.local.*",
|
|
25
|
+
|
|
26
|
+
// logs & misc
|
|
27
|
+
"*.log", "logs", "tmp", "temp",
|
|
28
|
+
|
|
29
|
+
// assets & generated
|
|
30
|
+
"**/*.min.*", "**/*.map", "**/*.snap", "public", "static", "storybook-static",
|
|
31
|
+
|
|
32
|
+
// tests/artifacts
|
|
33
|
+
"cypress/videos", "cypress/screenshots", "playwright-report", "test-results",
|
|
34
|
+
|
|
35
|
+
// monorepo tool outputs
|
|
36
|
+
".nx", ".rush", ".changeset/*-changeset/*.md", // keep `.changeset/*.md` itself included
|
|
37
|
+
|
|
38
|
+
// IDE
|
|
39
|
+
".idea", ".vscode/*.tsbuildinfo"
|
|
40
|
+
] },
|
|
41
|
+
|
|
42
|
+
// 1) core JS recommendations
|
|
18
43
|
js.configs.recommended,
|
|
19
44
|
|
|
20
|
-
//
|
|
45
|
+
// 2) TS type-aware recommendations
|
|
21
46
|
...tseslint.configs.recommendedTypeChecked,
|
|
22
47
|
|
|
23
|
-
//
|
|
48
|
+
// 3) opinionated, broadly accepted best practices
|
|
24
49
|
{
|
|
25
|
-
plugins: {
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"import/order": ["warn", {
|
|
31
|
-
alphabetize: { order: "asc", caseInsensitive: true },
|
|
32
|
-
"newlines-between": "always"
|
|
33
|
-
}],
|
|
34
|
-
|
|
35
|
-
"unused-imports/no-unused-imports": "warn",
|
|
36
|
-
"unused-imports/no-unused-vars": ["warn", {
|
|
37
|
-
vars: "all",
|
|
38
|
-
varsIgnorePattern: "^_",
|
|
39
|
-
args: "after-used",
|
|
40
|
-
argsIgnorePattern: "^_"
|
|
41
|
-
}]
|
|
50
|
+
plugins: {
|
|
51
|
+
import: importPlugin,
|
|
52
|
+
"unused-imports": unused,
|
|
53
|
+
promise
|
|
42
54
|
},
|
|
55
|
+
|
|
43
56
|
languageOptions: {
|
|
44
57
|
ecmaVersion: "latest",
|
|
45
58
|
sourceType: "module",
|
|
46
59
|
parserOptions: {
|
|
47
|
-
project
|
|
48
|
-
// tsconfigRootDir: new URL(".", import.meta.url) // usually
|
|
60
|
+
project
|
|
61
|
+
// tsconfigRootDir: new URL(".", import.meta.url) // usually not needed
|
|
49
62
|
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
rules: {
|
|
66
|
+
/* ---------- General safety / clarity ---------- */
|
|
67
|
+
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
68
|
+
"no-debugger": "warn",
|
|
69
|
+
eqeqeq: ["error", "always", { null: "ignore" }],
|
|
70
|
+
"no-var": "error",
|
|
71
|
+
"prefer-const": ["error", { destructuring: "all" }],
|
|
72
|
+
"object-shorthand": ["error", "always"],
|
|
73
|
+
"prefer-template": "warn",
|
|
74
|
+
"no-implicit-coercion": "warn",
|
|
75
|
+
"no-restricted-syntax": [
|
|
76
|
+
"error",
|
|
77
|
+
{
|
|
78
|
+
selector: "ForInStatement",
|
|
79
|
+
message:
|
|
80
|
+
"for..in iterates over prototype chain; use Object.keys()/Object.entries() or for..of."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
selector: "LabeledStatement",
|
|
84
|
+
message: "Labels make code confusing and hard to maintain."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
selector: "WithStatement",
|
|
88
|
+
message: "`with` is disallowed in strict mode."
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
|
|
92
|
+
/* ---------- Import hygiene ---------- */
|
|
93
|
+
"import/order": [
|
|
94
|
+
"warn",
|
|
95
|
+
{
|
|
96
|
+
groups: [
|
|
97
|
+
"builtin",
|
|
98
|
+
"external",
|
|
99
|
+
"internal",
|
|
100
|
+
["parent", "sibling", "index"],
|
|
101
|
+
"type",
|
|
102
|
+
"object",
|
|
103
|
+
"unknown"
|
|
104
|
+
],
|
|
105
|
+
alphabetize: { order: "asc", caseInsensitive: true },
|
|
106
|
+
"newlines-between": "always"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
// Avoid noisy false-positives without a TS resolver:
|
|
110
|
+
// "import/no-unresolved": "off",
|
|
111
|
+
|
|
112
|
+
/* ---------- Unused code ---------- */
|
|
113
|
+
"unused-imports/no-unused-imports": "warn",
|
|
114
|
+
"unused-imports/no-unused-vars": [
|
|
115
|
+
"warn",
|
|
116
|
+
{
|
|
117
|
+
vars: "all",
|
|
118
|
+
varsIgnorePattern: "^_",
|
|
119
|
+
args: "after-used",
|
|
120
|
+
argsIgnorePattern: "^_",
|
|
121
|
+
ignoreRestSiblings: true
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
|
|
125
|
+
/* ---------- Promise discipline ---------- */
|
|
126
|
+
"promise/no-return-wrap": "error",
|
|
127
|
+
"promise/no-new-statics": "error",
|
|
128
|
+
"promise/no-nesting": "warn",
|
|
129
|
+
"promise/no-promise-in-callback": "warn",
|
|
130
|
+
"promise/no-callback-in-promise": "warn",
|
|
131
|
+
"promise/valid-params": "error",
|
|
132
|
+
|
|
133
|
+
/* ---------- TypeScript-specific best practices ---------- */
|
|
134
|
+
// Prefer `import type` for types (tree-shaking & clarity)
|
|
135
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
136
|
+
"error",
|
|
137
|
+
{ prefer: "type-imports", disallowTypeAnnotations: false, fixStyle: "inline-type-imports" }
|
|
138
|
+
],
|
|
139
|
+
// Common foot-guns
|
|
140
|
+
"@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }],
|
|
141
|
+
"@typescript-eslint/no-misused-promises": [
|
|
142
|
+
"error",
|
|
143
|
+
{ checksVoidReturn: { attributes: false } }
|
|
144
|
+
],
|
|
145
|
+
"@typescript-eslint/no-unnecessary-condition": [
|
|
146
|
+
"warn",
|
|
147
|
+
{ allowConstantLoopConditions: true }
|
|
148
|
+
],
|
|
149
|
+
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
|
|
150
|
+
"@typescript-eslint/prefer-nullish-coalescing": [
|
|
151
|
+
"warn",
|
|
152
|
+
{
|
|
153
|
+
ignoreConditionalTests: true,
|
|
154
|
+
ignoreMixedLogicalExpressions: true
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"@typescript-eslint/prefer-optional-chain": "warn",
|
|
158
|
+
"@typescript-eslint/consistent-type-definitions": ["warn", "type"],
|
|
159
|
+
"@typescript-eslint/method-signature-style": ["warn", "property"],
|
|
160
|
+
// Allow top-level void for fire-and-forget promises
|
|
161
|
+
"@typescript-eslint/no-confusing-void-expression": [
|
|
162
|
+
"error",
|
|
163
|
+
{ ignoreVoidOperator: true, ignoreArrowShorthand: true }
|
|
164
|
+
],
|
|
165
|
+
// Avoid `async` without `await`
|
|
166
|
+
"@typescript-eslint/require-await": "error",
|
|
167
|
+
|
|
168
|
+
/* ---------- Async/await clarity ---------- */
|
|
169
|
+
"no-return-await": "error",
|
|
170
|
+
"no-await-in-loop": "warn"
|
|
50
171
|
}
|
|
51
172
|
}
|
|
52
173
|
);
|
package/src/base.js
CHANGED
|
@@ -1,53 +1,143 @@
|
|
|
1
|
+
// src/base.js
|
|
1
2
|
import js from "@eslint/js";
|
|
3
|
+
import stylistic from "@stylistic/eslint-plugin";
|
|
2
4
|
import importPlugin from "eslint-plugin-import";
|
|
5
|
+
import promise from "eslint-plugin-promise";
|
|
3
6
|
import unused from "eslint-plugin-unused-imports";
|
|
4
|
-
import
|
|
7
|
+
import globals from "globals";
|
|
8
|
+
import tseslint from "typescript-eslint"; // just the config composer
|
|
5
9
|
|
|
6
10
|
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
7
11
|
export default function base() {
|
|
8
12
|
return tseslint.config(
|
|
9
|
-
|
|
13
|
+
/* 0) ignores */
|
|
14
|
+
{
|
|
15
|
+
ignores: [
|
|
16
|
+
// package managers
|
|
17
|
+
"node_modules",
|
|
18
|
+
".pnpm-store", ".turbo", ".npm", ".yarn", ".yarn/*", "pnpm-lock.yaml", "yarn.lock",
|
|
19
|
+
|
|
20
|
+
// builds / caches / coverage
|
|
21
|
+
"dist", "build", "coverage", ".cache", ".eslintcache",
|
|
22
|
+
|
|
23
|
+
// env / local
|
|
24
|
+
".env", ".env.*", "*.local.*",
|
|
25
|
+
|
|
26
|
+
// logs & misc
|
|
27
|
+
"*.log", "logs", "tmp", "temp",
|
|
28
|
+
|
|
29
|
+
// assets & generated
|
|
30
|
+
"**/*.min.*", "**/*.map", "**/*.snap", "public", "static", "storybook-static",
|
|
31
|
+
|
|
32
|
+
// tests/artifacts
|
|
33
|
+
"cypress/videos", "cypress/screenshots", "playwright-report", "test-results",
|
|
34
|
+
|
|
35
|
+
// monorepo tool outputs
|
|
36
|
+
".nx", ".rush", ".changeset/*-changeset/*.md",
|
|
37
|
+
|
|
38
|
+
// IDE
|
|
39
|
+
".idea", ".vscode/*.tsbuildinfo",
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/* 1) core JS recommendations */
|
|
10
44
|
js.configs.recommended,
|
|
11
|
-
|
|
45
|
+
|
|
46
|
+
/* 2) extras */
|
|
12
47
|
{
|
|
13
|
-
plugins: {
|
|
48
|
+
plugins: {
|
|
49
|
+
import: importPlugin,
|
|
50
|
+
"unused-imports": unused,
|
|
51
|
+
promise,
|
|
52
|
+
"@stylistic": stylistic,
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
languageOptions: {
|
|
56
|
+
ecmaVersion: "latest",
|
|
57
|
+
sourceType: "module",
|
|
58
|
+
globals: {
|
|
59
|
+
...globals.es2024,
|
|
60
|
+
...globals.node,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
|
|
14
64
|
rules: {
|
|
65
|
+
/* ---------- General safety / clarity ---------- */
|
|
15
66
|
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
16
67
|
"no-debugger": "warn",
|
|
68
|
+
eqeqeq: ["error", "always", { null: "ignore" }],
|
|
69
|
+
"no-var": "error",
|
|
70
|
+
"prefer-const": ["error", { destructuring: "all" }],
|
|
71
|
+
"object-shorthand": ["error", "always"],
|
|
72
|
+
"prefer-template": "warn",
|
|
73
|
+
"no-implicit-coercion": "warn",
|
|
74
|
+
curly: ["error", "multi-line", "consistent"],
|
|
75
|
+
"no-use-before-define": ["error", { functions: false, classes: true, variables: false }],
|
|
76
|
+
|
|
77
|
+
"@stylistic/max-len": ["warn", {
|
|
78
|
+
code: 120,
|
|
79
|
+
tabWidth: 4,
|
|
80
|
+
ignoreUrls: true,
|
|
81
|
+
ignoreStrings: true,
|
|
82
|
+
ignoreTemplateLiterals: true,
|
|
83
|
+
ignoreComments: true,
|
|
84
|
+
}],
|
|
85
|
+
|
|
86
|
+
/* ---------- Import hygiene ---------- */
|
|
17
87
|
"import/order": ["warn", {
|
|
88
|
+
groups: [
|
|
89
|
+
"builtin",
|
|
90
|
+
"external",
|
|
91
|
+
"internal",
|
|
92
|
+
["parent", "sibling", "index"],
|
|
93
|
+
"type",
|
|
94
|
+
"object",
|
|
95
|
+
"unknown",
|
|
96
|
+
],
|
|
18
97
|
alphabetize: { order: "asc", caseInsensitive: true },
|
|
19
|
-
"newlines-between": "always"
|
|
98
|
+
"newlines-between": "always",
|
|
20
99
|
}],
|
|
21
|
-
"
|
|
22
|
-
|
|
100
|
+
// "import/no-unresolved": "off", // enable if you add a resolver
|
|
101
|
+
|
|
102
|
+
/* ---------- Unused code ---------- */
|
|
103
|
+
"no-unused-vars": "off",
|
|
104
|
+
"unused-imports/no-unused-imports": "error",
|
|
105
|
+
"unused-imports/no-unused-vars": ["error", {
|
|
23
106
|
vars: "all",
|
|
24
107
|
varsIgnorePattern: "^_",
|
|
25
108
|
args: "after-used",
|
|
26
|
-
argsIgnorePattern: "^_"
|
|
27
|
-
|
|
28
|
-
"indent": ["warn", "tab", {
|
|
29
|
-
"SwitchCase": 1,
|
|
30
|
-
"VariableDeclarator": 1,
|
|
31
|
-
"outerIIFEBody": 1,
|
|
32
|
-
"MemberExpression": 1,
|
|
33
|
-
"FunctionDeclaration": { "body": 1, "parameters": 1 },
|
|
34
|
-
"FunctionExpression": { "body": 1, "parameters": 1 },
|
|
35
|
-
"CallExpression": { "arguments": 1 },
|
|
36
|
-
"ArrayExpression": 1,
|
|
37
|
-
"ObjectExpression": 1,
|
|
38
|
-
"ImportDeclaration": 1,
|
|
39
|
-
"flatTernaryExpressions": false,
|
|
40
|
-
"ignoreComments": false
|
|
109
|
+
argsIgnorePattern: "^_",
|
|
110
|
+
ignoreRestSiblings: true,
|
|
41
111
|
}],
|
|
42
|
-
"no-mixed-spaces-and-tabs": ["warn", "smart-tabs"], // avoid mixing, allow smart alignment
|
|
43
|
-
"no-tabs": "off", // make sure tabs are allowed
|
|
44
112
|
|
|
113
|
+
/* ---------- Promise discipline ---------- */
|
|
114
|
+
"promise/no-return-wrap": "error",
|
|
115
|
+
"promise/no-new-statics": "error",
|
|
116
|
+
"promise/no-nesting": "warn",
|
|
117
|
+
"promise/no-promise-in-callback": "warn",
|
|
118
|
+
"promise/no-callback-in-promise": "warn",
|
|
119
|
+
"promise/valid-params": "error",
|
|
120
|
+
|
|
121
|
+
/* ---------- Stylistic: tabs, no JSX conflict ---------- */
|
|
122
|
+
"@stylistic/indent": ["error", "tab", {
|
|
123
|
+
SwitchCase: 1,
|
|
124
|
+
VariableDeclarator: 1,
|
|
125
|
+
outerIIFEBody: 1,
|
|
126
|
+
MemberExpression: 1,
|
|
127
|
+
FunctionDeclaration: { body: 1, parameters: 1 },
|
|
128
|
+
FunctionExpression: { body: 1, parameters: 1 },
|
|
129
|
+
CallExpression: { arguments: 1 },
|
|
130
|
+
ArrayExpression: 1,
|
|
131
|
+
ObjectExpression: 1,
|
|
132
|
+
ImportDeclaration: 1,
|
|
133
|
+
flatTernaryExpressions: false,
|
|
134
|
+
ignoreComments: false,
|
|
135
|
+
// avoid conflict with JSX rules handled in the react preset
|
|
136
|
+
ignoredNodes: ["JSXElement", "JSXElement *"],
|
|
137
|
+
}],
|
|
138
|
+
"@stylistic/no-mixed-spaces-and-tabs": ["warn", "smart-tabs"],
|
|
139
|
+
"no-tabs": "off",
|
|
45
140
|
},
|
|
46
|
-
languageOptions: {
|
|
47
|
-
ecmaVersion: "latest",
|
|
48
|
-
sourceType: "module"
|
|
49
|
-
// no parserOptions.project here (non type-aware preset)
|
|
50
|
-
}
|
|
51
141
|
}
|
|
52
142
|
);
|
|
53
143
|
}
|
package/src/express.js
CHANGED
|
@@ -1,16 +1,55 @@
|
|
|
1
|
-
import promise from "eslint-plugin-promise";
|
|
2
1
|
import security from "eslint-plugin-security";
|
|
3
2
|
|
|
4
3
|
import base from "./base.js";
|
|
4
|
+
|
|
5
|
+
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
5
6
|
export default function express() {
|
|
6
7
|
return [
|
|
7
8
|
...base(),
|
|
8
|
-
{
|
|
9
|
-
|
|
9
|
+
{ ignores: ["dist", "build", "coverage"] },
|
|
10
|
+
{
|
|
11
|
+
plugins: { security },
|
|
10
12
|
rules: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
13
|
+
/* ---------- Server-friendly overrides ---------- */
|
|
14
|
+
// Servers usually allow logging:
|
|
15
|
+
"no-console": "off",
|
|
16
|
+
|
|
17
|
+
/* ---------- Import hygiene ---------- */
|
|
18
|
+
// Disallow requiring deps not listed in package.json
|
|
19
|
+
"import/no-extraneous-dependencies": ["error", {
|
|
20
|
+
devDependencies: [
|
|
21
|
+
"**/*.test.*",
|
|
22
|
+
"**/*.spec.*",
|
|
23
|
+
"**/tests/**",
|
|
24
|
+
"**/scripts/**",
|
|
25
|
+
"**/*.config.*",
|
|
26
|
+
"**/eslint.config.*"
|
|
27
|
+
],
|
|
28
|
+
optionalDependencies: false,
|
|
29
|
+
peerDependencies: true
|
|
30
|
+
}],
|
|
31
|
+
|
|
32
|
+
/* ---------- Security checks (pragmatic set) ---------- */
|
|
33
|
+
// Avoid eval and similar dynamic code execution
|
|
34
|
+
"no-restricted-syntax": [
|
|
35
|
+
"error",
|
|
36
|
+
{ selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." },
|
|
37
|
+
{ selector: "NewExpression[callee.name='Function']", message: "Avoid new Function() in Node." }
|
|
38
|
+
],
|
|
39
|
+
"security/detect-child-process": "warn",
|
|
40
|
+
"security/detect-non-literal-fs-filename": "warn",
|
|
41
|
+
"security/detect-non-literal-regexp": "off",
|
|
42
|
+
"security/detect-possible-timing-attacks": "warn",
|
|
43
|
+
"security/detect-unsafe-regex": "warn",
|
|
44
|
+
// Too noisy for route objects & query builders:
|
|
45
|
+
"security/detect-object-injection": "off",
|
|
46
|
+
|
|
47
|
+
/* ---------- Promise discipline (adds to base) ---------- */
|
|
48
|
+
"promise/catch-or-return": ["warn", { allowThen: true, terminationMethod: ["catch", "finally"] }],
|
|
49
|
+
"promise/no-return-in-finally": "error",
|
|
50
|
+
"promise/prefer-await-to-then": "warn",
|
|
51
|
+
"promise/prefer-await-to-callbacks": "off"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
15
54
|
];
|
|
16
55
|
}
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
// src/index.js
|
|
2
|
+
|
|
3
|
+
// Import each preset’s default export:
|
|
4
|
+
import baseTypeChecked from "./base-typechecked.js";
|
|
5
|
+
import base from "./base.js";
|
|
6
|
+
import express from "./express.js";
|
|
7
|
+
import nestTypeChecked from "./nest-typechecked.js";
|
|
8
|
+
import nest from "./nest.js";
|
|
9
|
+
import next from "./next.js";
|
|
10
|
+
import reactNative from "./react-native.js";
|
|
11
|
+
import reactPdf from "./react-pdf.js";
|
|
12
|
+
import react from "./react.js";
|
|
13
|
+
|
|
14
|
+
// Re-export as **named** exports:
|
|
15
|
+
export {
|
|
16
|
+
base,
|
|
17
|
+
baseTypeChecked,
|
|
18
|
+
react,
|
|
19
|
+
next,
|
|
20
|
+
express,
|
|
21
|
+
nest,
|
|
22
|
+
reactNative,
|
|
23
|
+
reactPdf,
|
|
24
|
+
nestTypeChecked,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Optional: also provide a default object for people who prefer it.
|
|
28
|
+
// (Named imports are better for tree-shaking.)
|
|
29
|
+
export default {
|
|
30
|
+
base,
|
|
31
|
+
baseTypeChecked,
|
|
32
|
+
react,
|
|
33
|
+
next,
|
|
34
|
+
express,
|
|
35
|
+
nest,
|
|
36
|
+
reactNative,
|
|
37
|
+
reactPdf,
|
|
38
|
+
nestTypeChecked,
|
|
39
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import baseTypeChecked from "./base-typechecked.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type-aware NestJS preset (requires tsconfig).
|
|
5
|
+
* @param {string[]} project - tsconfig paths (default: ["tsconfig.json"])
|
|
6
|
+
* @returns {import('eslint').Linter.FlatConfig[]}
|
|
7
|
+
*/
|
|
8
|
+
export default function nestTypeChecked(project = ["tsconfig.json"]) {
|
|
9
|
+
return [
|
|
10
|
+
...baseTypeChecked(project),
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: {
|
|
15
|
+
process: "readonly",
|
|
16
|
+
__dirname: "readonly",
|
|
17
|
+
__filename: "readonly",
|
|
18
|
+
module: "readonly",
|
|
19
|
+
require: "readonly"
|
|
20
|
+
},
|
|
21
|
+
parserOptions: {
|
|
22
|
+
project
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
rules: {
|
|
27
|
+
"no-useless-constructor": "off",
|
|
28
|
+
"no-empty-function": ["warn", { allow: ["constructors", "methods"] }],
|
|
29
|
+
"class-methods-use-this": "off",
|
|
30
|
+
|
|
31
|
+
// TS-specific niceties for Nest
|
|
32
|
+
"@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }],
|
|
33
|
+
"@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: { attributes: false } }],
|
|
34
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
35
|
+
"error",
|
|
36
|
+
{ prefer: "type-imports", fixStyle: "inline-type-imports" }
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
"no-restricted-syntax": [
|
|
40
|
+
"error",
|
|
41
|
+
{ selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." },
|
|
42
|
+
{ selector: "NewExpression[callee.name='Function']", message: "Avoid new Function() in Node." }
|
|
43
|
+
],
|
|
44
|
+
|
|
45
|
+
"promise/catch-or-return": ["warn", { allowThen: true, terminationMethod: ["catch", "finally"] }]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
}
|
package/src/nest.js
CHANGED
|
@@ -1,15 +1,44 @@
|
|
|
1
|
-
import promise from "eslint-plugin-promise";
|
|
2
|
-
|
|
3
1
|
import base from "./base.js";
|
|
2
|
+
|
|
3
|
+
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
4
4
|
export default function nest() {
|
|
5
5
|
return [
|
|
6
6
|
...base(),
|
|
7
|
-
{
|
|
8
|
-
|
|
7
|
+
{ ignores: ["dist", "build", "coverage"] },
|
|
8
|
+
{
|
|
9
|
+
// Node-ish globals commonly used in Nest environments
|
|
10
|
+
languageOptions: {
|
|
11
|
+
globals: {
|
|
12
|
+
process: "readonly",
|
|
13
|
+
__dirname: "readonly",
|
|
14
|
+
__filename: "readonly",
|
|
15
|
+
module: "readonly",
|
|
16
|
+
require: "readonly"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
9
20
|
rules: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"no-useless-constructor": "off"
|
|
13
|
-
|
|
21
|
+
/* ----- Server-friendly relaxations for Nest patterns ----- */
|
|
22
|
+
// Constructors with only DI are fine
|
|
23
|
+
"no-useless-constructor": "off",
|
|
24
|
+
|
|
25
|
+
// Allow empty constructors/handlers when required by decorators/DI
|
|
26
|
+
"no-empty-function": ["warn", { allow: ["constructors", "methods"] }],
|
|
27
|
+
|
|
28
|
+
// Nest handlers/providers often don't need `this`
|
|
29
|
+
"class-methods-use-this": "off",
|
|
30
|
+
|
|
31
|
+
/* ----- General safety ----- */
|
|
32
|
+
"no-restricted-syntax": [
|
|
33
|
+
"error",
|
|
34
|
+
{ selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." },
|
|
35
|
+
{ selector: "NewExpression[callee.name='Function']", message: "Avoid new Function() in Node." }
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
/* ----- Promise discipline (base already enables most good ones) ----- */
|
|
39
|
+
// Keep this mild guard for route handlers/services
|
|
40
|
+
"promise/catch-or-return": ["warn", { allowThen: true, terminationMethod: ["catch", "finally"] }]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
14
43
|
];
|
|
15
44
|
}
|
package/src/next.js
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
import nextPlugin from "@next/eslint-plugin-next";
|
|
2
2
|
|
|
3
3
|
import react from "./react.js";
|
|
4
|
+
|
|
5
|
+
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
4
6
|
export default function next() {
|
|
5
7
|
return [
|
|
6
8
|
...react(),
|
|
7
|
-
{
|
|
9
|
+
{ ignores: [".next", ".vercel", "out"] },
|
|
10
|
+
{
|
|
11
|
+
plugins: { "@next/next": nextPlugin },
|
|
12
|
+
|
|
13
|
+
// Browser + Node globals commonly used in Next apps
|
|
14
|
+
languageOptions: {
|
|
15
|
+
globals: {
|
|
16
|
+
window: "readonly",
|
|
17
|
+
document: "readonly",
|
|
18
|
+
navigator: "readonly",
|
|
19
|
+
fetch: "readonly",
|
|
20
|
+
URL: "readonly",
|
|
21
|
+
Request: "readonly",
|
|
22
|
+
Response: "readonly",
|
|
23
|
+
Headers: "readonly",
|
|
24
|
+
process: "readonly"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
rules: {
|
|
29
|
+
"@next/next/no-img-element": "off",
|
|
30
|
+
"@next/next/no-sync-scripts": "error",
|
|
31
|
+
"@next/next/inline-script-id": "error",
|
|
32
|
+
"@next/next/no-css-tags": "error",
|
|
33
|
+
"@next/next/no-document-import-in-page": "error",
|
|
34
|
+
"@next/next/no-unwanted-polyfillio": "error",
|
|
35
|
+
"@next/next/no-typos": "error",
|
|
36
|
+
"@next/next/next-script-for-ga": "warn",
|
|
37
|
+
"@next/next/no-html-link-for-pages": "off"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
8
40
|
];
|
|
9
41
|
}
|
package/src/react-native.js
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import rn from "eslint-plugin-react-native";
|
|
2
2
|
|
|
3
3
|
import react from "./react.js";
|
|
4
|
+
|
|
5
|
+
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
4
6
|
export default function reactNative() {
|
|
5
7
|
return [
|
|
6
8
|
...react(),
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
+
{ ignores: ["android", "ios", ".expo", ".expo-shared", "expo-shared"] },
|
|
10
|
+
{
|
|
11
|
+
plugins: { "react-native": rn },
|
|
12
|
+
|
|
13
|
+
// Common RN globals
|
|
14
|
+
languageOptions: {
|
|
15
|
+
globals: {
|
|
16
|
+
__DEV__: "readonly",
|
|
17
|
+
fetch: "readonly",
|
|
18
|
+
FormData: "readonly",
|
|
19
|
+
alert: "readonly",
|
|
20
|
+
Request: "readonly",
|
|
21
|
+
Response: "readonly",
|
|
22
|
+
Headers: "readonly"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
9
26
|
rules: {
|
|
27
|
+
/* --- Core RN best practices --- */
|
|
10
28
|
"react-native/no-inline-styles": "warn",
|
|
11
29
|
"react-native/no-unused-styles": "warn",
|
|
12
|
-
"react-native/split-platform-components": "warn"
|
|
13
|
-
|
|
30
|
+
"react-native/split-platform-components": "warn",
|
|
31
|
+
|
|
32
|
+
// Useful extras (less noise than errors):
|
|
33
|
+
"react-native/no-raw-text": "warn", // text must be inside <Text>
|
|
34
|
+
"react-native/no-color-literals": "warn", // encourage theme/tokens
|
|
35
|
+
"react-native/no-single-element-style-arrays": "warn"
|
|
36
|
+
|
|
37
|
+
// If you hit unresolved imports with .ios/.android extensions,
|
|
38
|
+
// either add a TS resolver or disable the rule in this preset:
|
|
39
|
+
// "import/no-unresolved": "off"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
14
42
|
];
|
|
15
43
|
}
|
package/src/react-pdf.js
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
import reactHooks from "eslint-plugin-react-hooks";
|
|
2
2
|
|
|
3
3
|
import base from "./base.js";
|
|
4
|
+
|
|
5
|
+
/** @returns {import('eslint').Linter.FlatConfig[]} */
|
|
4
6
|
export default function reactPdf() {
|
|
5
7
|
return [
|
|
6
8
|
...base(),
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
+
{ ignores: ["*.pdf"] },
|
|
10
|
+
{
|
|
11
|
+
plugins: { "react-hooks": reactHooks },
|
|
12
|
+
|
|
13
|
+
// React-PDF usually runs in Node, not the browser
|
|
14
|
+
languageOptions: {
|
|
15
|
+
globals: {
|
|
16
|
+
process: "readonly",
|
|
17
|
+
Buffer: "readonly",
|
|
18
|
+
__dirname: "readonly",
|
|
19
|
+
__filename: "readonly",
|
|
20
|
+
module: "readonly",
|
|
21
|
+
require: "readonly"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
9
25
|
rules: {
|
|
26
|
+
/* React Hooks basics */
|
|
10
27
|
"react-hooks/rules-of-hooks": "error",
|
|
11
|
-
"react-hooks/exhaustive-deps": "warn"
|
|
12
|
-
|
|
28
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
29
|
+
|
|
30
|
+
/* Node-side safety */
|
|
31
|
+
"no-restricted-syntax": [
|
|
32
|
+
"error",
|
|
33
|
+
{ selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." },
|
|
34
|
+
{ selector: "NewExpression[callee.name='Function']", message: "Avoid new Function() in Node." }
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
/* Servers can log while generating PDFs */
|
|
38
|
+
"no-console": "off"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
13
41
|
];
|
|
14
42
|
}
|
package/src/react.js
CHANGED
|
@@ -1,30 +1,57 @@
|
|
|
1
|
+
// src/react.js
|
|
2
|
+
import stylistic from "@stylistic/eslint-plugin";
|
|
1
3
|
import reactPlugin from "eslint-plugin-react";
|
|
2
4
|
import reactHooks from "eslint-plugin-react-hooks";
|
|
3
5
|
import reactRefresh from "eslint-plugin-react-refresh";
|
|
6
|
+
import globals from "globals";
|
|
4
7
|
|
|
5
8
|
import base from "./base.js";
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
/** @param {{ files?: string[] }=} options
|
|
11
|
+
* @returns {import('eslint').Linter.FlatConfig[]} */
|
|
12
|
+
export default function react(options = {}) {
|
|
13
|
+
const files = options.files ?? ["**/*.{js,jsx,ts,tsx}"];
|
|
14
|
+
|
|
8
15
|
return [
|
|
9
16
|
...base(),
|
|
17
|
+
|
|
10
18
|
{
|
|
11
19
|
plugins: {
|
|
20
|
+
react: reactPlugin,
|
|
12
21
|
"react-hooks": reactHooks,
|
|
13
22
|
"react-refresh": reactRefresh,
|
|
14
|
-
"
|
|
23
|
+
"@stylistic": stylistic,
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
languageOptions: {
|
|
27
|
+
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
28
|
+
globals: { ...globals.browser },
|
|
15
29
|
},
|
|
30
|
+
|
|
31
|
+
settings: { react: { version: "detect" } },
|
|
32
|
+
|
|
16
33
|
rules: {
|
|
34
|
+
/* ---------- React hooks & fast refresh ---------- */
|
|
17
35
|
"react-hooks/rules-of-hooks": "error",
|
|
18
36
|
"react-hooks/exhaustive-deps": "warn",
|
|
19
|
-
"react-refresh/only-export-components":
|
|
37
|
+
"react-refresh/only-export-components": "off",
|
|
38
|
+
|
|
39
|
+
/* ---------- Core React sanity ---------- */
|
|
40
|
+
"react/react-in-jsx-scope": "off",
|
|
41
|
+
"react/prop-types": "off",
|
|
42
|
+
"react/jsx-key": ["error", { checkFragmentShorthand: true }],
|
|
43
|
+
"react/jsx-no-duplicate-props": "error",
|
|
44
|
+
"react/jsx-no-comment-textnodes": "warn",
|
|
45
|
+
"react/no-unstable-nested-components": "off",
|
|
46
|
+
"react/no-danger": "warn",
|
|
47
|
+
"react/self-closing-comp": "warn",
|
|
48
|
+
"react/jsx-boolean-value": ["warn", "never"],
|
|
49
|
+
"react/jsx-uses-vars": "error", // ensure JSX identifiers count as "used"
|
|
20
50
|
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
51
|
+
/* ---------- JSX indentation via @stylistic (tabs) ---------- */
|
|
52
|
+
"@stylistic/jsx-indent-props": ["error", "tab"],
|
|
53
|
+
// "@stylistic/jsx-quotes": ["warn", "prefer-double"],
|
|
24
54
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
}
|
|
55
|
+
},
|
|
56
|
+
].map(cfg => ("ignores" in cfg ? cfg : { ...cfg, files }));
|
|
57
|
+
}
|
package/src/test.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import base from "./base.js";
|
|
2
|
-
export default function test() {
|
|
3
|
-
return [
|
|
4
|
-
...base(),
|
|
5
|
-
{ languageOptions: { globals: {
|
|
6
|
-
describe: "readonly", it: "readonly", test: "readonly", expect: "readonly",
|
|
7
|
-
beforeAll: "readonly", afterAll: "readonly", beforeEach: "readonly", afterEach: "readonly"
|
|
8
|
-
} },
|
|
9
|
-
rules: { "no-undef": "off" } }
|
|
10
|
-
];
|
|
11
|
-
}
|