securityos/node_modules/eslint-plugin-unicorn/rules/string-content.js

187 lines
3.4 KiB
JavaScript
Raw Permalink Normal View History

2024-09-06 15:32:35 +00:00
'use strict';
const escapeString = require('./utils/escape-string.js');
const escapeTemplateElementRaw = require('./utils/escape-template-element-raw.js');
const {replaceTemplateElement} = require('./fix/index.js');
const defaultMessage = 'Prefer `{{suggest}}` over `{{match}}`.';
const SUGGESTION_MESSAGE_ID = 'replace';
const messages = {
[SUGGESTION_MESSAGE_ID]: 'Replace `{{match}}` with `{{suggest}}`.',
};
const ignoredIdentifier = new Set([
'gql',
'html',
'svg',
]);
const ignoredMemberExpressionObject = new Set([
'styled',
]);
const isIgnoredTag = node => {
if (!node.parent || !node.parent.parent || !node.parent.parent.tag) {
return false;
}
const {tag} = node.parent.parent;
if (tag.type === 'Identifier' && ignoredIdentifier.has(tag.name)) {
return true;
}
if (tag.type === 'MemberExpression') {
const {object} = tag;
if (
object.type === 'Identifier'
&& ignoredMemberExpressionObject.has(object.name)
) {
return true;
}
}
return false;
};
function getReplacements(patterns) {
return Object.entries(patterns)
.map(([match, options]) => {
if (typeof options === 'string') {
options = {
suggest: options,
};
}
return {
match,
regex: new RegExp(match, 'gu'),
fix: true,
...options,
};
});
}
/** @param {import('eslint').Rule.RuleContext} context */
const create = context => {
const {patterns} = {
patterns: {},
...context.options[0],
};
const replacements = getReplacements(patterns);
if (replacements.length === 0) {
return;
}
return {
'Literal, TemplateElement'(node) {
const {type, value, raw} = node;
let string;
if (type === 'Literal') {
string = value;
} else if (!isIgnoredTag(node)) {
string = value.raw;
}
if (!string || typeof string !== 'string') {
return;
}
const replacement = replacements.find(({regex}) => regex.test(string));
if (!replacement) {
return;
}
const {fix: autoFix, message = defaultMessage, match, suggest, regex} = replacement;
const problem = {
node,
message,
data: {
match,
suggest,
},
};
const fixed = string.replace(regex, suggest);
const fix = type === 'Literal'
? fixer => fixer.replaceText(
node,
escapeString(fixed, raw[0]),
)
: fixer => replaceTemplateElement(
fixer,
node,
escapeTemplateElementRaw(fixed),
);
if (autoFix) {
problem.fix = fix;
} else {
problem.suggest = [
{
messageId: SUGGESTION_MESSAGE_ID,
fix,
},
];
}
return problem;
},
};
};
const schema = [
{
type: 'object',
additionalProperties: false,
properties: {
patterns: {
type: 'object',
additionalProperties: {
anyOf: [
{
type: 'string',
},
{
type: 'object',
required: [
'suggest',
],
properties: {
suggest: {
type: 'string',
},
fix: {
type: 'boolean',
// Default: true
},
message: {
type: 'string',
// Default: ''
},
},
additionalProperties: false,
},
],
}},
},
},
];
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
description: 'Enforce better string content.',
},
fixable: 'code',
hasSuggestions: true,
schema,
messages,
},
};