eslint-plugin-lit 1.9.0 → 1.9.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.
Files changed (49) hide show
  1. package/lib/configs/all.d.ts +27 -0
  2. package/lib/configs/all.js +29 -0
  3. package/lib/configs/recommended.d.ts +12 -0
  4. package/lib/configs/recommended.js +14 -0
  5. package/lib/rules/attribute-value-entities.d.ts +7 -0
  6. package/lib/rules/attribute-value-entities.js +80 -0
  7. package/lib/rules/ban-attributes.d.ts +7 -0
  8. package/lib/rules/ban-attributes.js +64 -0
  9. package/lib/rules/binding-positions.d.ts +7 -0
  10. package/lib/rules/binding-positions.js +86 -0
  11. package/lib/rules/lifecycle-super.d.ts +7 -0
  12. package/lib/rules/lifecycle-super.js +118 -0
  13. package/lib/rules/no-duplicate-template-bindings.d.ts +7 -0
  14. package/lib/rules/no-duplicate-template-bindings.js +51 -0
  15. package/lib/rules/no-invalid-escape-sequences.d.ts +7 -0
  16. package/lib/rules/no-invalid-escape-sequences.js +54 -0
  17. package/lib/rules/no-invalid-html.d.ts +7 -0
  18. package/lib/rules/no-invalid-html.js +55 -0
  19. package/lib/rules/no-legacy-imports.d.ts +7 -0
  20. package/lib/rules/no-legacy-imports.js +91 -0
  21. package/lib/rules/no-legacy-template-syntax.d.ts +7 -0
  22. package/lib/rules/no-legacy-template-syntax.js +76 -0
  23. package/lib/rules/no-native-attributes.d.ts +7 -0
  24. package/lib/rules/no-native-attributes.js +79 -0
  25. package/lib/rules/no-private-properties.d.ts +7 -0
  26. package/lib/rules/no-private-properties.js +81 -0
  27. package/lib/rules/no-property-change-update.d.ts +7 -0
  28. package/lib/rules/no-property-change-update.js +134 -0
  29. package/lib/rules/no-template-arrow.d.ts +7 -0
  30. package/lib/rules/no-template-arrow.js +68 -0
  31. package/lib/rules/no-template-bind.d.ts +7 -0
  32. package/lib/rules/no-template-bind.js +70 -0
  33. package/lib/rules/no-template-map.d.ts +7 -0
  34. package/lib/rules/no-template-map.js +51 -0
  35. package/lib/rules/no-this-assign-in-render.d.ts +7 -0
  36. package/lib/rules/no-this-assign-in-render.js +98 -0
  37. package/lib/rules/no-useless-template-literals.d.ts +7 -0
  38. package/lib/rules/no-useless-template-literals.js +87 -0
  39. package/lib/rules/no-value-attribute.d.ts +7 -0
  40. package/lib/rules/no-value-attribute.js +64 -0
  41. package/lib/rules/prefer-nothing.d.ts +7 -0
  42. package/lib/rules/prefer-nothing.js +51 -0
  43. package/lib/rules/prefer-static-styles.d.ts +7 -0
  44. package/lib/rules/prefer-static-styles.js +93 -0
  45. package/lib/rules/quoted-expressions.d.ts +7 -0
  46. package/lib/rules/quoted-expressions.js +98 -0
  47. package/lib/rules/value-after-constraints.d.ts +7 -0
  48. package/lib/rules/value-after-constraints.js +82 -0
  49. package/package.json +3 -2
@@ -0,0 +1,27 @@
1
+ declare const config: {
2
+ plugins: string[];
3
+ rules: {
4
+ 'lit/attribute-value-entities': string;
5
+ 'lit/binding-positions': string;
6
+ 'lit/lifecycle-super': string;
7
+ 'lit/no-duplicate-template-bindings': string;
8
+ 'lit/no-invalid-escape-sequences': string;
9
+ 'lit/no-invalid-html': string;
10
+ 'lit/no-legacy-imports': string;
11
+ 'lit/no-legacy-template-syntax': string;
12
+ 'lit/no-native-attributes': string;
13
+ 'lit/no-private-properties': string;
14
+ 'lit/no-property-change-update': string;
15
+ 'lit/no-template-arrow': string;
16
+ 'lit/no-template-bind': string;
17
+ 'lit/no-template-map': string;
18
+ 'lit/no-this-assign-in-render': string;
19
+ 'lit/no-useless-template-literals': string;
20
+ 'lit/no-value-attribute': string;
21
+ 'lit/prefer-nothing': string;
22
+ 'lit/prefer-static-styles': string;
23
+ 'lit/quoted-expressions': string;
24
+ 'lit/value-after-constraints': string;
25
+ };
26
+ };
27
+ export default config;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config = {
4
+ plugins: ['lit'],
5
+ rules: {
6
+ 'lit/attribute-value-entities': 'error',
7
+ 'lit/binding-positions': 'error',
8
+ 'lit/lifecycle-super': 'error',
9
+ 'lit/no-duplicate-template-bindings': 'error',
10
+ 'lit/no-invalid-escape-sequences': 'error',
11
+ 'lit/no-invalid-html': 'error',
12
+ 'lit/no-legacy-imports': 'error',
13
+ 'lit/no-legacy-template-syntax': 'error',
14
+ 'lit/no-native-attributes': 'error',
15
+ 'lit/no-private-properties': 'error',
16
+ 'lit/no-property-change-update': 'error',
17
+ 'lit/no-template-arrow': 'error',
18
+ 'lit/no-template-bind': 'error',
19
+ 'lit/no-template-map': 'error',
20
+ 'lit/no-this-assign-in-render': 'error',
21
+ 'lit/no-useless-template-literals': 'error',
22
+ 'lit/no-value-attribute': 'error',
23
+ 'lit/prefer-nothing': 'error',
24
+ 'lit/prefer-static-styles': 'error',
25
+ 'lit/quoted-expressions': 'error',
26
+ 'lit/value-after-constraints': 'error'
27
+ }
28
+ };
29
+ exports.default = config;
@@ -0,0 +1,12 @@
1
+ declare const config: {
2
+ plugins: string[];
3
+ rules: {
4
+ 'lit/attribute-value-entities': string;
5
+ 'lit/binding-positions': string;
6
+ 'lit/no-duplicate-template-bindings': string;
7
+ 'lit/no-invalid-html': string;
8
+ 'lit/no-legacy-template-syntax': string;
9
+ 'lit/no-property-change-update': string;
10
+ };
11
+ };
12
+ export default config;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config = {
4
+ plugins: ['lit'],
5
+ rules: {
6
+ 'lit/attribute-value-entities': 'error',
7
+ 'lit/binding-positions': 'error',
8
+ 'lit/no-duplicate-template-bindings': 'error',
9
+ 'lit/no-invalid-html': 'error',
10
+ 'lit/no-legacy-template-syntax': 'error',
11
+ 'lit/no-property-change-update': 'error'
12
+ }
13
+ };
14
+ exports.default = config;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows unencoded HTML entities in attribute values
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows unencoded HTML entities in attribute values
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ const template_analyzer_1 = require("../template-analyzer");
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+ const rule = {
11
+ meta: {
12
+ docs: {
13
+ description: 'Disallows unencoded HTML entities in attribute values',
14
+ recommended: false,
15
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/attribute-value-entities.md'
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ unencoded: 'Attribute values may not contain unencoded HTML ' +
20
+ 'entities, e.g. use `&gt;` instead of `>`',
21
+ doubleQuotes: 'Attributes delimited by double quotes may not contain ' +
22
+ 'unencoded double quotes (e.g. `attr="bad"quote"`)',
23
+ singleQuotes: 'Attributes delimited by single quotes may not contain ' +
24
+ "unencoded single quotes (e.g. `attr='bad'quote'`)"
25
+ }
26
+ },
27
+ create(context) {
28
+ const source = context.getSourceCode();
29
+ const disallowedPattern = /([<>]|&(?!(#\d+|[a-z]+);))/;
30
+ //----------------------------------------------------------------------
31
+ // Helpers
32
+ //----------------------------------------------------------------------
33
+ //----------------------------------------------------------------------
34
+ // Public
35
+ //----------------------------------------------------------------------
36
+ return {
37
+ TaggedTemplateExpression: (node) => {
38
+ if (node.type === 'TaggedTemplateExpression' &&
39
+ node.tag.type === 'Identifier' &&
40
+ node.tag.name === 'html') {
41
+ const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
42
+ analyzer.traverse({
43
+ enterElement: (element) => {
44
+ var _a, _b, _c, _d;
45
+ // eslint-disable-next-line guard-for-in
46
+ for (const attr in element.attribs) {
47
+ const loc = analyzer.getLocationForAttribute(element, attr, source);
48
+ const rawValue = analyzer.getRawAttributeValue(element, attr);
49
+ if (!loc || !(rawValue === null || rawValue === void 0 ? void 0 : rawValue.value)) {
50
+ continue;
51
+ }
52
+ if (disallowedPattern.test(rawValue.value)) {
53
+ context.report({
54
+ loc: loc,
55
+ messageId: 'unencoded'
56
+ });
57
+ }
58
+ else if (((_a = rawValue.quotedValue) === null || _a === void 0 ? void 0 : _a.startsWith('"')) &&
59
+ ((_b = rawValue.value) === null || _b === void 0 ? void 0 : _b.includes('"'))) {
60
+ context.report({
61
+ loc: loc,
62
+ messageId: 'doubleQuotes'
63
+ });
64
+ }
65
+ else if (((_c = rawValue.quotedValue) === null || _c === void 0 ? void 0 : _c.startsWith("'")) &&
66
+ ((_d = rawValue.value) === null || _d === void 0 ? void 0 : _d.includes("'"))) {
67
+ context.report({
68
+ loc: loc,
69
+ messageId: 'singleQuotes'
70
+ });
71
+ }
72
+ }
73
+ }
74
+ });
75
+ }
76
+ }
77
+ };
78
+ }
79
+ };
80
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows a set of attributes from being used in templates
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows a set of attributes from being used in templates
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ const template_analyzer_1 = require("../template-analyzer");
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+ const rule = {
11
+ meta: {
12
+ docs: {
13
+ description: 'Disallows a set of attributes from being used in templates',
14
+ recommended: false,
15
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/ban-attributes.md'
16
+ },
17
+ schema: {
18
+ type: 'array',
19
+ items: {
20
+ type: 'string'
21
+ },
22
+ uniqueItems: true
23
+ },
24
+ messages: {
25
+ denied: 'The attribute "{{ attr }}" is not allowed in templates'
26
+ }
27
+ },
28
+ create(context) {
29
+ const source = context.getSourceCode();
30
+ const userDisallowedAttributes = context.options;
31
+ const disallowedAttributes = userDisallowedAttributes.map((attr) => attr.toLowerCase());
32
+ return {
33
+ TaggedTemplateExpression: (node) => {
34
+ if (node.type === 'TaggedTemplateExpression' &&
35
+ node.tag.type === 'Identifier' &&
36
+ node.tag.name === 'html') {
37
+ const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
38
+ analyzer.traverse({
39
+ enterElement: (element) => {
40
+ // eslint-disable-next-line guard-for-in
41
+ for (const attr in element.attribs) {
42
+ let attrNormalised = attr.toLowerCase();
43
+ if (attrNormalised.startsWith('?')) {
44
+ attrNormalised = attrNormalised.slice(1);
45
+ }
46
+ if (disallowedAttributes.includes(attrNormalised)) {
47
+ const loc = analyzer.getLocationForAttribute(element, attr, source);
48
+ if (loc) {
49
+ context.report({
50
+ loc: loc,
51
+ messageId: 'denied',
52
+ data: { attr: attrNormalised }
53
+ });
54
+ }
55
+ }
56
+ }
57
+ }
58
+ });
59
+ }
60
+ }
61
+ };
62
+ }
63
+ };
64
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows invalid binding positions in templates
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows invalid binding positions in templates
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ //------------------------------------------------------------------------------
7
+ // Rule Definition
8
+ //------------------------------------------------------------------------------
9
+ const rule = {
10
+ meta: {
11
+ docs: {
12
+ description: 'Disallows invalid binding positions in templates',
13
+ recommended: false,
14
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/binding-positions.md'
15
+ },
16
+ schema: [],
17
+ messages: {
18
+ noBindingTagName: 'Bindings cannot be used in place of tag names.',
19
+ noBindingAttributeName: 'Bindings cannot be used in place of attribute names.',
20
+ noBindingSelfClosingTag: 'Bindings at the end of a self-closing tag must be' +
21
+ ' followed by a space or quoted',
22
+ noBindingHTMLComment: 'Bindings cannot be used inside HTML comments.'
23
+ }
24
+ },
25
+ create(context) {
26
+ // variables should be defined here
27
+ const tagPattern = /<\/?$/;
28
+ const attrPattern = /^=/;
29
+ const selfClosingPattern = /^\/>/;
30
+ //----------------------------------------------------------------------
31
+ // Helpers
32
+ //----------------------------------------------------------------------
33
+ /**
34
+ * Determines if a given TemplateElement is contained within
35
+ * a HTML comment.
36
+ *
37
+ * @param {ESTree.TemplateElement=} expr Expression to test
38
+ * @return {boolean}
39
+ */
40
+ function isInsideComment(expr) {
41
+ return (expr !== undefined &&
42
+ expr.value.raw.lastIndexOf('<!--') > expr.value.raw.lastIndexOf('-->'));
43
+ }
44
+ //----------------------------------------------------------------------
45
+ // Public
46
+ //----------------------------------------------------------------------
47
+ return {
48
+ TaggedTemplateExpression: (node) => {
49
+ if (node.type === 'TaggedTemplateExpression' &&
50
+ node.tag.type === 'Identifier' &&
51
+ node.tag.name === 'html') {
52
+ for (let i = 0; i < node.quasi.expressions.length; i++) {
53
+ const expr = node.quasi.expressions[i];
54
+ const prev = node.quasi.quasis[i];
55
+ const next = node.quasi.quasis[i + 1];
56
+ if (tagPattern.test(prev.value.raw)) {
57
+ context.report({
58
+ node: expr,
59
+ messageId: 'noBindingTagName'
60
+ });
61
+ }
62
+ else if (next && attrPattern.test(next.value.raw)) {
63
+ context.report({
64
+ node: expr,
65
+ messageId: 'noBindingAttributeName'
66
+ });
67
+ }
68
+ else if (next && selfClosingPattern.test(next.value.raw)) {
69
+ context.report({
70
+ node: expr,
71
+ messageId: 'noBindingSelfClosingTag'
72
+ });
73
+ }
74
+ else if (isInsideComment(prev)) {
75
+ context.report({
76
+ node: expr,
77
+ messageId: 'noBindingHTMLComment'
78
+ });
79
+ }
80
+ }
81
+ }
82
+ }
83
+ };
84
+ }
85
+ };
86
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Enforces calling `super` in lifecycle methods
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Enforces calling `super` in lifecycle methods
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ const methodNames = ['connectedCallback', 'disconnectedCallback', 'update'];
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+ const rule = {
11
+ meta: {
12
+ docs: {
13
+ description: 'Enforces calling `super` in lifecycle methods',
14
+ recommended: false,
15
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/lifecycle-super.md'
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ callSuper: 'You must call `super.{{method}}` to avoid interrupting ' +
20
+ 'the lit rendering lifecycle'
21
+ }
22
+ },
23
+ create(context) {
24
+ // variables should be defined here
25
+ let inElement = false;
26
+ let currentMethod = null;
27
+ let superSeen = false;
28
+ //----------------------------------------------------------------------
29
+ // Helpers
30
+ //----------------------------------------------------------------------
31
+ /**
32
+ * Class entered
33
+ *
34
+ * @param {ESTree.Class} node Node entered
35
+ * @return {void}
36
+ */
37
+ function classEnter(node) {
38
+ if (!node.superClass ||
39
+ node.superClass.type !== 'Identifier' ||
40
+ node.superClass.name !== 'LitElement') {
41
+ return;
42
+ }
43
+ inElement = true;
44
+ }
45
+ /**
46
+ * Class exited
47
+ *
48
+ * @return {void}
49
+ */
50
+ function classExit() {
51
+ inElement = false;
52
+ }
53
+ /**
54
+ * Method entered
55
+ *
56
+ * @param {ESTree.MethodDefinition} node Node entered
57
+ * @return {void}
58
+ */
59
+ function methodEnter(node) {
60
+ if (!inElement ||
61
+ node.static === true ||
62
+ node.kind !== 'method' ||
63
+ node.key.type !== 'Identifier' ||
64
+ !methodNames.includes(node.key.name)) {
65
+ return;
66
+ }
67
+ currentMethod = node.key.name;
68
+ }
69
+ /**
70
+ * Method exited
71
+ *
72
+ * @param {ESTree.MethodDefinition} node Node entered
73
+ * @return {void}
74
+ */
75
+ function methodExit(node) {
76
+ if (currentMethod !== null && !superSeen) {
77
+ context.report({
78
+ node,
79
+ messageId: 'callSuper',
80
+ data: {
81
+ method: currentMethod
82
+ }
83
+ });
84
+ }
85
+ currentMethod = null;
86
+ superSeen = false;
87
+ }
88
+ /**
89
+ * Call expression entered
90
+ * @param {ESTree.CallExpression} node Node entered
91
+ * @return {void}
92
+ */
93
+ function callExpressionEnter(node) {
94
+ if (currentMethod === null) {
95
+ return;
96
+ }
97
+ if (node.callee.type === 'MemberExpression' &&
98
+ node.callee.object.type === 'Super' &&
99
+ node.callee.property.type === 'Identifier' &&
100
+ node.callee.property.name === currentMethod) {
101
+ superSeen = true;
102
+ }
103
+ }
104
+ //----------------------------------------------------------------------
105
+ // Public
106
+ //----------------------------------------------------------------------
107
+ return {
108
+ ClassExpression: (node) => classEnter(node),
109
+ ClassDeclaration: (node) => classEnter(node),
110
+ 'ClassExpression:exit': classExit,
111
+ 'ClassDeclaration:exit': classExit,
112
+ MethodDefinition: (node) => methodEnter(node),
113
+ 'MethodDefinition:exit': methodExit,
114
+ CallExpression: (node) => callExpressionEnter(node)
115
+ };
116
+ }
117
+ };
118
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows duplicate names in template bindings
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows duplicate names in template bindings
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ const template_analyzer_1 = require("../template-analyzer");
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+ const rule = {
11
+ meta: {
12
+ docs: {
13
+ description: 'Disallows duplicate names in template bindings',
14
+ recommended: false,
15
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-duplicate-template-bindings.md'
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ duplicateBinding: 'Duplicate bindings are not allowed.'
20
+ }
21
+ },
22
+ create(context) {
23
+ const source = context.getSourceCode();
24
+ //----------------------------------------------------------------------
25
+ // Helpers
26
+ //----------------------------------------------------------------------
27
+ //----------------------------------------------------------------------
28
+ // Public
29
+ //----------------------------------------------------------------------
30
+ return {
31
+ TaggedTemplateExpression: (node) => {
32
+ if (node.type === 'TaggedTemplateExpression' &&
33
+ node.tag.type === 'Identifier' &&
34
+ node.tag.name === 'html') {
35
+ const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
36
+ const dupeErrors = analyzer.errors.filter((err) => err.code === 'duplicate-attribute');
37
+ for (const err of dupeErrors) {
38
+ const loc = analyzer.resolveLocation(err, source);
39
+ if (loc) {
40
+ context.report({
41
+ loc: loc,
42
+ messageId: 'duplicateBinding'
43
+ });
44
+ }
45
+ }
46
+ }
47
+ }
48
+ };
49
+ }
50
+ };
51
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows invalid escape sequences in template strings
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows invalid escape sequences in template strings
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ //------------------------------------------------------------------------------
7
+ // Rule Definition
8
+ //------------------------------------------------------------------------------
9
+ const rule = {
10
+ meta: {
11
+ docs: {
12
+ description: 'Disallows invalid escape sequences in template strings',
13
+ recommended: false,
14
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-invalid-escape-sequences.md'
15
+ },
16
+ fixable: 'code',
17
+ schema: [],
18
+ messages: {
19
+ invalid: 'Some escape sequences are invalid in template strings. ' +
20
+ 'They should either be escaped again (e.g. "\\\\02c") or interpolated'
21
+ }
22
+ },
23
+ create(context) {
24
+ const source = context.getSourceCode();
25
+ const escapePattern = /(^|[^\\](?:\\\\)*)(\\([1-7][0-7]*|[0-7]{2,}))/g;
26
+ return {
27
+ TaggedTemplateExpression: (node) => {
28
+ if (node.type === 'TaggedTemplateExpression' &&
29
+ node.tag.type === 'Identifier' &&
30
+ node.tag.name === 'html') {
31
+ for (const quasi of node.quasi.quasis) {
32
+ if (quasi.range) {
33
+ const results = quasi.value.raw.matchAll(escapePattern);
34
+ for (const match of results) {
35
+ if (match.index !== undefined) {
36
+ const rangeStart = quasi.range[0] + 1 + match.index + match[1].length;
37
+ const rangeEnd = rangeStart + match[2].length;
38
+ const start = source.getLocFromIndex(rangeStart);
39
+ const end = source.getLocFromIndex(rangeEnd);
40
+ context.report({
41
+ loc: { start, end },
42
+ messageId: 'invalid',
43
+ fix: (fixer) => fixer.insertTextBeforeRange([rangeStart, rangeEnd], '\\')
44
+ });
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ };
52
+ }
53
+ };
54
+ module.exports = rule;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows invalid HTML in templates
3
+ * @author James Garbutt <https://github.com/43081j>
4
+ */
5
+ import { Rule } from 'eslint';
6
+ declare const rule: Rule.RuleModule;
7
+ export = rule;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Disallows invalid HTML in templates
4
+ * @author James Garbutt <https://github.com/43081j>
5
+ */
6
+ const template_analyzer_1 = require("../template-analyzer");
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+ const rule = {
11
+ meta: {
12
+ docs: {
13
+ description: 'Disallows invalid HTML in templates',
14
+ recommended: false,
15
+ url: 'https://github.com/43081j/eslint-plugin-lit/blob/master/docs/rules/no-invalid-html.md'
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ parseError: 'Template contained invalid HTML syntax, error was: {{ err }}'
20
+ }
21
+ },
22
+ create(context) {
23
+ const source = context.getSourceCode();
24
+ //----------------------------------------------------------------------
25
+ // Helpers
26
+ //----------------------------------------------------------------------
27
+ //----------------------------------------------------------------------
28
+ // Public
29
+ //----------------------------------------------------------------------
30
+ return {
31
+ TaggedTemplateExpression: (node) => {
32
+ if (node.type === 'TaggedTemplateExpression' &&
33
+ node.tag.type === 'Identifier' &&
34
+ node.tag.name === 'html') {
35
+ const analyzer = template_analyzer_1.TemplateAnalyzer.create(node);
36
+ for (const err of analyzer.errors) {
37
+ // Ignore these as the duplicate attributes rule handles them
38
+ if (err.code === 'duplicate-attribute') {
39
+ continue;
40
+ }
41
+ const loc = analyzer.resolveLocation(err, source);
42
+ if (loc) {
43
+ context.report({
44
+ loc: loc,
45
+ messageId: 'parseError',
46
+ data: { err: err.code }
47
+ });
48
+ }
49
+ }
50
+ }
51
+ }
52
+ };
53
+ }
54
+ };
55
+ module.exports = rule;