@zjutjh/eslint-config 0.4.1 → 0.5.2

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
@@ -78,6 +78,48 @@ export default [
78
78
  $ npm run lint
79
79
  ```
80
80
 
81
+ ## 代码格式化
82
+
83
+ 很多人在意代码的格式化,这里单独拿出一章讲。
84
+
85
+ 支持使用 `@stylistic/eslint-plugin` (lint 工具对格式的检查) 或者传统的 formatter 工具
86
+ (Prettier) 来对代码进行格式化。具体配置放在 `options.codeStyle` 下面。codeStyle 默认开启,以
87
+ `@stylistic/eslint-plugin` 做为默认的风格规范工具。如果要使用 formatter,需要手动开启。
88
+
89
+ > [!IMPORTANT]
90
+ > stylistic 和 formatter 在配置上互斥,选择其中之一即可,我们会保证两者在大部分场景下代码风格的一致性。一般来说,formatter 对格式的要求比 stylistic 更严格。
91
+
92
+ ```ts
93
+ // 启用 formatter,可选传入 prettier 的相关配置
94
+ export default [
95
+ ...(await zjutjh({
96
+ codeStyle: {
97
+ tool: "formatter"
98
+ prettier: {}
99
+ }
100
+ })),
101
+ ];
102
+ ```
103
+
104
+ stylistic 只对 js(x) 和 ts(x) 进行格式化,而 formatter 还对其他文件,如 css,vue template 等配置了格式化。
105
+ 如果你要格式化这些文件,需要配置编辑器来允许 eslint 校验这些类型的文件。
106
+
107
+ ```jsonc
108
+ // 可以参考仓库下的 .vscode/settings.json 给 vscode 配置
109
+ // @filename .vscode/settings.json
110
+ {
111
+ "prettier.enable": false, // 如果安装了 prettier 插件,需要在项目中关闭
112
+ "[scss]": {
113
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint"
114
+ },
115
+ "eslint.validate": [
116
+ "scss"
117
+ ]
118
+ }
119
+ ```
120
+ > [!WARNING]
121
+ > 我们使用 eslint 调用 prettier 可执行文件来进行代码格式化,所以你的编辑器不需要安装 prettier 插件,有 eslint 插件就行。prettier 的格式化配置声明在配置源码内部,如果启用了 prettier 插件,他读取不到内部的配置,会按照默认的配置来格式化,这会导致代码风格不一致。
122
+
81
123
  ## 开发指南
82
124
 
83
125
  推荐使用官方的 [@eslint/config-inspector](https://github.com/eslint/config-inspector) 来可视化调试配置。
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ type OptionsConfig = {
10
10
  vue?: boolean;
11
11
  ts?: boolean | (OptionsOverrides & OptionsTypeScriptParserOptions);
12
12
  taro?: boolean;
13
+ codeStyle?: boolean | OptionsCodeStyle<string>;
13
14
  overrides?: OverridesConfigs;
14
15
  };
15
16
  type OptionsOverrides = {
@@ -18,6 +19,21 @@ type OptionsOverrides = {
18
19
  interface OptionsTypeScriptParserOptions {
19
20
  parserOptions?: Partial<ParserOptions>;
20
21
  }
22
+ type OptionsStylistic = {
23
+ tool: "stylistic";
24
+ };
25
+ type OptionsFormatter = {
26
+ tool: "formatter";
27
+ /**
28
+ * 自定义 prettier 配置
29
+ * @see https://prettier.io/docs/options
30
+ */
31
+ prettier?: Record<string, any>;
32
+ };
33
+ /**
34
+ * 默认使用 `@stylistic/eslint-plugin` 作为 formatter
35
+ */
36
+ type OptionsCodeStyle<T extends string> = T extends OptionsStylistic["tool"] ? OptionsStylistic : OptionsFormatter;
21
37
 
22
38
  declare function zjutjh(options?: OptionsConfig): Promise<Linter.Config<Linter.RulesRecord>[]>;
23
39
 
package/dist/index.js CHANGED
@@ -1,6 +1,147 @@
1
1
  // src/factory.ts
2
2
  import { isPackageExists as isPackageExists2 } from "local-pkg";
3
3
 
4
+ // src/utils.ts
5
+ import { isPackageExists } from "local-pkg";
6
+ async function interopDefault(m) {
7
+ const resolved = await m;
8
+ return resolved.default || resolved;
9
+ }
10
+ async function ensurePackages(packages) {
11
+ const nonExistingPackages = packages.filter((i) => i && !isPackageExists(i));
12
+ if (nonExistingPackages.length !== 0) {
13
+ const message = `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}.`;
14
+ throw new Error(message);
15
+ }
16
+ }
17
+ function resolveSubOptions(options, key) {
18
+ if (typeof options[key] === "boolean") {
19
+ return {};
20
+ }
21
+ return options[key] || {};
22
+ }
23
+ function getOverrides(options, key) {
24
+ return {
25
+ ...options.overrides?.[key]
26
+ };
27
+ }
28
+
29
+ // src/configs/formatter.ts
30
+ var prettierOptions = {
31
+ printWidth: 100,
32
+ tabWidth: 2,
33
+ useTabs: false,
34
+ semi: true,
35
+ singleQuote: false,
36
+ quoteProps: "as-needed",
37
+ jsxSingleQuote: false,
38
+ trailingComma: "none",
39
+ bracketSpacing: true,
40
+ bracketSameLine: false,
41
+ arrowParens: "always",
42
+ requirePragma: false,
43
+ insertPragma: false,
44
+ proseWrap: "preserve",
45
+ htmlWhitespaceSensitivity: "css",
46
+ vueIndentScriptAndStyle: false,
47
+ endOfLine: "lf",
48
+ embeddedLanguageFormatting: "auto",
49
+ singleAttributePerLine: false
50
+ };
51
+ async function formatter(options) {
52
+ await ensurePackages([
53
+ "eslint-plugin-format"
54
+ ]);
55
+ const [
56
+ pluginFormat
57
+ ] = await Promise.all([
58
+ interopDefault(import("eslint-plugin-format"))
59
+ ]);
60
+ const mergedPrettierOptions = {
61
+ ...prettierOptions,
62
+ ...options?.prettier
63
+ };
64
+ return [
65
+ {
66
+ name: "zjutjh/formatter/setup",
67
+ plugins: {
68
+ format: pluginFormat
69
+ }
70
+ },
71
+ {
72
+ name: "zjutjh/formatter/css/rules",
73
+ languageOptions: {
74
+ parser: pluginFormat.parserPlain
75
+ },
76
+ files: [
77
+ "**/*.less",
78
+ "**/*.css",
79
+ "**/*.scss",
80
+ "**/*.sass"
81
+ ],
82
+ rules: {
83
+ "format/prettier": ["error", { parser: "css", ...mergedPrettierOptions }]
84
+ }
85
+ },
86
+ {
87
+ name: "zjutjh/formatter/vue/rules",
88
+ languageOptions: {
89
+ parser: pluginFormat.parserPlain
90
+ },
91
+ files: [
92
+ "**/*.vue"
93
+ ],
94
+ rules: {
95
+ "format/prettier": ["error", { parser: "vue", ...mergedPrettierOptions }]
96
+ }
97
+ },
98
+ {
99
+ name: "zjutjh/formatter/json/rules",
100
+ languageOptions: {
101
+ parser: pluginFormat.parserPlain
102
+ },
103
+ files: [
104
+ "**/*.json"
105
+ ],
106
+ rules: {
107
+ "format/prettier": ["error", { parser: "json", ...mergedPrettierOptions }]
108
+ }
109
+ },
110
+ {
111
+ name: "zjutjh/formatter/html/rules",
112
+ languageOptions: {
113
+ parser: pluginFormat.parserPlain
114
+ },
115
+ files: [
116
+ "**/*.html"
117
+ ],
118
+ rules: {
119
+ "format/prettier": ["error", { parser: "html", ...mergedPrettierOptions }]
120
+ }
121
+ },
122
+ {
123
+ name: "zjutjh/formatter/js/rules",
124
+ languageOptions: {
125
+ parser: pluginFormat.parserPlain
126
+ },
127
+ files: ["**/*.js", "**/*.jsx"],
128
+ rules: {
129
+ "format/prettier": ["error", { parser: "babel", ...mergedPrettierOptions }]
130
+ }
131
+ },
132
+ {
133
+ name: "zjutjh/formatter/ts/rules",
134
+ languageOptions: {
135
+ parser: pluginFormat.parserPlain
136
+ },
137
+ files: ["**/*.ts", "**/*.tsx"],
138
+ rules: {
139
+ "format/prettier": ["error", { parser: "typescript", ...mergedPrettierOptions }]
140
+ }
141
+ }
142
+ ];
143
+ }
144
+
4
145
  // src/configs/imports.ts
5
146
  import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
6
147
  function imports() {
@@ -57,10 +198,15 @@ function javascript() {
57
198
  ...eslintJS.configs.recommended.rules,
58
199
  "camelcase": "warn",
59
200
  "no-warning-comments": "warn",
60
- "no-console": ["warn", { allow: ["warn", "error"] }],
201
+ "no-console": ["warn", { allow: ["warn", "error", "info"] }],
61
202
  "no-var": "error",
62
203
  "no-undef": "off",
63
- "prefer-const": "warn"
204
+ "prefer-const": "warn",
205
+ "arrow-body-style": "error",
206
+ "no-nested-ternary": "error",
207
+ "curly": "error",
208
+ "no-else-return": "error",
209
+ "no-implicit-coercion": "error"
64
210
  }
65
211
  }
66
212
  ];
@@ -109,31 +255,6 @@ function stylistic(options) {
109
255
  ];
110
256
  }
111
257
 
112
- // src/utils.ts
113
- import { isPackageExists } from "local-pkg";
114
- async function interopDefault(m) {
115
- const resolved = await m;
116
- return resolved.default || resolved;
117
- }
118
- async function ensurePackages(packages) {
119
- const nonExistingPackages = packages.filter((i) => i && !isPackageExists(i));
120
- if (nonExistingPackages.length !== 0) {
121
- const message = `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}.`;
122
- throw new Error(message);
123
- }
124
- }
125
- function resolveSubOptions(options, key) {
126
- if (typeof options[key] === "boolean") {
127
- return {};
128
- }
129
- return options[key] || {};
130
- }
131
- function getOverrides(options, key) {
132
- return {
133
- ...options.overrides?.[key]
134
- };
135
- }
136
-
137
258
  // src/configs/typescript.ts
138
259
  async function typescript(options) {
139
260
  const {
@@ -155,6 +276,7 @@ async function typescript(options) {
155
276
  {
156
277
  name: "zjutjh/typescript/setup",
157
278
  plugins: {
279
+ // @ts-expect-error 依赖的类型有问题,不影响使用
158
280
  "@typescript-eslint": pluginTs
159
281
  }
160
282
  },
@@ -180,8 +302,10 @@ async function typescript(options) {
180
302
  rules: {
181
303
  ...pluginTs.configs.strict.rules,
182
304
  "@typescript-eslint/ban-ts-comment": ["error", { "ts-expect-error": "allow-with-description" }],
305
+ "@typescript-eslint/no-shadow": "error",
306
+ "@typescript-eslint/no-non-null-assertion": "error",
307
+ "@typescript-eslint/no-empty-function": "error",
183
308
  "@typescript-eslint/no-explicit-any": "off",
184
- "@typescript-eslint/no-non-null-assertion": "off",
185
309
  "@typescript-eslint/no-unused-expressions": ["error", {
186
310
  allowShortCircuit: true,
187
311
  allowTaggedTemplates: true,
@@ -232,7 +356,7 @@ async function vue(options) {
232
356
  ...pluginVue.configs["flat/recommended"].map((c) => c.rules).reduce((prev, curr) => ({ ...prev, ...curr }), {}),
233
357
  ...pluginVue.configs["flat/essential"].map((c) => c.rules).reduce((prev, curr) => ({ ...prev, ...curr }), {}),
234
358
  ...pluginVue.configs["flat/strongly-recommended"].map((c) => c.rules).reduce((prev, curr) => ({ ...prev, ...curr }), {}),
235
- "vue/multi-word-component-names": "warn",
359
+ "vue/multi-word-component-names": ["warn", { ignores: ["index"] }],
236
360
  "vue/component-name-in-template-casing": ["error", "kebab-case", { "registeredComponentsOnly": true }],
237
361
  "vue/max-attributes-per-line": ["error", { "singleline": { "max": 3 } }],
238
362
  "vue/prefer-true-attribute-shorthand": ["warn", options?.taro ? "never" : "always"],
@@ -247,30 +371,42 @@ async function zjutjh(options = {}) {
247
371
  const {
248
372
  vue: enableVue = isPackageExists2("vue"),
249
373
  ts: enableTs = isPackageExists2("typescript"),
250
- taro: enableTaro = isPackageExists2("@tarojs/taro")
374
+ taro: enableTaro = isPackageExists2("@tarojs/taro"),
375
+ codeStyle: enableCodeStyle = true
251
376
  } = options;
252
377
  const configs = [];
253
378
  configs.push(javascript());
254
- configs.push(
255
- stylistic({ overrides: getOverrides(options, "stylistic") })
256
- );
257
379
  configs.push(imports());
258
380
  const typescriptOptions = resolveSubOptions(options, "ts");
259
- if (enableTs) configs.push(
260
- await typescript({
261
- ...typescriptOptions,
262
- overrides: getOverrides(options, "ts")
263
- })
264
- );
381
+ if (enableTs) {
382
+ configs.push(
383
+ await typescript({
384
+ ...typescriptOptions,
385
+ overrides: getOverrides(options, "ts")
386
+ })
387
+ );
388
+ }
265
389
  if (enableVue) {
266
390
  configs.push(
267
391
  await vue({
268
- ts: !!enableTs,
392
+ ts: Boolean(enableTs),
269
393
  taro: enableTaro,
270
394
  overrides: getOverrides(options, "vue")
271
395
  })
272
396
  );
273
397
  }
398
+ const codeStyleOptions = resolveSubOptions(options, "codeStyle");
399
+ if (enableCodeStyle) {
400
+ if (codeStyleOptions.tool === "formatter") {
401
+ configs.push(await formatter(codeStyleOptions));
402
+ } else {
403
+ configs.push(
404
+ stylistic({
405
+ overrides: getOverrides(options, "stylistic")
406
+ })
407
+ );
408
+ }
409
+ }
274
410
  return configs.flat(1);
275
411
  }
276
412
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zjutjh/eslint-config",
3
3
  "type": "module",
4
- "version": "0.4.1",
4
+ "version": "0.5.2",
5
5
  "license": "ISC",
6
6
  "author": "zjutjh",
7
7
  "description": "Eslint config used by zjutjh",
@@ -10,10 +10,10 @@
10
10
  ],
11
11
  "exports": {
12
12
  ".": {
13
- "import": "./dist/index.js"
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
14
15
  }
15
16
  },
16
- "types": "./dist/index.d.ts",
17
17
  "engines": {
18
18
  "node": ">=18.18.0"
19
19
  },
@@ -25,7 +25,8 @@
25
25
  "@typescript-eslint/parser": "^8.26.1",
26
26
  "eslint-plugin-vue": "^10.0.0",
27
27
  "vue-eslint-parser": "^10.1.1",
28
- "eslint": "^9.9.0"
28
+ "eslint": "^9.9.0",
29
+ "eslint-plugin-format": "^1.0.1"
29
30
  },
30
31
  "peerDependenciesMeta": {
31
32
  "@typescript-eslint/eslint-plugin": {
@@ -39,6 +40,9 @@
39
40
  },
40
41
  "vue-eslint-parser": {
41
42
  "optional": true
43
+ },
44
+ "eslint-plugin-format": {
45
+ "optional": true
42
46
  }
43
47
  },
44
48
  "dependencies": {
@@ -55,6 +59,7 @@
55
59
  "@typescript-eslint/parser": "^8.26.1",
56
60
  "bumpp": "^10.1.0",
57
61
  "eslint": "^9.9.0",
62
+ "eslint-plugin-format": "^1.0.1",
58
63
  "eslint-plugin-vue": "^10.0.0",
59
64
  "jiti": "^2.4.2",
60
65
  "tsup": "^8.4.0",
@@ -66,6 +71,7 @@
66
71
  "lint": "eslint .",
67
72
  "typecheck": "tsc --noEmit",
68
73
  "dev": "config-inspector",
69
- "release": "pnpm build && bumpp && pnpm publish --access public"
74
+ "release": "bumpp && pnpm publish --access public",
75
+ "prepublish": "pnpm build"
70
76
  }
71
77
  }