129 lines
2.6 KiB
JavaScript
129 lines
2.6 KiB
JavaScript
import {
|
|
isCallExpression,
|
|
isStringLiteral,
|
|
} from './ast/index.js';
|
|
|
|
const MESSAGE_ID_ERROR = 'prefer-bigint-literals/error';
|
|
const MESSAGE_ID_SUGGESTION = 'prefer-bigint-literals/suggestion';
|
|
const messages = {
|
|
[MESSAGE_ID_ERROR]: 'Prefer using bigint literal over `BigInt(…)`.',
|
|
[MESSAGE_ID_SUGGESTION]: 'Replace with {{replacement}}.',
|
|
};
|
|
|
|
const canUseNumericLiteralRaw = numericLiteral => {
|
|
const raw = numericLiteral.raw.replaceAll('_', '').toLowerCase();
|
|
|
|
if (raw.includes('.')) {
|
|
return false;
|
|
}
|
|
|
|
const {value} = numericLiteral;
|
|
|
|
for (const {prefix, base} of [
|
|
{prefix: '0b', base: 2},
|
|
{prefix: '0o', base: 8},
|
|
{prefix: '0x', base: 16},
|
|
]) {
|
|
if (raw.startsWith(prefix)) {
|
|
return raw.slice(2) === value.toString(base);
|
|
}
|
|
}
|
|
|
|
if (raw.includes('e')) {
|
|
return false;
|
|
}
|
|
|
|
return raw === String(value);
|
|
};
|
|
|
|
function getReplacement(valueNode) {
|
|
if (isStringLiteral(valueNode)) {
|
|
const raw = valueNode.raw.slice(1, -1);
|
|
try {
|
|
BigInt(raw);
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
return {shouldUseSuggestion: false, text: `${raw.trimEnd()}n`};
|
|
}
|
|
|
|
const {value, raw} = valueNode;
|
|
|
|
if (!Number.isInteger(value)) {
|
|
return;
|
|
}
|
|
|
|
let bigint;
|
|
try {
|
|
bigint = BigInt(value);
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
const shouldUseSuggestion = !canUseNumericLiteralRaw(valueNode);
|
|
const text = shouldUseSuggestion ? `${bigint}n` : `${raw}n`;
|
|
return {shouldUseSuggestion, text};
|
|
}
|
|
|
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
const create = context => ({
|
|
CallExpression(callExpression) {
|
|
if (!isCallExpression(callExpression, {
|
|
name: 'BigInt',
|
|
argumentsLength: 1,
|
|
optional: false,
|
|
})) {
|
|
return;
|
|
}
|
|
|
|
const [valueNode] = callExpression.arguments;
|
|
const replacement = getReplacement(valueNode);
|
|
if (!replacement) {
|
|
return;
|
|
}
|
|
|
|
const problem = {
|
|
node: callExpression,
|
|
messageId: MESSAGE_ID_ERROR,
|
|
};
|
|
|
|
const {shouldUseSuggestion, text} = replacement;
|
|
|
|
/** @param {import('eslint').Rule.RuleFixer} fixer */
|
|
const fix = fixer => fixer.replaceText(callExpression, text);
|
|
|
|
if (shouldUseSuggestion || context.sourceCode.getCommentsInside(callExpression).length > 0) {
|
|
problem.suggest = [
|
|
{
|
|
messageId: MESSAGE_ID_SUGGESTION,
|
|
data: {
|
|
replacement: text.length < 20 ? `\`${text}\`` : 'a bigint literal',
|
|
},
|
|
fix,
|
|
},
|
|
];
|
|
} else {
|
|
problem.fix = fix;
|
|
}
|
|
|
|
return problem;
|
|
},
|
|
});
|
|
|
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
const config = {
|
|
create,
|
|
meta: {
|
|
type: 'suggestion',
|
|
docs: {
|
|
description: 'Prefer `BigInt` literals over the constructor.',
|
|
recommended: 'unopinionated',
|
|
},
|
|
fixable: 'code',
|
|
hasSuggestions: true,
|
|
messages,
|
|
},
|
|
};
|
|
|
|
export default config;
|