Skip to content

Commit 5e6e94e

Browse files
authored
fix: improve no-html rule's tag location reporting (#491)
* fix: improve no-html rule's tag location reporting * use regex pattern instead of indexOf * correctly handle > in HTML attribute values * add test * simplify regex for detecting line endings
1 parent e4b8de4 commit 5e6e94e

File tree

2 files changed

+215
-2
lines changed

2 files changed

+215
-2
lines changed

src/rules/no-html.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import { findOffsets } from "../util.js";
2424
// Helpers
2525
//-----------------------------------------------------------------------------
2626

27-
const htmlTagPattern = /<([a-z0-9]+(?:-[a-z0-9]+)*)/giu;
27+
const htmlTagPattern =
28+
/<([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)?>/giu;
29+
const lineEndingPattern = /\r\n?|\n/u;
2830

2931
//-----------------------------------------------------------------------------
3032
// Rule Definition
@@ -82,6 +84,7 @@ export default {
8284
let match;
8385

8486
while ((match = htmlTagPattern.exec(node.value)) !== null) {
87+
const fullMatch = match[0];
8588
const tagName = match[1];
8689
const { lineOffset, columnOffset } = findOffsets(
8790
node.value,
@@ -91,9 +94,17 @@ export default {
9194
line: node.position.start.line + lineOffset,
9295
column: node.position.start.column + columnOffset,
9396
};
97+
98+
const firstNewlineIndex =
99+
fullMatch.search(lineEndingPattern);
100+
const endColumn =
101+
firstNewlineIndex === -1
102+
? start.column + fullMatch.length
103+
: start.column + firstNewlineIndex;
104+
94105
const end = {
95106
line: start.line,
96-
column: start.column + match[0].length + 1,
107+
column: endColumn,
97108
};
98109

99110
const tagToCheck = allowedIgnoreCase

tests/rules/no-html.test.js

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,207 @@ ruleTester.run("no-html", rule, {
222222
},
223223
],
224224
},
225+
{
226+
code: "<span >Content</span>",
227+
errors: [
228+
{
229+
messageId: "disallowedElement",
230+
line: 1,
231+
column: 1,
232+
endLine: 1,
233+
endColumn: 8,
234+
data: { name: "span" },
235+
},
236+
],
237+
},
238+
{
239+
code: '<div id="foo">Some content</div>',
240+
errors: [
241+
{
242+
messageId: "disallowedElement",
243+
line: 1,
244+
column: 1,
245+
endLine: 1,
246+
endColumn: 15,
247+
data: {
248+
name: "div",
249+
},
250+
},
251+
],
252+
},
253+
{
254+
code: '<p class="text-center" data-attr="value">Content</p>',
255+
errors: [
256+
{
257+
messageId: "disallowedElement",
258+
line: 1,
259+
column: 1,
260+
endLine: 1,
261+
endColumn: 42,
262+
data: {
263+
name: "p",
264+
},
265+
},
266+
],
267+
},
268+
{
269+
code: '<span\nclass="highlight"\ndata-id="123">Text</span>',
270+
errors: [
271+
{
272+
messageId: "disallowedElement",
273+
line: 1,
274+
column: 1,
275+
endLine: 1,
276+
endColumn: 6,
277+
data: {
278+
name: "span",
279+
},
280+
},
281+
],
282+
},
283+
{
284+
code: '<span\rclass="highlight"\rdata-id="123">Text</span>',
285+
errors: [
286+
{
287+
messageId: "disallowedElement",
288+
line: 1,
289+
column: 1,
290+
endLine: 1,
291+
endColumn: 6,
292+
data: {
293+
name: "span",
294+
},
295+
},
296+
],
297+
},
298+
{
299+
code: '<span\r\nclass="highlight"\r\ndata-id="123">Text</span>',
300+
errors: [
301+
{
302+
messageId: "disallowedElement",
303+
line: 1,
304+
column: 1,
305+
endLine: 1,
306+
endColumn: 6,
307+
data: {
308+
name: "span",
309+
},
310+
},
311+
],
312+
},
313+
{
314+
code: '<div class="test" >Content</div>',
315+
errors: [
316+
{
317+
messageId: "disallowedElement",
318+
line: 1,
319+
column: 1,
320+
endLine: 1,
321+
endColumn: 24,
322+
data: { name: "div" },
323+
},
324+
],
325+
},
326+
{
327+
code: '<input type="text" placeholder="Enter text" />',
328+
errors: [
329+
{
330+
messageId: "disallowedElement",
331+
line: 1,
332+
column: 1,
333+
endLine: 1,
334+
endColumn: 47,
335+
data: {
336+
name: "input",
337+
},
338+
},
339+
],
340+
},
341+
{
342+
code: '<a href="#"><span class="highlight">Link</span></a>',
343+
errors: [
344+
{
345+
messageId: "disallowedElement",
346+
line: 1,
347+
column: 1,
348+
endLine: 1,
349+
endColumn: 13,
350+
data: { name: "a" },
351+
},
352+
{
353+
messageId: "disallowedElement",
354+
line: 1,
355+
column: 13,
356+
endLine: 1,
357+
endColumn: 37,
358+
data: { name: "span" },
359+
},
360+
],
361+
},
362+
{
363+
code: '<input placeholder=">" />',
364+
errors: [
365+
{
366+
messageId: "disallowedElement",
367+
line: 1,
368+
column: 1,
369+
endLine: 1,
370+
endColumn: 26,
371+
data: {
372+
name: "input",
373+
},
374+
},
375+
],
376+
},
377+
{
378+
code: "<input placeholder='>'></input>",
379+
errors: [
380+
{
381+
messageId: "disallowedElement",
382+
line: 1,
383+
column: 1,
384+
endLine: 1,
385+
endColumn: 24,
386+
data: {
387+
name: "input",
388+
},
389+
},
390+
],
391+
},
392+
{
393+
code: '<input\nplaceholder=">" />',
394+
errors: [
395+
{
396+
messageId: "disallowedElement",
397+
line: 1,
398+
column: 1,
399+
endLine: 1,
400+
endColumn: 7,
401+
data: {
402+
name: "input",
403+
},
404+
},
405+
],
406+
},
407+
{
408+
code: dedent`
409+
<!-- comment -->
410+
<input
411+
placeholder="Enter name"
412+
name="First Name"
413+
/>`,
414+
errors: [
415+
{
416+
messageId: "disallowedElement",
417+
line: 2,
418+
column: 1,
419+
endLine: 2,
420+
endColumn: 7,
421+
data: {
422+
name: "input",
423+
},
424+
},
425+
],
426+
},
225427
],
226428
});

0 commit comments

Comments
 (0)