@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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @eslint-umbrella/presets
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 4694128: chore(release): version packages
8
+
3
9
  ## 1.1.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-umbrella/presets",
3
- "version": "1.1.0",
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-promise": "6.5.1",
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
- "eslint-plugin-react": "7.37.1",
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-plugin-react-native": "^5.0.0"
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
  }
@@ -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"; // ← default import
5
+ import tseslint from "typescript-eslint";
6
6
 
7
7
  /**
8
- * Type-aware base preset (requires a tsconfig).
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
- // 1) general ignores
15
- { ignores: ["dist", "build", "coverage", "**/*.min.*"] },
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
- // 2) core JS rules
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
- // 3) TS type-aware recommended presets (this sets the TS parser & plugin)
45
+ // 2) TS type-aware recommendations
21
46
  ...tseslint.configs.recommendedTypeChecked,
22
47
 
23
- // 4) your extra plugins/rules + the project's tsconfig paths
48
+ // 3) opinionated, broadly accepted best practices
24
49
  {
25
- plugins: { import: importPlugin, "unused-imports": unused },
26
- rules: {
27
- "no-console": ["warn", { allow: ["warn", "error"] }],
28
- "no-debugger": "warn",
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, // ← pass your tsconfig paths here
48
- // tsconfigRootDir: new URL(".", import.meta.url) // usually NOT needed; consumer root is better
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 tseslint from "typescript-eslint"; // default 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
- { ignores: ["dist", "build", "coverage", "**/*.min.*"] },
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
- ...tseslint.configs.recommended,
45
+
46
+ /* 2) extras */
12
47
  {
13
- plugins: { import: importPlugin, "unused-imports": unused },
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
- "unused-imports/no-unused-imports": "warn",
22
- "unused-imports/no-unused-vars": ["warn", {
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
- { plugins: { security, promise },
9
- languageOptions: { globals: { process: "readonly", __dirname: "readonly", module: "readonly" } },
9
+ { ignores: ["dist", "build", "coverage"] },
10
+ {
11
+ plugins: { security },
10
12
  rules: {
11
- "no-restricted-syntax": ["warn", { selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." }],
12
- "promise/catch-or-return": "warn",
13
- "security/detect-object-injection": "off"
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
- export { default as base } from "./base.js";
2
- export { default as react } from "./react.js";
3
- export { default as next } from "./next.js";
4
- export { default as express } from "./express.js";
5
- export { default as nest } from "./nest.js";
6
- export { default as reactNative } from "./react-native.js";
7
- export { default as reactPdf } from "./react-pdf.js";
8
- export { default as test } from "./test.js";
9
- export { default as baseTypeChecked } from "./base-typechecked.js";
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
- { plugins: { promise },
8
- languageOptions: { globals: { process: "readonly" } },
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
- "no-restricted-syntax": ["warn", { selector: "CallExpression[callee.name='eval']", message: "Avoid eval() in Node." }],
11
- "promise/catch-or-return": "warn",
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
- { plugins: { "@next/next": nextPlugin }, rules: { "@next/next/no-img-element": "off" } }
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
  }
@@ -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
- { plugins: { "react-native": rn },
8
- languageOptions: { globals: { __DEV__: "readonly" } },
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
- { plugins: { "react-hooks": reactHooks },
8
- languageOptions: { globals: { process: "readonly", Buffer: "readonly" } },
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
- export default function react() {
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
- "react": reactPlugin
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": ["warn", { allowConstantExport: true }],
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
- // JSX indentation (2 spaces)
22
- "react/jsx-indent": ["warn", "tab"],
23
- "react/jsx-indent-props": ["warn", "tab"]
51
+ /* ---------- JSX indentation via @stylistic (tabs) ---------- */
52
+ "@stylistic/jsx-indent-props": ["error", "tab"],
53
+ // "@stylistic/jsx-quotes": ["warn", "prefer-double"],
24
54
  },
25
- settings: {
26
- react: { version: "detect" }
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
- }