eslint-plugin-light 0.0.18
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 +132 -0
- package/README.md +167 -0
- package/lib/configs/base.js +16 -0
- package/lib/configs/essential.js +5 -0
- package/lib/configs/recommended.js +7 -0
- package/lib/index.js +28 -0
- package/lib/rules/no-complex-key.js +62 -0
- package/lib/rules/no-import-vant.js +70 -0
- package/lib/rules/no-plus-turn-number.js +47 -0
- package/lib/rules/no-this-tip-uid.js +40 -0
- package/lib/rules/valid-vue-comp-import.js +281 -0
- package/package.json +51 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
|
+
|
|
5
|
+
### [0.0.16](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.15...v0.0.16) (2022-07-26)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features | 新功能
|
|
9
|
+
|
|
10
|
+
* 去掉打印信息 ([bde64c7](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/bde64c765562f5842aa988ee07d2caac27786a6c))
|
|
11
|
+
|
|
12
|
+
### [0.0.15](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.14...v0.0.15) (2022-07-26)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### ✨ Features | 新功能
|
|
16
|
+
|
|
17
|
+
* 优化包 ([906bcd7](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/906bcd7a2ff0291d331feef961b479135f11c526))
|
|
18
|
+
|
|
19
|
+
### [0.0.14](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.13...v0.0.14) (2022-07-26)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### ✨ Features | 新功能
|
|
23
|
+
|
|
24
|
+
* 脚本优化 ([d1e8172](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/d1e8172ac3777f1377864d3de76a50c55e4122dd))
|
|
25
|
+
* 脚本优化 ([9c6d844](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/9c6d844aad15475e1af7d97728dfcae84bd99693))
|
|
26
|
+
* **complex-key:** 不要使用复杂的key ([00e981b](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/00e981b8d81e097576163fa8078a09a315a22cba))
|
|
27
|
+
|
|
28
|
+
### [0.0.13](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.12...v0.0.13) (2022-07-26)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### ✨ Features | 新功能
|
|
32
|
+
|
|
33
|
+
* 优化脚本 ([ad9b142](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/ad9b142ee87aba73d45adcef3aa94ac3d5988b31))
|
|
34
|
+
|
|
35
|
+
### [0.0.12](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.11...v0.0.12) (2022-07-26)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### ✨ Features | 新功能
|
|
39
|
+
|
|
40
|
+
* 脚本优化 ([62f1181](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/62f11815bb458201991cca99716e773a3281c65b))
|
|
41
|
+
|
|
42
|
+
### [0.0.11](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.10...v0.0.11) (2022-07-26)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### ✨ Features | 新功能
|
|
46
|
+
|
|
47
|
+
* 脚本优化 ([3047f01](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/3047f01b89fdbdb8c72b0c5e511afb587d8ffc99))
|
|
48
|
+
* 优化发布脚本 ([a072ae8](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/a072ae8715352c9452072e8dfa9c85a343cf4f26))
|
|
49
|
+
|
|
50
|
+
### [0.0.10](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.9...v0.0.10) (2022-07-26)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### ✨ Features | 新功能
|
|
54
|
+
|
|
55
|
+
* 优化发布脚本 ([470a35f](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/470a35fc55153d41a362cf61492fde120a9ed3f7))
|
|
56
|
+
* 优化error信息 ([453430f](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/453430ff437d062cd1efa7bff96b87e975b2b414))
|
|
57
|
+
|
|
58
|
+
### [0.0.9](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.8...v0.0.9) (2022-07-22)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
### ✨ Features | 新功能
|
|
62
|
+
|
|
63
|
+
* 优化插件 ([13a84d5](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/13a84d5ce25788f5d3b3b63b84a0731f179ff12d))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### ✏️ Documentation | 文档
|
|
67
|
+
|
|
68
|
+
* 用法更新 ([78e344d](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/78e344d61c026f7c38960d41d7038888664ecb85))
|
|
69
|
+
|
|
70
|
+
### [0.0.8](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.7...v0.0.8) (2022-07-22)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### ✏️ Documentation | 文档
|
|
74
|
+
|
|
75
|
+
* 更新文档 ([d6e3a23](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/d6e3a23205244f41ecb8b8fff3bb9d9a07a2c734))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### 🚀 Chore | 构建/工程依赖/工具
|
|
79
|
+
|
|
80
|
+
* eslint ([63e2398](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/63e2398387b466c4e188a7f12a2d08586583f527))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### ✨ Features | 新功能
|
|
84
|
+
|
|
85
|
+
* **number:** 优化插件 ([003f290](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/003f290563b15d4cc1e8a6585a629bbfba2fce1e))
|
|
86
|
+
|
|
87
|
+
### [0.0.7](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.6...v0.0.7) (2022-07-22)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### ✏️ Documentation | 文档
|
|
91
|
+
|
|
92
|
+
* 文档优化 ([e7f5bfb](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/e7f5bfb9e67ac3f7cb9ea182200886feb22c9798))
|
|
93
|
+
* 优化文档 ([44e7b1e](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/44e7b1e5f5e230363dcfd6923d325e96db9509f4))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### ✨ Features | 新功能
|
|
97
|
+
|
|
98
|
+
* **no-plus-turn-number:** 转化+字符串 ([aa69796](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/aa69796620c554d745c2b5133b9699c9be363805))
|
|
99
|
+
|
|
100
|
+
### [0.0.6](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.5...v0.0.6) (2022-07-22)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### ✨ Features | 新功能
|
|
104
|
+
|
|
105
|
+
* 优化插件 ([c0e28fd](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/c0e28fda4d5ba7c6c90a0ad7fe799342b79e9b49))
|
|
106
|
+
|
|
107
|
+
### [0.0.5](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.4...v0.0.5) (2022-07-22)
|
|
108
|
+
|
|
109
|
+
### [0.0.4](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.3...v0.0.4) (2022-07-22)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
### 🐛 Bug Fixes | Bug 修复
|
|
113
|
+
|
|
114
|
+
* 兼容性处理 ([ba123c2](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/ba123c26093d66008e459cdc3bfaf5fea74588e1))
|
|
115
|
+
|
|
116
|
+
### [0.0.3](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/compare/v0.0.2...v0.0.3) (2022-07-22)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### 🚀 Chore | 构建/工程依赖/工具
|
|
120
|
+
|
|
121
|
+
* 增加versionrc ([1a8b239](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/1a8b2393618cbb16003298a1ec5e8b9bf44fb779))
|
|
122
|
+
* version ([dda6406](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/dda640604dd0bd34bf8937692781edd95b99d6ed))
|
|
123
|
+
|
|
124
|
+
### 0.0.2 (2022-07-22)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Features
|
|
128
|
+
|
|
129
|
+
* 安装version ([a295b91](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/a295b91936a3eaeeda3bbc53d3221d0c0ee4c6f8))
|
|
130
|
+
* 优化插件 ([803d743](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/803d7434d653776dd70d0ec85b8eb0c20bdbc578))
|
|
131
|
+
* init ([44f3cb3](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/44f3cb326e7fca1b4b4b54ec8f361b491b84f996))
|
|
132
|
+
* vue组件规则 ([80b3690](https://git.woa.com/pmd-mobile/support/eslint-plugin-pmd/commit/80b36908061aab76995f6b7bf1d7f6930bb6f7eb))
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# eslint-plugin-pmd
|
|
2
|
+
|
|
3
|
+
eslint插件
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## 1. Installation
|
|
7
|
+
|
|
8
|
+
You'll first need to install [ESLint](https://eslint.org/):
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm i eslint --save-dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Next, install `eslint-plugin-pmd`:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install @tencent/eslint-plugin-pmd --save-dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 2. Usage
|
|
21
|
+
|
|
22
|
+
Add `pmd` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"plugins": [
|
|
27
|
+
"pmd"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Then configure the rules you want to use under the rules section.
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"rules": {
|
|
38
|
+
"pmd/rule-name": 2
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
or use extends:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
extends: ['plugin:pmd/recommended'],
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 3. Supported Rules
|
|
53
|
+
|
|
54
|
+
### 3.1. pmd/valid-vue-comp-import
|
|
55
|
+
|
|
56
|
+
禁止从js文件中加载Vue组件
|
|
57
|
+
|
|
58
|
+
比如,
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
1. 导入地址是js/ts文件
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import SomeComp from 'src/local-component/ui/pages/user/account-manage/index.js';
|
|
65
|
+
|
|
66
|
+
// 或者省略了js/ts文件后缀
|
|
67
|
+
import SomeComp from 'src/local-component/ui/pages/user/account-manage/index';
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
如果加了--fix,会被转换为:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import SomeComp from 'src/local-component/ui/pages/user/account-manage/xxx.vue';
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
注意上面的`xxx.vue`是从`index.js`中分析得到的原始文件路径。
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
2. 导入一个目录,但目录存在index.js,这时候不管存不存在index.vue,uniapp转换都会失败
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
import SomeComp from 'src/local-component/ui/pages/user/account-manage';
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
可转换为:
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
import SomeComp from 'src/local-component/ui/pages/user/account-manage/xxx.vue';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
3. 具名导入
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import {
|
|
96
|
+
AComp,
|
|
97
|
+
BComp,
|
|
98
|
+
CComp,
|
|
99
|
+
DComp,
|
|
100
|
+
} from './comp';
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
可转换为:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
import AComp from 'src/local-component/module/tip-match/tip-match-schedule-tree-new/comp/a.vue';
|
|
107
|
+
import BComp from 'src/local-component/module/tip-match/tip-match-schedule-tree-new/comp/b.vue';
|
|
108
|
+
import CComp from 'src/local-component/module/tip-match/tip-match-schedule-tree-new/comp/c.vue';
|
|
109
|
+
import DComp from 'src/local-component/module/tip-match/tip-match-schedule-tree-new/comp/d.vue';
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3.2. pmd/no-plus-turn-number
|
|
113
|
+
|
|
114
|
+
禁止在vue的template中用+号转换字符串为数字
|
|
115
|
+
|
|
116
|
+
比如:
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<ScheduleItem
|
|
120
|
+
:child-id="+childId"
|
|
121
|
+
/>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
如果加了--fix,会被转化成:
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<ScheduleItem
|
|
128
|
+
:child-id="parseInt(childId, 10)"
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3.3 no-complex-key
|
|
133
|
+
|
|
134
|
+
不要在vue模板中使用复杂的key。包括:
|
|
135
|
+
|
|
136
|
+
1. 字符串拼接,如:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
:key="`hold` + index"`
|
|
140
|
+
```
|
|
141
|
+
2. 模板字符串,如:
|
|
142
|
+
```
|
|
143
|
+
:key="`hold-${index}`"
|
|
144
|
+
```
|
|
145
|
+
3. 将key提到一个函数中,如:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
:key="getHoldKey(index)"
|
|
149
|
+
|
|
150
|
+
getHoldKey(index) {
|
|
151
|
+
return `hold${index}`
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
最佳方式其实是提前处理好数据,这样性能会更好,也避免了key重复。
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
getData() {
|
|
159
|
+
items = items.map((item,index) => ({
|
|
160
|
+
...item,
|
|
161
|
+
key: `hold-${index}`
|
|
162
|
+
}))
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
uniapp中,key重复的话,会造成挂载在组件上面的事件参数为undefined,从而不成功。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
plugins: ['@tencent/eslint-plugin-pmd', 'vue'],
|
|
3
|
+
parser: require.resolve('vue-eslint-parser'),
|
|
4
|
+
parserOptions: {
|
|
5
|
+
ecmaVersion: 2020,
|
|
6
|
+
sourceType: 'module',
|
|
7
|
+
},
|
|
8
|
+
env: {
|
|
9
|
+
browser: true,
|
|
10
|
+
es6: true,
|
|
11
|
+
},
|
|
12
|
+
rules: {
|
|
13
|
+
'@tencent/pmd/valid-vue-comp-import': 2,
|
|
14
|
+
'@tencent/pmd/no-plus-turn-number': 2,
|
|
15
|
+
},
|
|
16
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview test
|
|
3
|
+
* @author
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
// ------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
// ------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const requireIndex = require('requireindex');
|
|
12
|
+
|
|
13
|
+
// ------------------------------------------------------------------------------
|
|
14
|
+
// Plugin Definition
|
|
15
|
+
// ------------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// import all rules in lib/rules
|
|
19
|
+
module.exports = {
|
|
20
|
+
rules: requireIndex(`${__dirname}/rules`),
|
|
21
|
+
configs: {
|
|
22
|
+
base: require('./configs/base'),
|
|
23
|
+
essential: require('./configs/essential'),
|
|
24
|
+
recommended: require('./configs/recommended'),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'problem',
|
|
4
|
+
schema: [],
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'vue模板中不要使用复杂的key',
|
|
7
|
+
},
|
|
8
|
+
messages: {
|
|
9
|
+
error: 'Do not use complex key',
|
|
10
|
+
funcError: 'Do not use function as key',
|
|
11
|
+
stringError: 'Do not use string concatenation as key',
|
|
12
|
+
tplError: 'Do not use template string as key',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
create(context) {
|
|
17
|
+
const fileName = context.getFilename();
|
|
18
|
+
if (!fileName.endsWith('.vue')) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
if (!context.parserServices?.defineTemplateBodyVisitor) {
|
|
22
|
+
return {
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return context.parserServices.defineTemplateBodyVisitor({
|
|
27
|
+
VAttribute(node) {
|
|
28
|
+
if (!node.key
|
|
29
|
+
|| node.key.type !== 'VDirectiveKey'
|
|
30
|
+
|| node.key?.argument?.type !== 'VIdentifier'
|
|
31
|
+
|| node.key?.argument?.name !== 'key'
|
|
32
|
+
) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (node?.value?.type === 'VExpressionContainer') {
|
|
37
|
+
if (node.value.expression?.type === 'CallExpression') {
|
|
38
|
+
// :key="getHoldKey(item)"
|
|
39
|
+
context.report({
|
|
40
|
+
node,
|
|
41
|
+
messageId: 'funcError',
|
|
42
|
+
});
|
|
43
|
+
} else if (node.value.expression?.type === 'BinaryExpression') {
|
|
44
|
+
// :key="index + 'hold'"
|
|
45
|
+
context.report({
|
|
46
|
+
node,
|
|
47
|
+
messageId: 'stringError',
|
|
48
|
+
});
|
|
49
|
+
} else if (node.value.expression?.type === 'TemplateLiteral') {
|
|
50
|
+
// :key="`${index}hold`"
|
|
51
|
+
context.report({
|
|
52
|
+
node,
|
|
53
|
+
messageId: 'tplError',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 合法的是MemberExpression,比如item.key
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'problem',
|
|
4
|
+
schema: [],
|
|
5
|
+
docs: {
|
|
6
|
+
description: '不要直接引用vant',
|
|
7
|
+
},
|
|
8
|
+
fixable: 'code',
|
|
9
|
+
messages: {
|
|
10
|
+
error: 'Do not import vant',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
create(context) {
|
|
15
|
+
const fileName = context.getFilename();
|
|
16
|
+
if (!fileName.endsWith('.vue')) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
ImportDeclaration: (node) => {
|
|
21
|
+
if (node.source.value === 'vant') {
|
|
22
|
+
context.report({
|
|
23
|
+
node,
|
|
24
|
+
messageId: 'error',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
// if (!context.parserServices?.defineTemplateBodyVisitor) {
|
|
30
|
+
// return {
|
|
31
|
+
// };
|
|
32
|
+
// }
|
|
33
|
+
|
|
34
|
+
// return context.parserServices.defineTemplateBodyVisitor({
|
|
35
|
+
// VAttribute(node) {
|
|
36
|
+
// if (!node.key
|
|
37
|
+
// || node.key.type !== 'VDirectiveKey'
|
|
38
|
+
// || node.key?.argument?.type !== 'VIdentifier'
|
|
39
|
+
// || node.key?.argument?.name !== 'key'
|
|
40
|
+
// ) {
|
|
41
|
+
// return;
|
|
42
|
+
// }
|
|
43
|
+
|
|
44
|
+
// if (node?.value?.type === 'VExpressionContainer') {
|
|
45
|
+
// if (node.value.expression?.type === 'CallExpression') {
|
|
46
|
+
// // :key="getHoldKey(item)"
|
|
47
|
+
// context.report({
|
|
48
|
+
// node,
|
|
49
|
+
// messageId: 'funcError',
|
|
50
|
+
// });
|
|
51
|
+
// } else if (node.value.expression?.type === 'BinaryExpression') {
|
|
52
|
+
// // :key="index + 'hold'"
|
|
53
|
+
// context.report({
|
|
54
|
+
// node,
|
|
55
|
+
// messageId: 'stringError',
|
|
56
|
+
// });
|
|
57
|
+
// } else if (node.value.expression?.type === 'TemplateLiteral') {
|
|
58
|
+
// // :key="`${index}hold`"
|
|
59
|
+
// context.report({
|
|
60
|
+
// node,
|
|
61
|
+
// messageId: 'tplError',
|
|
62
|
+
// });
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
// // 合法的是MemberExpression,比如item.key
|
|
66
|
+
// }
|
|
67
|
+
// },
|
|
68
|
+
// });
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'problem',
|
|
4
|
+
schema: [],
|
|
5
|
+
docs: {
|
|
6
|
+
description: 'vue模板中不能使用+号转换字符串',
|
|
7
|
+
},
|
|
8
|
+
fixable: 'code',
|
|
9
|
+
messages: {
|
|
10
|
+
error: 'Do not use plus symbol to transfer string',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
create(context) {
|
|
15
|
+
const fileName = context.getFilename();
|
|
16
|
+
if (!fileName.endsWith('.vue')) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
if (!context.parserServices?.defineTemplateBodyVisitor) {
|
|
20
|
+
return {
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return context.parserServices.defineTemplateBodyVisitor({
|
|
25
|
+
VAttribute(node) {
|
|
26
|
+
if (node.value && node.value.type === 'VExpressionContainer' && node.value.expression) {
|
|
27
|
+
const { operator } = node.value.expression;
|
|
28
|
+
const { argument } = node.value.expression;
|
|
29
|
+
|
|
30
|
+
// console.log('operator',operator)
|
|
31
|
+
// console.log('argument',argument && argument.type)
|
|
32
|
+
|
|
33
|
+
if (operator === '+' && argument && argument.type === 'Identifier') {
|
|
34
|
+
const { name } = node.value.expression.argument;
|
|
35
|
+
context.report({
|
|
36
|
+
node,
|
|
37
|
+
messageId: 'error',
|
|
38
|
+
fix(fixer) {
|
|
39
|
+
return fixer.replaceTextRange(node.value.expression.range, `parseInt(${name},10)`);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview no use this.tip_uid
|
|
3
|
+
* @author junshao
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
// ------------------------------------------------------------------------------
|
|
8
|
+
// Rule Definition
|
|
9
|
+
// ------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
12
|
+
module.exports = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
schema: [],
|
|
16
|
+
docs: {
|
|
17
|
+
description: '不能使用this.tip_uid',
|
|
18
|
+
},
|
|
19
|
+
fixable: null, // Or `code` or `whitespace`,
|
|
20
|
+
messages: {
|
|
21
|
+
noUseTipUidId: '不能使用this.tip_uid',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
create(context) {
|
|
26
|
+
return {
|
|
27
|
+
MemberExpression: (node) => {
|
|
28
|
+
if (node.object.type === 'ThisExpression'
|
|
29
|
+
&& node.property.type === 'Identifier'
|
|
30
|
+
&& node.property.name === 'tip_uid') {
|
|
31
|
+
context.report({
|
|
32
|
+
node,
|
|
33
|
+
messageId: 'noUseTipUidId',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
// visitor functions for different types of nodes
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const jsParser = require('@babel/eslint-parser');
|
|
4
|
+
|
|
5
|
+
const ROOT_PATH = process.cwd();
|
|
6
|
+
|
|
7
|
+
const VALID_FILES = ['js', 'ts', 'vue'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 获取引入文件的真实位置,如果是目录,返回目录,如果是文件,返回带后缀的文件
|
|
11
|
+
*/
|
|
12
|
+
function findCompDir(source = '', dirname = '') {
|
|
13
|
+
let compFile;
|
|
14
|
+
if (source.startsWith('.')) {
|
|
15
|
+
compFile = path.resolve(dirname, source);
|
|
16
|
+
} else if (source.startsWith('src')) {
|
|
17
|
+
compFile = path.resolve(process.cwd(), source);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (fs.existsSync(compFile)) {
|
|
21
|
+
return compFile;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < VALID_FILES.length; i++) {
|
|
25
|
+
const postfix = VALID_FILES[i];
|
|
26
|
+
const wholeFileName = `${compFile}.${postfix}`;
|
|
27
|
+
if (fs.existsSync(wholeFileName)) {
|
|
28
|
+
return wholeFileName;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
console.log('\x1B[31m%s\x1B[0m', '未找到引入文件');
|
|
32
|
+
return compFile;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getSourceAST(sourceFile) {
|
|
36
|
+
const jsData = fs.readFileSync(sourceFile, {
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let scriptAST = undefined;
|
|
41
|
+
|
|
42
|
+
const opts = {
|
|
43
|
+
sourceType: 'module',
|
|
44
|
+
requireConfigFile: false,
|
|
45
|
+
babelOptions: {
|
|
46
|
+
configFile: false,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
scriptAST = jsParser.parse(jsData, opts);
|
|
52
|
+
delete scriptAST.tokens;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.log('出错了:', e);
|
|
55
|
+
}
|
|
56
|
+
return scriptAST;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function getDefaultExportComp(ast) {
|
|
61
|
+
if (!ast) return '';
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const namedDeclarationNodes = ast.body.filter(item => item.type === 'ExportNamedDeclaration');
|
|
65
|
+
|
|
66
|
+
const namedObj = {};
|
|
67
|
+
// console.log('namedDeclarationNodes',namedDeclarationNodes.length)
|
|
68
|
+
for (let i = 0;i < namedDeclarationNodes.length;i++) {
|
|
69
|
+
const node = namedDeclarationNodes[i];
|
|
70
|
+
// console.log('node',node)
|
|
71
|
+
if (!node.specifiers || !node.specifiers.length) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// console.log('i',node)
|
|
75
|
+
const { value } = node.source;
|
|
76
|
+
const { name } = node.specifiers[0].exported;
|
|
77
|
+
namedObj[name] = value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// console.log('namedObj', namedObj);
|
|
81
|
+
|
|
82
|
+
if (namedObj.default) {
|
|
83
|
+
return namedObj;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const nodes = ast.body.filter(item => item.type === 'ExportDefaultDeclaration') || [];
|
|
87
|
+
if (nodes[0] && nodes[0].declaration.type === 'Identifier') {
|
|
88
|
+
// 是一个变量,代表从上面导入的
|
|
89
|
+
const { name } = nodes[0].declaration;
|
|
90
|
+
const importAst = ast.body.filter(item => item.type === 'ImportDeclaration' && !!item.specifiers.find(it => it.local.name === name));
|
|
91
|
+
// console.log('importAst', importAst)
|
|
92
|
+
namedObj.default = importAst[0].source.value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return namedObj;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getRelativePath(originPath = '') {
|
|
99
|
+
return originPath.replace(`${path.resolve(ROOT_PATH)}/`, '');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getDefaultExportPathFromSouceFile(sourceFile) {
|
|
103
|
+
const scriptAST = getSourceAST(sourceFile);
|
|
104
|
+
// console.log('scriptAST', scriptAST)
|
|
105
|
+
|
|
106
|
+
const defaultExportComp = getDefaultExportComp(scriptAST);
|
|
107
|
+
// console.log('defaultExportComp',defaultExportComp)
|
|
108
|
+
if (!defaultExportComp) return;
|
|
109
|
+
|
|
110
|
+
const obj = {};
|
|
111
|
+
const keys = Object.keys(defaultExportComp);
|
|
112
|
+
for (let i = 0;i < keys.length;i++) {
|
|
113
|
+
const name = keys[i];
|
|
114
|
+
const value = defaultExportComp[name];
|
|
115
|
+
const defaultFilePath = findCompDir(value, path.dirname(sourceFile));
|
|
116
|
+
const relativePath = getRelativePath(defaultFilePath);
|
|
117
|
+
obj[name] = relativePath;
|
|
118
|
+
}
|
|
119
|
+
// console.log('defaultFilePath',defaultFilePath)
|
|
120
|
+
|
|
121
|
+
// console.log('relativePath',relativePath)
|
|
122
|
+
return obj;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
function handleError({
|
|
127
|
+
specifiers,
|
|
128
|
+
sourceFile,
|
|
129
|
+
context,
|
|
130
|
+
node: one,
|
|
131
|
+
}) {
|
|
132
|
+
if (!specifiers || !specifiers.length) return;
|
|
133
|
+
const componentList = [];
|
|
134
|
+
let statementString = '\n';
|
|
135
|
+
const relativePath = getDefaultExportPathFromSouceFile(sourceFile);
|
|
136
|
+
|
|
137
|
+
const isJSError = sourceFile.endsWith('.js');
|
|
138
|
+
|
|
139
|
+
for (let i = 0;i < specifiers.length;i++) {
|
|
140
|
+
const specifier = specifiers[i];
|
|
141
|
+
const componentName = specifier.local.name;
|
|
142
|
+
// console.log('componentName', componentName);
|
|
143
|
+
componentList.push(componentName);
|
|
144
|
+
if (relativePath[componentName]) {
|
|
145
|
+
statementString += `import ${componentName} from '${relativePath[componentName]}';\n`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// console.log('relativePath', relativePath);
|
|
150
|
+
context.report({
|
|
151
|
+
node: one,
|
|
152
|
+
messageId: isJSError ? 'jsError' : 'tsError',
|
|
153
|
+
fix(fixer) {
|
|
154
|
+
/**
|
|
155
|
+
* 默认导入,比如:
|
|
156
|
+
*
|
|
157
|
+
* import BottomTipBarComp from 'xxx';
|
|
158
|
+
*
|
|
159
|
+
*/
|
|
160
|
+
const defaultCompStr = relativePath.default;
|
|
161
|
+
if (defaultCompStr && specifiers.length === 1 && specifiers[0].type === 'ImportDefaultSpecifier') {
|
|
162
|
+
return fixer.replaceTextRange(one.source.range, `'${defaultCompStr}'`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 具名导入,比如:
|
|
167
|
+
* import { xx, yy } from 'xxx';
|
|
168
|
+
*/
|
|
169
|
+
return [
|
|
170
|
+
fixer.insertTextBeforeRange(one.range, statementString),
|
|
171
|
+
fixer.removeRange(one.range),
|
|
172
|
+
];
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
meta: {
|
|
179
|
+
type: 'problem',
|
|
180
|
+
schema: [],
|
|
181
|
+
docs: {
|
|
182
|
+
description: '不能从js/ts文件中引入组件',
|
|
183
|
+
},
|
|
184
|
+
fixable: 'code',
|
|
185
|
+
messages: {
|
|
186
|
+
jsError: 'Do not import component from JS file',
|
|
187
|
+
tsError: 'Do not import component from TS file',
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
create(context) {
|
|
192
|
+
// const sourceCode = context.getSourceCode();
|
|
193
|
+
// const cwd = context.getCwd();
|
|
194
|
+
const fileName = context.getFilename();
|
|
195
|
+
|
|
196
|
+
// console.log('cwd', cwd)
|
|
197
|
+
// console.log('fileName', fileName)
|
|
198
|
+
const dirname = path.dirname(fileName);
|
|
199
|
+
// console.log('dirname', dirname);
|
|
200
|
+
if (!fileName.endsWith('.vue')) {
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
Program: (node) => {
|
|
206
|
+
const exportAst = node.body.find(item => item.type === 'ExportDefaultDeclaration');
|
|
207
|
+
if (!exportAst || !exportAst.declaration) return;
|
|
208
|
+
|
|
209
|
+
const properties = exportAst.declaration.properties || [];
|
|
210
|
+
const componentsNode = properties.find(item => item.key && item.key.name === 'components');
|
|
211
|
+
|
|
212
|
+
// console.log('componentsNode',componentsNode)
|
|
213
|
+
if (!componentsNode || !componentsNode.value
|
|
214
|
+
|| !componentsNode.value.properties
|
|
215
|
+
|| !componentsNode.value.properties.length
|
|
216
|
+
) return;
|
|
217
|
+
const components = componentsNode.value.properties.map(item => ((item.value && item.value.name) || ''));
|
|
218
|
+
|
|
219
|
+
// console.log('components', components);
|
|
220
|
+
|
|
221
|
+
if (!components || !components.length) return;
|
|
222
|
+
|
|
223
|
+
const importAst = node.body.filter(item => item.type === 'ImportDeclaration');
|
|
224
|
+
const realImport = importAst.filter(item => !!item.specifiers.find((it) => {
|
|
225
|
+
const name = (it.imported && it.imported.name) || (it.local && it.local.name);
|
|
226
|
+
return components.includes(name);
|
|
227
|
+
}));
|
|
228
|
+
// console.log('realImport',realImport, realImport.length)
|
|
229
|
+
|
|
230
|
+
for (let i = 0;i < realImport.length;i++) {
|
|
231
|
+
const one = realImport[i];
|
|
232
|
+
const { specifiers } = one;
|
|
233
|
+
const source = one.source.value;
|
|
234
|
+
|
|
235
|
+
if (source.endsWith('.vue')) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (source.endsWith('.js') || source.endsWith('.ts')) {
|
|
240
|
+
const compFile = findCompDir(source, dirname);
|
|
241
|
+
|
|
242
|
+
handleError({
|
|
243
|
+
specifiers,
|
|
244
|
+
sourceFile: compFile,
|
|
245
|
+
node: one,
|
|
246
|
+
context,
|
|
247
|
+
});
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const compFile = findCompDir(source, dirname);
|
|
252
|
+
|
|
253
|
+
if (fs.existsSync(compFile)) {
|
|
254
|
+
const stat = fs.lstatSync(compFile);
|
|
255
|
+
if (stat.isDirectory()) {
|
|
256
|
+
const indexJs = path.resolve(compFile, 'index.js');
|
|
257
|
+
const indexTs = path.resolve(compFile, 'index.ts');
|
|
258
|
+
if (fs.existsSync(indexJs)) {
|
|
259
|
+
handleError({
|
|
260
|
+
specifiers,
|
|
261
|
+
sourceFile: indexJs,
|
|
262
|
+
node: one,
|
|
263
|
+
context,
|
|
264
|
+
});
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (fs.existsSync(indexTs)) {
|
|
268
|
+
handleError({
|
|
269
|
+
specifiers,
|
|
270
|
+
sourceFile: indexTs,
|
|
271
|
+
node: one,
|
|
272
|
+
context,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-plugin-light",
|
|
3
|
+
"version": "0.0.18",
|
|
4
|
+
"description": "eslint插件",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"eslint",
|
|
7
|
+
"eslint plugin",
|
|
8
|
+
"eslint-plugin",
|
|
9
|
+
"eslint-plugin-uni"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
"lib/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md"
|
|
15
|
+
],
|
|
16
|
+
"author": "GuoWangYang",
|
|
17
|
+
"main": "lib/index.js",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"lint": "eslint --ext .js,.vue .",
|
|
20
|
+
"lint:fix": "eslint --fix --ext .js,.vue .",
|
|
21
|
+
"test": "mocha tests --recursive",
|
|
22
|
+
"create:mr": "node ./script/create-mr.js",
|
|
23
|
+
"release": "npm run release-patch && npm publish",
|
|
24
|
+
"release:first": "standard-version --first-release",
|
|
25
|
+
"release-major": "standard-version --release-as major",
|
|
26
|
+
"release-minor": "standard-version --release-as minor",
|
|
27
|
+
"release-patch": "standard-version --release-as patch"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"requireindex": "^1.2.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"eslint": ">=6",
|
|
34
|
+
"@babel/eslint-parser": ">=6"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@babel/core": "^7.18.9",
|
|
38
|
+
"@babel/eslint-parser": "^7.18.9",
|
|
39
|
+
"@tencent/eslint-config-light": "^1.4.6",
|
|
40
|
+
"axios": "^0.27.2",
|
|
41
|
+
"eslint": "^8.0.1",
|
|
42
|
+
"eslint-plugin-eslint-plugin": "^4.0.1",
|
|
43
|
+
"eslint-plugin-node": "^11.1.0",
|
|
44
|
+
"mocha": "^9.1.3",
|
|
45
|
+
"standard-version": "^9.5.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": "12.x || 14.x || >= 16"
|
|
49
|
+
},
|
|
50
|
+
"license": "ISC"
|
|
51
|
+
}
|