Skip to content

Instantly share code, notes, and snippets.

@longzheng
Last active October 3, 2025 08:29
Show Gist options
  • Select an option

  • Save longzheng/eb08f2283379cbfd4b5f32c40f3614cd to your computer and use it in GitHub Desktop.

Select an option

Save longzheng/eb08f2283379cbfd4b5f32c40f3614cd to your computer and use it in GitHub Desktop.
ESLint plugin to fix BigNumber.js import with namespace pollution in TypeScript
import requireBigNumberPlugin from './eslint/requireBigNumberImport.js';
import { defineConfig } from "eslint/config";
export default defineConfig([
{
...
plugins: {
'require-big-number': requireBigNumberPlugin,
},
rules: {
'require-big-number/require-big-number-import': `error`,
},
...
},
]);
/**
* Due to https://github.com/MikeMcl/bignumber.js/pull/392, BigNumber is incorrectly globally available in all files
* This leads to runtime errors if BigNumber is used without importing it
* This ESLint rule ensures that BigNumber is imported from 'bignumber.js' whenever it is used
*/
const RULE_NAME = `require-big-number-import`;
function getInsertionFix(fixer, sourceCode) {
const importDeclarations = sourceCode.ast.body.filter((node) => node.type === `ImportDeclaration`);
const importStatement = `import { BigNumber } from 'bignumber.js';\n`;
if (importDeclarations.length === 0) {
return fixer.insertTextBeforeRange([0, 0], `${importStatement}\n`);
}
const lastImport = importDeclarations[importDeclarations.length - 1];
return fixer.insertTextAfter(lastImport, `\n${importStatement}`);
}
/** @type {import('eslint').Rule.RuleModule} */
const rule = {
meta: {
type: `problem`,
docs: {
description: `Require importing BigNumber from "bignumber.js" whenever it is used`,
},
schema: [],
messages: {
missingImport: `Import { BigNumber } from 'bignumber.js' when using BigNumber.`,
},
fixable: `code`,
},
create(context) {
const sourceCode = context.getSourceCode();
let hasBigNumberImport = false;
const bigNumberImportDeclarations = [];
const usageNodes = new Set();
return {
ImportDeclaration(node) {
if (node.source.value !== `bignumber.js`) {
return;
}
bigNumberImportDeclarations.push(node);
const hasNamedImport = node.specifiers.some((specifier) => {
if (specifier.type !== `ImportSpecifier`) {
return false;
}
if (specifier.imported.name !== `BigNumber`) {
return false;
}
if (specifier.importKind === `type` || node.importKind === `type`) {
return false;
}
return true;
});
if (hasNamedImport) {
hasBigNumberImport = true;
}
},
TSTypeReference(node) {
if (node.typeName.type === `Identifier` && node.typeName.name === `BigNumber`) {
usageNodes.add(node.typeName);
}
},
TSQualifiedName(node) {
if (node.left.type === `Identifier` && node.left.name === `BigNumber`) {
usageNodes.add(node.left);
}
},
ClassDeclaration(node) {
if (node.superClass && node.superClass.type === `Identifier` && node.superClass.name === `BigNumber`) {
usageNodes.add(node.superClass);
}
},
ClassExpression(node) {
if (node.superClass && node.superClass.type === `Identifier` && node.superClass.name === `BigNumber`) {
usageNodes.add(node.superClass);
}
},
'Program:exit'() {
if (usageNodes.size === 0) {
return;
}
if (hasBigNumberImport) {
return;
}
const [firstNode] = usageNodes;
context.report({
node: firstNode,
messageId: `missingImport`,
fix(fixer) {
if (bigNumberImportDeclarations.some((node) => node.importKind === `type`)) {
return null;
}
return getInsertionFix(fixer, sourceCode);
},
});
},
};
},
};
export default {
rules: {
[RULE_NAME]: rule,
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment