xshell 1.0.85 → 1.0.86
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/package.json +12 -8
- package/xlint.d.ts +108 -0
- package/xlint.js +748 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.86",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -53,11 +53,12 @@
|
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@babel/core": "^7.24.
|
|
57
|
-
"@babel/parser": "^7.24.
|
|
56
|
+
"@babel/core": "^7.24.4",
|
|
57
|
+
"@babel/parser": "^7.24.4",
|
|
58
58
|
"@babel/traverse": "^7.24.1",
|
|
59
59
|
"@koa/cors": "^5.0.0",
|
|
60
60
|
"@types/ws": "^8.5.10",
|
|
61
|
+
"@typescript-eslint/utils": "^7.5.0",
|
|
61
62
|
"ali-oss": "^6.20.0",
|
|
62
63
|
"archiver": "^7.0.1",
|
|
63
64
|
"byte-size": "^8.1.1",
|
|
@@ -87,9 +88,9 @@
|
|
|
87
88
|
"through2": "^4.0.2",
|
|
88
89
|
"tough-cookie": "^4.1.3",
|
|
89
90
|
"tslib": "^2.6.2",
|
|
90
|
-
"typescript": "^5.4.
|
|
91
|
+
"typescript": "^5.4.4",
|
|
91
92
|
"ua-parser-js": "2.0.0-alpha.2",
|
|
92
|
-
"undici": "^6.
|
|
93
|
+
"undici": "^6.11.1",
|
|
93
94
|
"vinyl": "^3.0.0",
|
|
94
95
|
"vinyl-fs": "^4.0.0",
|
|
95
96
|
"ws": "^8.16.0",
|
|
@@ -105,19 +106,22 @@
|
|
|
105
106
|
"@types/babel__traverse": "^7.20.5",
|
|
106
107
|
"@types/byte-size": "^8.1.2",
|
|
107
108
|
"@types/chardet": "^0.8.3",
|
|
109
|
+
"@types/eslint": "^8.56.7",
|
|
110
|
+
"@types/estree": "^1.0.5",
|
|
108
111
|
"@types/gulp-sort": "2.0.4",
|
|
109
112
|
"@types/js-cookie": "^3.0.6",
|
|
110
113
|
"@types/koa": "^2.15.0",
|
|
111
114
|
"@types/koa-compress": "^4.0.6",
|
|
112
115
|
"@types/lodash": "^4.17.0",
|
|
113
116
|
"@types/mime-types": "^2.1.4",
|
|
114
|
-
"@types/node": "^20.
|
|
115
|
-
"@types/react": "^18.2.
|
|
117
|
+
"@types/node": "^20.12.5",
|
|
118
|
+
"@types/react": "^18.2.74",
|
|
116
119
|
"@types/through2": "^2.0.41",
|
|
117
120
|
"@types/tough-cookie": "^4.0.5",
|
|
118
121
|
"@types/ua-parser-js": "^0.7.39",
|
|
119
122
|
"@types/vinyl-fs": "^3.0.5",
|
|
120
|
-
"@types/vscode": "^1.
|
|
123
|
+
"@types/vscode": "^1.88.0",
|
|
124
|
+
"eslint": "^9.0.0"
|
|
121
125
|
},
|
|
122
126
|
"pnpm": {
|
|
123
127
|
"patchedDependencies": {
|
package/xlint.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type ESTree from 'estree';
|
|
2
|
+
import type { Rule } from 'eslint';
|
|
3
|
+
import { TSESTree, type TSESLint } from '@typescript-eslint/utils';
|
|
4
|
+
export declare const xlint_plugin: {
|
|
5
|
+
rules: {
|
|
6
|
+
'fold-jsdoc-comments': {
|
|
7
|
+
meta: {
|
|
8
|
+
readonly fixable: "whitespace";
|
|
9
|
+
readonly type: "layout";
|
|
10
|
+
};
|
|
11
|
+
create: (context: Rule.RuleContext) => {
|
|
12
|
+
Program(): void;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
'nonblock-statement-body-position-with-indentation': {
|
|
16
|
+
meta: {
|
|
17
|
+
readonly fixable: "whitespace";
|
|
18
|
+
readonly type: "layout";
|
|
19
|
+
};
|
|
20
|
+
create(context: Rule.RuleContext): {
|
|
21
|
+
IfStatement(node: ESTree.IfStatement & Rule.NodeParentExtension): void;
|
|
22
|
+
WhileStatement: (node: ESTree.WhileStatement & Rule.NodeParentExtension) => void;
|
|
23
|
+
DoWhileStatement: (node: ESTree.DoWhileStatement & Rule.NodeParentExtension) => void;
|
|
24
|
+
ForStatement: (node: ESTree.ForStatement & Rule.NodeParentExtension) => void;
|
|
25
|
+
ForInStatement: (node: ESTree.ForInStatement & Rule.NodeParentExtension) => void;
|
|
26
|
+
ForOfStatement: (node: ESTree.ForOfStatement & Rule.NodeParentExtension) => void;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
'empty-bracket-spacing': {
|
|
30
|
+
meta: {
|
|
31
|
+
readonly fixable: "whitespace";
|
|
32
|
+
readonly type: "layout";
|
|
33
|
+
};
|
|
34
|
+
create(context: Rule.RuleContext): {
|
|
35
|
+
ObjectExpression(node: ESTree.ObjectExpression & Rule.NodeParentExtension): void;
|
|
36
|
+
ArrayExpression(node: ESTree.ArrayExpression & Rule.NodeParentExtension): void;
|
|
37
|
+
BlockStatement(node: ESTree.BlockStatement & Rule.NodeParentExtension): void;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
'space-infix-ops-except-exponentiation': {
|
|
41
|
+
meta: {
|
|
42
|
+
messages: {
|
|
43
|
+
missingSpace: string;
|
|
44
|
+
redundantSpace: string;
|
|
45
|
+
};
|
|
46
|
+
fixable: "whitespace";
|
|
47
|
+
type: "layout";
|
|
48
|
+
};
|
|
49
|
+
create(context: TSESLint.RuleContext<'missingSpace' | 'redundantSpace', any[]>): {
|
|
50
|
+
AssignmentExpression: (node: any) => void;
|
|
51
|
+
AssignmentPattern: (node: any) => void;
|
|
52
|
+
BinaryExpression: (node: any) => void;
|
|
53
|
+
LogicalExpression: (node: any) => void;
|
|
54
|
+
ConditionalExpression: (node: any) => void;
|
|
55
|
+
VariableDeclarator: (node: any) => void;
|
|
56
|
+
TSEnumMember: (node: TSESTree.TSEnumMember) => void;
|
|
57
|
+
PropertyDefinition: (node: TSESTree.PropertyDefinition) => void;
|
|
58
|
+
TSTypeAliasDeclaration: (node: TSESTree.TSTypeAliasDeclaration) => void;
|
|
59
|
+
TSUnionType: (typeAnnotation: TSESTree.TSIntersectionType | TSESTree.TSUnionType) => void;
|
|
60
|
+
TSIntersectionType: (typeAnnotation: TSESTree.TSIntersectionType | TSESTree.TSUnionType) => void;
|
|
61
|
+
TSConditionalType: (node: TSESTree.TSConditionalType) => void;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
'space-in-for-statement': {
|
|
65
|
+
meta: {
|
|
66
|
+
readonly fixable: "whitespace";
|
|
67
|
+
readonly type: "layout";
|
|
68
|
+
};
|
|
69
|
+
create(context: Rule.RuleContext): {
|
|
70
|
+
ForStatement(node: ESTree.ForStatement & Rule.NodeParentExtension): void;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
'jsx-no-redundant-parenthesis-in-return': {
|
|
74
|
+
meta: {
|
|
75
|
+
readonly fixable: "whitespace";
|
|
76
|
+
readonly type: "layout";
|
|
77
|
+
};
|
|
78
|
+
create(context: Rule.RuleContext): {
|
|
79
|
+
ReturnStatement(node: ESTree.ReturnStatement & Rule.NodeParentExtension): void;
|
|
80
|
+
ArrowFunctionExpression(node: ESTree.ArrowFunctionExpression & Rule.NodeParentExtension): void;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
'keep-indent': {
|
|
84
|
+
meta: {
|
|
85
|
+
readonly fixable: "whitespace";
|
|
86
|
+
readonly type: "layout";
|
|
87
|
+
};
|
|
88
|
+
create(context: Rule.RuleContext): {
|
|
89
|
+
Program(): void;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
'func-style': {
|
|
93
|
+
meta: {
|
|
94
|
+
readonly fixable: "whitespace";
|
|
95
|
+
readonly type: "layout";
|
|
96
|
+
};
|
|
97
|
+
create(context: Rule.RuleContext): {
|
|
98
|
+
FunctionDeclaration(node: ESTree.FunctionDeclaration & Rule.NodeParentExtension): void;
|
|
99
|
+
'FunctionDeclaration:exit'(): void;
|
|
100
|
+
FunctionExpression(node: ESTree.FunctionExpression & Rule.NodeParentExtension): void;
|
|
101
|
+
'FunctionExpression:exit'(): void;
|
|
102
|
+
ThisExpression(): void;
|
|
103
|
+
ArrowFunctionExpression(): void;
|
|
104
|
+
'ArrowFunctionExpression:exit'(node: ESTree.ArrowFunctionExpression & Rule.NodeParentExtension): void;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
};
|
package/xlint.js
ADDED
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
import { AST_TOKEN_TYPES, ASTUtils, TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
const line_break_pattern = /\r\n|[\r\n\u2028\u2029]/u;
|
|
3
|
+
const meta = {
|
|
4
|
+
fixable: 'whitespace',
|
|
5
|
+
type: 'layout',
|
|
6
|
+
};
|
|
7
|
+
const UNIONS = ['|', '&'];
|
|
8
|
+
// 用 ast explorer 选 esprima 来看
|
|
9
|
+
// node.loc.start.line 下标从 1 开始
|
|
10
|
+
export const xlint_plugin = {
|
|
11
|
+
rules: {
|
|
12
|
+
'fold-jsdoc-comments': {
|
|
13
|
+
meta,
|
|
14
|
+
create: context => ({
|
|
15
|
+
Program() {
|
|
16
|
+
for (const comment of context.sourceCode.getAllComments())
|
|
17
|
+
if (comment.type === 'Block') {
|
|
18
|
+
const lines = comment.value.split(line_break_pattern);
|
|
19
|
+
if (lines.length >= 3 &&
|
|
20
|
+
/^\*\s*$/u.test(lines[0]) &&
|
|
21
|
+
lines.slice(1, -1).every(line => /^\s*\*/u.test(line)) &&
|
|
22
|
+
/^[\s\*]*$/u.test(lines.at(-1)))
|
|
23
|
+
context.report({
|
|
24
|
+
loc: {
|
|
25
|
+
start: comment.loc.start,
|
|
26
|
+
end: comment.loc.end
|
|
27
|
+
},
|
|
28
|
+
message: 'Redundant jsdoc comment.',
|
|
29
|
+
fix(fixer) {
|
|
30
|
+
const indent = ' '.repeat(comment.loc.start.column);
|
|
31
|
+
const indenter = (line) => line.replace(/^\s*\*\s?/u, `${indent} `);
|
|
32
|
+
return fixer.replaceTextRange(comment.range, lines.length === 3 ?
|
|
33
|
+
`/** ${lines[1].replace(/^\s*\*\s*/u, '')} */`
|
|
34
|
+
:
|
|
35
|
+
[
|
|
36
|
+
`/** ${lines[1].replace(/^\s*\*\s?/u, '')}`,
|
|
37
|
+
...lines.slice(2, -2).map(indenter),
|
|
38
|
+
`${indenter(lines.at(-2))} */`
|
|
39
|
+
].join('\n'));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
// 一条语句作为 body 时强制换行缩进
|
|
47
|
+
// if (1) body
|
|
48
|
+
// 更改为
|
|
49
|
+
// if (1)
|
|
50
|
+
// body
|
|
51
|
+
'nonblock-statement-body-position-with-indentation': {
|
|
52
|
+
meta,
|
|
53
|
+
create(context) {
|
|
54
|
+
const source = context.sourceCode;
|
|
55
|
+
const lines = source.getLines();
|
|
56
|
+
function lint(body, node) {
|
|
57
|
+
if (body.type !== 'BlockStatement') {
|
|
58
|
+
const token_before = source.getTokenBefore(body);
|
|
59
|
+
let indent = 0;
|
|
60
|
+
const line = lines[node.loc.start.line - 1];
|
|
61
|
+
for (let i = 0; i < line.length; i++)
|
|
62
|
+
if (line[i] === ' ')
|
|
63
|
+
indent++;
|
|
64
|
+
else if (line[i] === '\t')
|
|
65
|
+
indent += 4;
|
|
66
|
+
else
|
|
67
|
+
break;
|
|
68
|
+
if (token_before.loc.end.line === body.loc.start.line ||
|
|
69
|
+
body.loc.start.column !== indent + 4)
|
|
70
|
+
context.report({
|
|
71
|
+
node: body,
|
|
72
|
+
message: 'Expected a linebreak before this statement.',
|
|
73
|
+
fix: fixer => fixer.replaceTextRange([token_before.range[1], body.range[0]], '\n' + ' '.repeat(indent + 4))
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
IfStatement(node) {
|
|
79
|
+
lint(node.consequent, node);
|
|
80
|
+
// Check the `else` node, but don't check 'else if' statements.
|
|
81
|
+
if (node.alternate && node.alternate.type !== 'IfStatement')
|
|
82
|
+
lint(node.alternate, node);
|
|
83
|
+
},
|
|
84
|
+
WhileStatement: node => lint(node.body, node),
|
|
85
|
+
DoWhileStatement: node => lint(node.body, node),
|
|
86
|
+
ForStatement: node => lint(node.body, node),
|
|
87
|
+
ForInStatement: node => lint(node.body, node),
|
|
88
|
+
ForOfStatement: node => lint(node.body, node)
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
// let o = { }
|
|
93
|
+
// let a = [ ]
|
|
94
|
+
// function foo () { }
|
|
95
|
+
'empty-bracket-spacing': {
|
|
96
|
+
meta,
|
|
97
|
+
create(context) {
|
|
98
|
+
const source = context.sourceCode;
|
|
99
|
+
return {
|
|
100
|
+
ObjectExpression(node) {
|
|
101
|
+
// 是 {} 这样的空对象
|
|
102
|
+
if (node.properties.length === 0 && source.getText(node) === '{}')
|
|
103
|
+
context.report({
|
|
104
|
+
node,
|
|
105
|
+
message: 'Expected a space in this empty object.',
|
|
106
|
+
fix: fixer => fixer.replaceText(node, '{ }')
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
ArrayExpression(node) {
|
|
110
|
+
// 是 [] 这样的空数组
|
|
111
|
+
if (node.elements.length === 0 && source.getText(node) === '[]')
|
|
112
|
+
context.report({
|
|
113
|
+
node,
|
|
114
|
+
message: 'Expected a space in this empty array.',
|
|
115
|
+
fix: fixer => fixer.replaceText(node, '[ ]')
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
BlockStatement(node) {
|
|
119
|
+
if (node.body.length === 0 && source.getText(node) === '{}')
|
|
120
|
+
context.report({
|
|
121
|
+
node,
|
|
122
|
+
message: 'Expected a space in this empty block.',
|
|
123
|
+
fix: fixer => fixer.replaceText(node, '{ }')
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
// a + b**c
|
|
130
|
+
'space-infix-ops-except-exponentiation': {
|
|
131
|
+
meta: {
|
|
132
|
+
...meta,
|
|
133
|
+
messages: {
|
|
134
|
+
missingSpace: "Operator '{{operator}}' must be spaced.",
|
|
135
|
+
redundantSpace: "Operator '{{operator}}' must not be spaced."
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
// @ts-ignore
|
|
139
|
+
create(context) {
|
|
140
|
+
const int32Hint = true;
|
|
141
|
+
const { sourceCode } = context;
|
|
142
|
+
/** Returns the first token which violates the rule
|
|
143
|
+
@param {ASTNode} left The left node of the main node
|
|
144
|
+
@param {ASTNode} right The right node of the main node
|
|
145
|
+
@param {string} op The operator of the main node
|
|
146
|
+
@returns {Object} The violator token or null
|
|
147
|
+
@private */
|
|
148
|
+
function getFirstNonSpacedToken(left, right, op) {
|
|
149
|
+
const operator = sourceCode.getFirstTokenBetween(left, right, token => token.value === op);
|
|
150
|
+
const prev = sourceCode.getTokenBefore(operator);
|
|
151
|
+
const next = sourceCode.getTokenAfter(operator);
|
|
152
|
+
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next))
|
|
153
|
+
return operator;
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
Reports an AST node as a rule violation
|
|
158
|
+
@param {ASTNode} mainNode The node to report
|
|
159
|
+
@param {Object} culpritToken The token which has a problem
|
|
160
|
+
@returns {void}
|
|
161
|
+
@private
|
|
162
|
+
*/
|
|
163
|
+
function report(mainNode, culpritToken) {
|
|
164
|
+
context.report({
|
|
165
|
+
node: mainNode,
|
|
166
|
+
loc: culpritToken.loc,
|
|
167
|
+
messageId: 'missingSpace',
|
|
168
|
+
data: {
|
|
169
|
+
operator: culpritToken.value
|
|
170
|
+
},
|
|
171
|
+
fix(fixer) {
|
|
172
|
+
const previousToken = sourceCode.getTokenBefore(culpritToken);
|
|
173
|
+
const afterToken = sourceCode.getTokenAfter(culpritToken);
|
|
174
|
+
let fixString = '';
|
|
175
|
+
if (culpritToken.range[0] - previousToken.range[1] === 0)
|
|
176
|
+
fixString = ' ';
|
|
177
|
+
fixString += culpritToken.value;
|
|
178
|
+
if (afterToken.range[0] - culpritToken.range[1] === 0)
|
|
179
|
+
fixString += ' ';
|
|
180
|
+
return fixer.replaceText(culpritToken, fixString);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
Check if the node is binary then report
|
|
186
|
+
@param {ASTNode} node node to evaluate
|
|
187
|
+
@returns {void}
|
|
188
|
+
@private
|
|
189
|
+
*/
|
|
190
|
+
function checkBinary(node) {
|
|
191
|
+
const leftNode = node.left.typeAnnotation ? node.left.typeAnnotation : node.left;
|
|
192
|
+
const rightNode = node.right;
|
|
193
|
+
// search for = in AssignmentPattern nodes
|
|
194
|
+
const operator = node.operator || '=';
|
|
195
|
+
// 找到 ** operator
|
|
196
|
+
if (operator === '**') {
|
|
197
|
+
const op = sourceCode.getFirstTokenBetween(leftNode, rightNode, token => token.value === operator);
|
|
198
|
+
const prev = sourceCode.getTokenBefore(op);
|
|
199
|
+
const next = sourceCode.getTokenAfter(op);
|
|
200
|
+
if (sourceCode.isSpaceBetween(prev, op) || sourceCode.isSpaceBetween(op, next))
|
|
201
|
+
context.report({
|
|
202
|
+
node,
|
|
203
|
+
loc: op.loc,
|
|
204
|
+
messageId: 'redundantSpace',
|
|
205
|
+
data: {
|
|
206
|
+
operator: op.value
|
|
207
|
+
},
|
|
208
|
+
fix(fixer) {
|
|
209
|
+
const previousToken = sourceCode.getTokenBefore(op);
|
|
210
|
+
const afterToken = sourceCode.getTokenAfter(op);
|
|
211
|
+
let fixes = [];
|
|
212
|
+
if (op.range[0] > previousToken.range[1])
|
|
213
|
+
fixes.push(
|
|
214
|
+
// @ts-ignore
|
|
215
|
+
fixer.removeRange([previousToken.range[1], op.range[0]]));
|
|
216
|
+
if (afterToken.range[0] > op.range[1])
|
|
217
|
+
fixes.push(
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
fixer.removeRange([op.range[1], afterToken.range[0]]));
|
|
220
|
+
return fixes;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, operator);
|
|
226
|
+
if (nonSpacedNode)
|
|
227
|
+
if (!(int32Hint && sourceCode.getText(node).endsWith('|0')))
|
|
228
|
+
report(node, nonSpacedNode);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
Check if the node is conditional
|
|
233
|
+
@param {ASTNode} node node to evaluate
|
|
234
|
+
@returns {void}
|
|
235
|
+
@private
|
|
236
|
+
*/
|
|
237
|
+
function checkConditional(node) {
|
|
238
|
+
const nonSpacedConsequentNode = getFirstNonSpacedToken(node.test, node.consequent, '?');
|
|
239
|
+
const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate, ':');
|
|
240
|
+
if (nonSpacedConsequentNode)
|
|
241
|
+
report(node, nonSpacedConsequentNode);
|
|
242
|
+
if (nonSpacedAlternateNode)
|
|
243
|
+
report(node, nonSpacedAlternateNode);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
Check if the node is a variable
|
|
247
|
+
@param {ASTNode} node node to evaluate
|
|
248
|
+
@returns {void}
|
|
249
|
+
@private
|
|
250
|
+
*/
|
|
251
|
+
function checkVar(node) {
|
|
252
|
+
const leftNode = node.id.typeAnnotation ? node.id.typeAnnotation : node.id;
|
|
253
|
+
const rightNode = node.init;
|
|
254
|
+
if (rightNode) {
|
|
255
|
+
const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, '=');
|
|
256
|
+
if (nonSpacedNode)
|
|
257
|
+
report(node, nonSpacedNode);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function ts_report(operator) {
|
|
261
|
+
context.report({
|
|
262
|
+
node: operator,
|
|
263
|
+
messageId: 'missingSpace',
|
|
264
|
+
data: {
|
|
265
|
+
operator: operator.value
|
|
266
|
+
},
|
|
267
|
+
fix(fixer) {
|
|
268
|
+
const previousToken = sourceCode.getTokenBefore(operator);
|
|
269
|
+
const afterToken = sourceCode.getTokenAfter(operator);
|
|
270
|
+
let fixString = '';
|
|
271
|
+
if (operator.range[0] - previousToken.range[1] === 0)
|
|
272
|
+
fixString = ' ';
|
|
273
|
+
fixString += operator.value;
|
|
274
|
+
if (afterToken.range[0] - operator.range[1] === 0)
|
|
275
|
+
fixString += ' ';
|
|
276
|
+
return fixer.replaceText(operator, fixString);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
function isSpaceChar(token) {
|
|
281
|
+
return token.type === AST_TOKEN_TYPES.Punctuator && /^[=?:]$/.test(token.value);
|
|
282
|
+
}
|
|
283
|
+
function checkAndReportAssignmentSpace(leftNode, rightNode) {
|
|
284
|
+
if (!rightNode || !leftNode)
|
|
285
|
+
return;
|
|
286
|
+
const operator = sourceCode.getFirstTokenBetween(leftNode, rightNode, isSpaceChar);
|
|
287
|
+
const prev = sourceCode.getTokenBefore(operator);
|
|
288
|
+
const next = sourceCode.getTokenAfter(operator);
|
|
289
|
+
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next))
|
|
290
|
+
ts_report(operator);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
Check if it has an assignment char and report if it's faulty
|
|
294
|
+
@param node The node to report
|
|
295
|
+
*/
|
|
296
|
+
function checkForEnumAssignmentSpace(node) {
|
|
297
|
+
checkAndReportAssignmentSpace(node.id, node.initializer);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
Check if it has an assignment char and report if it's faulty
|
|
301
|
+
@param node The node to report
|
|
302
|
+
*/
|
|
303
|
+
function checkForPropertyDefinitionAssignmentSpace(node) {
|
|
304
|
+
const leftNode = node.optional && !node.typeAnnotation ? sourceCode.getTokenAfter(node.key) : node.typeAnnotation ?? node.key;
|
|
305
|
+
checkAndReportAssignmentSpace(leftNode, node.value);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
Check if it is missing spaces between type annotations chaining
|
|
309
|
+
@param typeAnnotation TypeAnnotations list
|
|
310
|
+
*/
|
|
311
|
+
function checkForTypeAnnotationSpace(typeAnnotation) {
|
|
312
|
+
const types = typeAnnotation.types;
|
|
313
|
+
types.forEach(type => {
|
|
314
|
+
const skipFunctionParenthesis = type.type === TSESTree.AST_NODE_TYPES.TSFunctionType ? ASTUtils.isNotOpeningBraceToken : 0;
|
|
315
|
+
const operator = sourceCode.getTokenBefore(type, skipFunctionParenthesis);
|
|
316
|
+
if (operator && UNIONS.includes(operator.value)) {
|
|
317
|
+
const prev = sourceCode.getTokenBefore(operator);
|
|
318
|
+
const next = sourceCode.getTokenAfter(operator);
|
|
319
|
+
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next))
|
|
320
|
+
ts_report(operator);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
Check if it has an assignment char and report if it's faulty
|
|
326
|
+
@param node The node to report
|
|
327
|
+
*/
|
|
328
|
+
function checkForTypeAliasAssignment(node) {
|
|
329
|
+
checkAndReportAssignmentSpace(node.typeParameters ?? node.id, node.typeAnnotation);
|
|
330
|
+
}
|
|
331
|
+
function checkForTypeConditional(node) {
|
|
332
|
+
checkAndReportAssignmentSpace(node.extendsType, node.trueType);
|
|
333
|
+
checkAndReportAssignmentSpace(node.trueType, node.falseType);
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
AssignmentExpression: checkBinary,
|
|
337
|
+
AssignmentPattern: checkBinary,
|
|
338
|
+
BinaryExpression: checkBinary,
|
|
339
|
+
LogicalExpression: checkBinary,
|
|
340
|
+
ConditionalExpression: checkConditional,
|
|
341
|
+
VariableDeclarator: checkVar,
|
|
342
|
+
TSEnumMember: checkForEnumAssignmentSpace,
|
|
343
|
+
PropertyDefinition: checkForPropertyDefinitionAssignmentSpace,
|
|
344
|
+
TSTypeAliasDeclaration: checkForTypeAliasAssignment,
|
|
345
|
+
TSUnionType: checkForTypeAnnotationSpace,
|
|
346
|
+
TSIntersectionType: checkForTypeAnnotationSpace,
|
|
347
|
+
TSConditionalType: checkForTypeConditional
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
// for (;;)
|
|
352
|
+
// 1;
|
|
353
|
+
// for (let i = 0; i < 10; i++)
|
|
354
|
+
// for ( ; i < 10; )
|
|
355
|
+
'space-in-for-statement': {
|
|
356
|
+
meta,
|
|
357
|
+
create(context) {
|
|
358
|
+
const source = context.sourceCode;
|
|
359
|
+
return {
|
|
360
|
+
ForStatement(node) {
|
|
361
|
+
const paren_left = source.getFirstToken(node, token => token.value === '(');
|
|
362
|
+
const semi0 = source.getTokenAfter(node.init || paren_left);
|
|
363
|
+
const semi1 = source.getTokenAfter(node.test || semi0);
|
|
364
|
+
const semi0_prev = source.getTokenBefore(semi0, { includeComments: true });
|
|
365
|
+
const semi0_next = source.getTokenAfter(semi0, { includeComments: true });
|
|
366
|
+
const semi1_next = source.getTokenAfter(semi1, { includeComments: true });
|
|
367
|
+
if (node.init || node.test || node.update) {
|
|
368
|
+
// for (let i = 0; i < 10; i++)
|
|
369
|
+
// for ( ; i < 10; )
|
|
370
|
+
// for ( ; ; i++)
|
|
371
|
+
// for (let i = 0;)
|
|
372
|
+
if (node.init) {
|
|
373
|
+
if (semi0.loc.start.line === semi0_prev.loc.end.line &&
|
|
374
|
+
semi0.loc.start.column !== semi0_prev.loc.end.column)
|
|
375
|
+
context.report({
|
|
376
|
+
loc: {
|
|
377
|
+
start: semi0_prev.loc.end,
|
|
378
|
+
end: semi0.loc.start
|
|
379
|
+
},
|
|
380
|
+
message: 'for 语句第一个分号前面应该没有多余空格',
|
|
381
|
+
node,
|
|
382
|
+
fix: fixer => fixer.removeRange([semi0_prev.range[1], semi0.range[0]])
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
else // for ( ;)
|
|
386
|
+
if (semi0.loc.start.line === semi0_prev.loc.end.line &&
|
|
387
|
+
semi0.loc.start.column - semi0_prev.loc.end.column !== 2)
|
|
388
|
+
context.report({
|
|
389
|
+
loc: {
|
|
390
|
+
start: semi0_prev.loc.end,
|
|
391
|
+
end: semi0.loc.start
|
|
392
|
+
},
|
|
393
|
+
message: 'for 语句第一个分号前面应该为 2 个空格',
|
|
394
|
+
node,
|
|
395
|
+
fix: fixer => fixer.replaceTextRange([semi0_prev.range[1], semi0.range[0]], ' ')
|
|
396
|
+
});
|
|
397
|
+
// for ( ; ;)
|
|
398
|
+
if (semi0_next.loc.start.line === semi0.loc.end.line &&
|
|
399
|
+
semi0_next.loc.start.column - semi0.loc.end.column !== 2)
|
|
400
|
+
context.report({
|
|
401
|
+
loc: {
|
|
402
|
+
start: semi0.loc.end,
|
|
403
|
+
end: semi0_next.loc.start
|
|
404
|
+
},
|
|
405
|
+
message: 'for 语句第一个分号后面应为 2 个空格',
|
|
406
|
+
node,
|
|
407
|
+
fix: fixer => fixer.replaceTextRange([semi0.range[1], semi0_next.range[0]], ' ')
|
|
408
|
+
});
|
|
409
|
+
// // for ( ; i > 0;)
|
|
410
|
+
if (node.test) {
|
|
411
|
+
const test_end = source.getLastToken(node.test);
|
|
412
|
+
if (semi1.loc.start.line === test_end.loc.end.line &&
|
|
413
|
+
semi1.loc.start.column !== test_end.loc.end.column)
|
|
414
|
+
context.report({
|
|
415
|
+
loc: {
|
|
416
|
+
start: test_end.loc.end,
|
|
417
|
+
end: semi1.loc.start,
|
|
418
|
+
},
|
|
419
|
+
message: 'for 语句第二个分号前面没有多余的空格',
|
|
420
|
+
node,
|
|
421
|
+
fix: fixer => fixer.removeRange([test_end.range[1], semi1.range[0]])
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
if (semi1_next.loc.start.line === semi1.loc.end.line &&
|
|
425
|
+
semi1_next.loc.start.column - semi1.loc.end.column !== 2)
|
|
426
|
+
context.report({
|
|
427
|
+
loc: {
|
|
428
|
+
start: semi1.loc.end,
|
|
429
|
+
end: semi1_next.loc.start
|
|
430
|
+
},
|
|
431
|
+
message: 'for 语句第二个分号后面应为 2 个空格',
|
|
432
|
+
node,
|
|
433
|
+
fix: fixer => fixer.replaceTextRange([semi1.range[1], semi1_next.range[0]], ' ')
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
else { // for (;;)
|
|
437
|
+
if (semi0.loc.start.line === semi0_prev.loc.end.line &&
|
|
438
|
+
semi0.loc.start.column !== semi0_prev.loc.end.column)
|
|
439
|
+
context.report({
|
|
440
|
+
loc: {
|
|
441
|
+
start: semi0_prev.loc.end,
|
|
442
|
+
end: semi0.loc.start
|
|
443
|
+
},
|
|
444
|
+
message: '无条件 for 语句第一个分号前面应该没有空格',
|
|
445
|
+
node,
|
|
446
|
+
fix: fixer => fixer.removeRange([semi0_prev.range[1], semi0.range[0]])
|
|
447
|
+
});
|
|
448
|
+
if (semi0_next.loc.start.line === semi0.loc.end.line &&
|
|
449
|
+
semi0_next.loc.start.column !== semi0.loc.end.column)
|
|
450
|
+
context.report({
|
|
451
|
+
loc: {
|
|
452
|
+
start: semi0.loc.end,
|
|
453
|
+
end: semi0_next.loc.start
|
|
454
|
+
},
|
|
455
|
+
message: '无条件 for 语句第一个分号后面应该没有空格',
|
|
456
|
+
node,
|
|
457
|
+
fix: fixer => fixer.removeRange([semi0.range[1], semi0_next.range[0]])
|
|
458
|
+
});
|
|
459
|
+
if (semi1_next.loc.start.line === semi1.loc.end.line &&
|
|
460
|
+
semi1_next.loc.start.column !== semi1.loc.end.column)
|
|
461
|
+
context.report({
|
|
462
|
+
loc: {
|
|
463
|
+
start: semi1.loc.end,
|
|
464
|
+
end: semi1_next.loc.start
|
|
465
|
+
},
|
|
466
|
+
message: '无条件 for 语句第二个分号后面应该没有空格',
|
|
467
|
+
node,
|
|
468
|
+
fix: fixer => fixer.removeRange([semi1.range[1], semi1_next.range[0]])
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
// return (
|
|
476
|
+
// <div>1234</div>
|
|
477
|
+
// )
|
|
478
|
+
// 改为
|
|
479
|
+
// return <div>1234</div>
|
|
480
|
+
'jsx-no-redundant-parenthesis-in-return': {
|
|
481
|
+
meta,
|
|
482
|
+
create(context) {
|
|
483
|
+
const source = context.sourceCode;
|
|
484
|
+
return {
|
|
485
|
+
ReturnStatement(node) {
|
|
486
|
+
if (node.argument?.type === 'JSXElement' || node.argument?.type === 'JSXFragment') {
|
|
487
|
+
const paren0 = source.getTokenBefore(node.argument);
|
|
488
|
+
const paren1 = source.getTokenAfter(node.argument);
|
|
489
|
+
if (paren0.type === 'Punctuator' && paren0.value === '(' &&
|
|
490
|
+
paren1.type === 'Punctuator' && paren1.value === ')') {
|
|
491
|
+
const paren0_next = source.getTokenAfter(paren0);
|
|
492
|
+
const paren1_prev = source.getTokenBefore(paren1);
|
|
493
|
+
context.report({
|
|
494
|
+
loc: {
|
|
495
|
+
start: paren0.loc.start,
|
|
496
|
+
end: paren1.loc.end
|
|
497
|
+
},
|
|
498
|
+
message: 'return jsx 表达式不应该有多余的括号',
|
|
499
|
+
node,
|
|
500
|
+
fix: fixer => ([
|
|
501
|
+
fixer.removeRange([paren0.range[0], paren0_next.range[0]]),
|
|
502
|
+
fixer.removeRange([paren1_prev.range[1], paren1.range[1]])
|
|
503
|
+
])
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
ArrowFunctionExpression(node) {
|
|
509
|
+
if (node.body?.type === 'JSXElement' || node.body?.type === 'JSXFragment') {
|
|
510
|
+
const paren0 = source.getTokenBefore(node.body);
|
|
511
|
+
const paren1 = source.getTokenAfter(node.body);
|
|
512
|
+
if (paren0.type === 'Punctuator' && paren0.value === '(' &&
|
|
513
|
+
paren1.type === 'Punctuator' && paren1.value === ')') {
|
|
514
|
+
const paren0_next = source.getTokenAfter(paren0);
|
|
515
|
+
const paren1_prev = source.getTokenBefore(paren1);
|
|
516
|
+
context.report({
|
|
517
|
+
loc: {
|
|
518
|
+
start: paren0.loc.start,
|
|
519
|
+
end: paren1.loc.end
|
|
520
|
+
},
|
|
521
|
+
message: 'return jsx 表达式不应该有多余的括号',
|
|
522
|
+
node,
|
|
523
|
+
fix: fixer => ([
|
|
524
|
+
fixer.removeRange([paren0.range[0], paren0_next.range[0]]),
|
|
525
|
+
fixer.removeRange([paren1_prev.range[1], paren1.range[1]])
|
|
526
|
+
])
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
'keep-indent': {
|
|
535
|
+
meta,
|
|
536
|
+
create(context) {
|
|
537
|
+
const source = context.sourceCode;
|
|
538
|
+
const lines = source.getLines();
|
|
539
|
+
return {
|
|
540
|
+
Program() {
|
|
541
|
+
// 参考 keep_indent 逻辑
|
|
542
|
+
if (lines.length >= 2) {
|
|
543
|
+
let last_line = { indent: 0, text: '' };
|
|
544
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
545
|
+
const _line = split_indent(lines[i]);
|
|
546
|
+
if (!_line.indent && !_line.text && last_line.indent) {
|
|
547
|
+
const pos = { column: 0, line: i + 1 };
|
|
548
|
+
const index = source.getIndexFromLoc(pos);
|
|
549
|
+
const node = source.getNodeByRangeIndex(index);
|
|
550
|
+
if (node && node.type !== 'Program')
|
|
551
|
+
context.report({
|
|
552
|
+
message: '空行应保留与上一个区块相同的缩进',
|
|
553
|
+
loc: { start: pos, end: pos },
|
|
554
|
+
fix: fixer => fixer.replaceTextRange([index, index], ' '.repeat(last_line.indent))
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
last_line = _line;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
'func-style': {
|
|
565
|
+
meta,
|
|
566
|
+
create(context) {
|
|
567
|
+
const source = context.sourceCode;
|
|
568
|
+
let stack = [];
|
|
569
|
+
const fix = (fixer, node) => fixer.replaceText(node.parent.parent, `${node.async ? 'async ' : ''}function ${node.generator ? '* ' : ''}` +
|
|
570
|
+
// todo: 丢失了泛型参数 const foo = <D = Zippable> (xxx: D) => { },如何加回来?
|
|
571
|
+
node.parent.id.name +
|
|
572
|
+
' (' +
|
|
573
|
+
node.params.map(param => source.getText(param)).join(', ') +
|
|
574
|
+
') ' +
|
|
575
|
+
source.getText(node.body));
|
|
576
|
+
return {
|
|
577
|
+
FunctionDeclaration(node) {
|
|
578
|
+
stack.push(false);
|
|
579
|
+
},
|
|
580
|
+
'FunctionDeclaration:exit'() {
|
|
581
|
+
stack.pop();
|
|
582
|
+
},
|
|
583
|
+
FunctionExpression(node) {
|
|
584
|
+
stack.push(false);
|
|
585
|
+
if (node.parent.type === 'VariableDeclarator')
|
|
586
|
+
context.report({
|
|
587
|
+
node: node.parent.parent,
|
|
588
|
+
message: '函数应该使用 function foo { } 这样来声明而不是 const foo = function () { }',
|
|
589
|
+
loc: node.parent.parent.loc,
|
|
590
|
+
fix: fixer => fix(fixer, node)
|
|
591
|
+
});
|
|
592
|
+
},
|
|
593
|
+
'FunctionExpression:exit'() {
|
|
594
|
+
stack.pop();
|
|
595
|
+
},
|
|
596
|
+
ThisExpression() {
|
|
597
|
+
if (stack.length > 0)
|
|
598
|
+
stack[stack.length - 1] = true;
|
|
599
|
+
},
|
|
600
|
+
ArrowFunctionExpression() {
|
|
601
|
+
stack.push(false);
|
|
602
|
+
},
|
|
603
|
+
'ArrowFunctionExpression:exit'(node) {
|
|
604
|
+
if (!stack.pop() && // not has this expr
|
|
605
|
+
node.parent.type === 'VariableDeclarator' &&
|
|
606
|
+
node.body.type === 'BlockStatement' && node.body.body.length // 多条语句才转换
|
|
607
|
+
)
|
|
608
|
+
context.report({
|
|
609
|
+
node: node.parent.parent,
|
|
610
|
+
message: '多条语句的函数应该使用 function foo { } 这样来声明而不是 const foo = () => { }',
|
|
611
|
+
loc: node.parent.parent.loc,
|
|
612
|
+
fix: fixer => fix(fixer, node)
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
// 规则太复杂了,暂时写不出来
|
|
619
|
+
// ... color ? [ ] : [
|
|
620
|
+
// '-c', 'color.status=false',
|
|
621
|
+
// '-c', 'color.ui=false',
|
|
622
|
+
// '-c', 'color.branch=false',
|
|
623
|
+
// '-c', 'color.ui=false',
|
|
624
|
+
// ],
|
|
625
|
+
// test ?
|
|
626
|
+
// ...
|
|
627
|
+
// : [ ]
|
|
628
|
+
// 'conditional-expresssion-style': {
|
|
629
|
+
// meta,
|
|
630
|
+
// create (context) {
|
|
631
|
+
// const source = context.sourceCode
|
|
632
|
+
// const lines = source.getLines()
|
|
633
|
+
// return {
|
|
634
|
+
// ConditionalExpression (node) {
|
|
635
|
+
// const question = source.getTokenAfter(node.test, not_closing_paren)
|
|
636
|
+
// const colon = source.getTokenAfter(node.consequent, not_closing_paren)
|
|
637
|
+
// const test_tail = source.getTokenBefore(question, { includeComments: true })
|
|
638
|
+
// const test_head = source.getFirstToken(node, { includeComments: true })
|
|
639
|
+
// const consequent_start = source.getTokenAfter(question, not_opening_paren)
|
|
640
|
+
// const consequent_tail = source.getTokenBefore(colon, not_closing_paren)
|
|
641
|
+
// const alternate_start = source.getTokenAfter(colon, not_opening_paren)
|
|
642
|
+
// const node_tail = source.getLastToken(node)
|
|
643
|
+
// // 三元运算符的 ? 应该和 test 条件末尾在同一行
|
|
644
|
+
// if (test_tail.loc.start.line !== question.loc.start.line)
|
|
645
|
+
// context.report({
|
|
646
|
+
// node,
|
|
647
|
+
// loc: {
|
|
648
|
+
// start: colon.loc.start,
|
|
649
|
+
// end: colon.loc.end
|
|
650
|
+
// },
|
|
651
|
+
// message: '条件表达式的 ? 应该和 test 条件末尾在同一行',
|
|
652
|
+
// fix: fixer => fixer.replaceTextRange([test_tail.range[1], colon.range[0]], ' ')
|
|
653
|
+
// })
|
|
654
|
+
// // 单行的条件表达式
|
|
655
|
+
// if (colon.loc.start.line === question.loc.start.line) {
|
|
656
|
+
// const alternate_start_with_comments = source.getTokenAfter(colon, { includeComments: true, filter: not_opening_paren })
|
|
657
|
+
// if (node_tail.loc.end.line !== colon.loc.start.line)
|
|
658
|
+
// context.report({
|
|
659
|
+
// node,
|
|
660
|
+
// loc: {
|
|
661
|
+
// start: node.alternate.loc.start,
|
|
662
|
+
// end: node_tail.loc.end
|
|
663
|
+
// },
|
|
664
|
+
// message: '单行条件表达式的 else 条件也应该在同一行',
|
|
665
|
+
// fix: fixer => fixer.replaceTextRange([colon.range[1], alternate_start_with_comments.range[0]], ' ')
|
|
666
|
+
// })
|
|
667
|
+
// } else { // 多行条件表达式
|
|
668
|
+
// if (consequent_tail.loc.end.line === colon.loc.start.line)
|
|
669
|
+
// context.report({
|
|
670
|
+
// node,
|
|
671
|
+
// loc: {
|
|
672
|
+
// start: consequent_tail.loc.start,
|
|
673
|
+
// end: colon.loc.start
|
|
674
|
+
// },
|
|
675
|
+
// message: '多行条件表达式的 : 应该在单独的一行',
|
|
676
|
+
// fix: fixer =>
|
|
677
|
+
// fixer.insertTextBefore(
|
|
678
|
+
// colon,
|
|
679
|
+
// '\n' +
|
|
680
|
+
// ' '.repeat(
|
|
681
|
+
// Math.max(split_indent(lines[consequent_tail.loc.end.line - 1]).indent - 4, 0)
|
|
682
|
+
// )
|
|
683
|
+
// )
|
|
684
|
+
// })
|
|
685
|
+
// if (
|
|
686
|
+
// alternate_start.loc.start.line === colon.loc.start.line &&
|
|
687
|
+
// (node.alternate.loc.start.line !== node.alternate.loc.end.line)
|
|
688
|
+
// )
|
|
689
|
+
// context.report({
|
|
690
|
+
// node,
|
|
691
|
+
// loc: {
|
|
692
|
+
// start: colon.loc.end,
|
|
693
|
+
// end: alternate_start.loc.start
|
|
694
|
+
// },
|
|
695
|
+
// message: '多行条件表达式的 : 应该在单独的一行',
|
|
696
|
+
// fix: fixer => fixer.insertTextBefore(
|
|
697
|
+
// alternate_start,
|
|
698
|
+
// '\n' +
|
|
699
|
+
// ' '.repeat(
|
|
700
|
+
// split_indent(lines[colon.loc.start.line - 1]).indent + 4
|
|
701
|
+
// )
|
|
702
|
+
// )
|
|
703
|
+
// })
|
|
704
|
+
// if (consequent_start.loc.start.line === question.loc.start.line)
|
|
705
|
+
// context.report({
|
|
706
|
+
// node,
|
|
707
|
+
// loc: {
|
|
708
|
+
// start: question.loc.end,
|
|
709
|
+
// end: test_head.loc.start
|
|
710
|
+
// },
|
|
711
|
+
// message: '多行条件表达式的 ? 这一行后面不应该有其他表达式,请换行',
|
|
712
|
+
// fix: fixer => fixer.insertTextBefore(
|
|
713
|
+
// consequent_start,
|
|
714
|
+
// '\n' +
|
|
715
|
+
// ' '.repeat(
|
|
716
|
+
// split_indent(lines[colon.loc.start.line - 1]).indent + 4
|
|
717
|
+
// )
|
|
718
|
+
// )
|
|
719
|
+
// })
|
|
720
|
+
// }
|
|
721
|
+
// }
|
|
722
|
+
// }
|
|
723
|
+
// }
|
|
724
|
+
// }
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
function split_indent(line) {
|
|
728
|
+
let i = 0;
|
|
729
|
+
let indent = 0;
|
|
730
|
+
for (; i < line.length; i++)
|
|
731
|
+
if (line[i] === ' ')
|
|
732
|
+
indent++;
|
|
733
|
+
else if (line[i] === '\t')
|
|
734
|
+
indent += 4;
|
|
735
|
+
else
|
|
736
|
+
break;
|
|
737
|
+
return {
|
|
738
|
+
indent,
|
|
739
|
+
text: line.slice(i)
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function not_opening_paren(token) {
|
|
743
|
+
return !(['(', '[', '{'].includes(token.value) && token.type === 'Punctuator');
|
|
744
|
+
}
|
|
745
|
+
function not_closing_paren(token) {
|
|
746
|
+
return !([')', ']', '}'].includes(token.value) && token.type === 'Punctuator');
|
|
747
|
+
}
|
|
748
|
+
//# sourceMappingURL=xlint.js.map
|