Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 29 additions & 38 deletions src/rules/no-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
* @author Nicholas C. Zakas
*/

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import { findOffsets } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
Expand All @@ -25,7 +19,7 @@ import { findOffsets } from "../util.js";
//-----------------------------------------------------------------------------

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

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -74,48 +68,45 @@ export default {
},

create(context) {
const { sourceCode } = context;
const [{ allowed, allowedIgnoreCase }] = context.options;
const allowedElements = new Set(
allowedIgnoreCase ? allowed.map(tag => tag.toLowerCase()) : allowed,
);

/**
* Normalize a tag name based on the `allowedIgnoreCase` option.
* @param {string} tagName The tag name to normalize.
* @returns {string} The normalized tag name.
*/
function normalizeTagName(tagName) {
return allowedIgnoreCase ? tagName.toLowerCase() : tagName;
}

const allowedElements = new Set(allowed.map(normalizeTagName));

return {
html(node) {
/** @type {RegExpExecArray} */
let match;

while ((match = htmlTagPattern.exec(node.value)) !== null) {
const fullMatch = match[0];
const tagName = match[1];
const { lineOffset, columnOffset } = findOffsets(
node.value,
match.index,
);
const start = {
line: node.position.start.line + lineOffset,
column: node.position.start.column + columnOffset,
};

const { tagName } = match.groups;
const firstNewlineIndex =
fullMatch.search(lineEndingPattern);
const endColumn =
firstNewlineIndex === -1
? start.column + fullMatch.length
: start.column + firstNewlineIndex;

const end = {
line: start.line,
column: endColumn,
};

const tagToCheck = allowedIgnoreCase
? tagName.toLowerCase()
: tagName;
if (
allowedElements.size === 0 ||
!allowedElements.has(tagToCheck)
) {

const startOffset = // Adjust `htmlTagPattern` match index to the full source code.
match.index + node.position.start.offset;
const endOffset =
startOffset +
(firstNewlineIndex === -1
? fullMatch.length
: firstNewlineIndex);

if (!allowedElements.has(normalizeTagName(tagName))) {
context.report({
loc: { start, end },
loc: {
start: sourceCode.getLocFromIndex(startOffset),
end: sourceCode.getLocFromIndex(endOffset),
},
messageId: "disallowedElement",
data: {
name: tagName,
Expand Down