123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- /**
- * @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')
- },
- messages: {
- unescapedEntity: 'HTML entity, `{{entity}}` , must be escaped.',
- unescapedEntityAlts: '`{{entity}}` can be escaped with {{alts}}.'
- },
- 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({
- node,
- loc: {line: i, column: start + index},
- messageId: 'unescapedEntity',
- data: {
- entity: entities[j]
- }
- });
- }
- } else if (c === entities[j].char) {
- context.report({
- node,
- loc: {line: i, column: start + index},
- messageId: 'unescapedEntityAlts',
- data: {
- entity: entities[j].char,
- alts: entities[j].alternatives.map((alt) => `\`${alt}\``).join(', ')
- }
- });
- }
- }
- }
- }
- }
- return {
- 'Literal, JSXText'(node) {
- if (jsxUtil.isJSX(node.parent)) {
- reportInvalidEntity(node);
- }
- }
- };
- }
- };
|