| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 | /** * @fileoverview HTML special characters should be escaped. * @author Patrick Hayes */'use strict';const docsUrl = require('../util/docsUrl');const jsxUtil = require('../util/jsx');const report = require('../util/report');// ------------------------------------------------------------------------------// 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: ['}'],}];const messages = {  unescapedEntity: 'HTML entity, `{{entity}}` , must be escaped.',  unescapedEntityAlts: '`{{entity}}` can be escaped with {{alts}}.',};module.exports = {  meta: {    docs: {      description: 'Disallow unescaped HTML entities from appearing in markup',      category: 'Possible Errors',      recommended: true,      url: docsUrl('no-unescaped-entities'),    },    messages,    schema: [{      type: 'object',      properties: {        forbid: {          type: 'array',          items: {            anyOf: [{              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 entities 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.slice(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]) {                report(context, messages.unescapedEntity, 'unescapedEntity', {                  node,                  loc: { line: i, column: start + index },                  data: {                    entity: entities[j],                  },                });              }            } else if (c === entities[j].char) {              report(context, messages.unescapedEntityAlts, 'unescapedEntityAlts', {                node,                loc: { line: i, column: start + index },                data: {                  entity: entities[j].char,                  alts: entities[j].alternatives.map((alt) => `\`${alt}\``).join(', '),                },              });            }          }        }      }    }    return {      'Literal, JSXText'(node) {        if (jsxUtil.isJSX(node.parent)) {          reportInvalidEntity(node);        }      },    };  },};
 |