ember-template-lint
Version:
Linter for Ember or Handlebars templates.
153 lines (137 loc) • 3.53 kB
JavaScript
const Rule = require('./base');
const ERROR_MESSAGE = 'Excess whitespace in layout detected.';
const whitespaceCharacterList = new Set([
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
'  ',
'​',
'​',
'​',
'​',
'​',
'​',
'‌',
'‌',
'‍',
'‍',
'‎',
'‎',
'‏',
'‏',
' ',
' ',
'  ',
'⁠',
'⁠',
'⁡',
'⁡',
'⁡',
'⁢',
'⁢',
'⁢',
'⁣',
'⁣',
'⁣',
]);
function isWhitespace(char) {
return whitespaceCharacterList.has(char);
}
function splitTextByEntity(input) {
let result = [];
for (let i = 0; i < input.length; i++) {
let current = input[i];
if (current === '&') {
let possibleEndIndex = input.indexOf(';', i);
// this is a stand alone `&`
if (possibleEndIndex === -1) {
result.push(current);
}
// now we know we have an "entity like thing"
let possibleEntity = input.slice(i, possibleEndIndex + 1);
if (whitespaceCharacterList.has(possibleEntity)) {
result.push(possibleEntity);
i += possibleEntity.length - 1;
} else {
result.push(current);
}
} else {
result.push(current);
}
}
return result;
}
// The goal here is to catch alternating non-whitespace/whitespace
// characters, for example, in 'W e l c o m e'.
//
// So the final pattern boils down to this:
//
// (whitespace)(non-whitespace)(whitespace)(non-whitespace)(whitespace)
//
// Specifically using this "5 alternations" rule since any less than this
// will return false positives and any more than this should not be
// necessary in 99.99% of cases
module.exports = class NoWhitespaceWithinWord extends Rule {
visitor() {
return {
TextNode(node, path) {
let parents = [...path.parents()];
if (
parents.some((parent) => parent.node.type === 'AttrNode') ||
parents.some(
(parent) => parent.node.type === 'ElementNode' && parent.node.tag === 'style'
)
) {
return;
}
let alternationCount = 0;
let source = this.sourceForNode(node);
let characters = splitTextByEntity(source);
for (let i = 0; i < characters.length; i++) {
let currentChar = characters[i];
let previousChar = i > 0 ? characters[i - 1] : undefined;
if (
(isWhitespace(currentChar) && !isWhitespace(previousChar)) ||
(!isWhitespace(currentChar) && isWhitespace(previousChar))
) {
alternationCount++;
} else {
alternationCount = 0;
}
if (alternationCount >= 5) {
this.log({
message: ERROR_MESSAGE,
line: node.loc && node.loc.start.line,
column: node.loc && node.loc.start.column,
source,
});
// no need to keep parsing, we've already reported
return;
}
}
},
};
}
};
module.exports.ERROR_MESSAGE = ERROR_MESSAGE;
;