isml-linter 5.38.2 → 5.39.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 +63 -33
- package/README.md +2 -1
- package/package.json +2 -2
- package/src/Constants.js +2 -2
- package/src/IsmlLinter.js +14 -0
- package/src/enums/{SfccTags.js → SfccTagContainer.js} +0 -0
- package/src/isml_tree/ContainerNode.js +1 -1
- package/src/isml_tree/IsmlNode.js +88 -83
- package/src/isml_tree/MaskUtils.js +2 -2
- package/src/isml_tree/ParseUtils.js +15 -13
- package/src/isml_tree/TreeBuilder.js +47 -18
- package/src/rules/prototypes/TreeRulePrototype.js +4 -8
- package/src/rules/tree/align-isset.js +2 -2
- package/src/rules/tree/contextual-attrs.js +4 -4
- package/src/rules/tree/custom-tags.js +6 -6
- package/src/rules/tree/disallow-tags.js +2 -2
- package/src/rules/tree/empty-eof.js +11 -11
- package/src/rules/tree/enforce-security.js +2 -2
- package/src/rules/tree/eslint-to-isscript.js +13 -12
- package/src/rules/tree/indent.js +73 -73
- package/src/rules/tree/leading-iscache.js +1 -1
- package/src/rules/tree/leading-iscontent.js +1 -1
- package/src/rules/tree/no-deprecated-attrs.js +4 -4
- package/src/rules/tree/no-embedded-isml.js +2 -2
- package/src/rules/tree/no-hardcode.js +6 -6
- package/src/rules/tree/no-iselse-slash.js +3 -3
- package/src/rules/tree/no-redundant-context.js +8 -8
- package/src/rules/tree/no-require-in-loop.js +8 -8
- package/src/rules/tree/one-element-per-line.js +3 -3
- package/src/util/ConsoleUtils.js +44 -2
- package/src/util/{CustomTagUtils.js → CustomTagContainer.js} +0 -0
- package/src/util/ExceptionUtils.js +27 -0
- package/src/util/RuleUtils.js +16 -6
- package/src/util/TempRuleUtils.js +4 -4
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
const MAX_TEXT_DISPLAY_SIZE = 30;
|
|
2
2
|
|
|
3
|
-
const ConfigUtils
|
|
4
|
-
const Constants
|
|
5
|
-
const
|
|
6
|
-
const ParseUtils
|
|
7
|
-
const MaskUtils
|
|
3
|
+
const ConfigUtils = require('../util/ConfigUtils');
|
|
4
|
+
const Constants = require('../Constants');
|
|
5
|
+
const SfccTagContainer = require('../enums/SfccTagContainer');
|
|
6
|
+
const ParseUtils = require('./ParseUtils');
|
|
7
|
+
const MaskUtils = require('./MaskUtils');
|
|
8
8
|
|
|
9
9
|
let ID_COUNTER = 0;
|
|
10
10
|
|
|
11
11
|
class IsmlNode {
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* @param {String}
|
|
14
|
+
* @param {String} head node opening tag value, including attributes
|
|
15
15
|
* @param {Number} lineNumber node starting line number
|
|
16
16
|
* @param {Number} columnNumber node starting column number
|
|
17
17
|
* @param {Number} globalPos node starting position since the beginning of the file
|
|
18
18
|
* @param {Boolean} isEmbeddedNode whether the node is part of an embedded "sub tree", as a tag attribute
|
|
19
19
|
*/
|
|
20
|
-
constructor(
|
|
21
|
-
this.id
|
|
22
|
-
this.
|
|
23
|
-
this.lineNumber
|
|
24
|
-
this.columnNumber
|
|
25
|
-
this.endLineNumber
|
|
26
|
-
this.globalPos
|
|
27
|
-
this.depth
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.parent
|
|
33
|
-
this.children
|
|
34
|
-
this.childNo
|
|
35
|
-
this.isEmbeddedNode
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
20
|
+
constructor(head = '(root)', lineNumber = 0, columnNumber, globalPos, isEmbeddedNode) {
|
|
21
|
+
this.id = ID_COUNTER++;
|
|
22
|
+
this.head = head; // '<div class="my_class">'
|
|
23
|
+
this.lineNumber = lineNumber; // 7
|
|
24
|
+
this.columnNumber = columnNumber; // 12
|
|
25
|
+
this.endLineNumber = lineNumber + ParseUtils.getLineBreakQty(head.trim()); // 9
|
|
26
|
+
this.globalPos = globalPos; // 184
|
|
27
|
+
this.depth = 0; // Isml dom tree node depth
|
|
28
|
+
this.tail = ''; // '</div>'
|
|
29
|
+
this.tailLineNumber = null; // 9
|
|
30
|
+
this.tailColumnNumber = null; // 12
|
|
31
|
+
this.tailGlobalPos = null; // 207
|
|
32
|
+
this.parent = null; // Parent isml node;
|
|
33
|
+
this.children = []; // Child isml nodes;
|
|
34
|
+
this.childNo = 0;
|
|
35
|
+
this.isEmbeddedNode = !!isEmbeddedNode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Tail is the element corresponding closing tag, such as </div>
|
|
39
|
+
setTail(value, lineNumber, columnNumber, globalPos) {
|
|
40
|
+
this.tail += value;
|
|
41
|
+
this.tailLineNumber = lineNumber;
|
|
42
|
+
this.tailColumnNumber = columnNumber;
|
|
43
|
+
this.tailGlobalPos = globalPos;
|
|
44
|
+
this.tailEndLineNumber = lineNumber;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Returns a string. Examples: 'div', 'isprint', 'doctype';
|
|
@@ -50,9 +50,9 @@ class IsmlNode {
|
|
|
50
50
|
return 'text';
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const
|
|
53
|
+
const head = this.head.trim();
|
|
54
54
|
|
|
55
|
-
if (
|
|
55
|
+
if (head.startsWith('<!--')) {
|
|
56
56
|
return 'html_comment';
|
|
57
57
|
} else if (this.isDocType()) {
|
|
58
58
|
return 'doctype';
|
|
@@ -60,9 +60,9 @@ class IsmlNode {
|
|
|
60
60
|
return 'dynamic_element';
|
|
61
61
|
} else if (this.isContainer()) {
|
|
62
62
|
return 'multi_clause';
|
|
63
|
-
} else if (!
|
|
63
|
+
} else if (!head) {
|
|
64
64
|
return 'empty';
|
|
65
|
-
} else if (
|
|
65
|
+
} else if (head === '(root)') {
|
|
66
66
|
return 'root';
|
|
67
67
|
} else if (!this.isTag()) {
|
|
68
68
|
return 'text';
|
|
@@ -70,23 +70,23 @@ class IsmlNode {
|
|
|
70
70
|
|
|
71
71
|
const regex = /<[a-zA-Z\d_]*(\s|>|\/)/g;
|
|
72
72
|
|
|
73
|
-
return
|
|
73
|
+
return head.match(regex)[0].slice(1, -1);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
getLastLineNumber() {
|
|
77
|
-
return this.
|
|
78
|
-
this.
|
|
77
|
+
return this.tailLineNumber ?
|
|
78
|
+
this.tailLineNumber :
|
|
79
79
|
this.endLineNumber;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
isDocType() {
|
|
83
|
-
return this.
|
|
83
|
+
return this.head.toLowerCase().trim().startsWith('<!doctype ');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Checks if the node type is dynamically set, such in:
|
|
87
87
|
// <${aPdictVariable} />
|
|
88
88
|
isDynamicElement() {
|
|
89
|
-
return this.
|
|
89
|
+
return this.head.trim().startsWith('<${');
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
isInSameLineAsParent() {
|
|
@@ -98,7 +98,7 @@ class IsmlNode {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
isMultiLineOpeningTag() {
|
|
101
|
-
return this.isTag() && ParseUtils.getLineBreakQty(this.
|
|
101
|
+
return this.isTag() && ParseUtils.getLineBreakQty(this.head.trim()) > 0;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
isInSameLineAsPreviousSibling() {
|
|
@@ -135,28 +135,28 @@ class IsmlNode {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
addChild(newNode) {
|
|
138
|
-
if (newNode.
|
|
138
|
+
if (newNode.head.trim()) {
|
|
139
139
|
newNode.depth = this.depth + 1;
|
|
140
140
|
newNode.parent = this;
|
|
141
141
|
newNode.childNo = this.children.length;
|
|
142
142
|
this.children.push(newNode);
|
|
143
143
|
this.newestChildNode = newNode;
|
|
144
144
|
} else {
|
|
145
|
-
this.
|
|
145
|
+
this.tail = newNode.head + this.tail;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
newNode.isEmbeddedNode = this.isEmbeddedNode;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
getLastChild()
|
|
151
|
+
getLastChild() { return this.children[this.children.length - 1]; }
|
|
152
152
|
getChildrenQty() { return this.children.length; }
|
|
153
|
-
hasChildren()
|
|
153
|
+
hasChildren() { return this.children.length > 0; }
|
|
154
154
|
|
|
155
155
|
getIndentationSize() {
|
|
156
156
|
return getNodeIndentationSize(this, true);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
getTailIndentationSize() {
|
|
160
160
|
return getNodeIndentationSize(this, false);
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -174,14 +174,14 @@ class IsmlNode {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
isTag() {
|
|
177
|
-
const value = this.
|
|
177
|
+
const value = this.head.trim();
|
|
178
178
|
|
|
179
179
|
return value.startsWith('<') &&
|
|
180
180
|
value.endsWith('>');
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
isHtmlTag() {
|
|
184
|
-
const value = this.
|
|
184
|
+
const value = this.head.trim();
|
|
185
185
|
|
|
186
186
|
return value.startsWith('<') &&
|
|
187
187
|
!value.startsWith('<is') &&
|
|
@@ -189,11 +189,11 @@ class IsmlNode {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
isIsmlTag() {
|
|
192
|
-
return this.
|
|
192
|
+
return this.head.trim().startsWith('<is');
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
isStandardIsmlTag() {
|
|
196
|
-
return !!
|
|
196
|
+
return !!SfccTagContainer[this.getType()];
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
isCustomIsmlTag() {
|
|
@@ -217,14 +217,14 @@ class IsmlNode {
|
|
|
217
217
|
// For an unwrapped ${someVariable} element, returns true;
|
|
218
218
|
// For tags and hardcoded strings , returns false;
|
|
219
219
|
isExpression() {
|
|
220
|
-
const value = this.
|
|
220
|
+
const value = this.head.trim();
|
|
221
221
|
|
|
222
222
|
return value.startsWith('${') &&
|
|
223
223
|
value.endsWith('}');
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
isIsmlComment() {
|
|
227
|
-
const value = this.
|
|
227
|
+
const value = this.head.trim();
|
|
228
228
|
|
|
229
229
|
return value === '<iscomment>';
|
|
230
230
|
}
|
|
@@ -234,14 +234,14 @@ class IsmlNode {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
isHtmlComment() {
|
|
237
|
-
const value = this.
|
|
237
|
+
const value = this.head.trim();
|
|
238
238
|
|
|
239
239
|
return value.startsWith('<!--') &&
|
|
240
240
|
value.endsWith('-->');
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
isConditionalComment() {
|
|
244
|
-
const value = this.
|
|
244
|
+
const value = this.head.trim();
|
|
245
245
|
|
|
246
246
|
return value.startsWith('<!--[');
|
|
247
247
|
}
|
|
@@ -281,7 +281,7 @@ class IsmlNode {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
getTrailingValue() {
|
|
284
|
-
return this.
|
|
284
|
+
return this.tail || this.head;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
// Checks if node is HTML 5 void element;
|
|
@@ -298,9 +298,9 @@ class IsmlNode {
|
|
|
298
298
|
this.isDocType() ||
|
|
299
299
|
this.isVoidElement() ||
|
|
300
300
|
this.isHtmlComment() ||
|
|
301
|
-
this.isTag() && this.
|
|
301
|
+
this.isTag() && this.head.trim().endsWith('/>')) ||
|
|
302
302
|
this.isCustomIsmlTag() ||
|
|
303
|
-
this.isIsmlTag() &&
|
|
303
|
+
this.isIsmlTag() && SfccTagContainer[this.getType()] && SfccTagContainer[this.getType()]['self-closing'];
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
isOfType(type) {
|
|
@@ -320,7 +320,7 @@ class IsmlNode {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
isEmpty() {
|
|
323
|
-
return !this.
|
|
323
|
+
return !this.head.trim();
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
isFirstChild() {
|
|
@@ -331,7 +331,7 @@ class IsmlNode {
|
|
|
331
331
|
const firstChild = this.parent.children[0];
|
|
332
332
|
|
|
333
333
|
return this.lineNumber === firstChild.lineNumber &&
|
|
334
|
-
this.
|
|
334
|
+
this.head === firstChild.head;
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
isLastChild() {
|
|
@@ -370,7 +370,7 @@ class IsmlNode {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
if (!this.isRoot() && !this.isContainer()) {
|
|
373
|
-
stream += this.
|
|
373
|
+
stream += this.head;
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
for (let i = 0; i < this.children.length; i++) {
|
|
@@ -379,7 +379,7 @@ class IsmlNode {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
if (!this.isRoot() && !this.isContainer()) {
|
|
382
|
-
stream += this.
|
|
382
|
+
stream += this.tail;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
return stream;
|
|
@@ -410,11 +410,11 @@ class IsmlNode {
|
|
|
410
410
|
*/
|
|
411
411
|
|
|
412
412
|
const getAttributes = node => {
|
|
413
|
-
const
|
|
414
|
-
const
|
|
415
|
-
const firstSpaceAfterTagPos = ParseUtils.getFirstEmptyCharPos(
|
|
416
|
-
const leadingEmptySpaceQty = ParseUtils.getNextNonEmptyCharPos(
|
|
417
|
-
const afterTagContent =
|
|
413
|
+
const trimmedHead = node.head.trim();
|
|
414
|
+
const nodeHead = trimmedHead.substring(1, trimmedHead.length - 1);
|
|
415
|
+
const firstSpaceAfterTagPos = ParseUtils.getFirstEmptyCharPos(trimmedHead);
|
|
416
|
+
const leadingEmptySpaceQty = ParseUtils.getNextNonEmptyCharPos(nodeHead);
|
|
417
|
+
const afterTagContent = nodeHead.substring(leadingEmptySpaceQty + firstSpaceAfterTagPos);
|
|
418
418
|
const stringifiedAttributeList = getStringifiedAttributeArray(afterTagContent);
|
|
419
419
|
const attributeList = [];
|
|
420
420
|
|
|
@@ -428,13 +428,13 @@ const getAttributes = node => {
|
|
|
428
428
|
|
|
429
429
|
// Used for debugging purposes only;
|
|
430
430
|
const getDisplayText = node => {
|
|
431
|
-
let displayText = node.
|
|
431
|
+
let displayText = node.head;
|
|
432
432
|
|
|
433
433
|
displayText = displayText
|
|
434
434
|
.replace(new RegExp(Constants.EOL, 'g'), '')
|
|
435
435
|
.replace(/ +(?= )/g, '');
|
|
436
436
|
|
|
437
|
-
if (node.
|
|
437
|
+
if (node.head.length > MAX_TEXT_DISPLAY_SIZE - 3) {
|
|
438
438
|
displayText = displayText.substring(0, MAX_TEXT_DISPLAY_SIZE - 3) + '...';
|
|
439
439
|
}
|
|
440
440
|
|
|
@@ -478,7 +478,12 @@ const getStringifiedAttributeArray = content => {
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
for (let i = 0; i < maskedAttributeList.length; i++) {
|
|
481
|
-
|
|
481
|
+
let fullAttribute = content.substring(attrStartPosList[i] - 1, attrStartPosList[i] + maskedAttributeList[i].length).trim();
|
|
482
|
+
|
|
483
|
+
if (fullAttribute.endsWith('/')) {
|
|
484
|
+
fullAttribute = fullAttribute.slice(0, -1) + ' ';
|
|
485
|
+
}
|
|
486
|
+
|
|
482
487
|
result.push(fullAttribute);
|
|
483
488
|
}
|
|
484
489
|
|
|
@@ -490,16 +495,16 @@ const parseAttribute = (node, attributeList, index) => {
|
|
|
490
495
|
const isAttributeANestedIsmlTag = attribute.startsWith('<is');
|
|
491
496
|
const isExpressionAttribute = attribute.startsWith('${') && attribute.endsWith('}');
|
|
492
497
|
const trimmedAttribute = attribute.trim();
|
|
493
|
-
const
|
|
494
|
-
const localPos = getAttributeLocalPos(
|
|
495
|
-
const leadingContent =
|
|
498
|
+
const trimmedNodeHead = node.head.trim();
|
|
499
|
+
const localPos = getAttributeLocalPos(trimmedNodeHead, trimmedAttribute);
|
|
500
|
+
const leadingContent = trimmedNodeHead.substring(0, localPos);
|
|
496
501
|
const leadingLineBreakQty = ParseUtils.getLineBreakQty(leadingContent);
|
|
497
502
|
const isInSameLineAsTagName = leadingLineBreakQty === 0;
|
|
498
503
|
const assignmentCharPos = trimmedAttribute.indexOf('=');
|
|
499
504
|
const name = assignmentCharPos >= 0 ? trimmedAttribute.substring(0, assignmentCharPos) : trimmedAttribute;
|
|
500
505
|
const value = assignmentCharPos >= 0 ? trimmedAttribute.substring(assignmentCharPos + 2, trimmedAttribute.length - 1) : null;
|
|
501
506
|
const valueList = getAttributeValueList(value);
|
|
502
|
-
const attrLocalPos =
|
|
507
|
+
const attrLocalPos = trimmedNodeHead.indexOf(trimmedAttribute);
|
|
503
508
|
const valueLocalPos = trimmedAttribute.indexOf(value);
|
|
504
509
|
const lineNumber = node.lineNumber + leadingLineBreakQty;
|
|
505
510
|
const globalPos = node.globalPos + localPos + leadingLineBreakQty - lineNumber + 1;
|
|
@@ -513,7 +518,7 @@ const parseAttribute = (node, attributeList, index) => {
|
|
|
513
518
|
leadingContent.length - leadingContent.lastIndexOf(Constants.EOL);
|
|
514
519
|
|
|
515
520
|
const isFirstInLine = !isInSameLineAsTagName
|
|
516
|
-
&&
|
|
521
|
+
&& trimmedNodeHead
|
|
517
522
|
.split(Constants.EOL)
|
|
518
523
|
.find(attrLine => attrLine.indexOf(attributeNameFirstLine) >= 0)
|
|
519
524
|
.trim()
|
|
@@ -570,26 +575,26 @@ const parseAttribute = (node, attributeList, index) => {
|
|
|
570
575
|
/**
|
|
571
576
|
* Two attributes can have the same name, and that is handled here;
|
|
572
577
|
*/
|
|
573
|
-
const getAttributeLocalPos = (
|
|
578
|
+
const getAttributeLocalPos = (trimmedNodeHead, trimmedAttribute) => {
|
|
574
579
|
const maskedTrimmedAttribute = MaskUtils.maskQuoteContent(trimmedAttribute);
|
|
575
|
-
const
|
|
580
|
+
const maskedTrimmedNodeHead = MaskUtils.maskQuoteContent(trimmedNodeHead);
|
|
576
581
|
|
|
577
|
-
let attributeLocalPos =
|
|
578
|
-
const isCorrectAttribute =
|
|
579
|
-
let
|
|
582
|
+
let attributeLocalPos = maskedTrimmedNodeHead.indexOf(maskedTrimmedAttribute);
|
|
583
|
+
const isCorrectAttribute = trimmedNodeHead.indexOf(trimmedAttribute) === attributeLocalPos;
|
|
584
|
+
let remainingNodeHead = maskedTrimmedNodeHead.substring(attributeLocalPos + 1);
|
|
580
585
|
|
|
581
586
|
while (!isCorrectAttribute) {
|
|
582
|
-
const tempLocalPos =
|
|
587
|
+
const tempLocalPos = remainingNodeHead.indexOf(maskedTrimmedAttribute) + 1;
|
|
583
588
|
|
|
584
589
|
attributeLocalPos += tempLocalPos;
|
|
585
590
|
|
|
586
|
-
const remainingContent =
|
|
591
|
+
const remainingContent = trimmedNodeHead.substring(attributeLocalPos);
|
|
587
592
|
|
|
588
593
|
if (remainingContent.startsWith(trimmedAttribute)) {
|
|
589
594
|
break;
|
|
590
595
|
}
|
|
591
596
|
|
|
592
|
-
|
|
597
|
+
remainingNodeHead = remainingNodeHead.substring(tempLocalPos);
|
|
593
598
|
}
|
|
594
599
|
|
|
595
600
|
return attributeLocalPos;
|
|
@@ -601,7 +606,7 @@ const getNodeIndentationSize = (node, isNodeHead) => {
|
|
|
601
606
|
return 0;
|
|
602
607
|
}
|
|
603
608
|
|
|
604
|
-
const content = isNodeHead ? node.
|
|
609
|
+
const content = isNodeHead ? node.head : node.tail;
|
|
605
610
|
const precedingEmptySpacesLength = content.search(/\S|$/);
|
|
606
611
|
const fullPrecedingEmptySpaces = content.substring(0, precedingEmptySpacesLength);
|
|
607
612
|
const lineBreakLastPos = Math.max(fullPrecedingEmptySpaces.lastIndexOf(Constants.EOL), 0);
|
|
@@ -20,7 +20,6 @@ const maskIgnorableContent = (content, shouldMaskBorders, templatePath) => {
|
|
|
20
20
|
maskedContent = maskInBetweenIsscriptTags(maskedContent);
|
|
21
21
|
maskedContent = maskInBetweenForTagWithAttributes(maskedContent, 'script', 'type=\'text/javascript\'');
|
|
22
22
|
maskedContent = maskInBetweenForTagWithAttributes(maskedContent, 'script', 'type="text/javascript"');
|
|
23
|
-
maskedContent = maskQuoteContent(maskedContent);
|
|
24
23
|
maskedContent = maskInBetweenForTagWithAttributes(maskedContent, 'isscript');
|
|
25
24
|
|
|
26
25
|
checkTagBalance(maskedContent, content, templatePath);
|
|
@@ -28,6 +27,7 @@ const maskIgnorableContent = (content, shouldMaskBorders, templatePath) => {
|
|
|
28
27
|
maskedContent = maskInBetweenForTagWithAttributes(maskedContent, 'script');
|
|
29
28
|
maskedContent = maskInBetweenForTagWithAttributes(maskedContent, 'style');
|
|
30
29
|
maskedContent = maskInBetween2(maskedContent, '<', '>');
|
|
30
|
+
maskedContent = maskQuoteContent(maskedContent);
|
|
31
31
|
maskedContent = maskIsifTagContent(maskedContent);
|
|
32
32
|
maskedContent = maskIsprintTagContent(maskedContent);
|
|
33
33
|
|
|
@@ -306,7 +306,7 @@ const maskIsifTagContent = content => {
|
|
|
306
306
|
if (remainingContent.startsWith(closingTag)) {
|
|
307
307
|
isWithinIsifTag = false;
|
|
308
308
|
maskedContent += closingTag;
|
|
309
|
-
i += closingTag.length;
|
|
309
|
+
i += closingTag.length - 1;
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* simply analyze it and return relevant information;
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const path
|
|
8
|
-
const Constants
|
|
9
|
-
const ExceptionUtils
|
|
10
|
-
const
|
|
11
|
-
const GeneralUtils
|
|
12
|
-
const MaskUtils
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const Constants = require('../Constants');
|
|
9
|
+
const ExceptionUtils = require('../util/ExceptionUtils');
|
|
10
|
+
const SfccTagContainer = require('../enums/SfccTagContainer');
|
|
11
|
+
const GeneralUtils = require('../util/GeneralUtils');
|
|
12
|
+
const MaskUtils = require('./MaskUtils');
|
|
13
13
|
|
|
14
14
|
const getNextNonEmptyChar = content => {
|
|
15
15
|
return content.replace(new RegExp(Constants.EOL, 'g'), '').trim()[0];
|
|
@@ -89,14 +89,14 @@ const checkBalance = (node, templatePath) => {
|
|
|
89
89
|
if (!node.isRoot() &&
|
|
90
90
|
node.parent && !node.parent.isContainer() &&
|
|
91
91
|
(node.isHtmlTag() || node.isIsmlTag()) &&
|
|
92
|
-
!node.isSelfClosing() && !node.
|
|
92
|
+
!node.isSelfClosing() && !node.tail
|
|
93
93
|
&& !node.parent.isOfType('iscomment')
|
|
94
94
|
) {
|
|
95
95
|
throw ExceptionUtils.unbalancedElementError(
|
|
96
96
|
node.getType(),
|
|
97
97
|
node.lineNumber,
|
|
98
98
|
node.globalPos,
|
|
99
|
-
node.
|
|
99
|
+
node.head.trim().length,
|
|
100
100
|
templatePath
|
|
101
101
|
);
|
|
102
102
|
}
|
|
@@ -174,6 +174,8 @@ const parseTagOrExpressionElement = (state, newElement) => {
|
|
|
174
174
|
|
|
175
175
|
if (isTag) {
|
|
176
176
|
newElement.tagType = getElementType(trimmedElement);
|
|
177
|
+
|
|
178
|
+
newElement.isCustomTag = newElement.type === 'ismlTag' && !SfccTagContainer[newElement.tagType];
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
newElement.isSelfClosing = isSelfClosing(trimmedElement);
|
|
@@ -193,13 +195,13 @@ const parseTextElement = (state, newElement) => {
|
|
|
193
195
|
|
|
194
196
|
const getElementType = trimmedElement => {
|
|
195
197
|
if (trimmedElement.startsWith('</')) {
|
|
196
|
-
const
|
|
198
|
+
const tailElementType = trimmedElement.slice(2, -1);
|
|
197
199
|
|
|
198
|
-
if (
|
|
200
|
+
if (tailElementType.startsWith('${')) {
|
|
199
201
|
return 'dynamic_element';
|
|
200
202
|
}
|
|
201
203
|
|
|
202
|
-
return
|
|
204
|
+
return tailElementType;
|
|
203
205
|
} else {
|
|
204
206
|
|
|
205
207
|
const typeValueLastPos = Math.min(...[
|
|
@@ -229,10 +231,10 @@ function isSelfClosing(trimmedElement) {
|
|
|
229
231
|
const isHtmlComment = trimmedElement.startsWith('<!--') && trimmedElement.endsWith('-->');
|
|
230
232
|
const isClosingTag = trimmedElement.endsWith('/>');
|
|
231
233
|
const isIsmlTag = trimmedElement.startsWith('<is');
|
|
232
|
-
const isStandardIsmlTag = !!
|
|
234
|
+
const isStandardIsmlTag = !!SfccTagContainer[elementType];
|
|
233
235
|
const isCustomIsmlTag = isIsmlTag && !isStandardIsmlTag;
|
|
234
236
|
const isExpression = trimmedElement.startsWith('${') && trimmedElement.endsWith('}');
|
|
235
|
-
const isSfccSelfClosingTag =
|
|
237
|
+
const isSfccSelfClosingTag = SfccTagContainer[elementType] && SfccTagContainer[elementType]['self-closing'];
|
|
236
238
|
|
|
237
239
|
// 'isif' tag is never self-closing;
|
|
238
240
|
if (['isif'].indexOf(elementType) >= 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const IsmlNode = require('./IsmlNode');
|
|
3
3
|
const ParseUtils = require('./ParseUtils');
|
|
4
|
+
const MaskUtils = require('./MaskUtils');
|
|
4
5
|
const ContainerNode = require('./ContainerNode');
|
|
5
6
|
const ExceptionUtils = require('../util/ExceptionUtils');
|
|
6
7
|
const GeneralUtils = require('../util/GeneralUtils');
|
|
@@ -13,6 +14,9 @@ const parse = (content, templatePath, isCrlfLineBreak, isEmbeddedNode) => {
|
|
|
13
14
|
|
|
14
15
|
for (let i = 0; i < elementList.length; i++) {
|
|
15
16
|
const element = elementList[i];
|
|
17
|
+
|
|
18
|
+
validateNodeHead(element, templatePath);
|
|
19
|
+
|
|
16
20
|
const newNode = new IsmlNode(element.value, element.lineNumber, element.columnNumber, element.globalPos, isEmbeddedNode);
|
|
17
21
|
|
|
18
22
|
const containerResult = parseContainerElements(element, currentParent, newNode, templatePath);
|
|
@@ -38,7 +42,7 @@ const parseContainerElements = (element, currentParent, newNode, templatePath) =
|
|
|
38
42
|
currentParent.getType(),
|
|
39
43
|
currentParent.lineNumber,
|
|
40
44
|
currentParent.globalPos,
|
|
41
|
-
currentParent.
|
|
45
|
+
currentParent.head.trim().length,
|
|
42
46
|
templatePath
|
|
43
47
|
);
|
|
44
48
|
}
|
|
@@ -84,12 +88,12 @@ const parseContainerElements = (element, currentParent, newNode, templatePath) =
|
|
|
84
88
|
currentParent.getType(),
|
|
85
89
|
currentParent.lineNumber,
|
|
86
90
|
currentParent.globalPos,
|
|
87
|
-
currentParent.
|
|
91
|
+
currentParent.head.trim().length,
|
|
88
92
|
templatePath
|
|
89
93
|
);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
currentParent.
|
|
96
|
+
currentParent.setTail(element.value, element.lineNumber, element.columnNumber, element.globalPos);
|
|
93
97
|
currentParent = currentParent.parent.parent;
|
|
94
98
|
|
|
95
99
|
return {
|
|
@@ -115,8 +119,15 @@ const parseNonContainerElements = (element, currentParent, newNode, templatePath
|
|
|
115
119
|
}
|
|
116
120
|
} else if (element.isClosingTag) {
|
|
117
121
|
|
|
122
|
+
const parentLastChild = currentParent.getLastChild();
|
|
123
|
+
|
|
118
124
|
if (element.tagType === currentParent.getType()) {
|
|
119
|
-
currentParent.
|
|
125
|
+
currentParent.setTail(element.value, element.lineNumber, element.columnNumber, element.globalPos);
|
|
126
|
+
|
|
127
|
+
} else if (element.isCustomTag && element.tagType === parentLastChild.getType()) {
|
|
128
|
+
parentLastChild.setTail(element.value, element.lineNumber, element.columnNumber, element.globalPos);
|
|
129
|
+
|
|
130
|
+
currentParent = parentLastChild;
|
|
120
131
|
|
|
121
132
|
} else if (element.isClosingTag && currentParent.isRoot()) {
|
|
122
133
|
throw ExceptionUtils.unbalancedElementError(
|
|
@@ -158,22 +169,22 @@ const postProcess = (node, data = {}) => {
|
|
|
158
169
|
for (let i = 0; i < node.children.length; i++) {
|
|
159
170
|
const child = node.children[i];
|
|
160
171
|
|
|
161
|
-
if (child.
|
|
172
|
+
if (child.head.indexOf('template="util/modules"') >= 0) {
|
|
162
173
|
data.moduleDefinition = {
|
|
163
|
-
value : child.
|
|
174
|
+
value : child.head,
|
|
164
175
|
lineNumber : child.lineNumber,
|
|
165
176
|
globalPos : child.globalPos,
|
|
166
|
-
length : child.
|
|
177
|
+
length : child.head.trim().length
|
|
167
178
|
};
|
|
168
179
|
}
|
|
169
180
|
|
|
170
181
|
if (child.isCustomIsmlTag()) {
|
|
171
182
|
data.customModuleArray = data.customModuleArray || [];
|
|
172
183
|
data.customModuleArray.push({
|
|
173
|
-
value : child.
|
|
184
|
+
value : child.head,
|
|
174
185
|
lineNumber : child.lineNumber,
|
|
175
186
|
globalPos : child.globalPos,
|
|
176
|
-
length : child.
|
|
187
|
+
length : child.head.trim().length
|
|
177
188
|
});
|
|
178
189
|
}
|
|
179
190
|
|
|
@@ -211,10 +222,10 @@ const build = (templatePath, content, isCrlfLineBreak) => {
|
|
|
211
222
|
|
|
212
223
|
/**
|
|
213
224
|
* In the main part of tree build, a node A might hold the next node B's indentation in the last part of
|
|
214
|
-
* A, be it in its value or
|
|
225
|
+
* A, be it in its value or tail value. This function removes that trailing indentation from A and
|
|
215
226
|
* adds it to B as a leading indentation;
|
|
216
227
|
*/
|
|
217
|
-
|
|
228
|
+
const rectifyNodeIndentation = (node, child) => {
|
|
218
229
|
const previousSibling = child.getPreviousSibling();
|
|
219
230
|
|
|
220
231
|
if (child.isContainer()) {
|
|
@@ -222,21 +233,39 @@ function rectifyNodeIndentation(node, child) {
|
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
if (previousSibling && previousSibling.isOfType('text')) {
|
|
225
|
-
const trailingLineBreakQty = ParseUtils.getTrailingEmptyCharsQty(previousSibling.
|
|
236
|
+
const trailingLineBreakQty = ParseUtils.getTrailingEmptyCharsQty(previousSibling.head);
|
|
226
237
|
|
|
227
|
-
previousSibling.
|
|
228
|
-
child.
|
|
238
|
+
previousSibling.head = previousSibling.head.substring(0, previousSibling.head.length - trailingLineBreakQty);
|
|
239
|
+
child.head = ParseUtils.getBlankSpaceString(trailingLineBreakQty) + child.head;
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
if (child.isLastChild() && child.isOfType('text')) {
|
|
232
243
|
let trailingLineBreakQty = 0;
|
|
233
244
|
|
|
234
|
-
trailingLineBreakQty = ParseUtils.getTrailingEmptyCharsQty(child.
|
|
235
|
-
child.
|
|
245
|
+
trailingLineBreakQty = ParseUtils.getTrailingEmptyCharsQty(child.head);
|
|
246
|
+
child.head = child.head.substring(0, child.head.length - trailingLineBreakQty);
|
|
247
|
+
|
|
248
|
+
node.tail = ParseUtils.getBlankSpaceString(trailingLineBreakQty) + node.tail;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const validateNodeHead = (element, templatePath) => {
|
|
253
|
+
|
|
254
|
+
if (element.type !== 'text') {
|
|
255
|
+
const trimmedElement = element.value.trim();
|
|
256
|
+
const maskedElement = MaskUtils.maskQuoteContent(trimmedElement);
|
|
236
257
|
|
|
237
|
-
|
|
258
|
+
if (maskedElement.endsWith('_')) {
|
|
259
|
+
throw ExceptionUtils.unbalancedQuotesError(
|
|
260
|
+
element.tagType,
|
|
261
|
+
element.lineNumber,
|
|
262
|
+
element.globalPos,
|
|
263
|
+
trimmedElement.length,
|
|
264
|
+
templatePath
|
|
265
|
+
);
|
|
266
|
+
}
|
|
238
267
|
}
|
|
239
|
-
}
|
|
268
|
+
};
|
|
240
269
|
|
|
241
270
|
module.exports.build = build;
|
|
242
271
|
module.exports.parse = parse;
|