isml-linter 5.41.0 → 5.42.1

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.42.1] - 2023-01-28
4
+
5
+ ### Fixed
6
+ - Keep template original linebreak if one is not explictly set in the ISML Linter configuration file. It was forcing a Unix linebreak;
7
+
8
+ ## [5.42.0] - 2023-01-25
9
+
10
+ ### Added
11
+ - Introducing ["strict-void-elements" rule][strict-void-elements-readme];
12
+
13
+ ### Changed
14
+ - Allow Closing Tags for Void Elements in AST. "strict-void-elements" rule will optionally handle that invalid scenario;
15
+
16
+ ### Fixed
17
+ - "indent" Rule - Add "line" Attribute to Quote Issue Report;
18
+
3
19
  ## [5.41.0] - 2023-01-21
4
20
 
5
21
  ### Added
@@ -1048,6 +1064,8 @@
1048
1064
  ### Added
1049
1065
  - Linter is published;
1050
1066
 
1067
+ [5.42.1]: https://github.com/FabiowQuixada/isml-linter/compare/v5.42.0...v5.42.1
1068
+ [5.42.0]: https://github.com/FabiowQuixada/isml-linter/compare/v5.41.0...v5.42.0
1051
1069
  [5.41.0]: https://github.com/FabiowQuixada/isml-linter/compare/v5.40.5...v5.41.0
1052
1070
  [5.40.5]: https://github.com/FabiowQuixada/isml-linter/compare/v5.40.4...v5.40.5
1053
1071
  [5.40.4]: https://github.com/FabiowQuixada/isml-linter/compare/v5.40.3...v5.40.4
@@ -1211,11 +1229,12 @@
1211
1229
  [api-docs]: <docs/api.md>
1212
1230
  [license]: <LICENSE>
1213
1231
 
1214
- [disallow-tags-readme]: <docs/rules/disallow-tags.md>
1215
- [no-br-readme]: <docs/rules/no-br.md>
1216
- [no-inline-style-readme]: <docs/rules/no-br.md>
1217
- [no-isscript-readme]: <docs/rules/no-isscript.md>
1218
- [enforce-security-readme]: <docs/rules/enforce-security.md>
1219
- [no-hardcode-readme]: <docs/rules/no-hardcode.md>
1220
- [indent-readme]: <docs/rules/indent.md>
1221
- [eslint-to-isscript-readme]: <docs/rules/eslint-to-isscript.md>
1232
+ [strict-void-elements-readme]: <docs/rules/strict-void-elements.md>
1233
+ [disallow-tags-readme]: <docs/rules/disallow-tags.md>
1234
+ [no-br-readme]: <docs/rules/no-br.md>
1235
+ [no-inline-style-readme]: <docs/rules/no-br.md>
1236
+ [no-isscript-readme]: <docs/rules/no-isscript.md>
1237
+ [enforce-security-readme]: <docs/rules/enforce-security.md>
1238
+ [no-hardcode-readme]: <docs/rules/no-hardcode.md>
1239
+ [indent-readme]: <docs/rules/indent.md>
1240
+ [eslint-to-isscript-readme]: <docs/rules/eslint-to-isscript.md>
package/README.md CHANGED
@@ -3,7 +3,7 @@ ISML Linter is a tool for examining if your project's templates follow a specifi
3
3
 
4
4
  - Styles that are defined by your team;
5
5
  - Syntactic errors related to `<is* >` tags;
6
- - Coding conventions recommended by SalesForce;
6
+ - Coding conventions recommended by Salesforce;
7
7
  - Git conflicts that may accidentally be left unresolved;
8
8
 
9
9
  Please feel free to make suggestions and help make this linter better. :) The set of currently available rules can be found below.
@@ -161,7 +161,7 @@ Please check the [Generic Configurations for Rules][generic-rule-config] page.
161
161
  | ------------------------------------------------------------------------------------ |:-----------------------------------------|
162
162
  | :exclamation: [no-br][no-br-readme] | <span style="color:orange">[Deprecated]</span> Disallows `<br/>` tags. Enable this rule if you prefer to use CSS to handle vertical spacing |
163
163
  | [no-git-conflict][no-git-conflict-readme] | Disallows unresolved Git conflicts |
164
- | [no-import-package][no-import-package-readme] | Disallows `importPackage()` function. It is recommended by SalesForce to use `require()` instead |
164
+ | [no-import-package][no-import-package-readme] | Disallows `importPackage()` function. It is recommended by Salesforce to use `require()` instead |
165
165
  | :exclamation: [no-isscript][no-isscript-readme] | <span style="color:orange">[Deprecated]</span> Disallows `<isscript/>` tag in template. Enable this rule if you prefer logic to be kept in a separate .ds/.js file |
166
166
  | :wrench: [no-trailing-spaces][no-trailing-spaces-readme] | Disallows trailing blank spaces |
167
167
  | :wrench: [no-space-only-lines][no-space-only-lines-readme] | Disallows lines that contain only blank spaces, i.e., unnecessarily indented |
@@ -189,6 +189,7 @@ Please check the [Generic Configurations for Rules][generic-rule-config] page.
189
189
  | :small_orange_diamond: [align-isset][align-isset-readme] | Aligns contiguous `<isset>` tags attributes' columns |
190
190
  | :small_orange_diamond: [enforce-security][enforce-security-readme] | Enforces security measures |
191
191
  | :wrench: :small_orange_diamond: [no-redundant-context][no-redundant-context-readme] | Prevents use of unnecessary contexts, such as `dw.web.Resource` |
192
+ | :small_orange_diamond: [strict-void-elements][strict-void-elements-readme] | Disallows closing tags for void elements, such as `<input>` and `<img>` |
192
193
 
193
194
  You are more than welcome to contribute with us! Please check the [contribute section][contribute-docs].
194
195
 
@@ -235,6 +236,7 @@ This project was conceived by its author without any financial support, with the
235
236
  [disallow-tags-readme]: <docs/rules/disallow-tags.md>
236
237
  [enforce-security-readme]: <docs/rules/enforce-security.md>
237
238
  [no-redundant-context-readme]: <docs/rules/no-redundant-context.md>
239
+ [strict-void-elements-readme]: <docs/rules/strict-void-elements.md>
238
240
 
239
241
  [api-docs]: <docs/api.md>
240
242
  [cli-docs]: <docs/cli.md>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isml-linter",
3
- "version": "5.41.0",
3
+ "version": "5.42.1",
4
4
  "author": "Fabiow Quixadá <ftquixada@gmail.com>",
5
5
  "license": "MIT",
6
6
  "main": "src/publicApi.js",
@@ -38,6 +38,7 @@ const config = {
38
38
  values : ['isscript', 'br', 'style', 'iframe']
39
39
  },
40
40
  'enforce-security' : {},
41
+ 'strict-void-elements' : {},
41
42
 
42
43
  // Other
43
44
  'lowercase-filename' : {}
package/src/IsmlLinter.js CHANGED
@@ -101,12 +101,12 @@ const getEmptyResult = () => {
101
101
  };
102
102
  };
103
103
 
104
- const checkTemplate = (templatePath, data, content, templateName) => {
104
+ const parseAndPossiblyFixTemplate = (templatePath, data, content, templateName) => {
105
105
  const formattedTemplatePath = GeneralUtils.formatTemplatePath(templatePath);
106
106
  const templateResults = getEmptyResult();
107
107
 
108
108
  try {
109
- const parseResult = RuleUtils.checkTemplate(templatePath, data, content, templateName);
109
+ const parseResult = RuleUtils.parseAndPossiblyFixTemplate(templatePath, data, content, templateName);
110
110
 
111
111
  if (parseResult.fixed) {
112
112
  templateResults.templatesFixed++;
@@ -232,7 +232,7 @@ Linter.run = (pathData, content, data = {}) => {
232
232
  const isIgnored = FileUtils.isIgnored(templatePath);
233
233
 
234
234
  if (!isIgnored) {
235
- const templateResults = checkTemplate(templatePath, data, content, templateName);
235
+ const templateResults = parseAndPossiblyFixTemplate(templatePath, data, content, templateName);
236
236
 
237
237
  finalResult = merge(finalResult, templateResults);
238
238
  }
@@ -6,6 +6,7 @@ const SfccTagContainer = require('../enums/SfccTagContainer');
6
6
  const ParseUtils = require('./ParseUtils');
7
7
  const MaskUtils = require('./MaskUtils');
8
8
  const ExceptionUtils = require('../util/ExceptionUtils');
9
+ const GeneralUtils = require('../util/GeneralUtils');
9
10
 
10
11
  let ID_COUNTER = 0;
11
12
 
@@ -149,6 +150,7 @@ class IsmlNode {
149
150
  newNode.isEmbeddedNode = this.isEmbeddedNode;
150
151
  }
151
152
 
153
+ getChild(number) { return this.children[number]; }
152
154
  getLastChild() { return this.children[this.children.length - 1]; }
153
155
  getChildrenQty() { return this.children.length; }
154
156
  hasChildren() { return this.children.length > 0; }
@@ -382,24 +384,11 @@ class IsmlNode {
382
384
  return rootNode;
383
385
  }
384
386
 
385
- toString(stream = '') {
387
+ toString() {
386
388
 
387
- if (!this.isContainer() && this.isEmpty() && !this.isLastChild()) {
388
- return stream;
389
- }
390
-
391
- if (!this.isRoot() && !this.isContainer()) {
392
- stream += this.head;
393
- }
394
-
395
- for (let i = 0; i < this.children.length; i++) {
396
- const child = this.children[i];
397
- stream = child.toString(stream);
398
- }
389
+ let stream = privateToString(this);
399
390
 
400
- if (!this.isRoot() && !this.isContainer()) {
401
- stream += this.tail;
402
- }
391
+ stream = GeneralUtils.applyLineBreak(stream, this.getRoot().tree.originalLineBreak);
403
392
 
404
393
  return stream;
405
394
  }
@@ -428,6 +417,27 @@ class IsmlNode {
428
417
  * will be available for use only within IsmlNode methods;
429
418
  */
430
419
 
420
+ const privateToString = (node, stream = '') => {
421
+ if (!node.isContainer() && node.isEmpty() && !node.isLastChild()) {
422
+ return stream;
423
+ }
424
+
425
+ if (!node.isRoot() && !node.isContainer()) {
426
+ stream += node.head;
427
+ }
428
+
429
+ for (let i = 0; i < node.children.length; i++) {
430
+ const child = node.children[i];
431
+ stream = privateToString(child, stream);
432
+ }
433
+
434
+ if (!node.isRoot() && !node.isContainer()) {
435
+ stream += node.tail;
436
+ }
437
+
438
+ return stream;
439
+ };
440
+
431
441
  const getAttributes = node => {
432
442
  const trimmedHead = node.head.trim();
433
443
  const nodeHead = trimmedHead.substring(1, trimmedHead.length - 1);
@@ -136,7 +136,7 @@ const parseNextElement = state => {
136
136
  }
137
137
 
138
138
  state.elementList.push(newElement);
139
-
139
+
140
140
  if (newElement.type === 'htmlTag' && newElement.value.indexOf('<isif') >= 0 && newElement.value.indexOf('</isif') < 0) {
141
141
  throw ExceptionUtils.invalidNestedIsifError(
142
142
  newElement.tagType,
@@ -275,6 +275,7 @@ const getNextClosingTagOrExpressionEndPos = content => {
275
275
  };
276
276
 
277
277
  const getInitialState = (templateContent, templatePath, isCrlfLineBreak) => {
278
+ // TODO Check if "GeneralUtils.toLF" can be removed;
278
279
  const originalContent = GeneralUtils.toLF(templateContent);
279
280
  const originalShadowContent = MaskUtils.maskIgnorableContent(originalContent, null, templatePath);
280
281
 
@@ -32,6 +32,10 @@ const parse = (content, templatePath, isCrlfLineBreak, isEmbeddedNode) => {
32
32
 
33
33
  ParseUtils.checkBalance(rootNode, templatePath);
34
34
 
35
+ rootNode.tree = {
36
+ originalLineBreak : GeneralUtils.getFileLineBreakStyle(content),
37
+ };
38
+
35
39
  return rootNode;
36
40
  };
37
41
 
@@ -110,14 +114,8 @@ const parseContainerElements = (element, currentParent, newNode, templatePath) =
110
114
 
111
115
  const parseNonContainerElements = (element, currentParent, newNode, templatePath) => {
112
116
  if (element.isSelfClosing) {
113
- if (element.isClosingTag && element.isVoidElement) {
114
- throw ExceptionUtils.voidElementClosingTag(
115
- element.tagType,
116
- element.lineNumber,
117
- element.globalPos,
118
- element.value.trim().length,
119
- templatePath
120
- );
117
+ if (element.isClosingTag && element.isVoidElement) {
118
+ currentParent.getLastChild().setTail(element.value, element.lineNumber, element.columnNumber, element.globalPos);
121
119
  } else {
122
120
  currentParent.addChild(newNode);
123
121
  }
@@ -209,15 +207,17 @@ const build = (templatePath, content, isCrlfLineBreak) => {
209
207
 
210
208
  const ParseStatus = require('../enums/ParseStatus');
211
209
 
212
- const result = {
210
+ const templateContent = content || fs.readFileSync(templatePath, 'utf-8');
211
+ const result = {
212
+ originalLineBreak : GeneralUtils.getFileLineBreakStyle(templateContent),
213
213
  templatePath,
214
214
  status : ParseStatus.NO_ERRORS
215
215
  };
216
216
 
217
217
  try {
218
- const templateContent = GeneralUtils.toLF(content || fs.readFileSync(templatePath, 'utf-8'));
219
- result.rootNode = parse(templateContent, templatePath, isCrlfLineBreak);
220
- result.data = postProcess(result.rootNode);
218
+ const formattedTemplateContent = templateContent;
219
+ result.rootNode = parse(formattedTemplateContent, templatePath, isCrlfLineBreak);
220
+ result.data = postProcess(result.rootNode);
221
221
 
222
222
  result.rootNode.tree = result;
223
223
 
package/src/publicApi.js CHANGED
@@ -24,7 +24,7 @@ module.exports = {
24
24
  autofixConfig.autoFix = true;
25
25
  IsmlLinter.setConfig(autofixConfig);
26
26
  } else {
27
- autofixConfig = IsmlLinter.getConfig();
27
+ autofixConfig = IsmlLinter.getConfig();
28
28
  autofixConfig.autoFix = true;
29
29
  IsmlLinter.setConfig(autofixConfig);
30
30
  }
@@ -1,5 +1,4 @@
1
1
  const SingleLineRulePrototype = require('../prototypes/SingleLineRulePrototype');
2
- const Constants = require('../../Constants');
3
2
  const GeneralUtils = require('../../util/GeneralUtils');
4
3
 
5
4
  const ruleId = require('path').basename(__filename).slice(0, -3);
@@ -21,8 +20,9 @@ Rule.getColumnNumber = function() {
21
20
 
22
21
  Rule.check = function(templateContent) {
23
22
 
23
+ const lineBreak = GeneralUtils.getFileLineBreakStyle(templateContent);
24
24
  const maxLines = this.getConfigs().max;
25
- const lineArray = GeneralUtils.toLF(templateContent).split(Constants.EOL);
25
+ const lineArray = templateContent.split(lineBreak);
26
26
  const columnNumber = this.getColumnNumber();
27
27
  const occurrenceList = [];
28
28
 
@@ -17,9 +17,9 @@ Rule.getColumnNumber = function() {
17
17
  Rule.getFixedContent = function(templateContent) {
18
18
  const GeneralUtils = require('../../util/GeneralUtils');
19
19
 
20
- const activeLineBreak = GeneralUtils.getActiveLineBreak();
21
- const lineArray = templateContent.split(Constants.EOL);
22
- const result = [];
20
+ const lineBreak = GeneralUtils.getFileLineBreakStyle(templateContent);
21
+ const lineArray = templateContent.split(lineBreak);
22
+ const result = [];
23
23
 
24
24
  for (let i = 0; i < lineArray.length; i++) {
25
25
  const line = lineArray[i];
@@ -27,7 +27,7 @@ Rule.getFixedContent = function(templateContent) {
27
27
  result.push(line.trim() ? line : line.trim());
28
28
  }
29
29
 
30
- return result.join(activeLineBreak);
30
+ return GeneralUtils.applyLineBreak(result.join(lineBreak));
31
31
  };
32
32
 
33
33
  Rule.getFirstOccurrence = function(line) {
@@ -16,9 +16,11 @@ Rule.getColumnNumber = function(line) {
16
16
  };
17
17
 
18
18
  Rule.getFixedContent = function(templateContent) {
19
+ const lineBreak = GeneralUtils.getFileLineBreakStyle(templateContent);
19
20
  const indent = IndentRule.getIndentation();
20
21
  const fixedContent = templateContent.replace(/\t/g, indent);
21
- return GeneralUtils.applyActiveLineBreaks(fixedContent);
22
+
23
+ return GeneralUtils.applyLineBreak(fixedContent, lineBreak);
22
24
  };
23
25
 
24
26
  Rule.getFirstOccurrence = function(line) {
@@ -1,5 +1,4 @@
1
1
  const SingleLineRulePrototype = require('../prototypes/SingleLineRulePrototype');
2
- const Constants = require('../../Constants');
3
2
  const ParseUtils = require('../../isml_tree/ParseUtils');
4
3
 
5
4
  const ruleId = require('path').basename(__filename).slice(0, -3);
@@ -19,9 +18,9 @@ Rule.getColumnNumber = function(line) {
19
18
  Rule.getFixedContent = function(templateContent) {
20
19
  const GeneralUtils = require('../../util/GeneralUtils');
21
20
 
22
- const activeLineBreak = GeneralUtils.getActiveLineBreak();
23
- const lineArray = GeneralUtils.toLF(templateContent).split(Constants.EOL);
24
- const result = [];
21
+ const lineBreak = GeneralUtils.getFileLineBreakStyle(templateContent);
22
+ const lineArray = templateContent.split(lineBreak);
23
+ const result = [];
25
24
 
26
25
  for (let i = 0; i < lineArray.length; i++) {
27
26
  const line = lineArray[i];
@@ -29,7 +28,7 @@ Rule.getFixedContent = function(templateContent) {
29
28
  result.push(line.replace(/\s+$/g, ''));
30
29
  }
31
30
 
32
- return result.join(activeLineBreak);
31
+ return GeneralUtils.applyLineBreak(result.join(lineBreak), lineBreak);
33
32
  };
34
33
 
35
34
  Rule.getFirstOccurrence = function(line) {
@@ -1,14 +1,14 @@
1
1
  const RulePrototype = require('./RulePrototype');
2
2
  const ConfigUtils = require('../../util/ConfigUtils');
3
- const Constants = require('../../Constants');
4
3
  const GeneralUtils = require('../../util/GeneralUtils');
5
4
 
6
5
  const SingleLineRulePrototype = Object.create(RulePrototype);
7
6
 
8
7
  SingleLineRulePrototype.check = function(templateContent, data = { isCrlfLineBreak : false }) {
9
8
 
9
+ const lineBreak = GeneralUtils.getFileLineBreakStyle(templateContent);
10
10
  const config = ConfigUtils.load();
11
- const lineArray = GeneralUtils.toLF(templateContent).split(Constants.EOL);
11
+ const lineArray = templateContent.split(lineBreak);
12
12
  const occurrenceList = [];
13
13
  let globalPos = 0;
14
14
 
@@ -41,10 +41,10 @@ SingleLineRulePrototype.check = function(templateContent, data = { isCrlfLineBre
41
41
  };
42
42
  }
43
43
 
44
- return {
44
+ return {
45
45
  occurrenceList,
46
46
  fixedContent : templateContent
47
- };
47
+ };
48
48
  };
49
49
 
50
50
  module.exports = SingleLineRulePrototype;
@@ -65,20 +65,4 @@ TreeRulePrototype.return = function(node, occurrenceList, config) {
65
65
  }
66
66
  };
67
67
 
68
- TreeRulePrototype.fix = function(stream = '') {
69
-
70
- if (!this.isRoot() && !this.isContainer()) {
71
- stream += this.head;
72
- }
73
-
74
- for (let i = 0; i < this.children.length; i++) {
75
- const node = this.children[i];
76
- stream = node.isBroken() ?
77
- node.toString(stream) :
78
- this.getFixedContent(node, stream);
79
- }
80
-
81
- return stream;
82
- };
83
-
84
68
  module.exports = TreeRulePrototype;
@@ -2,7 +2,6 @@ const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
2
  const ParseUtils = require('../../isml_tree/ParseUtils');
3
3
  const Constants = require('../../Constants');
4
4
  const ConfigUtils = require('../../util/ConfigUtils');
5
- const GeneralUtils = require('../../util/GeneralUtils');
6
5
 
7
6
  const ruleId = require('path').basename(__filename).slice(0, -3);
8
7
  const description = 'Not eslint-valid';
@@ -121,7 +120,7 @@ Rule.getFixedContent = function(node) {
121
120
  node.head = reIndent(content, ismlOffset);
122
121
  }
123
122
 
124
- return GeneralUtils.applyActiveLineBreaks(node.toString());
123
+ return node.toString();
125
124
  };
126
125
 
127
126
  const unindent = (content, indentSize) => {
@@ -161,8 +161,15 @@ Rule.isQuoteClosingCharBroken = function(node) {
161
161
  '';
162
162
 
163
163
  if (message) {
164
+ const lineBreak = node.getRoot().tree.originalLineBreak;
165
+ const line = node
166
+ .getRoot()
167
+ .toString()
168
+ .split(lineBreak)[lineNumber - 1];
169
+
164
170
  result.push({
165
171
  quoteChar : attribute.quoteChar,
172
+ line : line,
166
173
  lineNumber : lineNumber,
167
174
  columnNumber : columnNumber,
168
175
  globalPos : globalPos,
@@ -1,7 +1,6 @@
1
1
  const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
2
  const RuleUtils = require('../../util/TempRuleUtils');
3
3
  const Constants = require('../../Constants');
4
- const GeneralUtils = require('../../util/GeneralUtils');
5
4
 
6
5
  const TAG_TYPE = 'iscache';
7
6
  const ruleId = require('path').basename(__filename).slice(0, -3);
@@ -33,7 +32,7 @@ Rule.getFixedContent = function(rootNode) {
33
32
  }
34
33
  }
35
34
 
36
- return GeneralUtils.applyActiveLineBreaks(rootNode.toString());
35
+ return rootNode.toString();
37
36
  };
38
37
 
39
38
  module.exports = Rule;
@@ -1,7 +1,6 @@
1
1
  const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
2
  const Constants = require('../../Constants');
3
3
  const RuleUtils = require('../../util/TempRuleUtils');
4
- const GeneralUtils = require('../../util/GeneralUtils');
5
4
 
6
5
  const TAG_TYPE = 'iscontent';
7
6
  const ruleId = require('path').basename(__filename).slice(0, -3);
@@ -29,7 +28,7 @@ Rule.getFixedContent = function(rootNode) {
29
28
  }
30
29
  }
31
30
 
32
- return GeneralUtils.applyActiveLineBreaks(rootNode.toString());
31
+ return rootNode.toString();
33
32
  };
34
33
 
35
34
  module.exports = Rule;
@@ -1,6 +1,5 @@
1
1
  const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
2
  const MaskUtils = require('../../isml_tree/MaskUtils');
3
- const GeneralUtils = require('../../util/GeneralUtils');
4
3
 
5
4
  const ruleId = require('path').basename(__filename).slice(0, -3);
6
5
  const description = 'Slash is not allowed for "iselse" nor "iselseif" tags';
@@ -29,7 +28,7 @@ Rule.getFixedContent = function(node) {
29
28
  this.getFixedContent(child);
30
29
  }
31
30
 
32
- return GeneralUtils.applyActiveLineBreaks(node.toString());
31
+ return node.toString();
33
32
  };
34
33
 
35
34
  module.exports = Rule;
@@ -2,7 +2,6 @@ const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
2
  const IndentRule = require('../tree/indent');
3
3
  const ConfigUtils = require('../../util/ConfigUtils');
4
4
  const Constants = require('../../Constants');
5
- const GeneralUtils = require('../../util/GeneralUtils');
6
5
  const TreeBuilder = require('../../isml_tree/TreeBuilder');
7
6
 
8
7
  const ruleId = require('path').basename(__filename).slice(0, -3);
@@ -42,7 +41,7 @@ Rule.getFixedContent = rootNode => {
42
41
  const newRootNode = TreeBuilder.build(null, stringifiedTree).rootNode;
43
42
  const partialFixContent = IndentRule.getFixedContent(newRootNode);
44
43
 
45
- return GeneralUtils.applyActiveLineBreaks(partialFixContent);
44
+ return partialFixContent;
46
45
  };
47
46
 
48
47
  const addLineBreaks = node => {
@@ -0,0 +1,46 @@
1
+ const TreeRulePrototype = require('../prototypes/TreeRulePrototype');
2
+
3
+ const ruleId = require('path').basename(__filename).slice(0, -3);
4
+ const description = 'This is a void element, and as such, should not have a corresponding closing tag';
5
+
6
+ const Rule = Object.create(TreeRulePrototype);
7
+
8
+ Rule.init(ruleId, description);
9
+
10
+ Rule.isBroken = function(node) {
11
+ return node.isVoidElement() && node.tail.length > 0;
12
+ };
13
+
14
+ Rule.check = function(node, data) {
15
+
16
+ const ruleConfig = this.getConfigs();
17
+ let occurrenceList = [];
18
+
19
+ occurrenceList = this.checkChildren(node, data);
20
+
21
+ if (this.isBroken(node)) {
22
+ const error = this.getError(
23
+ node.tail.trim(),
24
+ node.tailLineNumber,
25
+ node.tailColumnNumber,
26
+ node.tailGlobalPos,
27
+ node.tail.trim().length,
28
+ `"<${node.getType()}>" is a void element, and as such, should not have a corresponding closing tag`
29
+ );
30
+
31
+ occurrenceList.push(error);
32
+ }
33
+
34
+ return this.return(node, occurrenceList, ruleConfig);
35
+ };
36
+
37
+ Rule.getFixedContent = node => {
38
+
39
+ if (node.isVoidElement()) {
40
+ node.setTail('', null, null, null);
41
+ }
42
+
43
+ return node.toString();
44
+ };
45
+
46
+ module.exports = Rule;
@@ -126,6 +126,8 @@ const displayUnknownErrors = lintResult => {
126
126
  return partialSum;
127
127
  };
128
128
 
129
+ const getPluralTermString = (singularTerm, qty) => `${qty} ${singularTerm}${qty > 1 ? 's' : ''}`;
130
+
129
131
  const displayOccurrenceList = lintResult => {
130
132
 
131
133
  displayUnparseableErrors(lintResult);
@@ -148,19 +150,19 @@ const displayOccurrenceList = lintResult => {
148
150
  console.log(Constants.EOL + chalk`{bold Linted ${lintResult.totalTemplatesQty} templates in ${lintResult.elapsedTime} seconds.}`);
149
151
 
150
152
  if (occurrenceList.error.qty > 0) {
151
- console.log(chalk`{bold ${occurrenceList.error.qty} error(s) found.}`);
153
+ console.log(chalk`{bold ${getPluralTermString('error', occurrenceList.error.qty)} found.}`);
152
154
  }
153
155
 
154
156
  if (occurrenceList.warning.qty > 0) {
155
- console.log(chalk`{bold ${occurrenceList.warning.qty} warning(s) found.}`);
157
+ console.log(chalk`{bold ${getPluralTermString('warning', occurrenceList.warning.qty)} found.}`);
156
158
  }
157
159
 
158
160
  if (occurrenceList.info.qty > 0) {
159
- console.log(chalk`{bold ${occurrenceList.info.qty} info(s) found.}`);
161
+ console.log(chalk`{bold ${getPluralTermString('info', occurrenceList.info.qty)} found.}`);
160
162
  }
161
163
 
162
164
  if (lintResult.INVALID_TEMPLATE && lintResult.INVALID_TEMPLATE.length > 0) {
163
- console.log(chalk`{bold ${lintResult.INVALID_TEMPLATE.length} template(s) have an invalid ISML tree.}`);
165
+ console.log(chalk`{bold ${getPluralTermString('template', lintResult.INVALID_TEMPLATE.length)} have an invalid ISML tree.}`);
164
166
  }
165
167
 
166
168
  if (lintResult.UNKNOWN_ERROR && lintResult.UNKNOWN_ERROR.length > 0) {
@@ -34,12 +34,52 @@ const getActiveLineBreak = () => {
34
34
  return configLineBreak || Constants.EOL;
35
35
  };
36
36
 
37
+ const getFileLineBreakStyle = templateContent => {
38
+ const indexOfLF = templateContent.indexOf(Constants.lineBreak.unix, 1);
39
+
40
+ if (indexOfLF === -1) {
41
+ if (templateContent.indexOf('\r') !== -1) {
42
+ return '\r';
43
+ }
44
+
45
+ return Constants.lineBreak.unix;
46
+ }
47
+
48
+ if (templateContent[indexOfLF - 1] === '\r') {
49
+ return Constants.lineBreak.windows;
50
+ }
51
+
52
+ return Constants.lineBreak.unix;
53
+ };
54
+
37
55
  module.exports.formatTemplatePath = filePath => {
38
56
  return filePath.replace(/\//g, path.sep);
39
57
  };
40
58
 
41
59
  module.exports.toLF = content => {
42
- return content.replace(/\r\n/g, '\n');
60
+ return content.replace(/\r\n/g, Constants.lineBreak.unix);
61
+ };
62
+
63
+ module.exports.getFileLineBreakStyle = getFileLineBreakStyle;
64
+
65
+ module.exports.applyLineBreak = (content, lineBreak) => {
66
+ const config = ConfigUtils.load();
67
+ const configLineBreak = config.linebreakStyle && Constants.lineBreak[config.linebreakStyle];
68
+ const templateHasWindowsLineBreak = content.indexOf(Constants.lineBreak.windows) >= 0;
69
+
70
+ if (configLineBreak) {
71
+ lineBreak = configLineBreak;
72
+ }
73
+
74
+ if (lineBreak === Constants.lineBreak.windows && !templateHasWindowsLineBreak) {
75
+ return content
76
+ .replace(new RegExp(Constants.lineBreak.unix, 'g'), lineBreak);
77
+ } else if (lineBreak === Constants.lineBreak.unix && templateHasWindowsLineBreak) {
78
+ return content
79
+ .replace(new RegExp(Constants.lineBreak.windows, 'g'), lineBreak);
80
+ }
81
+
82
+ return content;
43
83
  };
44
84
 
45
85
  module.exports.applyActiveLineBreaks = content => {
@@ -6,7 +6,6 @@ const ConfigUtils = require('./ConfigUtils');
6
6
  const lowercaseFilenameRule = require('../rules/line_by_line/lowercase-filename');
7
7
  const CustomTagContainer = require('./CustomTagContainer');
8
8
  const CustomModulesRule = require('../rules/tree/custom-tags');
9
- const GeneralUtils = require('./GeneralUtils');
10
9
  const ConsoleUtils = require('./ConsoleUtils');
11
10
  const ExceptionUtils = require('./ExceptionUtils');
12
11
 
@@ -64,7 +63,7 @@ const checkCustomTag = tag => {
64
63
  }
65
64
  };
66
65
 
67
- const applyRuleResult = (config, ruleResult, templatePath, templateResults, rule) => {
66
+ const fixTemplateOrReportIssues = (config, ruleResult, templatePath, templateResults, rule) => {
68
67
  if (config.autoFix && ruleResult.fixedContent) {
69
68
  fs.writeFileSync(templatePath, ruleResult.fixedContent);
70
69
  templateResults.fixed = true;
@@ -78,7 +77,7 @@ const applyRuleResult = (config, ruleResult, templatePath, templateResults, rule
78
77
  }
79
78
  };
80
79
 
81
- const applyRuleOnTemplate = (ruleArray, templatePath, root, config, data) => {
80
+ const fixTemplateOrReportIssuesForRuleList = (ruleArray, templatePath, root, config, data) => {
82
81
  const templateResults = {
83
82
  fixed : false,
84
83
  errors : {},
@@ -92,9 +91,9 @@ const applyRuleOnTemplate = (ruleArray, templatePath, root, config, data) => {
92
91
  if (!rule.shouldIgnore(templatePath)) {
93
92
  try {
94
93
  ConsoleUtils.displayVerboseMessage(`Applying "${rule.id}" rule`, 1);
95
- const ruleResults = rule.check(root, templateResults.data);
94
+ const ruleResults = rule.check(root, templateResults.data);
96
95
  templateResults.finalContent = ruleResults.fixedContent;
97
- applyRuleResult(config, ruleResults, templatePath, templateResults, rule);
96
+ fixTemplateOrReportIssues(config, ruleResults, templatePath, templateResults, rule);
98
97
 
99
98
  } catch (error) {
100
99
  throw ExceptionUtils.ruleApplianceError(rule, error, templatePath);
@@ -112,10 +111,10 @@ const findNodeOfType = (node, type) => {
112
111
  if (child.isOfType(type)) {
113
112
  result = child;
114
113
  return true;
115
- } else {
116
- result = findNodeOfType(child, type) || result;
117
114
  }
118
115
 
116
+ result = findNodeOfType(child, type) || result;
117
+
119
118
  return false;
120
119
  });
121
120
 
@@ -170,7 +169,7 @@ const checkFileName = (filename, templateContent) => {
170
169
  return templateResults;
171
170
  };
172
171
 
173
- const checkTreeRules = (templatePath, templateContent, config, data) => {
172
+ const checkAndPossiblyFixTreeRules = (templatePath, templateContent, config, data) => {
174
173
  if (!config.disableTreeParse) {
175
174
  ConsoleUtils.displayVerboseMessage(`Building tree for "${templatePath}"`, 1);
176
175
  const tree = TreeBuilder.build(templatePath, templateContent);
@@ -181,7 +180,7 @@ const checkTreeRules = (templatePath, templateContent, config, data) => {
181
180
 
182
181
  const ruleArray = getEnabledTreeRules();
183
182
 
184
- return applyRuleOnTemplate(
183
+ return fixTemplateOrReportIssuesForRuleList(
185
184
  ruleArray,
186
185
  templatePath,
187
186
  tree.rootNode,
@@ -190,10 +189,10 @@ const checkTreeRules = (templatePath, templateContent, config, data) => {
190
189
  }
191
190
  };
192
191
 
193
- const checkLineByLineRules = (templatePath, templateContent, config, data) => {
192
+ const checkAndPossiblyFixLineByLineRules = (templatePath, templateContent, config, data) => {
194
193
  const ruleArray = getEnabledLineRules();
195
194
 
196
- return applyRuleOnTemplate(
195
+ return fixTemplateOrReportIssuesForRuleList(
197
196
  ruleArray,
198
197
  templatePath,
199
198
  templateContent,
@@ -222,12 +221,12 @@ const checkCustomModules = () => {
222
221
  return moduleResults;
223
222
  };
224
223
 
225
- const checkTemplate = (templatePath, data, content = '', templateName = '') => {
224
+ const parseAndPossiblyFixTemplate = (templatePath, data, content = '', templateName = '') => {
226
225
  ConsoleUtils.displayVerboseMessage(`\nChecking "${templatePath}" template`);
227
226
  const config = ConfigUtils.load();
228
- const templateContent = GeneralUtils.toLF(content || fs.readFileSync(templatePath, 'utf-8'));
229
- const lineResults = checkLineByLineRules(templatePath, templateContent, config, data);
230
- const treeResults = checkTreeRules(templatePath, lineResults.finalContent, config, data) || { errors : [] };
227
+ const templateContent = content || fs.readFileSync(templatePath, 'utf-8');
228
+ const lineResults = checkAndPossiblyFixLineByLineRules(templatePath, templateContent, config, data);
229
+ const treeResults = checkAndPossiblyFixTreeRules(templatePath, lineResults.finalContent, config, data) || { errors : [] };
231
230
  const filenameResults = checkFileName(templateName, templateContent);
232
231
 
233
232
  return {
@@ -290,7 +289,7 @@ const getEnabledTreeRules = () => {
290
289
  module.exports.getAllLineRules = () => lineByLineRules;
291
290
  module.exports.findNodeOfType = findNodeOfType;
292
291
  module.exports.isTypeAmongTheFirstElements = isTypeAmongTheFirstElements;
293
- module.exports.checkTemplate = checkTemplate;
292
+ module.exports.parseAndPossiblyFixTemplate = parseAndPossiblyFixTemplate;
294
293
  module.exports.checkCustomModules = checkCustomModules;
295
294
  module.exports.getAvailableRulesQty = getAvailableRulesQty;
296
295
  module.exports.getLevelGroup = getLevelGroup;
@@ -6,7 +6,6 @@
6
6
  ===========================================================================
7
7
  **/
8
8
 
9
- const path = require('path');
10
9
  const fs = require('fs');
11
10
  const Constants = require('../Constants');
12
11
  const TreeBuilder = require('../isml_tree/TreeBuilder');
@@ -38,7 +37,7 @@ const checkCustomTag = tag => {
38
37
  }
39
38
  };
40
39
 
41
- const applyRuleResult = (config, ruleResult, templatePath, templateResults, rule) => {
40
+ const fixTemplateOrReportIssues = (config, ruleResult, templatePath, templateResults, rule) => {
42
41
  if (config.autoFix && ruleResult.fixedContent) {
43
42
  fs.writeFileSync(templatePath, ruleResult.fixedContent);
44
43
  templateResults.fixed = true;
@@ -49,7 +48,7 @@ const applyRuleResult = (config, ruleResult, templatePath, templateResults, rule
49
48
  }
50
49
  };
51
50
 
52
- const applyRuleOnTemplate = (ruleArray, templatePath, root, config) => {
51
+ const fixTemplateOrReportIssuesForRuleList = (ruleArray, templatePath, root, config) => {
53
52
  const templateResults = {
54
53
  fixed : false,
55
54
  errors : {}
@@ -59,7 +58,7 @@ const applyRuleOnTemplate = (ruleArray, templatePath, root, config) => {
59
58
  const rule = ruleArray[i];
60
59
  if (!rule.shouldIgnore(templatePath)) {
61
60
  const ruleResults = rule.check(root, templateResults.data);
62
- applyRuleResult(config, ruleResults, templatePath, templateResults, rule);
61
+ fixTemplateOrReportIssues(config, ruleResults, templatePath, templateResults, rule);
63
62
  }
64
63
  }
65
64
 
@@ -130,7 +129,7 @@ const checkFileName = (filename, templateContent) => {
130
129
  return templateResults;
131
130
  };
132
131
 
133
- const checkTreeRules = (templatePath, templateContent, config) => {
132
+ const checkAndPossiblyFixTreeRules = (templatePath, templateContent, config) => {
134
133
  if (!config.disableTreeParse) {
135
134
  const tree = TreeBuilder.build(templatePath, templateContent);
136
135
 
@@ -140,7 +139,7 @@ const checkTreeRules = (templatePath, templateContent, config) => {
140
139
 
141
140
  const ruleArray = getEnabledTreeRules();
142
141
 
143
- return applyRuleOnTemplate(
142
+ return fixTemplateOrReportIssuesForRuleList(
144
143
  ruleArray,
145
144
  templatePath,
146
145
  tree.rootNode,
@@ -148,10 +147,10 @@ const checkTreeRules = (templatePath, templateContent, config) => {
148
147
  }
149
148
  };
150
149
 
151
- const checkLineByLineRules = (templatePath, templateContent, config) => {
150
+ const checkAndPossiblyFixLineByLineRules = (templatePath, templateContent, config) => {
152
151
  const ruleArray = getEnabledLineRules();
153
152
 
154
- return applyRuleOnTemplate(
153
+ return fixTemplateOrReportIssuesForRuleList(
155
154
  ruleArray,
156
155
  templatePath,
157
156
  templateContent,
@@ -176,11 +175,11 @@ const checkCustomModules = () => {
176
175
  return moduleResults;
177
176
  };
178
177
 
179
- const checkTemplate = (templatePath, content, templateName) => {
178
+ const parseAndPossiblyFixTemplate = (templatePath, content, templateName) => {
180
179
  const config = ConfigUtils.load();
181
180
  const templateContent = content || fs.readFileSync(templatePath, 'utf-8');
182
- const lineResults = checkLineByLineRules(templatePath, templateContent, config);
183
- const treeResults = checkTreeRules(templatePath, templateContent, config) || { errors : [] };
181
+ const lineResults = checkAndPossiblyFixLineByLineRules(templatePath, templateContent, config);
182
+ const treeResults = checkAndPossiblyFixTreeRules(templatePath, templateContent, config) || { errors : [] };
184
183
  const filenameResults = checkFileName(templateName, templateContent);
185
184
 
186
185
  return {
@@ -227,6 +226,6 @@ const getEnabledTreeRules = () => {
227
226
  module.exports.getAllLineRules = () => lineByLineRules;
228
227
  module.exports.findNodeOfType = findNodeOfType;
229
228
  module.exports.isTypeAmongTheFirstElements = isTypeAmongTheFirstElements;
230
- module.exports.checkTemplate = checkTemplate;
229
+ module.exports.parseAndPossiblyFixTemplate = parseAndPossiblyFixTemplate;
231
230
  module.exports.checkCustomModules = checkCustomModules;
232
231
  module.exports.getAvailableRulesQty = getAvailableRulesQty;