/** * @fileoverview HTML special characters should be escaped. * @author Patrick Hayes */ 'use strict'; const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ // NOTE: '<' and '{' are also problematic characters, but they do not need // to be included here because it is a syntax error when these characters are // included accidentally. const DEFAULTS = [{ char: '>', alternatives: ['>'] }, { char: '"', alternatives: ['"', '“', '"', '”'] }, { char: '\'', alternatives: [''', '‘', ''', '’'] }, { char: '}', alternatives: ['}'] }]; module.exports = { meta: { docs: { description: 'Detect unescaped HTML entities, which might represent malformed tags', category: 'Possible Errors', recommended: true, url: docsUrl('no-unescaped-entities') }, schema: [{ type: 'object', properties: { forbid: { type: 'array', items: { oneOf: [{ type: 'string' }, { type: 'object', properties: { char: { type: 'string' }, alternatives: { type: 'array', uniqueItems: true, items: { type: 'string' } } } }] } } }, additionalProperties: false }] }, create(context) { function reportInvalidEntity(node) { const configuration = context.options[0] || {}; const entities = configuration.forbid || DEFAULTS; // HTML entites are already escaped in node.value (as well as node.raw), // so pull the raw text from context.getSourceCode() for (let i = node.loc.start.line; i <= node.loc.end.line; i++) { let rawLine = context.getSourceCode().lines[i - 1]; let start = 0; let end = rawLine.length; if (i === node.loc.start.line) { start = node.loc.start.column; } if (i === node.loc.end.line) { end = node.loc.end.column; } rawLine = rawLine.substring(start, end); for (let j = 0; j < entities.length; j++) { for (let index = 0; index < rawLine.length; index++) { const c = rawLine[index]; if (typeof entities[j] === 'string') { if (c === entities[j]) { context.report({ loc: {line: i, column: start + index}, message: `HTML entity, \`${entities[j]}\` , must be escaped.`, node }); } } else if (c === entities[j].char) { context.report({ loc: {line: i, column: start + index}, message: `\`${entities[j].char}\` can be escaped with ${entities[j].alternatives.map(alt => `\`${alt}\``).join(', ')}.`, node }); } } } } } return { 'Literal, JSXText': function (node) { if (jsxUtil.isJSX(node.parent)) { reportInvalidEntity(node); } } }; } };