1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const docsUrl = require('../util/docsUrl');
|
9 | const jsxUtil = require('../util/jsx');
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | const DEFAULTS = [{
|
19 | char: '>',
|
20 | alternatives: ['>']
|
21 | }, {
|
22 | char: '"',
|
23 | alternatives: ['"', '“', '"', '”']
|
24 | }, {
|
25 | char: '\'',
|
26 | alternatives: [''', '‘', ''', '’']
|
27 | }, {
|
28 | char: '}',
|
29 | alternatives: ['}']
|
30 | }];
|
31 |
|
32 | module.exports = {
|
33 | meta: {
|
34 | docs: {
|
35 | description: 'Detect unescaped HTML entities, which might represent malformed tags',
|
36 | category: 'Possible Errors',
|
37 | recommended: true,
|
38 | url: docsUrl('no-unescaped-entities')
|
39 | },
|
40 | schema: [{
|
41 | type: 'object',
|
42 | properties: {
|
43 | forbid: {
|
44 | type: 'array',
|
45 | items: {
|
46 | oneOf: [{
|
47 | type: 'string'
|
48 | }, {
|
49 | type: 'object',
|
50 | properties: {
|
51 | char: {
|
52 | type: 'string'
|
53 | },
|
54 | alternatives: {
|
55 | type: 'array',
|
56 | uniqueItems: true,
|
57 | items: {
|
58 | type: 'string'
|
59 | }
|
60 | }
|
61 | }
|
62 | }]
|
63 | }
|
64 | }
|
65 | },
|
66 | additionalProperties: false
|
67 | }]
|
68 | },
|
69 |
|
70 | create(context) {
|
71 | function reportInvalidEntity(node) {
|
72 | const configuration = context.options[0] || {};
|
73 | const entities = configuration.forbid || DEFAULTS;
|
74 |
|
75 |
|
76 |
|
77 | for (let i = node.loc.start.line; i <= node.loc.end.line; i++) {
|
78 | let rawLine = context.getSourceCode().lines[i - 1];
|
79 | let start = 0;
|
80 | let end = rawLine.length;
|
81 | if (i === node.loc.start.line) {
|
82 | start = node.loc.start.column;
|
83 | }
|
84 | if (i === node.loc.end.line) {
|
85 | end = node.loc.end.column;
|
86 | }
|
87 | rawLine = rawLine.substring(start, end);
|
88 | for (let j = 0; j < entities.length; j++) {
|
89 | for (let index = 0; index < rawLine.length; index++) {
|
90 | const c = rawLine[index];
|
91 | if (typeof entities[j] === 'string') {
|
92 | if (c === entities[j]) {
|
93 | context.report({
|
94 | loc: {line: i, column: start + index},
|
95 | message: `HTML entity, \`${entities[j]}\` , must be escaped.`,
|
96 | node
|
97 | });
|
98 | }
|
99 | } else if (c === entities[j].char) {
|
100 | context.report({
|
101 | loc: {line: i, column: start + index},
|
102 | message: `\`${entities[j].char}\` can be escaped with ${entities[j].alternatives.map((alt) => `\`${alt}\``).join(', ')}.`,
|
103 | node
|
104 | });
|
105 | }
|
106 | }
|
107 | }
|
108 | }
|
109 | }
|
110 |
|
111 | return {
|
112 | 'Literal, JSXText'(node) {
|
113 | if (jsxUtil.isJSX(node.parent)) {
|
114 | reportInvalidEntity(node);
|
115 | }
|
116 | }
|
117 | };
|
118 | }
|
119 | };
|