Last active
October 3, 2025 08:29
-
-
Save longzheng/eb08f2283379cbfd4b5f32c40f3614cd to your computer and use it in GitHub Desktop.
ESLint plugin to fix BigNumber.js import with namespace pollution in TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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`, | |
| }, | |
| ... | |
| }, | |
| ]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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