eslint-plugin-kqinfo 0.1.4
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 +116 -0
- package/index.js +20 -0
- package/package.json +30 -0
- package/rules/no-direct-remax-app-env-check.js +104 -0
- package/rules/no-env-hardcoded-redirect.js +243 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# eslint-plugin-kqinfo
|
|
2
|
+
|
|
3
|
+
KQInfo 通用 ESLint 规则集。
|
|
4
|
+
|
|
5
|
+
## 已内置规则
|
|
6
|
+
|
|
7
|
+
- `kqinfo/no-env-hardcoded-redirect`([文档](docs/rules/no-env-hardcoded-redirect.md))
|
|
8
|
+
- 禁止通过 `process.env.REMAX_APP_PLATFORM` 分支,并在分支内直接硬编码 `window.location.href/assign/replace` 跳转 URL;建议统一从 `@/config/env.ts` 读取跳转地址。
|
|
9
|
+
- `kqinfo/no-direct-remax-app-env-check`([文档](docs/rules/no-direct-remax-app-env-check.md))
|
|
10
|
+
- 禁止在业务代码中直接使用 `process.env.REMAX_APP_ENV` 判断环境,应通过 `envService` 导出的函数判断。
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add -D eslint-plugin-kqinfo
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 使用
|
|
19
|
+
|
|
20
|
+
### 方式一:推荐配置
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
// .eslintrc.js
|
|
24
|
+
module.exports = {
|
|
25
|
+
extends: ['plugin:kqinfo/recommended'],
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 方式二:按规则单独启用
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
// .eslintrc.js
|
|
33
|
+
module.exports = {
|
|
34
|
+
plugins: ['kqinfo'],
|
|
35
|
+
rules: {
|
|
36
|
+
'kqinfo/no-env-hardcoded-redirect': 'error',
|
|
37
|
+
'kqinfo/no-direct-remax-app-env-check': 'error',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 第二条规则示例
|
|
43
|
+
|
|
44
|
+
### 错误示例
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
if (process.env.REMAX_APP_ENV === 'production') {
|
|
48
|
+
doSomething();
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 正确示例
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { isProd } from '@/utils/envService';
|
|
56
|
+
|
|
57
|
+
if (isProd()) {
|
|
58
|
+
doSomething();
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 规则示例
|
|
63
|
+
|
|
64
|
+
### 错误示例
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
if (process.env.REMAX_APP_PLATFORM === 'production') {
|
|
68
|
+
window.location.href = 'https://wx.cqrenji.cn/hu-patients/p40005/#/pages/onlineQuery/index';
|
|
69
|
+
} else {
|
|
70
|
+
window.location.href = 'https://wx.cqrenji.cn/hu-patients-dev/p40005/dev/#/pages/onlineQuery/index';
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 正确示例
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { onlineQueryUrl } from '@/config/env';
|
|
78
|
+
|
|
79
|
+
window.location.href = onlineQueryUrl;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 本地测试
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm test
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 发布
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 1) 为本次变更新增 changeset 说明(会生成 .changeset/*.md)
|
|
92
|
+
npm run changeset
|
|
93
|
+
|
|
94
|
+
# 2) 生成版本号与 CHANGELOG.md
|
|
95
|
+
npm run changeset:version
|
|
96
|
+
|
|
97
|
+
# 3) 提交版本文件后发布到 npm
|
|
98
|
+
npm run changeset:publish
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## GitLab CI 自动发布(自部署)
|
|
102
|
+
|
|
103
|
+
仓库已提供 `.gitlab-ci.yml`,包含 `test`、`release`、`publish` 三个阶段:
|
|
104
|
+
|
|
105
|
+
- `test`:普通分支提交时执行 `npm test`。
|
|
106
|
+
- `release`:默认分支 push 时,若存在 changeset,则自动执行 `changeset version`、提交版本变更并打 `vX.Y.Z` 标签。
|
|
107
|
+
- `publish`:当推送 `v*` 标签时自动执行 `npm publish`。
|
|
108
|
+
|
|
109
|
+
在 GitLab 项目变量中至少配置:
|
|
110
|
+
|
|
111
|
+
- `RELEASE_GITLAB_TOKEN`:用于 push release commit 与 tag,建议使用具备 `write_repository` 权限的 Project Access Token。
|
|
112
|
+
- `NPM_TOKEN`:npm 发布令牌。
|
|
113
|
+
- `GITLAB_RELEASE_USER_NAME`(可选):发布提交用户名,默认 `release-bot`。
|
|
114
|
+
- `GITLAB_RELEASE_USER_EMAIL`(可选):发布提交邮箱,默认 `release-bot@local`。
|
|
115
|
+
|
|
116
|
+
建议将上述变量设置为 `Masked + Protected`,并仅允许受保护分支/标签触发发布。
|
package/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const noEnvHardcodedRedirect = require('./rules/no-env-hardcoded-redirect');
|
|
4
|
+
const noDirectRemaxAppEnvCheck = require('./rules/no-direct-remax-app-env-check');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
rules: {
|
|
8
|
+
'no-env-hardcoded-redirect': noEnvHardcodedRedirect,
|
|
9
|
+
'no-direct-remax-app-env-check': noDirectRemaxAppEnvCheck,
|
|
10
|
+
},
|
|
11
|
+
configs: {
|
|
12
|
+
recommended: {
|
|
13
|
+
plugins: ['kqinfo'],
|
|
14
|
+
rules: {
|
|
15
|
+
'kqinfo/no-env-hardcoded-redirect': 'error',
|
|
16
|
+
'kqinfo/no-direct-remax-app-env-check': 'error',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-plugin-kqinfo",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "KQInfo ESLint 规则集",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"rules"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node tests/no-env-hardcoded-redirect.test.js && node tests/no-direct-remax-app-env-check.test.js",
|
|
12
|
+
"changeset": "changeset",
|
|
13
|
+
"changeset:version": "changeset version",
|
|
14
|
+
"changeset:publish": "changeset publish"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"eslint": ">=7"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@changesets/cli": "^2.29.8",
|
|
21
|
+
"eslint": "^7.29.0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"eslint",
|
|
25
|
+
"eslintplugin",
|
|
26
|
+
"kqinfo",
|
|
27
|
+
"rules"
|
|
28
|
+
],
|
|
29
|
+
"license": "UNLICENSED"
|
|
30
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_ALLOW_FILE_PATTERNS = ['[/\\\\]envService\\.(js|jsx|ts|tsx)$'];
|
|
4
|
+
|
|
5
|
+
function isIdentifier(node, name) {
|
|
6
|
+
return node && node.type === 'Identifier' && node.name === name;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isStringLiteralNode(node, value) {
|
|
10
|
+
return node && node.type === 'Literal' && node.value === value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isRmaxAppEnvMember(node) {
|
|
14
|
+
if (!node || node.type !== 'MemberExpression') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const objectNode = node.object;
|
|
19
|
+
const propertyNode = node.property;
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
!objectNode ||
|
|
23
|
+
objectNode.type !== 'MemberExpression' ||
|
|
24
|
+
objectNode.computed ||
|
|
25
|
+
!isIdentifier(objectNode.object, 'process') ||
|
|
26
|
+
!isIdentifier(objectNode.property, 'env')
|
|
27
|
+
) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (node.computed) {
|
|
32
|
+
return isStringLiteralNode(propertyNode, 'REMAX_APP_ENV');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return isIdentifier(propertyNode, 'REMAX_APP_ENV');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isAllowedFile(filename, allowFileRegexList) {
|
|
39
|
+
if (!filename || filename === '<input>') {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (let index = 0; index < allowFileRegexList.length; index += 1) {
|
|
44
|
+
if (allowFileRegexList[index].test(filename)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
meta: {
|
|
54
|
+
type: 'problem',
|
|
55
|
+
docs: {
|
|
56
|
+
description:
|
|
57
|
+
'禁止在业务代码中直接使用 process.env.REMAX_APP_ENV 判断环境,应通过 envService 导出的函数判断。',
|
|
58
|
+
category: 'Best Practices',
|
|
59
|
+
recommended: false,
|
|
60
|
+
},
|
|
61
|
+
schema: [
|
|
62
|
+
{
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
allowFilePatterns: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
items: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
messages: {
|
|
76
|
+
forbidden:
|
|
77
|
+
'禁止直接使用 process.env.REMAX_APP_ENV,请改为使用 envService 导出的环境判断函数。',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
create(context) {
|
|
81
|
+
const option = context.options[0] || {};
|
|
82
|
+
const allowFilePatterns =
|
|
83
|
+
option.allowFilePatterns || DEFAULT_ALLOW_FILE_PATTERNS;
|
|
84
|
+
const allowFileRegexList = allowFilePatterns.map(
|
|
85
|
+
(pattern) => new RegExp(pattern),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const filename = context.getFilename();
|
|
89
|
+
if (isAllowedFile(filename, allowFileRegexList)) {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
MemberExpression(node) {
|
|
95
|
+
if (isRmaxAppEnvMember(node)) {
|
|
96
|
+
context.report({
|
|
97
|
+
node,
|
|
98
|
+
messageId: 'forbidden',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const URL_PREFIX_REGEXP = /^https?:\/\//i;
|
|
4
|
+
|
|
5
|
+
function isIdentifier(node, name) {
|
|
6
|
+
return node && node.type === 'Identifier' && node.name === name;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isStringLiteralNode(node, value) {
|
|
10
|
+
if (!node) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (node.type === 'Literal') {
|
|
14
|
+
return node.value === value;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isEnvMember(node) {
|
|
20
|
+
if (!node || node.type !== 'MemberExpression') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const objectNode = node.object;
|
|
25
|
+
const propertyNode = node.property;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
objectNode &&
|
|
29
|
+
objectNode.type === 'MemberExpression' &&
|
|
30
|
+
!objectNode.computed &&
|
|
31
|
+
isIdentifier(objectNode.object, 'process') &&
|
|
32
|
+
isIdentifier(objectNode.property, 'env') &&
|
|
33
|
+
((node.computed &&
|
|
34
|
+
isStringLiteralNode(propertyNode, 'REMAX_APP_PLATFORM')) ||
|
|
35
|
+
(!node.computed && isIdentifier(propertyNode, 'REMAX_APP_PLATFORM')))
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function walkAstNode(node, matcher) {
|
|
40
|
+
if (!node || typeof node !== 'object') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (matcher(node)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const keys = Object.keys(node);
|
|
49
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
50
|
+
const key = keys[index];
|
|
51
|
+
if (key === 'parent') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const value = node[key];
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
for (let itemIndex = 0; itemIndex < value.length; itemIndex += 1) {
|
|
58
|
+
const item = value[itemIndex];
|
|
59
|
+
if (
|
|
60
|
+
item &&
|
|
61
|
+
typeof item.type === 'string' &&
|
|
62
|
+
walkAstNode(item, matcher)
|
|
63
|
+
) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
value &&
|
|
72
|
+
typeof value.type === 'string' &&
|
|
73
|
+
walkAstNode(value, matcher)
|
|
74
|
+
) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function containsEnvPlatformCheck(node) {
|
|
83
|
+
return walkAstNode(node, isEnvMember);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isWindowLocationMember(node) {
|
|
87
|
+
if (!node || node.type !== 'MemberExpression' || node.computed) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const objectNode = node.object;
|
|
92
|
+
const propertyNode = node.property;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
objectNode &&
|
|
96
|
+
objectNode.type === 'MemberExpression' &&
|
|
97
|
+
!objectNode.computed &&
|
|
98
|
+
isIdentifier(objectNode.object, 'window') &&
|
|
99
|
+
isIdentifier(objectNode.property, 'location') &&
|
|
100
|
+
isIdentifier(propertyNode, 'href')
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isWindowLocationMethodCall(node) {
|
|
105
|
+
if (!node || node.type !== 'CallExpression') {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const calleeNode = node.callee;
|
|
110
|
+
if (
|
|
111
|
+
!calleeNode ||
|
|
112
|
+
calleeNode.type !== 'MemberExpression' ||
|
|
113
|
+
calleeNode.computed
|
|
114
|
+
) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
calleeNode.object &&
|
|
120
|
+
calleeNode.object.type === 'MemberExpression' &&
|
|
121
|
+
!calleeNode.object.computed &&
|
|
122
|
+
isIdentifier(calleeNode.object.object, 'window') &&
|
|
123
|
+
isIdentifier(calleeNode.object.property, 'location') &&
|
|
124
|
+
(isIdentifier(calleeNode.property, 'assign') ||
|
|
125
|
+
isIdentifier(calleeNode.property, 'replace'))
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isHardcodedUrlExpression(node) {
|
|
130
|
+
if (!node) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
135
|
+
return URL_PREFIX_REGEXP.test(node.value.trim());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (node.type === 'TemplateLiteral') {
|
|
139
|
+
return node.quasis.some((quasiNode) =>
|
|
140
|
+
URL_PREFIX_REGEXP.test((quasiNode.value.cooked || '').trim()),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (node.type === 'BinaryExpression' && node.operator === '+') {
|
|
145
|
+
return (
|
|
146
|
+
isHardcodedUrlExpression(node.left) ||
|
|
147
|
+
isHardcodedUrlExpression(node.right)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function containsHardcodedRedirectInBranch(node) {
|
|
155
|
+
return walkAstNode(node, (currentNode) => {
|
|
156
|
+
if (
|
|
157
|
+
currentNode.type === 'AssignmentExpression' &&
|
|
158
|
+
currentNode.operator === '=' &&
|
|
159
|
+
isWindowLocationMember(currentNode.left)
|
|
160
|
+
) {
|
|
161
|
+
return isHardcodedUrlExpression(currentNode.right);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (isWindowLocationMethodCall(currentNode)) {
|
|
165
|
+
return isHardcodedUrlExpression(currentNode.arguments[0]);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return false;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = {
|
|
173
|
+
meta: {
|
|
174
|
+
type: 'problem',
|
|
175
|
+
docs: {
|
|
176
|
+
description: '禁止基于 REMAX_APP_PLATFORM 分支并硬编码跳转 URL',
|
|
177
|
+
category: 'Best Practices',
|
|
178
|
+
recommended: false,
|
|
179
|
+
},
|
|
180
|
+
schema: [],
|
|
181
|
+
messages: {
|
|
182
|
+
forbidden:
|
|
183
|
+
'禁止使用 process.env.REMAX_APP_PLATFORM 做分支并直接硬编码跳转 URL,请改为统一配置映射。',
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
create(context) {
|
|
187
|
+
return {
|
|
188
|
+
IfStatement(node) {
|
|
189
|
+
if (!containsEnvPlatformCheck(node.test)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const hasRedirectInConsequent = containsHardcodedRedirectInBranch(
|
|
194
|
+
node.consequent,
|
|
195
|
+
);
|
|
196
|
+
const hasRedirectInAlternate = containsHardcodedRedirectInBranch(
|
|
197
|
+
node.alternate,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (hasRedirectInConsequent && hasRedirectInAlternate) {
|
|
201
|
+
context.report({
|
|
202
|
+
node: node.test,
|
|
203
|
+
messageId: 'forbidden',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
ConditionalExpression(node) {
|
|
208
|
+
if (!containsEnvPlatformCheck(node.test)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const parentNode = node.parent;
|
|
213
|
+
if (
|
|
214
|
+
parentNode &&
|
|
215
|
+
parentNode.type === 'AssignmentExpression' &&
|
|
216
|
+
parentNode.right === node &&
|
|
217
|
+
isWindowLocationMember(parentNode.left) &&
|
|
218
|
+
(isHardcodedUrlExpression(node.consequent) ||
|
|
219
|
+
isHardcodedUrlExpression(node.alternate))
|
|
220
|
+
) {
|
|
221
|
+
context.report({
|
|
222
|
+
node: node.test,
|
|
223
|
+
messageId: 'forbidden',
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
parentNode &&
|
|
230
|
+
isWindowLocationMethodCall(parentNode) &&
|
|
231
|
+
parentNode.arguments[0] === node &&
|
|
232
|
+
(isHardcodedUrlExpression(node.consequent) ||
|
|
233
|
+
isHardcodedUrlExpression(node.alternate))
|
|
234
|
+
) {
|
|
235
|
+
context.report({
|
|
236
|
+
node: node.test,
|
|
237
|
+
messageId: 'forbidden',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
};
|