@masknet/eslint-plugin
Version:
eslint plugin for masknet
121 lines • 4 kB
JavaScript
import { createRule } from "../../rule.js";
// https://unicode.org/reports/tr18
// https://unicode.org/reports/tr51
const BUILT_PATTERN = /\P{ASCII}/u;
export default createRule({
name: 'unicode/specific-set',
meta: {
type: 'problem',
fixable: 'code',
docs: {
description: 'Limit the range of literal characters',
},
schema: [
{
type: 'object',
properties: {
pattern: { type: 'string' },
flags: { type: 'string' },
only: { type: 'string', enum: ['code', 'comment'] },
},
additionalProperties: false,
},
],
messages: {
illegal: 'Illegal character detected',
},
},
defaultOptions: [{ pattern: BUILT_PATTERN.source, flags: 'u', only: undefined }],
create(context, [{ pattern, flags, only }]) {
const regex = pattern ? new RegExp(pattern, flags) : BUILT_PATTERN;
return makeProgramListener(regex, (node, kind) => {
if (only && only !== kind)
return;
const matcher = new RegExp(regex.source, 'gu');
const fix = getFixer(node, matcher);
context.report({ node, messageId: 'illegal', fix });
});
},
});
export function makeProgramListener(pattern, onReport) {
return {
Program(program) {
for (const token of program.tokens ?? []) {
const value = getValue(token);
if (value === false)
continue;
if (!pattern.test(value))
continue;
onReport(token, 'code');
}
for (const comment of program.comments ?? []) {
if (!pattern.test(comment.value))
continue;
onReport(comment, 'comment');
}
},
};
}
export function getFixer(token, pattern) {
switch (token.type) {
case 'String':
case 'Template': {
return (fixer) => {
const prefix = token.value.slice(0, 1);
const suffix = token.value.slice(-1);
const modified = escape(token.value.slice(1, -1), pattern);
return fixer.replaceText(token, `${prefix}${modified}${suffix}`);
};
}
case 'JSXText': {
return (fixer) => {
const modified = token.value.replace(pattern, (match) => `&#x${toString(match.codePointAt(0))};`);
return fixer.replaceText(token, modified);
};
}
case 'RegularExpression': {
return (fixer) => {
const flags = new Set(token.regex.flags);
flags.add('u');
const re = new RegExp(escape(token.regex.pattern, pattern), [...flags].join(''));
return fixer.replaceText(token, re.toString());
};
}
}
return;
}
export function escape(input, pattern) {
return input.replace(pattern, (match) => {
const point = match.codePointAt(0);
return point > 65_535 ? `\\u{${toString(point)}}` : `\\u${toString(point)}`;
});
}
function toString(point) {
return point.toString(16).padStart(4, '0').toUpperCase();
}
function getValue(token) {
switch (token.type) {
case 'String':
case 'Template': {
return token.value.slice(1, -1);
}
case 'Identifier': {
if (token.value.startsWith('#')) {
return token.value.slice(1);
}
return token.value;
}
case 'PrivateIdentifier': {
return token.value;
}
case 'RegularExpression': {
return token.regex.pattern;
}
case 'JSXText':
case 'JSXIdentifier': {
return token.value;
}
}
return false;
}
//# sourceMappingURL=specific-set.js.map