isml-linter 5.39.1 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1177 -1142
- package/LICENSE +21 -21
- package/README.md +245 -245
- package/bin/isml-linter.js +32 -32
- package/ismllinter.config.js +33 -33
- package/package.json +53 -53
- package/scaffold_files/ismllinter.config.js +47 -47
- package/src/Builder.js +15 -15
- package/src/Constants.js +139 -139
- package/src/IsmlLinter.js +255 -255
- package/src/enums/ParseStatus.js +5 -5
- package/src/enums/SfccTagContainer.js +287 -287
- package/src/isml_tree/ContainerNode.js +38 -38
- package/src/isml_tree/IsmlNode.js +692 -658
- package/src/isml_tree/MaskUtils.js +421 -419
- package/src/isml_tree/ParseUtils.js +515 -434
- package/src/isml_tree/TreeBuilder.js +273 -271
- package/src/publicApi.js +24 -24
- package/src/rules/line_by_line/enforce-isprint.js +53 -53
- package/src/rules/line_by_line/enforce-require.js +35 -35
- package/src/rules/line_by_line/lowercase-filename.js +29 -29
- package/src/rules/line_by_line/max-lines.js +37 -37
- package/src/rules/line_by_line/no-br.js +36 -36
- package/src/rules/line_by_line/no-git-conflict.js +43 -43
- package/src/rules/line_by_line/no-import-package.js +34 -34
- package/src/rules/line_by_line/no-inline-style.js +34 -34
- package/src/rules/line_by_line/no-isscript.js +34 -34
- package/src/rules/line_by_line/no-space-only-lines.js +47 -47
- package/src/rules/line_by_line/no-tabs.js +38 -38
- package/src/rules/line_by_line/no-trailing-spaces.js +52 -52
- package/src/rules/prototypes/RulePrototype.js +79 -79
- package/src/rules/prototypes/SingleLineRulePrototype.js +47 -47
- package/src/rules/prototypes/TreeRulePrototype.js +84 -84
- package/src/rules/tree/align-isset.js +87 -87
- package/src/rules/tree/contextual-attrs.js +105 -105
- package/src/rules/tree/custom-tags.js +54 -54
- package/src/rules/tree/disallow-tags.js +39 -39
- package/src/rules/tree/empty-eof.js +66 -66
- package/src/rules/tree/enforce-security.js +85 -85
- package/src/rules/tree/eslint-to-isscript.js +179 -179
- package/src/rules/tree/indent.js +856 -853
- package/src/rules/tree/leading-iscache.js +39 -43
- package/src/rules/tree/leading-iscontent.js +35 -39
- package/src/rules/tree/max-depth.js +54 -54
- package/src/rules/tree/no-deprecated-attrs.js +67 -67
- package/src/rules/tree/no-embedded-isml.js +17 -17
- package/src/rules/tree/no-hardcode.js +51 -51
- package/src/rules/tree/no-iselse-slash.js +35 -35
- package/src/rules/tree/no-redundant-context.js +134 -134
- package/src/rules/tree/no-require-in-loop.js +63 -63
- package/src/rules/tree/one-element-per-line.js +82 -76
- package/src/util/CommandLineUtils.js +19 -19
- package/src/util/ConfigUtils.js +219 -219
- package/src/util/ConsoleUtils.js +327 -327
- package/src/util/CustomTagContainer.js +45 -45
- package/src/util/ExceptionUtils.js +149 -136
- package/src/util/FileUtils.js +79 -79
- package/src/util/GeneralUtils.js +60 -60
- package/src/util/NativeExtensionUtils.js +6 -6
- package/src/util/RuleUtils.js +295 -295
- package/src/util/TempRuleUtils.js +232 -232
package/src/rules/tree/indent.js
CHANGED
|
@@ -1,853 +1,856 @@
|
|
|
1
|
-
const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
|
|
2
|
-
const ParseUtils = require('../../isml_tree/ParseUtils');
|
|
3
|
-
const TreeBuilder = require('../../isml_tree/TreeBuilder');
|
|
4
|
-
const Constants = require('../../Constants');
|
|
5
|
-
|
|
6
|
-
const ruleId = require('path').basename(__filename).slice(0, -3);
|
|
7
|
-
const description = 'Line incorrectly indented';
|
|
8
|
-
|
|
9
|
-
const Rule = Object.create(TreeRulePrototype);
|
|
10
|
-
|
|
11
|
-
Rule.init(ruleId, description);
|
|
12
|
-
|
|
13
|
-
Rule.getDefaultAttrs = () => {
|
|
14
|
-
return {
|
|
15
|
-
size : 4,
|
|
16
|
-
attributeOffset : 4,
|
|
17
|
-
standAloneClosingChars : {
|
|
18
|
-
nonSelfClosingTag : 'always',
|
|
19
|
-
selfClosingTag : 'never',
|
|
20
|
-
quote : 'never'
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
Rule.getIndentation = function(depth = 1) {
|
|
26
|
-
const indentationSize = this.getConfigs().indent * depth;
|
|
27
|
-
let indentation = '';
|
|
28
|
-
|
|
29
|
-
for (let i = 0; i < indentationSize; ++i) {
|
|
30
|
-
indentation += ' ';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return indentation;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
Rule.getAttributeIndentationOffset = function() {
|
|
37
|
-
const offsetSize = this.getConfigs().attributeOffset;
|
|
38
|
-
let indentation = '';
|
|
39
|
-
|
|
40
|
-
for (let i = 0; i < offsetSize; ++i) {
|
|
41
|
-
indentation += ' ';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return indentation;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
Rule.isBroken = function(node) {
|
|
48
|
-
|
|
49
|
-
const configIndentSize = this.getConfigs().indent;
|
|
50
|
-
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
51
|
-
const actualIndentation = getActualIndentation(node);
|
|
52
|
-
|
|
53
|
-
return !node.isRoot() &&
|
|
54
|
-
!node.isContainer() &&
|
|
55
|
-
!node.isEmpty() &&
|
|
56
|
-
!node.isInSameLineAsParentEnd() &&
|
|
57
|
-
expectedIndentation !== actualIndentation &&
|
|
58
|
-
!node.isInSameLineAsPreviousSibling();
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
Rule.isTailBroken = function(node) {
|
|
62
|
-
|
|
63
|
-
const configIndentSize = this.getConfigs().indent;
|
|
64
|
-
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
65
|
-
const actualIndentation = getActualTailIndentation(node);
|
|
66
|
-
const isInSameLineAsOpeningTag = node.lineNumber === node.tailLineNumber;
|
|
67
|
-
const isInSameLineAsLastChild = node.hasChildren() && node.getLastChild().getLastLineNumber() === node.tailLineNumber;
|
|
68
|
-
|
|
69
|
-
return !node.isRoot() &&
|
|
70
|
-
!node.isContainer() &&
|
|
71
|
-
!node.isEmpty() &&
|
|
72
|
-
!node.isInSameLineAsParent() &&
|
|
73
|
-
expectedIndentation !== actualIndentation &&
|
|
74
|
-
!isInSameLineAsLastChild &&
|
|
75
|
-
!isInSameLineAsOpeningTag;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
Rule.isClosingCharBroken = function(node) {
|
|
79
|
-
|
|
80
|
-
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
81
|
-
const isFirstChildInSameLine = node.hasChildren() && node.endLineNumber === node.children[0].lineNumber;
|
|
82
|
-
|
|
83
|
-
if (!node.isTag()
|
|
84
|
-
|| !node.isMultiLineOpeningTag()
|
|
85
|
-
|| isFirstChildInSameLine
|
|
86
|
-
|| !closingCharsConfigs
|
|
87
|
-
|| !node.isSelfClosing() && (!closingCharsConfigs.nonSelfClosingTag || closingCharsConfigs.nonSelfClosingTag === 'any')
|
|
88
|
-
|| node.isSelfClosing() && (!closingCharsConfigs.selfClosingTag || closingCharsConfigs.selfClosingTag === 'any')
|
|
89
|
-
) {
|
|
90
|
-
return {
|
|
91
|
-
isBroken : false
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (node.isSelfClosing()) {
|
|
96
|
-
if (closingCharsConfigs.selfClosingTag === 'always') {
|
|
97
|
-
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
98
|
-
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '/>';
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
isBroken : !isClosingCharStandingAlone,
|
|
102
|
-
config : 'always'
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
} else if (closingCharsConfigs.selfClosingTag === 'never') {
|
|
106
|
-
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
107
|
-
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '/>';
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
isBroken : isClosingCharStandingAlone,
|
|
111
|
-
config : 'never'
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
if (closingCharsConfigs.nonSelfClosingTag === 'always') {
|
|
116
|
-
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
117
|
-
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '>';
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
isBroken : !isClosingCharStandingAlone,
|
|
121
|
-
config : 'always'
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
} else if (closingCharsConfigs.nonSelfClosingTag === 'never') {
|
|
125
|
-
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
126
|
-
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '>';
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
isBroken : isClosingCharStandingAlone,
|
|
130
|
-
config : 'never'
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
Rule.isQuoteClosingCharBroken = function(node) {
|
|
137
|
-
|
|
138
|
-
const quoteConfig = Rule.getConfigs().standAloneClosingChars.quote;
|
|
139
|
-
const attributeList = node.getAttributeList();
|
|
140
|
-
const result = [];
|
|
141
|
-
|
|
142
|
-
for (let i = 0; i < attributeList.length; i++) {
|
|
143
|
-
const attribute = attributeList[i];
|
|
144
|
-
|
|
145
|
-
if (attribute.value) {
|
|
146
|
-
const attributeValueLineList = attribute.fullContent.trim().split(Constants.EOL);
|
|
147
|
-
const isClosingCharStandingAlone = attributeValueLineList[attributeValueLineList.length - 1].trim() === attribute.quoteChar;
|
|
148
|
-
const lineNumber = attribute.lineNumber + ParseUtils.getLineBreakQty(attribute.fullContent);
|
|
149
|
-
const lineList = attribute.value.split(Constants.EOL);
|
|
150
|
-
const columnNumber = lineList[lineList.length - 1].length + 1;
|
|
151
|
-
const globalPos = attribute.globalPos
|
|
152
|
-
+ attribute.fullContent.lastIndexOf(attribute.quoteChar)
|
|
153
|
-
+ lineNumber - 2;
|
|
154
|
-
const formattedLineList = lineList
|
|
155
|
-
.map( line => line.trim())
|
|
156
|
-
.filter( line => line );
|
|
157
|
-
const lastLine = formattedLineList[formattedLineList.length - 1];
|
|
158
|
-
|
|
159
|
-
const message = quoteConfig === 'always' && !isClosingCharStandingAlone ? getStandAloneQuoteDescription(lastLine) :
|
|
160
|
-
quoteConfig === 'never' && isClosingCharStandingAlone ? getNonStandAloneQuoteDescription(lastLine) :
|
|
161
|
-
'';
|
|
162
|
-
|
|
163
|
-
if (message) {
|
|
164
|
-
result.push({
|
|
165
|
-
quoteChar : attribute.quoteChar,
|
|
166
|
-
lineNumber : lineNumber,
|
|
167
|
-
columnNumber : columnNumber,
|
|
168
|
-
globalPos : globalPos,
|
|
169
|
-
length : attribute.quoteChar.length,
|
|
170
|
-
message : message
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return result;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
Rule.check = function(node, data) {
|
|
180
|
-
|
|
181
|
-
const ruleConfig = this.getConfigs();
|
|
182
|
-
const typeArray = ['script', 'iscomment'];
|
|
183
|
-
let occurrenceList = [];
|
|
184
|
-
|
|
185
|
-
if (node.isRoot() || !node.parent.isOneOfTypes(typeArray)) {
|
|
186
|
-
occurrenceList = this.checkChildren(node, data);
|
|
187
|
-
|
|
188
|
-
const errorGlobalPos = node.globalPos - getActualIndentation(node);
|
|
189
|
-
const attributeErrorList = getAttributeErrorList(node);
|
|
190
|
-
|
|
191
|
-
// Checks node value;
|
|
192
|
-
if (this.isBroken(node)) {
|
|
193
|
-
const configIndentSize = ruleConfig.indent;
|
|
194
|
-
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
195
|
-
const actualIndentation = getActualIndentation(node);
|
|
196
|
-
const nodeHead = node.head.trim();
|
|
197
|
-
const occurrenceLength = actualIndentation === 0 ?
|
|
198
|
-
nodeHead.length + ParseUtils.getLineBreakQty(nodeHead) :
|
|
199
|
-
getActualIndentation(node);
|
|
200
|
-
|
|
201
|
-
const error = this.getError(
|
|
202
|
-
node.head.trim(),
|
|
203
|
-
node.lineNumber,
|
|
204
|
-
node.columnNumber,
|
|
205
|
-
errorGlobalPos,
|
|
206
|
-
occurrenceLength,
|
|
207
|
-
getOccurrenceDescription(expectedIndentation, actualIndentation)
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
occurrenceList.push(error);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (attributeErrorList.length > 0) {
|
|
214
|
-
for (let i = 0; i < attributeErrorList.length; i++) {
|
|
215
|
-
const attributeError = attributeErrorList[i];
|
|
216
|
-
occurrenceList.push(attributeError);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Checks node tail value;
|
|
221
|
-
if (node.tail && this.isTailBroken(node)) {
|
|
222
|
-
const configIndentSize = ruleConfig.indent;
|
|
223
|
-
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
224
|
-
const actualIndentation = getActualTailIndentation(node);
|
|
225
|
-
const nodeTail = node.tail.trim();
|
|
226
|
-
const occurrenceLength = actualIndentation === 0 ?
|
|
227
|
-
nodeTail.length + ParseUtils.getLineBreakQty(nodeTail) :
|
|
228
|
-
getActualTailIndentation(node);
|
|
229
|
-
const tailGlobalPos = node.tailGlobalPos - getActualTailIndentation(node);
|
|
230
|
-
|
|
231
|
-
const error = this.getError(
|
|
232
|
-
node.tail.trim(),
|
|
233
|
-
node.tailLineNumber,
|
|
234
|
-
node.tailColumnNumber,
|
|
235
|
-
tailGlobalPos,
|
|
236
|
-
occurrenceLength,
|
|
237
|
-
getOccurrenceDescription(expectedIndentation, actualIndentation)
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
occurrenceList.push(error);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const quoteOccurrenceList = this.isQuoteClosingCharBroken(node);
|
|
244
|
-
occurrenceList.push(...quoteOccurrenceList);
|
|
245
|
-
|
|
246
|
-
const checkResult = this.isClosingCharBroken(node);
|
|
247
|
-
if (checkResult.isBroken) {
|
|
248
|
-
if (node.isSelfClosing()) {
|
|
249
|
-
const closingChar = '/>';
|
|
250
|
-
const globalPos = node.globalPos
|
|
251
|
-
+ node.head.trim().lastIndexOf(closingChar);
|
|
252
|
-
const lineList = node.head.split(Constants.EOL);
|
|
253
|
-
const columnNumber = lineList[lineList.length - 1].lastIndexOf(closingChar) + 1;
|
|
254
|
-
const message = checkResult.config === 'always' ?
|
|
255
|
-
getStandAloneCharDescription(node.getType(), closingChar) :
|
|
256
|
-
getNonStandAloneCharDescription(node.getType(), closingChar);
|
|
257
|
-
|
|
258
|
-
const error = this.getError(
|
|
259
|
-
node.head.trim(),
|
|
260
|
-
node.endLineNumber,
|
|
261
|
-
columnNumber,
|
|
262
|
-
globalPos,
|
|
263
|
-
closingChar.length,
|
|
264
|
-
message
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
occurrenceList.push(error);
|
|
268
|
-
|
|
269
|
-
} else {
|
|
270
|
-
const closingChar = '>';
|
|
271
|
-
const globalPos = node.globalPos
|
|
272
|
-
+ node.head.trim().length - closingChar.length;
|
|
273
|
-
const lineList = node.head.trim().split(Constants.EOL);
|
|
274
|
-
const columnNumber = lineList[lineList.length - 1].lastIndexOf(closingChar) + 1;
|
|
275
|
-
const message = checkResult.config === 'always' ?
|
|
276
|
-
getStandAloneCharDescription(node.getType(), closingChar) :
|
|
277
|
-
getNonStandAloneCharDescription(node.getType(), closingChar);
|
|
278
|
-
|
|
279
|
-
const error = this.getError(
|
|
280
|
-
node.head.trim(),
|
|
281
|
-
node.endLineNumber,
|
|
282
|
-
columnNumber,
|
|
283
|
-
globalPos,
|
|
284
|
-
closingChar.length,
|
|
285
|
-
message
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
occurrenceList.push(error);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return this.return(node, occurrenceList, ruleConfig);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
Rule.getFixedContent = node => {
|
|
297
|
-
if (node.isRoot() || !node.parent.isOfType('script')) {
|
|
298
|
-
removeAllIndentation(node);
|
|
299
|
-
addCorrectIndentation(node);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return node.toString();
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* PRIVATE FUNCTIONS
|
|
307
|
-
*/
|
|
308
|
-
|
|
309
|
-
const getAttributeValueErrorList = function(node, attribute) {
|
|
310
|
-
|
|
311
|
-
if (!attribute.value) {
|
|
312
|
-
return [];
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const configIndentSize = Rule.getConfigs().indent;
|
|
316
|
-
const configAttributeOffsetSize = Rule.getConfigs().attributeOffset;
|
|
317
|
-
const result = [];
|
|
318
|
-
|
|
319
|
-
if (attribute.hasMultilineValue) {
|
|
320
|
-
const expectedIndentation = node.depth * configIndentSize + configAttributeOffsetSize;
|
|
321
|
-
const attributeValueList = attribute.value
|
|
322
|
-
.split(Constants.EOL)
|
|
323
|
-
.filter( attr => attr.trim() && ['{', '}'].indexOf(attr.trim()) === -1);
|
|
324
|
-
|
|
325
|
-
for (let i = 0; i < attributeValueList.length; i++) {
|
|
326
|
-
|
|
327
|
-
if (i > 0) {
|
|
328
|
-
const partialResult = getAttributeNestedValueError(attribute, attributeValueList, i, expectedIndentation, configAttributeOffsetSize);
|
|
329
|
-
|
|
330
|
-
if (partialResult.error) {
|
|
331
|
-
result.push(partialResult.error);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (partialResult.shouldContinueLoop) {
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const error = getAttributeValueError(attribute, attributeValueList, i, expectedIndentation);
|
|
340
|
-
|
|
341
|
-
if (error) {
|
|
342
|
-
result.push(error);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return result;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const getAttributeErrorList = function(node) {
|
|
351
|
-
const configIndentSize = Rule.getConfigs().indent;
|
|
352
|
-
const attributeList = node.getAttributeList();
|
|
353
|
-
const result = [];
|
|
354
|
-
|
|
355
|
-
for (let i = 0; i < attributeList.length; i++) {
|
|
356
|
-
const attribute = attributeList[i];
|
|
357
|
-
|
|
358
|
-
if (!attribute.isInSameLineAsTagName && attribute.isFirstInLine) {
|
|
359
|
-
const expectedIndentation = node.depth * configIndentSize;
|
|
360
|
-
|
|
361
|
-
if (attribute.columnNumber - 1 !== expectedIndentation) {
|
|
362
|
-
const occurrenceGlobalPos = attribute.globalPos + node.lineNumber - attribute.columnNumber;
|
|
363
|
-
const occurrenceLength = attribute.columnNumber === 1 ?
|
|
364
|
-
attribute.length + ParseUtils.getLineBreakQty(attribute.value) :
|
|
365
|
-
attribute.columnNumber - 1;
|
|
366
|
-
|
|
367
|
-
const error = Rule.getError(
|
|
368
|
-
attribute.fullContent,
|
|
369
|
-
attribute.lineNumber,
|
|
370
|
-
attribute.columnNumber,
|
|
371
|
-
occurrenceGlobalPos,
|
|
372
|
-
occurrenceLength,
|
|
373
|
-
getOccurrenceDescription(expectedIndentation, attribute.columnNumber - 1)
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
result.push(error);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const attributeErrorList = getAttributeValueErrorList(node, attribute);
|
|
381
|
-
result.push(...attributeErrorList);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return result;
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const removeIndentation = content => {
|
|
388
|
-
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
389
|
-
const endingPos = content.length - ParseUtils.getNextNonEmptyCharPos(content.split('').reverse().join(''));
|
|
390
|
-
const fullLeadingContent = content.substring(0, startingPos);
|
|
391
|
-
const actualContent = content.substring(startingPos, endingPos);
|
|
392
|
-
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
393
|
-
const fullTrailingContent = content.substring(endingPos);
|
|
394
|
-
const lastLineBreakPos = fullTrailingContent.lastIndexOf(Constants.EOL);
|
|
395
|
-
const trimmedTrailingContent = lastLineBreakPos === -1 ?
|
|
396
|
-
fullTrailingContent :
|
|
397
|
-
fullTrailingContent.substring(0, lastLineBreakPos + 1);
|
|
398
|
-
|
|
399
|
-
// Removes indentation from node attributes;
|
|
400
|
-
const indentlessContent = actualContent
|
|
401
|
-
.split(Constants.EOL)
|
|
402
|
-
.map(line => line.trimStart())
|
|
403
|
-
.join(Constants.EOL);
|
|
404
|
-
|
|
405
|
-
return preLineBreakContent + indentlessContent + trimmedTrailingContent;
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const addIndentationToText = node => {
|
|
409
|
-
const content = node.head;
|
|
410
|
-
const lineArray = content
|
|
411
|
-
.split(Constants.EOL)
|
|
412
|
-
.filter( (line, i) => !(i === 0 && line === ''))
|
|
413
|
-
.map(line => line.trimStart());
|
|
414
|
-
|
|
415
|
-
const formattedLineArray = [];
|
|
416
|
-
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
417
|
-
const fullLeadingContent = content.substring(0, startingPos);
|
|
418
|
-
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
419
|
-
const correctIndentation = node.isInSameLineAsParent() ? '' : Rule.getIndentation(node.depth - 1);
|
|
420
|
-
|
|
421
|
-
for (let i = 0; i < lineArray.length; i++) {
|
|
422
|
-
let formattedLine = lineArray[i];
|
|
423
|
-
|
|
424
|
-
if (lineArray[i].length !== 0) {
|
|
425
|
-
formattedLine = correctIndentation + lineArray[i];
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (!(i === 0 && formattedLine.length === 0)) {
|
|
429
|
-
formattedLineArray.push(formattedLine);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return preLineBreakContent + formattedLineArray.join(Constants.EOL);
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const getIndentedNestedIsmlContent = (attribute, nodeIndentation, attributeOffset) => {
|
|
437
|
-
if (attribute.fullContent.startsWith('<isif')) {
|
|
438
|
-
const attributeRootNode = TreeBuilder.parse(attribute.fullContent, null, null, true);
|
|
439
|
-
const fixedContent = Rule.getFixedContent(attributeRootNode);
|
|
440
|
-
|
|
441
|
-
return fixedContent
|
|
442
|
-
.split(Constants.EOL)
|
|
443
|
-
.map( (line, i) => {
|
|
444
|
-
if (i === 0 && attribute.isFirstInLine && !attribute.isInSameLineAsTagName && attribute.index !== 0) {
|
|
445
|
-
return Constants.EOL + nodeIndentation + attributeOffset + line;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return nodeIndentation + attributeOffset + line;
|
|
449
|
-
})
|
|
450
|
-
.join(Constants.EOL);
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
const getTreeBuiltAttributeIndentedValue = (attribute, nodeIndentation, attributeOffset) => {
|
|
455
|
-
const attributeRootNode = TreeBuilder.parse(attribute.value, null, null, true);
|
|
456
|
-
const fixedContent = Rule.getFixedContent(attributeRootNode);
|
|
457
|
-
|
|
458
|
-
return fixedContent
|
|
459
|
-
.split(Constants.EOL)
|
|
460
|
-
.map( (line, i) => {
|
|
461
|
-
|
|
462
|
-
// If line contains only blank spaces;
|
|
463
|
-
if (!line.trim()) {
|
|
464
|
-
return '';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (i === 0 && attribute.isFirstInLine && !attribute.isInSameLineAsTagName && attribute.index !== 0) {
|
|
468
|
-
return Constants.EOL + nodeIndentation + attributeOffset + line;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (i === 0 && attribute.isFirstValueInSameLineAsAttributeName) {
|
|
472
|
-
return line;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return nodeIndentation + attributeOffset + line;
|
|
476
|
-
})
|
|
477
|
-
.filter( (line, i, list) => {
|
|
478
|
-
if (i === 0 && attribute.isFirstValueInSameLineAsAttributeName && line === '') {
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (i === list.length - 1 && line === '') {
|
|
483
|
-
return false;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return i === 0 || line;
|
|
487
|
-
})
|
|
488
|
-
.join(Constants.EOL);
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const getAttributeIndentedValues = (attribute, nodeIndentation, attributeOffset) => {
|
|
492
|
-
let result = '';
|
|
493
|
-
|
|
494
|
-
if (attribute.value) {
|
|
495
|
-
if (attribute.value.indexOf('<is') >= 0) {
|
|
496
|
-
result = '=' + attribute.quoteChar + getTreeBuiltAttributeIndentedValue(attribute, nodeIndentation, attributeOffset + attributeOffset);
|
|
497
|
-
|
|
498
|
-
} else {
|
|
499
|
-
result = '=' + attribute.quoteChar + attribute.value
|
|
500
|
-
.split(Constants.EOL)
|
|
501
|
-
.filter( value => value )
|
|
502
|
-
.map( (value, i) => {
|
|
503
|
-
if (i === 0) {
|
|
504
|
-
return attribute.isFirstValueInSameLineAsAttributeName ?
|
|
505
|
-
value :
|
|
506
|
-
Constants.EOL + nodeIndentation + attributeOffset + attributeOffset + value;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
return nodeIndentation + attributeOffset + attributeOffset + value;
|
|
510
|
-
})
|
|
511
|
-
.join(Constants.EOL);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
return result;
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const indentAttribute = (attributeList, index, nodeIndentation, attributeOffset) => {
|
|
520
|
-
const attribute = attributeList[index];
|
|
521
|
-
let result = '';
|
|
522
|
-
|
|
523
|
-
const isInSameLineAsPreviousAttribute = index > 0 && attributeList[index - 1].lineNumber === attribute.lineNumber;
|
|
524
|
-
|
|
525
|
-
if (attribute.hasMultilineValue) {
|
|
526
|
-
if (attribute.isNestedIsmlTag) {
|
|
527
|
-
result += getIndentedNestedIsmlContent(attribute, nodeIndentation, attributeOffset);
|
|
528
|
-
|
|
529
|
-
} else {
|
|
530
|
-
let attributePrefix = '';
|
|
531
|
-
|
|
532
|
-
if (attribute.isFirstInLine) {
|
|
533
|
-
if (index > 0) {
|
|
534
|
-
attributePrefix += Constants.EOL;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
attributePrefix += nodeIndentation + attributeOffset;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
const formattedAttributeName = attribute.isExpressionAttribute ?
|
|
541
|
-
getExpressionAttributeIndentation(attribute.name, nodeIndentation, attributeOffset) :
|
|
542
|
-
attribute.name;
|
|
543
|
-
|
|
544
|
-
const formattedAttributeValue = getAttributeIndentedValues(attribute, nodeIndentation, attributeOffset);
|
|
545
|
-
|
|
546
|
-
const closingQuote = shouldAddIndentationToClosingQuote(attribute) ?
|
|
547
|
-
Constants.EOL + nodeIndentation + attributeOffset + attribute.quoteChar :
|
|
548
|
-
attribute.quoteChar;
|
|
549
|
-
|
|
550
|
-
const valueList = attributePrefix
|
|
551
|
-
+ (index > 0 && attribute.isInSameLineAsTagName ? ' ' : '')
|
|
552
|
-
+ formattedAttributeName
|
|
553
|
-
+ formattedAttributeValue
|
|
554
|
-
+ (attribute.isExpressionAttribute ? '' : closingQuote);
|
|
555
|
-
|
|
556
|
-
result += valueList;
|
|
557
|
-
}
|
|
558
|
-
} else {
|
|
559
|
-
if (isInSameLineAsPreviousAttribute) {
|
|
560
|
-
result += ' ';
|
|
561
|
-
|
|
562
|
-
} else if (!attribute.isInSameLineAsTagName) {
|
|
563
|
-
if (index !== 0) {
|
|
564
|
-
result += Constants.EOL;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
result += nodeIndentation + attributeOffset;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
result += attribute.fullContent;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
return result;
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const getExpressionAttributeIndentation = (attributeValue, nodeIndentation, attributeOffset) => {
|
|
577
|
-
return attributeValue
|
|
578
|
-
.split(Constants.EOL)
|
|
579
|
-
.map( (line, i) => {
|
|
580
|
-
return nodeIndentation + attributeOffset + (i > 0 ? attributeOffset : '') + line;
|
|
581
|
-
})
|
|
582
|
-
.join(Constants.EOL)
|
|
583
|
-
.trimStart();
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
const getClosingChars = node => {
|
|
587
|
-
const nodeHead = node.head.trim();
|
|
588
|
-
|
|
589
|
-
if (nodeHead.endsWith(' />')) {
|
|
590
|
-
return ' />';
|
|
591
|
-
} else if (nodeHead.endsWith('/>')) {
|
|
592
|
-
return ' />';
|
|
593
|
-
} else if (nodeHead.endsWith(' >')) {
|
|
594
|
-
return ' >';
|
|
595
|
-
} else if (nodeHead.endsWith('>')) {
|
|
596
|
-
return '>';
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return '';
|
|
600
|
-
};
|
|
601
|
-
|
|
602
|
-
const addIndentation = (node, isOpeningTag) => {
|
|
603
|
-
const content = isOpeningTag ? node.head : node.tail;
|
|
604
|
-
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
605
|
-
const endingPos = content.length - ParseUtils.getNextNonEmptyCharPos(content.split('').reverse().join(''));
|
|
606
|
-
const fullLeadingContent = content.substring(0, startingPos);
|
|
607
|
-
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
608
|
-
const fullTrailingContent = content.substring(endingPos);
|
|
609
|
-
const nodeIndentation = node.isInSameLineAsParent() && isOpeningTag ? '' : Rule.getIndentation(node.depth - 1);
|
|
610
|
-
const attributeOffset = Rule.getAttributeIndentationOffset();
|
|
611
|
-
const attributeList = node.getAttributeList();
|
|
612
|
-
let contentResult = '';
|
|
613
|
-
|
|
614
|
-
if (isOpeningTag) {
|
|
615
|
-
const shouldAddIndentationToClosingChar = shouldAddIndentationToClosingChars(node);
|
|
616
|
-
const closingChars = getClosingChars(node);
|
|
617
|
-
const tagNameEndPos = ParseUtils.getLeadingLineBreakQty(node.head)
|
|
618
|
-
+ ParseUtils.getFirstEmptyCharPos(node.head.trim()) + 1;
|
|
619
|
-
|
|
620
|
-
if (ParseUtils.getLineBreakQty(node.head.trim()) > 0 || shouldAddIndentationToClosingChar && node.isSelfClosing()) {
|
|
621
|
-
contentResult = attributeList.length > 0 ?
|
|
622
|
-
node.head.substring(0, tagNameEndPos).trimStart() :
|
|
623
|
-
node.head.trimStart();
|
|
624
|
-
|
|
625
|
-
for (let i = 0; i < attributeList.length; i++) {
|
|
626
|
-
contentResult += indentAttribute(attributeList, i, nodeIndentation, attributeOffset);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (attributeList.length > 0) {
|
|
630
|
-
contentResult += shouldAddIndentationToClosingChar ?
|
|
631
|
-
Constants.EOL + nodeIndentation + closingChars.trimStart() :
|
|
632
|
-
closingChars;
|
|
633
|
-
}
|
|
634
|
-
} else {
|
|
635
|
-
contentResult = node.head.trim();
|
|
636
|
-
}
|
|
637
|
-
} else {
|
|
638
|
-
contentResult = node.tail.trim();
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
return preLineBreakContent + nodeIndentation + contentResult + fullTrailingContent;
|
|
642
|
-
};
|
|
643
|
-
|
|
644
|
-
const removeAllIndentation = node => {
|
|
645
|
-
if (!node.isRoot() && !node.isContainer() && !node.parent.isOneOfTypes(['isscript', 'script'])) {
|
|
646
|
-
|
|
647
|
-
const shouldRemoveHeadIndentation = node.head && !node.isInSameLineAsPreviousSibling() && !node.isInSameLineAsParent() && !(node.lineNumber === node.parent.endLineNumber);
|
|
648
|
-
const shouldRemoveTailIndentation = node.tail && !(node.hasChildren() && node.getLastChild().lineNumber === node.tailLineNumber);
|
|
649
|
-
|
|
650
|
-
if (shouldRemoveHeadIndentation) {
|
|
651
|
-
node.head = removeIndentation(node.head);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (shouldRemoveTailIndentation) {
|
|
655
|
-
node.tail = removeIndentation(node.tail);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
660
|
-
removeAllIndentation(node.children[i]);
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
const addCorrectIndentation = node => {
|
|
665
|
-
|
|
666
|
-
if (!node.isRoot() && !node.isContainer() && !node.parent.isOneOfTypes(['isscript', 'script'])) {
|
|
667
|
-
if (node.parent.isOfType('iscomment')) {
|
|
668
|
-
const shouldAddIndentationToText = checkIfShouldAddIndentationToHead(node);
|
|
669
|
-
|
|
670
|
-
if (shouldAddIndentationToText) {
|
|
671
|
-
node.head = addIndentationToText(node);
|
|
672
|
-
}
|
|
673
|
-
} else {
|
|
674
|
-
const shouldAddIndentationToHead = checkIfShouldAddIndentationToHead(node);
|
|
675
|
-
const shouldAddIndentationToTail = checkIfShouldAddIndentationToTail(node);
|
|
676
|
-
|
|
677
|
-
if (shouldAddIndentationToHead) {
|
|
678
|
-
node.head = addIndentation(node, true);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
if (shouldAddIndentationToTail) {
|
|
682
|
-
node.tail = addIndentation(node, false);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
688
|
-
addCorrectIndentation(node.children[i]);
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
const shouldAddIndentationToClosingChars = node => {
|
|
693
|
-
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
694
|
-
|
|
695
|
-
if (node.isSelfClosing()) {
|
|
696
|
-
if (closingCharsConfigs.selfClosingTag === 'always') {
|
|
697
|
-
return true;
|
|
698
|
-
} else if (closingCharsConfigs.selfClosingTag === 'never') {
|
|
699
|
-
return false;
|
|
700
|
-
}
|
|
701
|
-
} else {
|
|
702
|
-
if (closingCharsConfigs.nonSelfClosingTag === 'always') {
|
|
703
|
-
return true;
|
|
704
|
-
} else if (closingCharsConfigs.nonSelfClosingTag === 'never') {
|
|
705
|
-
return false;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
const lineList = node.head.split(Constants.EOL);
|
|
710
|
-
const lastLine = lineList[lineList.length - 1];
|
|
711
|
-
|
|
712
|
-
return ['/>', '>'].indexOf(lastLine) >= 0;
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
const shouldAddIndentationToClosingQuote = attribute => {
|
|
716
|
-
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
717
|
-
|
|
718
|
-
if (!attribute.value || !closingCharsConfigs.quote || closingCharsConfigs.quote === 'always') {
|
|
719
|
-
return true;
|
|
720
|
-
} else if (closingCharsConfigs.quote === 'never') {
|
|
721
|
-
return false;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
const lineList = attribute.value.split(Constants.EOL);
|
|
725
|
-
const lastLine = lineList[lineList.length - 1];
|
|
726
|
-
|
|
727
|
-
return lastLine.trim().length === 0;
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
const checkIfShouldAddIndentationToHead = node => {
|
|
731
|
-
const previousSibling = node.getPreviousSibling();
|
|
732
|
-
const isInSameLineAsPrevSiblingLastLine = !node.isRoot() &&
|
|
733
|
-
previousSibling &&
|
|
734
|
-
node.lineNumber === previousSibling.getLastLineNumber();
|
|
735
|
-
const isInSameLineAsParentHeadEnd = node.parent.endLineNumber === node.lineNumber && !node.parent.isContainer();
|
|
736
|
-
|
|
737
|
-
const shouldAdd = !node.isRoot() &&
|
|
738
|
-
!isInSameLineAsPrevSiblingLastLine &&
|
|
739
|
-
!isInSameLineAsParentHeadEnd &&
|
|
740
|
-
(node.isFirstChild() || previousSibling && node.lineNumber !== previousSibling.lineNumber) &&
|
|
741
|
-
node.head && node.lineNumber !== node.parent.endLineNumber;
|
|
742
|
-
|
|
743
|
-
return shouldAdd;
|
|
744
|
-
};
|
|
745
|
-
|
|
746
|
-
const checkIfShouldAddIndentationToTail = node => {
|
|
747
|
-
const hasTail = !!node.tail;
|
|
748
|
-
const isLastClause = !!node.parent && node.parent.isContainer() && !node.isLastChild();
|
|
749
|
-
const isInSameLineAsChild = !node.hasChildren() || node.getLastChild().isInSameLineAsParent();
|
|
750
|
-
const isTailInSameLineAsChild = !node.hasChildren() || node.tailLineNumber === node.getLastChild().getLastLineNumber();
|
|
751
|
-
const isBrokenIntoMultipleLines = !node.hasChildren() && node.tailLineNumber && node.lineNumber !== node.tailLineNumber;
|
|
752
|
-
|
|
753
|
-
const shouldAdd = hasTail &&
|
|
754
|
-
!isTailInSameLineAsChild &&
|
|
755
|
-
!isInSameLineAsChild &&
|
|
756
|
-
!isLastClause
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
.
|
|
773
|
-
.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const
|
|
848
|
-
const
|
|
849
|
-
const
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
1
|
+
const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
|
|
2
|
+
const ParseUtils = require('../../isml_tree/ParseUtils');
|
|
3
|
+
const TreeBuilder = require('../../isml_tree/TreeBuilder');
|
|
4
|
+
const Constants = require('../../Constants');
|
|
5
|
+
|
|
6
|
+
const ruleId = require('path').basename(__filename).slice(0, -3);
|
|
7
|
+
const description = 'Line incorrectly indented';
|
|
8
|
+
|
|
9
|
+
const Rule = Object.create(TreeRulePrototype);
|
|
10
|
+
|
|
11
|
+
Rule.init(ruleId, description);
|
|
12
|
+
|
|
13
|
+
Rule.getDefaultAttrs = () => {
|
|
14
|
+
return {
|
|
15
|
+
size : 4,
|
|
16
|
+
attributeOffset : 4,
|
|
17
|
+
standAloneClosingChars : {
|
|
18
|
+
nonSelfClosingTag : 'always',
|
|
19
|
+
selfClosingTag : 'never',
|
|
20
|
+
quote : 'never'
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
Rule.getIndentation = function(depth = 1) {
|
|
26
|
+
const indentationSize = this.getConfigs().indent * depth;
|
|
27
|
+
let indentation = '';
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < indentationSize; ++i) {
|
|
30
|
+
indentation += ' ';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return indentation;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
Rule.getAttributeIndentationOffset = function() {
|
|
37
|
+
const offsetSize = this.getConfigs().attributeOffset;
|
|
38
|
+
let indentation = '';
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < offsetSize; ++i) {
|
|
41
|
+
indentation += ' ';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return indentation;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
Rule.isBroken = function(node) {
|
|
48
|
+
|
|
49
|
+
const configIndentSize = this.getConfigs().indent;
|
|
50
|
+
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
51
|
+
const actualIndentation = getActualIndentation(node);
|
|
52
|
+
|
|
53
|
+
return !node.isRoot() &&
|
|
54
|
+
!node.isContainer() &&
|
|
55
|
+
!node.isEmpty() &&
|
|
56
|
+
!node.isInSameLineAsParentEnd() &&
|
|
57
|
+
expectedIndentation !== actualIndentation &&
|
|
58
|
+
!node.isInSameLineAsPreviousSibling();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Rule.isTailBroken = function(node) {
|
|
62
|
+
|
|
63
|
+
const configIndentSize = this.getConfigs().indent;
|
|
64
|
+
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
65
|
+
const actualIndentation = getActualTailIndentation(node);
|
|
66
|
+
const isInSameLineAsOpeningTag = node.lineNumber === node.tailLineNumber;
|
|
67
|
+
const isInSameLineAsLastChild = node.hasChildren() && node.getLastChild().getLastLineNumber() === node.tailLineNumber;
|
|
68
|
+
|
|
69
|
+
return !node.isRoot() &&
|
|
70
|
+
!node.isContainer() &&
|
|
71
|
+
!node.isEmpty() &&
|
|
72
|
+
!node.isInSameLineAsParent() &&
|
|
73
|
+
expectedIndentation !== actualIndentation &&
|
|
74
|
+
!isInSameLineAsLastChild &&
|
|
75
|
+
!isInSameLineAsOpeningTag;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
Rule.isClosingCharBroken = function(node) {
|
|
79
|
+
|
|
80
|
+
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
81
|
+
const isFirstChildInSameLine = node.hasChildren() && node.endLineNumber === node.children[0].lineNumber;
|
|
82
|
+
|
|
83
|
+
if (!node.isTag()
|
|
84
|
+
|| !node.isMultiLineOpeningTag()
|
|
85
|
+
|| isFirstChildInSameLine
|
|
86
|
+
|| !closingCharsConfigs
|
|
87
|
+
|| !node.isSelfClosing() && (!closingCharsConfigs.nonSelfClosingTag || closingCharsConfigs.nonSelfClosingTag === 'any')
|
|
88
|
+
|| node.isSelfClosing() && (!closingCharsConfigs.selfClosingTag || closingCharsConfigs.selfClosingTag === 'any')
|
|
89
|
+
) {
|
|
90
|
+
return {
|
|
91
|
+
isBroken : false
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (node.isSelfClosing()) {
|
|
96
|
+
if (closingCharsConfigs.selfClosingTag === 'always') {
|
|
97
|
+
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
98
|
+
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '/>';
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
isBroken : !isClosingCharStandingAlone,
|
|
102
|
+
config : 'always'
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
} else if (closingCharsConfigs.selfClosingTag === 'never') {
|
|
106
|
+
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
107
|
+
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '/>';
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
isBroken : isClosingCharStandingAlone,
|
|
111
|
+
config : 'never'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
if (closingCharsConfigs.nonSelfClosingTag === 'always') {
|
|
116
|
+
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
117
|
+
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '>';
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
isBroken : !isClosingCharStandingAlone,
|
|
121
|
+
config : 'always'
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
} else if (closingCharsConfigs.nonSelfClosingTag === 'never') {
|
|
125
|
+
const nodeHeadLineList = node.head.trim().split(Constants.EOL);
|
|
126
|
+
const isClosingCharStandingAlone = nodeHeadLineList[nodeHeadLineList.length - 1].trim() === '>';
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
isBroken : isClosingCharStandingAlone,
|
|
130
|
+
config : 'never'
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
Rule.isQuoteClosingCharBroken = function(node) {
|
|
137
|
+
|
|
138
|
+
const quoteConfig = Rule.getConfigs().standAloneClosingChars.quote;
|
|
139
|
+
const attributeList = node.getAttributeList();
|
|
140
|
+
const result = [];
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < attributeList.length; i++) {
|
|
143
|
+
const attribute = attributeList[i];
|
|
144
|
+
|
|
145
|
+
if (attribute.value) {
|
|
146
|
+
const attributeValueLineList = attribute.fullContent.trim().split(Constants.EOL);
|
|
147
|
+
const isClosingCharStandingAlone = attributeValueLineList[attributeValueLineList.length - 1].trim() === attribute.quoteChar;
|
|
148
|
+
const lineNumber = attribute.lineNumber + ParseUtils.getLineBreakQty(attribute.fullContent);
|
|
149
|
+
const lineList = attribute.value.split(Constants.EOL);
|
|
150
|
+
const columnNumber = lineList[lineList.length - 1].length + 1;
|
|
151
|
+
const globalPos = attribute.globalPos
|
|
152
|
+
+ attribute.fullContent.lastIndexOf(attribute.quoteChar)
|
|
153
|
+
+ lineNumber - 2;
|
|
154
|
+
const formattedLineList = lineList
|
|
155
|
+
.map( line => line.trim())
|
|
156
|
+
.filter( line => line );
|
|
157
|
+
const lastLine = formattedLineList[formattedLineList.length - 1];
|
|
158
|
+
|
|
159
|
+
const message = quoteConfig === 'always' && !isClosingCharStandingAlone ? getStandAloneQuoteDescription(lastLine) :
|
|
160
|
+
quoteConfig === 'never' && isClosingCharStandingAlone ? getNonStandAloneQuoteDescription(lastLine) :
|
|
161
|
+
'';
|
|
162
|
+
|
|
163
|
+
if (message) {
|
|
164
|
+
result.push({
|
|
165
|
+
quoteChar : attribute.quoteChar,
|
|
166
|
+
lineNumber : lineNumber,
|
|
167
|
+
columnNumber : columnNumber,
|
|
168
|
+
globalPos : globalPos,
|
|
169
|
+
length : attribute.quoteChar.length,
|
|
170
|
+
message : message
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
Rule.check = function(node, data) {
|
|
180
|
+
|
|
181
|
+
const ruleConfig = this.getConfigs();
|
|
182
|
+
const typeArray = ['script', 'iscomment'];
|
|
183
|
+
let occurrenceList = [];
|
|
184
|
+
|
|
185
|
+
if (node.isRoot() || !node.parent.isOneOfTypes(typeArray)) {
|
|
186
|
+
occurrenceList = this.checkChildren(node, data);
|
|
187
|
+
|
|
188
|
+
const errorGlobalPos = node.globalPos - getActualIndentation(node);
|
|
189
|
+
const attributeErrorList = getAttributeErrorList(node);
|
|
190
|
+
|
|
191
|
+
// Checks node value;
|
|
192
|
+
if (this.isBroken(node)) {
|
|
193
|
+
const configIndentSize = ruleConfig.indent;
|
|
194
|
+
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
195
|
+
const actualIndentation = getActualIndentation(node);
|
|
196
|
+
const nodeHead = node.head.trim();
|
|
197
|
+
const occurrenceLength = actualIndentation === 0 ?
|
|
198
|
+
nodeHead.length + ParseUtils.getLineBreakQty(nodeHead) :
|
|
199
|
+
getActualIndentation(node);
|
|
200
|
+
|
|
201
|
+
const error = this.getError(
|
|
202
|
+
node.head.trim(),
|
|
203
|
+
node.lineNumber,
|
|
204
|
+
node.columnNumber,
|
|
205
|
+
errorGlobalPos,
|
|
206
|
+
occurrenceLength,
|
|
207
|
+
getOccurrenceDescription(expectedIndentation, actualIndentation)
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
occurrenceList.push(error);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (attributeErrorList.length > 0) {
|
|
214
|
+
for (let i = 0; i < attributeErrorList.length; i++) {
|
|
215
|
+
const attributeError = attributeErrorList[i];
|
|
216
|
+
occurrenceList.push(attributeError);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Checks node tail value;
|
|
221
|
+
if (node.tail && this.isTailBroken(node)) {
|
|
222
|
+
const configIndentSize = ruleConfig.indent;
|
|
223
|
+
const expectedIndentation = getExpectedIndentation(node, configIndentSize);
|
|
224
|
+
const actualIndentation = getActualTailIndentation(node);
|
|
225
|
+
const nodeTail = node.tail.trim();
|
|
226
|
+
const occurrenceLength = actualIndentation === 0 ?
|
|
227
|
+
nodeTail.length + ParseUtils.getLineBreakQty(nodeTail) :
|
|
228
|
+
getActualTailIndentation(node);
|
|
229
|
+
const tailGlobalPos = node.tailGlobalPos - getActualTailIndentation(node);
|
|
230
|
+
|
|
231
|
+
const error = this.getError(
|
|
232
|
+
node.tail.trim(),
|
|
233
|
+
node.tailLineNumber,
|
|
234
|
+
node.tailColumnNumber,
|
|
235
|
+
tailGlobalPos,
|
|
236
|
+
occurrenceLength,
|
|
237
|
+
getOccurrenceDescription(expectedIndentation, actualIndentation)
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
occurrenceList.push(error);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const quoteOccurrenceList = this.isQuoteClosingCharBroken(node);
|
|
244
|
+
occurrenceList.push(...quoteOccurrenceList);
|
|
245
|
+
|
|
246
|
+
const checkResult = this.isClosingCharBroken(node);
|
|
247
|
+
if (checkResult.isBroken) {
|
|
248
|
+
if (node.isSelfClosing()) {
|
|
249
|
+
const closingChar = '/>';
|
|
250
|
+
const globalPos = node.globalPos
|
|
251
|
+
+ node.head.trim().lastIndexOf(closingChar);
|
|
252
|
+
const lineList = node.head.split(Constants.EOL);
|
|
253
|
+
const columnNumber = lineList[lineList.length - 1].lastIndexOf(closingChar) + 1;
|
|
254
|
+
const message = checkResult.config === 'always' ?
|
|
255
|
+
getStandAloneCharDescription(node.getType(), closingChar) :
|
|
256
|
+
getNonStandAloneCharDescription(node.getType(), closingChar);
|
|
257
|
+
|
|
258
|
+
const error = this.getError(
|
|
259
|
+
node.head.trim(),
|
|
260
|
+
node.endLineNumber,
|
|
261
|
+
columnNumber,
|
|
262
|
+
globalPos,
|
|
263
|
+
closingChar.length,
|
|
264
|
+
message
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
occurrenceList.push(error);
|
|
268
|
+
|
|
269
|
+
} else {
|
|
270
|
+
const closingChar = '>';
|
|
271
|
+
const globalPos = node.globalPos
|
|
272
|
+
+ node.head.trim().length - closingChar.length;
|
|
273
|
+
const lineList = node.head.trim().split(Constants.EOL);
|
|
274
|
+
const columnNumber = lineList[lineList.length - 1].lastIndexOf(closingChar) + 1;
|
|
275
|
+
const message = checkResult.config === 'always' ?
|
|
276
|
+
getStandAloneCharDescription(node.getType(), closingChar) :
|
|
277
|
+
getNonStandAloneCharDescription(node.getType(), closingChar);
|
|
278
|
+
|
|
279
|
+
const error = this.getError(
|
|
280
|
+
node.head.trim(),
|
|
281
|
+
node.endLineNumber,
|
|
282
|
+
columnNumber,
|
|
283
|
+
globalPos,
|
|
284
|
+
closingChar.length,
|
|
285
|
+
message
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
occurrenceList.push(error);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return this.return(node, occurrenceList, ruleConfig);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
Rule.getFixedContent = node => {
|
|
297
|
+
if (node.isRoot() || !node.parent.isOfType('script')) {
|
|
298
|
+
removeAllIndentation(node);
|
|
299
|
+
addCorrectIndentation(node);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return node.toString();
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* PRIVATE FUNCTIONS
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
const getAttributeValueErrorList = function(node, attribute) {
|
|
310
|
+
|
|
311
|
+
if (!attribute.value) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const configIndentSize = Rule.getConfigs().indent;
|
|
316
|
+
const configAttributeOffsetSize = Rule.getConfigs().attributeOffset;
|
|
317
|
+
const result = [];
|
|
318
|
+
|
|
319
|
+
if (attribute.hasMultilineValue) {
|
|
320
|
+
const expectedIndentation = node.depth * configIndentSize + configAttributeOffsetSize;
|
|
321
|
+
const attributeValueList = attribute.value
|
|
322
|
+
.split(Constants.EOL)
|
|
323
|
+
.filter( attr => attr.trim() && ['{', '}'].indexOf(attr.trim()) === -1);
|
|
324
|
+
|
|
325
|
+
for (let i = 0; i < attributeValueList.length; i++) {
|
|
326
|
+
|
|
327
|
+
if (i > 0) {
|
|
328
|
+
const partialResult = getAttributeNestedValueError(attribute, attributeValueList, i, expectedIndentation, configAttributeOffsetSize);
|
|
329
|
+
|
|
330
|
+
if (partialResult.error) {
|
|
331
|
+
result.push(partialResult.error);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (partialResult.shouldContinueLoop) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const error = getAttributeValueError(attribute, attributeValueList, i, expectedIndentation);
|
|
340
|
+
|
|
341
|
+
if (error) {
|
|
342
|
+
result.push(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return result;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const getAttributeErrorList = function(node) {
|
|
351
|
+
const configIndentSize = Rule.getConfigs().indent;
|
|
352
|
+
const attributeList = node.getAttributeList();
|
|
353
|
+
const result = [];
|
|
354
|
+
|
|
355
|
+
for (let i = 0; i < attributeList.length; i++) {
|
|
356
|
+
const attribute = attributeList[i];
|
|
357
|
+
|
|
358
|
+
if (!attribute.isInSameLineAsTagName && attribute.isFirstInLine) {
|
|
359
|
+
const expectedIndentation = node.depth * configIndentSize;
|
|
360
|
+
|
|
361
|
+
if (attribute.columnNumber - 1 !== expectedIndentation) {
|
|
362
|
+
const occurrenceGlobalPos = attribute.globalPos + node.lineNumber - attribute.columnNumber;
|
|
363
|
+
const occurrenceLength = attribute.columnNumber === 1 ?
|
|
364
|
+
attribute.length + ParseUtils.getLineBreakQty(attribute.value) :
|
|
365
|
+
attribute.columnNumber - 1;
|
|
366
|
+
|
|
367
|
+
const error = Rule.getError(
|
|
368
|
+
attribute.fullContent,
|
|
369
|
+
attribute.lineNumber,
|
|
370
|
+
attribute.columnNumber,
|
|
371
|
+
occurrenceGlobalPos,
|
|
372
|
+
occurrenceLength,
|
|
373
|
+
getOccurrenceDescription(expectedIndentation, attribute.columnNumber - 1)
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
result.push(error);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const attributeErrorList = getAttributeValueErrorList(node, attribute);
|
|
381
|
+
result.push(...attributeErrorList);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const removeIndentation = content => {
|
|
388
|
+
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
389
|
+
const endingPos = content.length - ParseUtils.getNextNonEmptyCharPos(content.split('').reverse().join(''));
|
|
390
|
+
const fullLeadingContent = content.substring(0, startingPos);
|
|
391
|
+
const actualContent = content.substring(startingPos, endingPos);
|
|
392
|
+
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
393
|
+
const fullTrailingContent = content.substring(endingPos);
|
|
394
|
+
const lastLineBreakPos = fullTrailingContent.lastIndexOf(Constants.EOL);
|
|
395
|
+
const trimmedTrailingContent = lastLineBreakPos === -1 ?
|
|
396
|
+
fullTrailingContent :
|
|
397
|
+
fullTrailingContent.substring(0, lastLineBreakPos + 1);
|
|
398
|
+
|
|
399
|
+
// Removes indentation from node attributes;
|
|
400
|
+
const indentlessContent = actualContent
|
|
401
|
+
.split(Constants.EOL)
|
|
402
|
+
.map(line => line.trimStart())
|
|
403
|
+
.join(Constants.EOL);
|
|
404
|
+
|
|
405
|
+
return preLineBreakContent + indentlessContent + trimmedTrailingContent;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const addIndentationToText = node => {
|
|
409
|
+
const content = node.head;
|
|
410
|
+
const lineArray = content
|
|
411
|
+
.split(Constants.EOL)
|
|
412
|
+
.filter( (line, i) => !(i === 0 && line === ''))
|
|
413
|
+
.map(line => line.trimStart());
|
|
414
|
+
|
|
415
|
+
const formattedLineArray = [];
|
|
416
|
+
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
417
|
+
const fullLeadingContent = content.substring(0, startingPos);
|
|
418
|
+
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
419
|
+
const correctIndentation = node.isInSameLineAsParent() ? '' : Rule.getIndentation(node.depth - 1);
|
|
420
|
+
|
|
421
|
+
for (let i = 0; i < lineArray.length; i++) {
|
|
422
|
+
let formattedLine = lineArray[i];
|
|
423
|
+
|
|
424
|
+
if (lineArray[i].length !== 0) {
|
|
425
|
+
formattedLine = correctIndentation + lineArray[i];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (!(i === 0 && formattedLine.length === 0)) {
|
|
429
|
+
formattedLineArray.push(formattedLine);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return preLineBreakContent + formattedLineArray.join(Constants.EOL);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const getIndentedNestedIsmlContent = (attribute, nodeIndentation, attributeOffset) => {
|
|
437
|
+
if (attribute.fullContent.startsWith('<isif')) {
|
|
438
|
+
const attributeRootNode = TreeBuilder.parse(attribute.fullContent, null, null, true);
|
|
439
|
+
const fixedContent = Rule.getFixedContent(attributeRootNode);
|
|
440
|
+
|
|
441
|
+
return fixedContent
|
|
442
|
+
.split(Constants.EOL)
|
|
443
|
+
.map( (line, i) => {
|
|
444
|
+
if (i === 0 && attribute.isFirstInLine && !attribute.isInSameLineAsTagName && attribute.index !== 0) {
|
|
445
|
+
return Constants.EOL + nodeIndentation + attributeOffset + line;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return nodeIndentation + attributeOffset + line;
|
|
449
|
+
})
|
|
450
|
+
.join(Constants.EOL);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const getTreeBuiltAttributeIndentedValue = (attribute, nodeIndentation, attributeOffset) => {
|
|
455
|
+
const attributeRootNode = TreeBuilder.parse(attribute.value, null, null, true);
|
|
456
|
+
const fixedContent = Rule.getFixedContent(attributeRootNode);
|
|
457
|
+
|
|
458
|
+
return fixedContent
|
|
459
|
+
.split(Constants.EOL)
|
|
460
|
+
.map( (line, i) => {
|
|
461
|
+
|
|
462
|
+
// If line contains only blank spaces;
|
|
463
|
+
if (!line.trim()) {
|
|
464
|
+
return '';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (i === 0 && attribute.isFirstInLine && !attribute.isInSameLineAsTagName && attribute.index !== 0) {
|
|
468
|
+
return Constants.EOL + nodeIndentation + attributeOffset + line;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (i === 0 && attribute.isFirstValueInSameLineAsAttributeName) {
|
|
472
|
+
return line;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return nodeIndentation + attributeOffset + line;
|
|
476
|
+
})
|
|
477
|
+
.filter( (line, i, list) => {
|
|
478
|
+
if (i === 0 && attribute.isFirstValueInSameLineAsAttributeName && line === '') {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (i === list.length - 1 && line === '') {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return i === 0 || line;
|
|
487
|
+
})
|
|
488
|
+
.join(Constants.EOL);
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const getAttributeIndentedValues = (attribute, nodeIndentation, attributeOffset) => {
|
|
492
|
+
let result = '';
|
|
493
|
+
|
|
494
|
+
if (attribute.value) {
|
|
495
|
+
if (attribute.value.indexOf('<is') >= 0) {
|
|
496
|
+
result = '=' + attribute.quoteChar + getTreeBuiltAttributeIndentedValue(attribute, nodeIndentation, attributeOffset + attributeOffset);
|
|
497
|
+
|
|
498
|
+
} else {
|
|
499
|
+
result = '=' + attribute.quoteChar + attribute.value
|
|
500
|
+
.split(Constants.EOL)
|
|
501
|
+
.filter( value => value )
|
|
502
|
+
.map( (value, i) => {
|
|
503
|
+
if (i === 0) {
|
|
504
|
+
return attribute.isFirstValueInSameLineAsAttributeName ?
|
|
505
|
+
value :
|
|
506
|
+
Constants.EOL + nodeIndentation + attributeOffset + attributeOffset + value;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return nodeIndentation + attributeOffset + attributeOffset + value;
|
|
510
|
+
})
|
|
511
|
+
.join(Constants.EOL);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return result;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
const indentAttribute = (attributeList, index, nodeIndentation, attributeOffset) => {
|
|
520
|
+
const attribute = attributeList[index];
|
|
521
|
+
let result = '';
|
|
522
|
+
|
|
523
|
+
const isInSameLineAsPreviousAttribute = index > 0 && attributeList[index - 1].lineNumber === attribute.lineNumber;
|
|
524
|
+
|
|
525
|
+
if (attribute.hasMultilineValue) {
|
|
526
|
+
if (attribute.isNestedIsmlTag) {
|
|
527
|
+
result += getIndentedNestedIsmlContent(attribute, nodeIndentation, attributeOffset);
|
|
528
|
+
|
|
529
|
+
} else {
|
|
530
|
+
let attributePrefix = '';
|
|
531
|
+
|
|
532
|
+
if (attribute.isFirstInLine) {
|
|
533
|
+
if (index > 0) {
|
|
534
|
+
attributePrefix += Constants.EOL;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
attributePrefix += nodeIndentation + attributeOffset;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const formattedAttributeName = attribute.isExpressionAttribute ?
|
|
541
|
+
getExpressionAttributeIndentation(attribute.name, nodeIndentation, attributeOffset) :
|
|
542
|
+
attribute.name;
|
|
543
|
+
|
|
544
|
+
const formattedAttributeValue = getAttributeIndentedValues(attribute, nodeIndentation, attributeOffset);
|
|
545
|
+
|
|
546
|
+
const closingQuote = shouldAddIndentationToClosingQuote(attribute) ?
|
|
547
|
+
Constants.EOL + nodeIndentation + attributeOffset + attribute.quoteChar :
|
|
548
|
+
attribute.quoteChar;
|
|
549
|
+
|
|
550
|
+
const valueList = attributePrefix
|
|
551
|
+
+ (index > 0 && attribute.isInSameLineAsTagName ? ' ' : '')
|
|
552
|
+
+ formattedAttributeName
|
|
553
|
+
+ formattedAttributeValue
|
|
554
|
+
+ (attribute.isExpressionAttribute ? '' : closingQuote);
|
|
555
|
+
|
|
556
|
+
result += valueList;
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
if (isInSameLineAsPreviousAttribute) {
|
|
560
|
+
result += ' ';
|
|
561
|
+
|
|
562
|
+
} else if (!attribute.isInSameLineAsTagName) {
|
|
563
|
+
if (index !== 0) {
|
|
564
|
+
result += Constants.EOL;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
result += nodeIndentation + attributeOffset;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
result += attribute.fullContent;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return result;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
const getExpressionAttributeIndentation = (attributeValue, nodeIndentation, attributeOffset) => {
|
|
577
|
+
return attributeValue
|
|
578
|
+
.split(Constants.EOL)
|
|
579
|
+
.map( (line, i) => {
|
|
580
|
+
return nodeIndentation + attributeOffset + (i > 0 ? attributeOffset : '') + line;
|
|
581
|
+
})
|
|
582
|
+
.join(Constants.EOL)
|
|
583
|
+
.trimStart();
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const getClosingChars = node => {
|
|
587
|
+
const nodeHead = node.head.trim();
|
|
588
|
+
|
|
589
|
+
if (nodeHead.endsWith(' />')) {
|
|
590
|
+
return ' />';
|
|
591
|
+
} else if (nodeHead.endsWith('/>')) {
|
|
592
|
+
return ' />';
|
|
593
|
+
} else if (nodeHead.endsWith(' >')) {
|
|
594
|
+
return ' >';
|
|
595
|
+
} else if (nodeHead.endsWith('>')) {
|
|
596
|
+
return '>';
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return '';
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
const addIndentation = (node, isOpeningTag) => {
|
|
603
|
+
const content = isOpeningTag ? node.head : node.tail;
|
|
604
|
+
const startingPos = ParseUtils.getNextNonEmptyCharPos(content);
|
|
605
|
+
const endingPos = content.length - ParseUtils.getNextNonEmptyCharPos(content.split('').reverse().join(''));
|
|
606
|
+
const fullLeadingContent = content.substring(0, startingPos);
|
|
607
|
+
const preLineBreakContent = fullLeadingContent.substring(0, fullLeadingContent.lastIndexOf(Constants.EOL) + 1);
|
|
608
|
+
const fullTrailingContent = content.substring(endingPos);
|
|
609
|
+
const nodeIndentation = node.isInSameLineAsParent() && isOpeningTag ? '' : Rule.getIndentation(node.depth - 1);
|
|
610
|
+
const attributeOffset = Rule.getAttributeIndentationOffset();
|
|
611
|
+
const attributeList = node.getAttributeList();
|
|
612
|
+
let contentResult = '';
|
|
613
|
+
|
|
614
|
+
if (isOpeningTag) {
|
|
615
|
+
const shouldAddIndentationToClosingChar = shouldAddIndentationToClosingChars(node);
|
|
616
|
+
const closingChars = getClosingChars(node);
|
|
617
|
+
const tagNameEndPos = ParseUtils.getLeadingLineBreakQty(node.head)
|
|
618
|
+
+ ParseUtils.getFirstEmptyCharPos(node.head.trim()) + 1;
|
|
619
|
+
|
|
620
|
+
if (ParseUtils.getLineBreakQty(node.head.trim()) > 0 || shouldAddIndentationToClosingChar && node.isSelfClosing()) {
|
|
621
|
+
contentResult = attributeList.length > 0 ?
|
|
622
|
+
node.head.substring(0, tagNameEndPos).trimStart() :
|
|
623
|
+
node.head.trimStart();
|
|
624
|
+
|
|
625
|
+
for (let i = 0; i < attributeList.length; i++) {
|
|
626
|
+
contentResult += indentAttribute(attributeList, i, nodeIndentation, attributeOffset);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (attributeList.length > 0) {
|
|
630
|
+
contentResult += shouldAddIndentationToClosingChar ?
|
|
631
|
+
Constants.EOL + nodeIndentation + closingChars.trimStart() :
|
|
632
|
+
closingChars;
|
|
633
|
+
}
|
|
634
|
+
} else {
|
|
635
|
+
contentResult = node.head.trim();
|
|
636
|
+
}
|
|
637
|
+
} else {
|
|
638
|
+
contentResult = node.tail.trim();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return preLineBreakContent + nodeIndentation + contentResult + fullTrailingContent;
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const removeAllIndentation = node => {
|
|
645
|
+
if (!node.isRoot() && !node.isContainer() && !node.parent.isOneOfTypes(['isscript', 'script'])) {
|
|
646
|
+
|
|
647
|
+
const shouldRemoveHeadIndentation = node.head && !node.isInSameLineAsPreviousSibling() && !node.isInSameLineAsParent() && !(node.lineNumber === node.parent.endLineNumber);
|
|
648
|
+
const shouldRemoveTailIndentation = node.tail && !(node.hasChildren() && node.getLastChild().lineNumber === node.tailLineNumber);
|
|
649
|
+
|
|
650
|
+
if (shouldRemoveHeadIndentation) {
|
|
651
|
+
node.head = removeIndentation(node.head);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (shouldRemoveTailIndentation) {
|
|
655
|
+
node.tail = removeIndentation(node.tail);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
660
|
+
removeAllIndentation(node.children[i]);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const addCorrectIndentation = node => {
|
|
665
|
+
|
|
666
|
+
if (!node.isRoot() && !node.isContainer() && !node.parent.isOneOfTypes(['isscript', 'script'])) {
|
|
667
|
+
if (node.parent.isOfType('iscomment')) {
|
|
668
|
+
const shouldAddIndentationToText = checkIfShouldAddIndentationToHead(node);
|
|
669
|
+
|
|
670
|
+
if (shouldAddIndentationToText) {
|
|
671
|
+
node.head = addIndentationToText(node);
|
|
672
|
+
}
|
|
673
|
+
} else {
|
|
674
|
+
const shouldAddIndentationToHead = checkIfShouldAddIndentationToHead(node);
|
|
675
|
+
const shouldAddIndentationToTail = checkIfShouldAddIndentationToTail(node);
|
|
676
|
+
|
|
677
|
+
if (shouldAddIndentationToHead) {
|
|
678
|
+
node.head = addIndentation(node, true);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (shouldAddIndentationToTail) {
|
|
682
|
+
node.tail = addIndentation(node, false);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
688
|
+
addCorrectIndentation(node.children[i]);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
const shouldAddIndentationToClosingChars = node => {
|
|
693
|
+
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
694
|
+
|
|
695
|
+
if (node.isSelfClosing()) {
|
|
696
|
+
if (closingCharsConfigs.selfClosingTag === 'always') {
|
|
697
|
+
return true;
|
|
698
|
+
} else if (closingCharsConfigs.selfClosingTag === 'never') {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
if (closingCharsConfigs.nonSelfClosingTag === 'always') {
|
|
703
|
+
return true;
|
|
704
|
+
} else if (closingCharsConfigs.nonSelfClosingTag === 'never') {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const lineList = node.head.split(Constants.EOL);
|
|
710
|
+
const lastLine = lineList[lineList.length - 1];
|
|
711
|
+
|
|
712
|
+
return ['/>', '>'].indexOf(lastLine) >= 0;
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
const shouldAddIndentationToClosingQuote = attribute => {
|
|
716
|
+
const closingCharsConfigs = Rule.getConfigs().standAloneClosingChars;
|
|
717
|
+
|
|
718
|
+
if (!attribute.value || !closingCharsConfigs.quote || closingCharsConfigs.quote === 'always') {
|
|
719
|
+
return true;
|
|
720
|
+
} else if (closingCharsConfigs.quote === 'never') {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const lineList = attribute.value.split(Constants.EOL);
|
|
725
|
+
const lastLine = lineList[lineList.length - 1];
|
|
726
|
+
|
|
727
|
+
return lastLine.trim().length === 0;
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
const checkIfShouldAddIndentationToHead = node => {
|
|
731
|
+
const previousSibling = node.getPreviousSibling();
|
|
732
|
+
const isInSameLineAsPrevSiblingLastLine = !node.isRoot() &&
|
|
733
|
+
previousSibling &&
|
|
734
|
+
node.lineNumber === previousSibling.getLastLineNumber();
|
|
735
|
+
const isInSameLineAsParentHeadEnd = node.parent.endLineNumber === node.lineNumber && !node.parent.isContainer();
|
|
736
|
+
|
|
737
|
+
const shouldAdd = !node.isRoot() &&
|
|
738
|
+
!isInSameLineAsPrevSiblingLastLine &&
|
|
739
|
+
!isInSameLineAsParentHeadEnd &&
|
|
740
|
+
(node.isFirstChild() || previousSibling && node.lineNumber !== previousSibling.lineNumber) &&
|
|
741
|
+
node.head && node.lineNumber !== node.parent.endLineNumber;
|
|
742
|
+
|
|
743
|
+
return shouldAdd;
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
const checkIfShouldAddIndentationToTail = node => {
|
|
747
|
+
const hasTail = !!node.tail;
|
|
748
|
+
const isLastClause = !!node.parent && node.parent.isContainer() && !node.isLastChild();
|
|
749
|
+
const isInSameLineAsChild = !node.hasChildren() || node.getLastChild().isInSameLineAsParent();
|
|
750
|
+
const isTailInSameLineAsChild = !node.hasChildren() || node.tailLineNumber === node.getLastChild().getLastLineNumber();
|
|
751
|
+
const isBrokenIntoMultipleLines = !node.hasChildren() && node.tailLineNumber && node.lineNumber !== node.tailLineNumber;
|
|
752
|
+
|
|
753
|
+
const shouldAdd = hasTail &&
|
|
754
|
+
!isTailInSameLineAsChild &&
|
|
755
|
+
!isInSameLineAsChild &&
|
|
756
|
+
!isLastClause
|
|
757
|
+
// TODO Works for a specific case only. Might have side effects;
|
|
758
|
+
||
|
|
759
|
+
node.isOfType('iscomment') && !isTailInSameLineAsChild
|
|
760
|
+
||
|
|
761
|
+
isBrokenIntoMultipleLines;
|
|
762
|
+
|
|
763
|
+
return shouldAdd;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// TODO This a workaround, it should be handled directly in ParseUtils.getElementList();
|
|
767
|
+
const getEslintChildTrailingSpaces = node => {
|
|
768
|
+
if (node.isOfType('isscript')) {
|
|
769
|
+
const child = node.getLastChild();
|
|
770
|
+
|
|
771
|
+
const trailingSpacesQty = child.head
|
|
772
|
+
.replace(/\r\n/g, '_')
|
|
773
|
+
.split('')
|
|
774
|
+
.reverse()
|
|
775
|
+
.join('')
|
|
776
|
+
.search(/\S/);
|
|
777
|
+
|
|
778
|
+
return trailingSpacesQty - 1;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return 0;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const getAttributeNestedValueError = (attribute, attributeValueList, i, expectedIndentation, configAttributeOffsetSize) => {
|
|
785
|
+
const tagList = ['isif', 'iselse', 'iselseif'];
|
|
786
|
+
let shouldContinueLoop = false;
|
|
787
|
+
|
|
788
|
+
for (let j = 0; j < tagList.length; j++) {
|
|
789
|
+
const element = tagList[j];
|
|
790
|
+
const previousAttribute = attributeValueList[i - 1];
|
|
791
|
+
|
|
792
|
+
if (previousAttribute.indexOf(element) >= 0 && previousAttribute.indexOf('</isif>') === -1) {
|
|
793
|
+
const error = getAttributeValueError(attribute, attributeValueList, i, expectedIndentation + configAttributeOffsetSize);
|
|
794
|
+
|
|
795
|
+
shouldContinueLoop = true;
|
|
796
|
+
|
|
797
|
+
if (error) {
|
|
798
|
+
return {
|
|
799
|
+
error,
|
|
800
|
+
shouldContinueLoop
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
return {
|
|
807
|
+
shouldContinueLoop
|
|
808
|
+
};
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const getAttributeValueError = (attribute, attributeValueList, i, expectedIndentation) => {
|
|
812
|
+
const attributeValue = attributeValueList[i];
|
|
813
|
+
const valueColumnNumber = ParseUtils.getLeadingEmptyChars(attributeValue).length;
|
|
814
|
+
const attributeValueStartPos = attribute.value.indexOf(attributeValue);
|
|
815
|
+
const attributeValuePrefix = attribute.value.substring(0, attributeValueStartPos);
|
|
816
|
+
const isValueInSameLineAsAttributeName = ParseUtils.getLineBreakQty(attributeValuePrefix) === 0;
|
|
817
|
+
|
|
818
|
+
if (!isValueInSameLineAsAttributeName && valueColumnNumber !== expectedIndentation) {
|
|
819
|
+
const attributeValueFullPrefix = attribute.fullContent.substring(0, attribute.fullContent.indexOf(attributeValue.trim()));
|
|
820
|
+
const lineBreakQty = ParseUtils.getLineBreakQty(attributeValueFullPrefix);
|
|
821
|
+
const occurrenceGlobalPos = attribute.globalPos + attribute.fullContent.indexOf(attributeValueList[i]) + attribute.lineNumber - 1;
|
|
822
|
+
const lineNumber = attribute.lineNumber + lineBreakQty;
|
|
823
|
+
|
|
824
|
+
const occurrenceColumnNumber = valueColumnNumber === 0 ?
|
|
825
|
+
valueColumnNumber :
|
|
826
|
+
0;
|
|
827
|
+
|
|
828
|
+
const occurrenceLength = valueColumnNumber === 0 ?
|
|
829
|
+
attributeValue.length :
|
|
830
|
+
valueColumnNumber;
|
|
831
|
+
|
|
832
|
+
const error = Rule.getError(
|
|
833
|
+
attributeValue.trim(),
|
|
834
|
+
lineNumber,
|
|
835
|
+
occurrenceColumnNumber,
|
|
836
|
+
occurrenceGlobalPos,
|
|
837
|
+
occurrenceLength,
|
|
838
|
+
getOccurrenceDescription(expectedIndentation, valueColumnNumber)
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
return error;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return null;
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
const getStandAloneQuoteDescription = lastValue => `Closing quote should not be in the same line as "${lastValue}"`;
|
|
848
|
+
const getNonStandAloneQuoteDescription = lastValue => `Closing quote should be in the same line as "${lastValue}"`;
|
|
849
|
+
const getStandAloneCharDescription = (tagName, closingChars) => `"${closingChars}" should not be in the same line as <${tagName}> tag last attribute`;
|
|
850
|
+
const getNonStandAloneCharDescription = (tagName, closingChars) => `"${closingChars}" should be in the same line as <${tagName}> tag last attribute`;
|
|
851
|
+
const getOccurrenceDescription = (expected, actual) => `Expected indentation of ${expected} spaces but found ${actual}`;
|
|
852
|
+
const getExpectedIndentation = (node, configIndentSize) => (node.depth - 1) * configIndentSize;
|
|
853
|
+
const getActualIndentation = node => node.getIndentationSize();
|
|
854
|
+
const getActualTailIndentation = node => node.getTailIndentationSize() + getEslintChildTrailingSpaces(node);
|
|
855
|
+
|
|
856
|
+
module.exports = Rule;
|