@ncontiero/eslint-config 7.2.0 → 7.3.0-beta.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.
Files changed (3) hide show
  1. package/dist/index.d.mts +561 -852
  2. package/dist/index.mjs +112 -154
  3. package/package.json +28 -27
package/dist/index.mjs CHANGED
@@ -153,12 +153,63 @@ function ensurePackages(packages) {
153
153
  if (nonExistingPackages.length === 0) return;
154
154
  throw new Error(`This package(s) are required for this config: ${nonExistingPackages.join(", ")}. Please install them.`);
155
155
  }
156
- async function composer(...items) {
156
+ async function composer({ items, pluginsNameMap }) {
157
157
  let configs = [];
158
158
  const flatResolved = (await Promise.all(items)).flat();
159
159
  configs = [...configs, ...flatResolved];
160
+ if (pluginsNameMap) configs = renamePluginInConfigs(configs, pluginsNameMap);
160
161
  return configs;
161
162
  }
163
+ /**
164
+ * Rename plugin prefixes in a rule object.
165
+ * Accepts a map of prefixes to rename.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { renameRules } from "@ncontiero/eslint-config";
170
+ *
171
+ * export default [
172
+ * {
173
+ * rules: renameRules(
174
+ * {
175
+ * "@typescript-eslint/indent": "error",
176
+ * },
177
+ * { "@typescript-eslint": "ts" },
178
+ * ),
179
+ * },
180
+ * ];
181
+ * ```
182
+ */
183
+ function renameRules(rules, map) {
184
+ return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
185
+ for (const [from, to] of Object.entries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
186
+ return [key, value];
187
+ }));
188
+ }
189
+ /**
190
+ * Rename plugin names a flat configs array
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * import { renamePluginInConfigs } from "@ncontiero/eslint-config";
195
+ * import someConfigs from "./some-configs";
196
+ *
197
+ * export default renamePluginInConfigs(someConfigs, {
198
+ * "@typescript-eslint": "ts",
199
+ * });
200
+ * ```
201
+ */
202
+ function renamePluginInConfigs(configs, map) {
203
+ return configs.map((i) => {
204
+ const clone = { ...i };
205
+ if (clone.rules) clone.rules = renameRules(clone.rules, map);
206
+ if (clone.plugins) clone.plugins = Object.fromEntries(Object.entries(clone.plugins).map(([key, value]) => {
207
+ if (key in map) return [map[key], value];
208
+ return [key, value];
209
+ }));
210
+ return clone;
211
+ });
212
+ }
162
213
  function toArray(value) {
163
214
  return Array.isArray(value) ? value : [value];
164
215
  }
@@ -760,12 +811,11 @@ const ReactRouterPackages = [
760
811
  ];
761
812
  const NextJsPackages = ["next"];
762
813
  async function react(options = {}) {
763
- const { files = [GLOB_REACT], overrides = {}, reactQuery, typescript } = options;
814
+ const { files = [GLOB_REACT], overrides = {}, reactQuery } = options;
764
815
  if (reactQuery) ensurePackages(["@tanstack/eslint-plugin-query"]);
765
- const [pluginA11y, pluginReact, pluginReactHooks, pluginReactRefresh] = await Promise.all([
816
+ const [pluginA11y, pluginReact, pluginReactRefresh] = await Promise.all([
766
817
  interopDefault(import("eslint-plugin-jsx-a11y")),
767
- interopDefault(import("eslint-plugin-react")),
768
- interopDefault(import("eslint-plugin-react-hooks")),
818
+ interopDefault(import("@eslint-react/eslint-plugin")),
769
819
  interopDefault(import("eslint-plugin-react-refresh"))
770
820
  ]);
771
821
  const isAllowConstantExport = ReactRefreshAllowPackages.some((i) => isPackageExists(i));
@@ -773,15 +823,12 @@ async function react(options = {}) {
773
823
  const isUsingNextJs = NextJsPackages.some((i) => isPackageExists(i));
774
824
  return [
775
825
  {
776
- files,
777
826
  name: "ncontiero/react/setup",
778
827
  plugins: {
779
828
  "jsx-a11y": pluginA11y,
780
829
  react: pluginReact,
781
- "react-hooks": pluginReactHooks,
782
830
  "react-refresh": pluginReactRefresh
783
- },
784
- settings: { react: { version: "detect" } }
831
+ }
785
832
  },
786
833
  reactQuery ? {
787
834
  ...(await interopDefault(import("@tanstack/eslint-plugin-query"))).configs["flat/recommended"][0],
@@ -789,149 +836,13 @@ async function react(options = {}) {
789
836
  } : {},
790
837
  {
791
838
  files,
792
- languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
839
+ languageOptions: {
840
+ parserOptions: { ecmaFeatures: { jsx: true } },
841
+ sourceType: "module"
842
+ },
793
843
  name: "ncontiero/react/rules",
794
844
  rules: {
795
- "react-hooks/component-hook-factories": "warn",
796
- "react-hooks/error-boundaries": "warn",
797
- "react-hooks/exhaustive-deps": "warn",
798
- "react-hooks/globals": "warn",
799
- "react-hooks/immutability": "warn",
800
- "react-hooks/incompatible-library": "warn",
801
- "react-hooks/preserve-manual-memoization": "warn",
802
- "react-hooks/purity": "warn",
803
- "react-hooks/refs": "warn",
804
- "react-hooks/rules-of-hooks": "error",
805
- "react-hooks/set-state-in-effect": "warn",
806
- "react-hooks/set-state-in-render": "warn",
807
- "react-hooks/static-components": "warn",
808
- "react-hooks/unsupported-syntax": "warn",
809
- "react-hooks/use-memo": "warn",
810
- "react-refresh/only-export-components": ["warn", {
811
- allowConstantExport: isAllowConstantExport,
812
- allowExportNames: [...isUsingNextJs ? [
813
- "experimental_ppr",
814
- "dynamic",
815
- "dynamicParams",
816
- "revalidate",
817
- "fetchCache",
818
- "runtime",
819
- "preferredRegion",
820
- "maxDuration",
821
- "metadata",
822
- "generateMetadata",
823
- "viewport",
824
- "generateViewport",
825
- "generateImageMetadata",
826
- "generateSitemaps",
827
- "generateStaticParams"
828
- ] : [], ...isUsingReactRouter ? [
829
- "meta",
830
- "links",
831
- "headers",
832
- "loader",
833
- "action",
834
- "clientLoader",
835
- "clientAction",
836
- "handle",
837
- "shouldRevalidate"
838
- ] : []]
839
- }],
840
- "react/boolean-prop-naming": ["off", {
841
- rule: "^(is|has|are|can|should|did|will)[A-Z]([A-Za-z0-9])+",
842
- validateNested: true
843
- }],
844
- "react/button-has-type": ["error", {
845
- button: true,
846
- reset: false,
847
- submit: true
848
- }],
849
- "react/display-name": ["error", { ignoreTranspilerName: false }],
850
- "react/hook-use-state": ["error"],
851
- "react/iframe-missing-sandbox": ["error"],
852
- "react/jsx-boolean-value": ["error", "never"],
853
- "react/jsx-filename-extension": ["warn", { extensions: [".tsx"] }],
854
- "react/jsx-fragments": ["error", "syntax"],
855
- "react/jsx-handler-names": ["error", {
856
- eventHandlerPrefix: "handle",
857
- eventHandlerPropPrefix: "on"
858
- }],
859
- "react/jsx-key": ["error", {
860
- checkFragmentShorthand: true,
861
- checkKeyMustBeforeSpread: true,
862
- warnOnDuplicates: true
863
- }],
864
- "react/jsx-no-bind": ["error", { allowArrowFunctions: true }],
865
- "react/jsx-no-comment-textnodes": "error",
866
- "react/jsx-no-constructed-context-values": "error",
867
- "react/jsx-no-duplicate-props": ["error"],
868
- "react/jsx-no-leaked-render": ["error"],
869
- "react/jsx-no-script-url": ["error", [{
870
- name: "Link",
871
- props: ["to"]
872
- }]],
873
- "react/jsx-no-target-blank": ["error", {
874
- forms: true,
875
- links: true,
876
- warnOnSpreadAttributes: true
877
- }],
878
- "react/jsx-no-undef": "error",
879
- "react/jsx-no-useless-fragment": "error",
880
- "react/jsx-uses-react": "error",
881
- "react/jsx-uses-vars": "error",
882
- "react/no-access-state-in-setstate": "error",
883
- "react/no-array-index-key": "error",
884
- "react/no-arrow-function-lifecycle": "error",
885
- "react/no-children-prop": "error",
886
- "react/no-danger": "error",
887
- "react/no-danger-with-children": "error",
888
- "react/no-deprecated": "error",
889
- "react/no-did-update-set-state": "error",
890
- "react/no-direct-mutation-state": "error",
891
- "react/no-find-dom-node": "error",
892
- "react/no-invalid-html-attribute": "error",
893
- "react/no-is-mounted": "error",
894
- "react/no-namespace": "error",
895
- "react/no-object-type-as-default-prop": "error",
896
- "react/no-redundant-should-component-update": "error",
897
- "react/no-render-return-value": "error",
898
- "react/no-string-refs": ["error", { noTemplateLiterals: true }],
899
- "react/no-this-in-sfc": ["error"],
900
- "react/no-typos": ["error"],
901
- "react/no-unescaped-entities": "error",
902
- "react/no-unknown-property": "error",
903
- "react/no-unstable-nested-components": ["error"],
904
- "react/no-unused-class-component-methods": ["error"],
905
- "react/no-unused-prop-types": ["error", {
906
- customValidators: [],
907
- skipShapeProps: true
908
- }],
909
- "react/no-unused-state": ["error"],
910
- "react/no-will-update-set-state": ["error"],
911
- "react/prefer-es6-class": ["error", "always"],
912
- "react/prefer-exact-props": ["error"],
913
- "react/prefer-read-only-props": ["error"],
914
- "react/prefer-stateless-function": ["error", { ignorePureComponents: true }],
915
- "react/prop-types": ["error", {
916
- customValidators: [],
917
- ignore: [],
918
- skipUndeclared: false
919
- }],
920
- "react/react-in-jsx-scope": "off",
921
- "react/require-render-return": "error",
922
- "react/self-closing-comp": ["error", {
923
- component: true,
924
- html: true
925
- }],
926
- "react/sort-default-props": ["error"],
927
- "react/state-in-constructor": ["error", "never"],
928
- "react/static-property-placement": ["error", "property assignment"],
929
- "react/style-prop-object": ["error", { allow: ["FormattedNumber"] }],
930
- "react/void-dom-elements-no-children": ["error"],
931
- ...typescript ? {
932
- "react/jsx-no-undef": "off",
933
- "react/prop-types": "off"
934
- } : {},
845
+ ...pluginReact.configs.recommended.rules,
935
846
  "jsx-a11y/alt-text": ["warn", {
936
847
  area: [],
937
848
  elements: [
@@ -1058,6 +969,50 @@ async function react(options = {}) {
1058
969
  "jsx-a11y/role-supports-aria-props": ["warn"],
1059
970
  "jsx-a11y/scope": ["warn"],
1060
971
  "jsx-a11y/tabindex-no-positive": ["warn"],
972
+ "react-refresh/only-export-components": ["warn", {
973
+ allowConstantExport: isAllowConstantExport,
974
+ allowExportNames: [...isUsingNextJs ? [
975
+ "experimental_ppr",
976
+ "dynamic",
977
+ "dynamicParams",
978
+ "revalidate",
979
+ "fetchCache",
980
+ "runtime",
981
+ "preferredRegion",
982
+ "maxDuration",
983
+ "metadata",
984
+ "generateMetadata",
985
+ "viewport",
986
+ "generateViewport",
987
+ "generateImageMetadata",
988
+ "generateSitemaps",
989
+ "generateStaticParams"
990
+ ] : [], ...isUsingReactRouter ? [
991
+ "meta",
992
+ "links",
993
+ "headers",
994
+ "loader",
995
+ "action",
996
+ "clientLoader",
997
+ "clientAction",
998
+ "handle",
999
+ "shouldRevalidate"
1000
+ ] : []]
1001
+ }],
1002
+ "react/dom-no-missing-button-type": "warn",
1003
+ "react/dom-no-missing-iframe-sandbox": "warn",
1004
+ "react/dom-no-unknown-property": "warn",
1005
+ "react/dom-no-unsafe-target-blank": "warn",
1006
+ "react/globals": "warn",
1007
+ "react/immutability": "warn",
1008
+ "react/jsx-no-useless-fragment": "warn",
1009
+ "react/no-duplicate-key": "warn",
1010
+ "react/no-leaked-conditional-rendering": "error",
1011
+ "react/no-missing-component-display-name": "warn",
1012
+ "react/no-unstable-context-value": "warn",
1013
+ "react/no-unstable-default-props": "error",
1014
+ "react/no-unused-props": "warn",
1015
+ "react/refs": "warn",
1061
1016
  ...overrides
1062
1017
  }
1063
1018
  }
@@ -1653,6 +1608,7 @@ const flatConfigProps = [
1653
1608
  "rules",
1654
1609
  "settings"
1655
1610
  ];
1611
+ const defaultPluginRenaming = { "@eslint-react": "react" };
1656
1612
  function resolveSubOptions(options, key) {
1657
1613
  return typeof options[key] === "boolean" ? {} : options[key] || {};
1658
1614
  }
@@ -1714,8 +1670,7 @@ function ncontiero(options = {}, ...userConfigs) {
1714
1670
  if (enableRegexp) configs.push(regexp(typeof enableRegexp === "boolean" ? {} : enableRegexp));
1715
1671
  if (enableReact) configs.push(react({
1716
1672
  overrides: getOverrides(options, "react"),
1717
- reactQuery: !!enableTanStackReactQuery,
1718
- typescript: !!enableTypescript
1673
+ reactQuery: !!enableTanStackReactQuery
1719
1674
  }));
1720
1675
  if (enableNextJs) configs.push(nextJs({ overrides: getOverrides(options, "nextjs") }));
1721
1676
  if (options.html ?? true) configs.push(html({
@@ -1733,7 +1688,10 @@ function ncontiero(options = {}, ...userConfigs) {
1733
1688
  return acc;
1734
1689
  }, {});
1735
1690
  if (Object.keys(fusedConfig).length > 0) configs.push([fusedConfig]);
1736
- return composer(...configs, ...userConfigs);
1691
+ return composer({
1692
+ items: [...configs, ...userConfigs],
1693
+ pluginsNameMap: defaultPluginRenaming
1694
+ });
1737
1695
  }
1738
1696
  //#endregion
1739
- export { GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_REACT, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, combine, command, comments, composer, deMorgan, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTanStackReactQuery, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, toArray, toml, typescript, unicorn, yml };
1697
+ export { GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_REACT, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, combine, command, comments, composer, deMorgan, defaultPluginRenaming, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTanStackReactQuery, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, renamePluginInConfigs, renameRules, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, toArray, toml, typescript, unicorn, yml };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@ncontiero/eslint-config",
3
3
  "type": "module",
4
- "version": "7.2.0",
4
+ "version": "7.3.0-beta.1",
5
+ "packageManager": "pnpm@10.33.2",
5
6
  "description": "Nicolas's ESLint config.",
6
7
  "author": {
7
8
  "name": "Nicolas Contiero",
@@ -27,6 +28,19 @@
27
28
  "publishConfig": {
28
29
  "access": "public"
29
30
  },
31
+ "scripts": {
32
+ "build": "pnpm typegen && tsdown",
33
+ "dev": "pnpm dlx @eslint/config-inspector --config eslint-inspector.config.ts",
34
+ "build:inspector": "pnpm build && pnpm dlx @eslint/config-inspector build --config eslint-inspector.config.ts",
35
+ "typegen": "tsx scripts/typegen.ts",
36
+ "test": "vitest",
37
+ "lint": "eslint .",
38
+ "lint:fix": "pnpm lint --fix",
39
+ "typecheck": "tsc --noEmit",
40
+ "check": "pnpm lint && pnpm typecheck",
41
+ "release": "changeset publish",
42
+ "prepare": "husky"
43
+ },
30
44
  "peerDependencies": {
31
45
  "@tanstack/eslint-plugin-query": ">=5.50.0",
32
46
  "eslint": ">=9.20.0"
@@ -38,12 +52,13 @@
38
52
  },
39
53
  "dependencies": {
40
54
  "@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
55
+ "@eslint-react/eslint-plugin": "^4.2.3",
41
56
  "@eslint/markdown": "^8.0.1",
42
57
  "@html-eslint/eslint-plugin": "^0.59.0",
43
58
  "@html-eslint/parser": "^0.59.0",
44
59
  "@next/eslint-plugin-next": "^16.2.4",
45
- "@typescript-eslint/eslint-plugin": "^8.58.2",
46
- "@typescript-eslint/parser": "^8.58.2",
60
+ "@typescript-eslint/eslint-plugin": "^8.59.0",
61
+ "@typescript-eslint/parser": "^8.59.0",
47
62
  "eslint-config-flat-gitignore": "^2.3.0",
48
63
  "eslint-merge-processors": "^2.0.0",
49
64
  "eslint-plugin-antfu": "^3.2.2",
@@ -55,11 +70,9 @@
55
70
  "eslint-plugin-jsonc": "^3.1.2",
56
71
  "eslint-plugin-jsx-a11y": "^6.10.2",
57
72
  "eslint-plugin-n": "^17.24.0",
58
- "eslint-plugin-perfectionist": "^5.8.0",
73
+ "eslint-plugin-perfectionist": "^5.9.0",
59
74
  "eslint-plugin-prettier": "^5.5.5",
60
75
  "eslint-plugin-promise": "^7.2.1",
61
- "eslint-plugin-react": "^7.37.5",
62
- "eslint-plugin-react-hooks": "^7.0.1",
63
76
  "eslint-plugin-react-refresh": "^0.5.2",
64
77
  "eslint-plugin-regexp": "^3.1.0",
65
78
  "eslint-plugin-toml": "^1.3.1",
@@ -73,24 +86,24 @@
73
86
  "yaml-eslint-parser": "^2.0.0"
74
87
  },
75
88
  "devDependencies": {
76
- "@changesets/cli": "^2.30.0",
89
+ "@changesets/cli": "^2.31.0",
77
90
  "@commitlint/cli": "^20.5.0",
78
91
  "@commitlint/config-conventional": "^20.5.0",
79
- "@ncontiero/changelog-github": "^2.1.2",
92
+ "@ncontiero/changelog-github": "^2.1.3",
80
93
  "@ncontiero/prettier-config": "^1.0.0",
81
- "@tanstack/eslint-plugin-query": "^5.99.0",
94
+ "@tanstack/eslint-plugin-query": "^5.100.1",
82
95
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
83
96
  "@types/node": "^25.6.0",
84
- "eslint": "^10.2.0",
97
+ "eslint": "^10.2.1",
85
98
  "eslint-typegen": "^2.3.1",
86
99
  "execa": "^9.6.1",
87
100
  "husky": "^9.1.7",
88
101
  "lint-staged": "^16.4.0",
89
102
  "tinyglobby": "^0.2.16",
90
- "tsdown": "^0.21.9",
103
+ "tsdown": "^0.21.10",
91
104
  "tsx": "^4.21.0",
92
- "typescript": "^6.0.2",
93
- "vitest": "^4.1.4"
105
+ "typescript": "^6.0.3",
106
+ "vitest": "^4.1.5"
94
107
  },
95
108
  "engines": {
96
109
  "node": ">=20.19.0"
@@ -98,17 +111,5 @@
98
111
  "lint-staged": {
99
112
  "*": "pnpm lint:fix"
100
113
  },
101
- "prettier": "@ncontiero/prettier-config",
102
- "scripts": {
103
- "build": "pnpm typegen && tsdown",
104
- "dev": "pnpm dlx @eslint/config-inspector --config eslint-inspector.config.ts",
105
- "build:inspector": "pnpm build && pnpm dlx @eslint/config-inspector build --config eslint-inspector.config.ts",
106
- "typegen": "tsx scripts/typegen.ts",
107
- "test": "vitest",
108
- "lint": "eslint .",
109
- "lint:fix": "pnpm lint --fix",
110
- "typecheck": "tsc --noEmit",
111
- "check": "pnpm lint && pnpm typecheck",
112
- "release": "changeset publish"
113
- }
114
- }
114
+ "prettier": "@ncontiero/prettier-config"
115
+ }