forked from ChromeDevTools/devtools-frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlit-html-no-attribute-quotes.js
92 lines (84 loc) · 3.83 KB
/
lit-html-no-attribute-quotes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
const {isLitHtmlTemplateCall} = require('./utils.js');
function templateElementPartStartsWithDoubleQuote(templateElementPartNode) {
return templateElementPartNode.value.raw.startsWith('"');
}
function templateElementPartEndsWithEqualsDoubleQuote(templateElementPartNode) {
return templateElementPartNode.value.raw.endsWith('="');
}
function removeQuotesFromAttribute({fixer, firstPart, secondPart}) {
const [, rangeOfOpeningTemplatePartEnd] = firstPart.range;
// From the first part, we need to remove the last character, which is the double quote.
// We can do this by fetching the range of the node (range = start and end position on the line)
// However, for the template part with the opening quote, the range will also contain the ${ of the interpolation:
// <p class="${
// ^^^^^^^^^^^^ this is the text covered by the [start, end] range.
// So what we need to do is remove the quote, and leave the last two characters alone.
// Therefore we remove the third character back from the end, and only remove a single character, leaving the ${ part alone.
const startingQuoteRangeToRemove = [rangeOfOpeningTemplatePartEnd - 3, rangeOfOpeningTemplatePartEnd - 2];
const [rangeOfClosingTemplatePartStart] = secondPart.range;
// It's a similar story for the second part where the range includes the }:
// }">foo</p>
// ^^^^^^^^^^ this is the range
// So therefore we get the start of the range, and add one to it, to dodge the } character, and then remove only the quote.
const endingQuoteRangeToRemove = [rangeOfClosingTemplatePartStart + 1, rangeOfClosingTemplatePartStart + 2];
return [fixer.removeRange(startingQuoteRangeToRemove), fixer.removeRange(endingQuoteRangeToRemove)];
}
/**
* @type {import('eslint').Rule.RuleModule}
*/
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'ensure no extra quotes around attributes when the value is interpolated',
category: 'Possible Errors',
},
fixable: 'code',
messages: {
attributeQuotesNotRequired:
'When interpolating a value as an attribute in LitHtml you do not need double quotes around it.',
},
schema: [] // no options
},
create: function(context) {
return {
TaggedTemplateExpression(node) {
if (!isLitHtmlTemplateCall(node)) {
return;
}
// quasis here = the static parts of a template expression
// expressions = the dynamic parts of a template expression
// For example, given: <p class="${foo}"> we will have:
// quasis: ['<p class="', '">']
// expressions: ['foo'] (it's actually an AST node representing ${foo})
// So what we do is walk through and look for quasi pairs where:
// 1. the first ends with ="
// 2. the second begins with "
// We can then be confident that we have found an attribute with quotes around it.
node.quasi.quasis.forEach((templateElement, index) => {
if (templateElementPartEndsWithEqualsDoubleQuote(templateElement)) {
const nextElement = node.quasi.quasis[index + 1];
if (nextElement && templateElementPartStartsWithDoubleQuote(nextElement)) {
const expressionBetweenTheParts = node.quasi.expressions[index];
context.report({
node: expressionBetweenTheParts,
messageId: 'attributeQuotesNotRequired',
fix(fixer) {
return removeQuotesFromAttribute({
fixer,
firstPart: templateElement,
secondPart: nextElement,
});
}
});
}
}
});
},
};
}
};