@lincy/eslint-config 4.3.1 → 4.5.0

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 CHANGED
@@ -135,7 +135,7 @@ export default lincy({
135
135
  /**
136
136
  * 是否启用 stylistic 格式化规则
137
137
  * @default 默认值: true
138
- * @example 可选: false | { indent?: number | 'tab'; quotes?: 'single' | 'double'; jsx?: boolean; semi?: boolean }
138
+ * @example 可选: false | { indent?: number | 'tab'; quotes?: 'single' | 'double'; jsx?: boolean; semi?: boolean; lessOpinionated?: boolean; }
139
139
  */
140
140
  stylistic: true,
141
141
  /**
@@ -508,7 +508,7 @@ pnpm add eslint-plugin-format -D
508
508
  运行“npx eslint”会提示您安装所需的依赖项,当然,也可以手动安装它们:
509
509
 
510
510
  ```bash
511
- pnpm i -D eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-refresh
511
+ pnpm i -D @eslint-react/eslint-plugin eslint-plugin-react-hooks eslint-plugin-react-refresh
512
512
  ```
513
513
 
514
514
  #### UnoCSS
@@ -540,14 +540,14 @@ pnpm i -D @unocss/eslint-plugin
540
540
 
541
541
  建议使用[configuration comments]单独选择每个文件。(https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1).
542
542
 
543
- ```js
543
+ ```ts
544
544
  /* eslint perfectionist/sort-objects: "error" */
545
545
  const objectWantedToSort = {
546
546
  a: 2,
547
547
  b: 1,
548
548
  c: 3,
549
549
  }
550
- /* eslint perfectionist/sort-objects: "off" */
550
+ /* e slint perfectionist/sort-objects: "off" */
551
551
  ```
552
552
 
553
553
  ### 类型感知规则
package/dist/index.cjs CHANGED
@@ -440,7 +440,7 @@ async function javascript(options = {}) {
440
440
  "unused-imports/no-unused-imports": isInEditor ? "off" : "error",
441
441
  "unused-imports/no-unused-vars": [
442
442
  "error",
443
- { args: "after-used", argsIgnorePattern: "^_", vars: "all", varsIgnorePattern: "^_" }
443
+ { args: "after-used", argsIgnorePattern: "^_", ignoreRestSiblings: true, vars: "all", varsIgnorePattern: "^_" }
444
444
  ],
445
445
  "use-isnan": ["error", { enforceForIndexOf: true, enforceForSwitchCase: true }],
446
446
  "valid-typeof": ["error", { requireStringLiterals: true }],
@@ -470,8 +470,9 @@ function renameRules(rules, map) {
470
470
  return Object.fromEntries(
471
471
  Object.entries(rules).map(([key, value]) => {
472
472
  for (const [from, to] of Object.entries(map)) {
473
- if (key.startsWith(`${from}/`))
473
+ if (key.startsWith(`${from}/`)) {
474
474
  return [to + key.slice(from.length), value];
475
+ }
475
476
  }
476
477
  return [key, value];
477
478
  })
@@ -480,13 +481,15 @@ function renameRules(rules, map) {
480
481
  function renamePluginInConfigs(configs, map) {
481
482
  return configs.map((i) => {
482
483
  const clone = { ...i };
483
- if (clone.rules)
484
+ if (clone.rules) {
484
485
  clone.rules = renameRules(clone.rules, map);
486
+ }
485
487
  if (clone.plugins) {
486
488
  clone.plugins = Object.fromEntries(
487
489
  Object.entries(clone.plugins).map(([key, value]) => {
488
- if (key in map)
490
+ if (key in map) {
489
491
  return [map[key], value];
492
+ }
490
493
  return [key, value];
491
494
  })
492
495
  );
@@ -502,11 +505,13 @@ async function interopDefault(m) {
502
505
  return resolved.default || resolved;
503
506
  }
504
507
  async function ensurePackages(packages) {
505
- if (import_node_process.default.stdout.isTTY === false)
508
+ if (import_node_process.default.stdout.isTTY === false) {
506
509
  return;
510
+ }
507
511
  const nonExistingPackages = packages.filter((i) => !(0, import_local_pkg.isPackageExists)(i));
508
- if (nonExistingPackages.length === 0)
512
+ if (nonExistingPackages.length === 0) {
509
513
  return;
514
+ }
510
515
  const { default: prompts } = await import("prompts");
511
516
  const { result } = await prompts([
512
517
  {
@@ -515,8 +520,9 @@ async function ensurePackages(packages) {
515
520
  type: "confirm"
516
521
  }
517
522
  ]);
518
- if (result)
523
+ if (result) {
519
524
  await import("@antfu/install-pkg").then((i) => i.installPackage(nonExistingPackages, { dev: true }));
525
+ }
520
526
  }
521
527
 
522
528
  // src/configs/jsdoc.ts
@@ -741,6 +747,7 @@ var parserPlain2 = __toESM(require("eslint-parser-plain"), 1);
741
747
  var StylisticConfigDefaults = {
742
748
  indent: 4,
743
749
  jsx: true,
750
+ lessOpinionated: true,
744
751
  quotes: "single",
745
752
  semi: false
746
753
  };
@@ -752,6 +759,7 @@ async function stylistic(options = {}) {
752
759
  const {
753
760
  indent,
754
761
  jsx,
762
+ lessOpinionated,
755
763
  quotes,
756
764
  semi
757
765
  } = typeof stylistic2 === "boolean" ? StylisticConfigDefaults : { ...StylisticConfigDefaults, ...stylistic2 };
@@ -774,9 +782,13 @@ async function stylistic(options = {}) {
774
782
  rules: {
775
783
  ...config.rules,
776
784
  "antfu/consistent-list-newline": "off",
777
- "antfu/if-newline": "error",
778
- "antfu/top-level-function": "error",
779
- "curly": ["error", "multi-or-nest", "consistent"],
785
+ ...lessOpinionated ? {
786
+ curly: ["error", "all"]
787
+ } : {
788
+ "antfu/if-newline": "error",
789
+ "antfu/top-level-function": "error",
790
+ "curly": ["error", "multi-or-nest", "consistent"]
791
+ },
780
792
  // 覆盖`stylistic`默认规则
781
793
  "style/member-delimiter-style": ["error", { multiline: { delimiter: "none" } }],
782
794
  "style/multiline-ternary": ["error", "never"],
@@ -889,7 +901,7 @@ async function formatters(options = {}, stylistic2 = {}) {
889
901
  }
890
902
  if (options.html) {
891
903
  configs.push({
892
- files: ["**/*.html"],
904
+ files: [GLOB_HTML],
893
905
  languageOptions: {
894
906
  parser: parserPlain2
895
907
  },
@@ -985,152 +997,116 @@ async function react(options = {}) {
985
997
  typescript: typescript2 = true,
986
998
  version = "detect"
987
999
  } = options;
1000
+ const tsconfigPath = options?.tsconfigPath ? toArray(options.tsconfigPath) : void 0;
1001
+ const isTypeAware = !!tsconfigPath || !typescript2;
988
1002
  await ensurePackages([
989
- "eslint-plugin-react",
1003
+ "@eslint-react/eslint-plugin",
990
1004
  "eslint-plugin-react-hooks",
991
1005
  "eslint-plugin-react-refresh"
992
1006
  ]);
993
1007
  const [
994
1008
  pluginReact,
995
1009
  pluginReactHooks,
996
- pluginReactRefresh
1010
+ pluginReactRefresh,
1011
+ parserTs
997
1012
  ] = await Promise.all([
998
- // @ts-expect-error missing types
999
- interopDefault(import("eslint-plugin-react")),
1013
+ interopDefault(import("@eslint-react/eslint-plugin")),
1000
1014
  // @ts-expect-error missing types
1001
1015
  interopDefault(import("eslint-plugin-react-hooks")),
1002
1016
  // @ts-expect-error missing types
1003
- interopDefault(import("eslint-plugin-react-refresh"))
1017
+ interopDefault(import("eslint-plugin-react-refresh")),
1018
+ interopDefault(import("@typescript-eslint/parser"))
1004
1019
  ]);
1005
1020
  const _isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some(
1006
1021
  (i) => (0, import_local_pkg2.isPackageExists)(i)
1007
1022
  );
1023
+ const plugins = pluginReact.configs.all.plugins;
1008
1024
  return [
1009
1025
  {
1010
1026
  name: "eslint:react:setup",
1011
1027
  plugins: {
1012
- "react": pluginReact,
1028
+ "react": plugins["@eslint-react"],
1029
+ "react-dom": plugins["@eslint-react/dom"],
1013
1030
  "react-hooks": pluginReactHooks,
1031
+ "react-hooks-extra": plugins["@eslint-react/hooks-extra"],
1032
+ "react-naming-convention": plugins["@eslint-react/naming-convention"],
1014
1033
  "react-refresh": pluginReactRefresh
1015
1034
  }
1016
1035
  },
1017
1036
  {
1018
1037
  files,
1019
1038
  languageOptions: {
1039
+ parser: parserTs,
1020
1040
  parserOptions: {
1021
1041
  ecmaFeatures: {
1022
1042
  jsx
1023
- }
1043
+ },
1044
+ ...isTypeAware ? { project: tsconfigPath } : {}
1024
1045
  }
1025
1046
  },
1026
1047
  name: "eslint:react:rules",
1027
1048
  rules: {
1028
- // react-hooks
1049
+ // recommended rules from @eslint-react/dom
1050
+ "react-dom/no-children-in-void-dom-elements": "warn",
1051
+ "react-dom/no-dangerously-set-innerhtml": "warn",
1052
+ "react-dom/no-dangerously-set-innerhtml-with-children": "error",
1053
+ "react-dom/no-find-dom-node": "error",
1054
+ "react-dom/no-missing-button-type": "warn",
1055
+ "react-dom/no-missing-iframe-sandbox": "warn",
1056
+ "react-dom/no-namespace": "error",
1057
+ "react-dom/no-render-return-value": "error",
1058
+ "react-dom/no-script-url": "warn",
1059
+ "react-dom/no-unsafe-iframe-sandbox": "warn",
1060
+ "react-dom/no-unsafe-target-blank": "warn",
1061
+ // recommended rules react-hooks
1029
1062
  "react-hooks/exhaustive-deps": "warn",
1030
1063
  "react-hooks/rules-of-hooks": "error",
1031
- // react-refresh
1064
+ // react refresh
1032
1065
  // 'react-refresh/only-export-components': [
1033
1066
  // 'warn',
1034
- // { allowConstantExport: _isAllowConstantExport },
1067
+ // { allowConstantExport: isAllowConstantExport },
1035
1068
  // ],
1036
- "react-refresh/only-export-components": "off",
1037
- // react
1038
- "react/boolean-prop-naming": "error",
1039
- "react/button-has-type": "error",
1040
- "react/default-props-match-prop-types": "error",
1041
- "react/destructuring-assignment": "error",
1042
- "react/display-name": "error",
1043
- "react/forbid-component-props": "off",
1044
- // 禁止组件上使用某些 props
1045
- "react/forbid-dom-props": "error",
1046
- "react/forbid-elements": "error",
1047
- "react/forbid-foreign-prop-types": "error",
1048
- "react/forbid-prop-types": "error",
1049
- "react/function-component-definition": "error",
1050
- "react/hook-use-state": "off",
1051
- // useState 钩子值和 setter 变量的解构和对称命名
1052
- "react/iframe-missing-sandbox": "error",
1053
- "react/jsx-boolean-value": "error",
1054
- "react/jsx-filename-extension": "off",
1055
- // 禁止可能包含 JSX 文件扩展名
1056
- "react/jsx-fragments": "error",
1057
- "react/jsx-handler-names": "error",
1058
- "react/jsx-key": "error",
1059
- "react/jsx-max-depth": "off",
1060
- // 强制 JSX 最大深度
1061
- "react/jsx-no-bind": "off",
1062
- // .bind()JSX 属性中禁止使用箭头函数
1063
- "react/jsx-no-comment-textnodes": "error",
1064
- "react/jsx-no-constructed-context-values": "error",
1065
- "react/jsx-no-duplicate-props": "error",
1066
- "react/jsx-no-leaked-render": "error",
1067
- "react/jsx-no-literals": "off",
1068
- // 禁止在 JSX 中使用字符串文字
1069
- "react/jsx-no-script-url": "error",
1070
- "react/jsx-no-target-blank": "error",
1071
- "react/jsx-no-undef": "error",
1072
- "react/jsx-no-useless-fragment": "error",
1073
- "react/jsx-props-no-spreading": "off",
1074
- // 强制任何 JSX 属性都不会传播
1075
- "react/jsx-uses-react": "error",
1076
- "react/jsx-uses-vars": "error",
1069
+ // recommended rules from @eslint-react
1070
+ "react/ensure-forward-ref-using-ref": "warn",
1077
1071
  "react/no-access-state-in-setstate": "error",
1078
- "react/no-adjacent-inline-elements": "error",
1079
- "react/no-array-index-key": "error",
1080
- "react/no-arrow-function-lifecycle": "error",
1081
- "react/no-children-prop": "error",
1082
- "react/no-danger": "off",
1083
- // 禁止使用 dangerouslySetInnerHTML
1084
- "react/no-danger-with-children": "error",
1085
- "react/no-deprecated": "error",
1086
- "react/no-did-mount-set-state": "error",
1087
- "react/no-did-update-set-state": "error",
1072
+ "react/no-array-index-key": "warn",
1073
+ "react/no-children-count": "warn",
1074
+ "react/no-children-for-each": "warn",
1075
+ "react/no-children-map": "warn",
1076
+ "react/no-children-only": "warn",
1077
+ "react/no-children-prop": "warn",
1078
+ "react/no-children-to-array": "warn",
1079
+ "react/no-clone-element": "warn",
1080
+ "react/no-comment-textnodes": "warn",
1081
+ "react/no-component-will-mount": "error",
1082
+ "react/no-component-will-receive-props": "error",
1083
+ "react/no-component-will-update": "error",
1084
+ "react/no-create-ref": "error",
1088
1085
  "react/no-direct-mutation-state": "error",
1089
- "react/no-find-dom-node": "error",
1090
- "react/no-invalid-html-attribute": "error",
1091
- "react/no-is-mounted": "error",
1092
- "react/no-multi-comp": "error",
1093
- "react/no-namespace": "error",
1094
- "react/no-object-type-as-default-prop": "error",
1086
+ "react/no-duplicate-key": "error",
1087
+ "react/no-implicit-key": "error",
1088
+ "react/no-missing-key": "error",
1089
+ "react/no-nested-components": "warn",
1095
1090
  "react/no-redundant-should-component-update": "error",
1096
- "react/no-render-return-value": "error",
1097
- "react/no-set-state": "error",
1091
+ "react/no-set-state-in-component-did-mount": "warn",
1092
+ "react/no-set-state-in-component-did-update": "warn",
1093
+ "react/no-set-state-in-component-will-update": "warn",
1098
1094
  "react/no-string-refs": "error",
1099
- "react/no-this-in-sfc": "error",
1100
- "react/no-typos": "error",
1101
- "react/no-unescaped-entities": "error",
1102
- "react/no-unknown-property": "error",
1103
- "react/no-unsafe": "off",
1104
- // 禁止使用不安全的生命周期方法
1105
- "react/no-unstable-nested-components": "error",
1106
- "react/no-unused-class-component-methods": "error",
1107
- "react/no-unused-prop-types": "error",
1108
- "react/no-unused-state": "error",
1109
- "react/no-will-update-set-state": "error",
1110
- "react/prefer-es6-class": "error",
1111
- "react/prefer-exact-props": "error",
1112
- "react/prefer-read-only-props": "error",
1113
- "react/prefer-stateless-function": "error",
1114
- "react/prop-types": "error",
1115
- "react/react-in-jsx-scope": "off",
1116
- // 使用 JSX 时需要引入 React
1117
- "react/require-default-props": "off",
1118
- // 为每个非必需 prop 强制执行 defaultProps 定义
1119
- "react/require-optimization": "error",
1120
- "react/require-render-return": "error",
1121
- "react/self-closing-comp": "error",
1122
- "react/sort-comp": "error",
1123
- "react/sort-default-props": "error",
1124
- "react/sort-prop-types": "error",
1125
- "react/state-in-constructor": "error",
1126
- "react/static-property-placement": "error",
1127
- "react/style-prop-object": "error",
1128
- "react/void-dom-elements-no-children": "error",
1129
- "style/jsx-pascal-case": "error",
1130
- ...typescript2 ? {
1131
- "react/jsx-no-undef": "off",
1132
- "react/prop-type": "off"
1095
+ "react/no-unsafe-component-will-mount": "warn",
1096
+ "react/no-unsafe-component-will-receive-props": "warn",
1097
+ "react/no-unsafe-component-will-update": "warn",
1098
+ "react/no-unstable-context-value": "error",
1099
+ "react/no-unstable-default-props": "error",
1100
+ "react/no-unused-class-component-members": "warn",
1101
+ "react/no-unused-state": "warn",
1102
+ "react/no-useless-fragment": "warn",
1103
+ "react/prefer-destructuring-assignment": "warn",
1104
+ "react/prefer-shorthand-boolean": "warn",
1105
+ "react/prefer-shorthand-fragment": "warn",
1106
+ ...isTypeAware ? {
1107
+ "react/no-leaked-conditional-rendering": "warn"
1133
1108
  } : {},
1109
+ // overrides
1134
1110
  ...overrides
1135
1111
  },
1136
1112
  settings: {
@@ -1960,6 +1936,10 @@ var VuePackages = [
1960
1936
  "@slidev/cli"
1961
1937
  ];
1962
1938
  var defaultPluginRenaming = {
1939
+ "@eslint-react": "react",
1940
+ "@eslint-react/dom": "react-dom",
1941
+ "@eslint-react/hooks-extra": "react-hooks-extra",
1942
+ "@eslint-react/naming-convention": "react-naming-convention",
1963
1943
  "@stylistic": "style",
1964
1944
  "@typescript-eslint": "ts",
1965
1945
  "import-x": "import",
@@ -1985,16 +1965,18 @@ function lincy(options = {}, ...userConfigs) {
1985
1965
  } = options;
1986
1966
  const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
1987
1967
  if (stylisticOptions) {
1988
- if (!("jsx" in stylisticOptions))
1968
+ if (!("jsx" in stylisticOptions)) {
1989
1969
  stylisticOptions.jsx = options.jsx ?? true;
1970
+ }
1990
1971
  }
1991
1972
  const configs = [];
1992
1973
  if (enableGitignore) {
1993
1974
  if (typeof enableGitignore !== "boolean") {
1994
1975
  configs.push(interopDefault(import("eslint-config-flat-gitignore")).then((r) => [r(enableGitignore)]));
1995
1976
  } else {
1996
- if (import_node_fs.default.existsSync(".gitignore"))
1977
+ if (import_node_fs.default.existsSync(".gitignore")) {
1997
1978
  configs.push(interopDefault(import("eslint-config-flat-gitignore")).then((r) => [r()]));
1979
+ }
1998
1980
  }
1999
1981
  }
2000
1982
  configs.push(
@@ -2017,8 +1999,9 @@ function lincy(options = {}, ...userConfigs) {
2017
1999
  // Optional plugins (installed but not enabled by default)
2018
2000
  perfectionist()
2019
2001
  );
2020
- if (enableVue)
2002
+ if (enableVue) {
2021
2003
  componentExts.push("vue");
2004
+ }
2022
2005
  if (enableTypeScript) {
2023
2006
  configs.push(typescript({
2024
2007
  ...typeof enableTypeScript !== "boolean" ? enableTypeScript : {},
@@ -2098,20 +2081,23 @@ function lincy(options = {}, ...userConfigs) {
2098
2081
  ));
2099
2082
  }
2100
2083
  const fusedConfig = flatConfigProps.reduce((acc, key) => {
2101
- if (key in options)
2084
+ if (key in options) {
2102
2085
  acc[key] = options[key];
2086
+ }
2103
2087
  return acc;
2104
2088
  }, {});
2105
- if (Object.keys(fusedConfig).length)
2089
+ if (Object.keys(fusedConfig).length) {
2106
2090
  configs.push([fusedConfig]);
2107
- let pipeline = new import_eslint_flat_config_utils.FlatConfigComposer();
2108
- pipeline = pipeline.append(
2091
+ }
2092
+ let composer = new import_eslint_flat_config_utils.FlatConfigComposer();
2093
+ composer = composer.append(
2109
2094
  ...configs,
2110
2095
  ...userConfigs
2111
2096
  );
2112
- if (autoRenamePlugins)
2113
- pipeline = pipeline.renamePlugins(defaultPluginRenaming);
2114
- return pipeline;
2097
+ if (autoRenamePlugins) {
2098
+ composer = composer.renamePlugins(defaultPluginRenaming);
2099
+ }
2100
+ return composer;
2115
2101
  }
2116
2102
 
2117
2103
  // src/index.ts