eslint-plugin-playlyfe
Version:
custom eslint stuff for playlyfe
158 lines (122 loc) • 3.7 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _redent = _interopRequireDefault(require("redent"));
const Rule = {
meta: {
docs: {
description: 'Enforce correct indentation of multiline template strings',
category: '',
recommended: true
},
fixable: 'code',
schema: []
},
create(context) {
return {
TemplateLiteral(node) {
const sourceCode = context.getSourceCode();
const textWithTicks = sourceCode.getText(node);
if (!startsWithEmptyLine(textWithTicks)) {
return;
}
const parentIndent = getParentIndentation(node, sourceCode);
const contentIndent = getContentIndentation(node, sourceCode);
const expectedContentIndent = parentIndent + 2;
if (contentIndent !== expectedContentIndent || checkClosingTickLineEmptyAndNotIndented(textWithTicks, parentIndent)) {
context.report({
message: 'Not correctly indented',
node,
fix(fixer) {
const indentedText = redentText(textWithTicks, {
tick: parentIndent,
content: expectedContentIndent
});
return fixer.replaceText(node, indentedText);
}
});
}
}
};
}
};
module.exports = Rule;
function redentText(text, indentation) {
const content = text.substr(1, text.length - 2);
const indentedContent = (0, _redent.default)(content, indentation.content);
const lines = indentedContent.split('\n'); // add opening tick
lines[0] = `\`${lines[0]}`; // add closing tick
const lastLine = lines[lines.length - 1];
lines[lines.length - 1] = isEmptyLine(lastLine) ? (0, _redent.default)('`', indentation.tick) : `${lastLine}\``;
return lines.join('\n');
}
function checkClosingTickLineEmptyAndNotIndented(textWithTicks, expectedIndentation) {
const lines = textWithTicks.split('\n');
const lastLine = lines[lines.length - 1];
const isEmpty = lastLine.trim() === '`';
return isEmpty && findIndentation(lastLine) !== expectedIndentation;
} // NOTE: text will include '`'
// `testerr`
// `
// some multiline
// text
// `
function startsWithEmptyLine(text) {
const len = text.length; // starting with 1 (to ignore starting '`')
for (let i = 1; i < len; i += 1) {
const ch = text[i];
if (ch === '\n') {
return true;
}
if (!isWhitespace(ch)) {
return false;
}
}
return false;
}
function getParentIndentation(node, sourceCode) {
// For code: const ssss = someText(`Tester`)
// a: Tester`)
// b: const ssss = someText(`Tester`)
// c: const ssss = someText(`
const a = sourceCode.getText(node, -1);
const b = sourceCode.getText(node, 100);
const c = b.replace(a, '');
let indentation = 0;
let i = c.length - 1;
while (i > 0) {
const ch = c[i];
if (ch === '\n') {
break;
}
if (isWhitespace(ch)) {
indentation += 1;
} else {
indentation = 0;
}
i -= 1;
}
return indentation;
}
function getContentIndentation(node, sourceCode) {
// content is text without ticks
const content = sourceCode.getText(node, -1, -1);
return findIndentation(content);
} // Will return indentation of first non-empty line
function findIndentation(str) {
const lines = str.split('\n');
const nonEmptyLine = lines.find(line => !isEmptyLine(line));
if (!nonEmptyLine) {
return 0;
}
let ind = 0;
while (isWhitespace(nonEmptyLine[ind])) {
ind += 1;
}
return ind;
}
function isWhitespace(ch) {
return ch === ' ';
}
function isEmptyLine(line) {
return line.trim() === '';
}