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);
- }
- },
- };
- },
- };
|