diff --git a/packages/eslint-plugin/docs/rules/no-require-imports.md b/packages/eslint-plugin/docs/rules/no-require-imports.md index 746ca6dff7ed..d67d639b9cb8 100644 --- a/packages/eslint-plugin/docs/rules/no-require-imports.md +++ b/packages/eslint-plugin/docs/rules/no-require-imports.md @@ -28,6 +28,28 @@ import { lib2 } from 'lib2'; import * as lib3 from 'lib3'; ``` +## Options + +### `allow` + +A array of strings. These strings will be compiled into regular expressions with the `u` flag and be used to test against the imported path. A common use case is to allow importing `package.json`. This is because `package.json` commonly lives outside of the TS root directory, so statically importing it would lead to root directory conflicts, especially with `resolveJsonModule` enabled. You can also use it to allow importing any JSON if your environment doesn't support JSON modules, or use it for other cases where `import` statements cannot work. + +With `{allow: ['/package\\.json$']}`: + + + +### ❌ Incorrect + +```ts +console.log(require('../data.json').version); +``` + +### ✅ Correct + +```ts +console.log(require('../package.json').version); +``` + ## When Not To Use It If your project frequently uses older CommonJS `require`s, then this rule might not be applicable to you. diff --git a/packages/eslint-plugin/docs/rules/no-var-requires.md b/packages/eslint-plugin/docs/rules/no-var-requires.md index f4605ac335bd..0bd0b2b564b9 100644 --- a/packages/eslint-plugin/docs/rules/no-var-requires.md +++ b/packages/eslint-plugin/docs/rules/no-var-requires.md @@ -28,6 +28,28 @@ require('foo'); import foo from 'foo'; ``` +## Options + +### `allow` + +A array of strings. These strings will be compiled into regular expressions with the `u` flag and be used to test against the imported path. A common use case is to allow importing `package.json`. This is because `package.json` commonly lives outside of the TS root directory, so statically importing it would lead to root directory conflicts, especially with `resolveJsonModule` enabled. You can also use it to allow importing any JSON if your environment doesn't support JSON modules, or use it for other cases where `import` statements cannot work. + +With `{allow: ['/package\\.json$']}`: + + + +### ❌ Incorrect + +```ts +const foo = require('../data.json'); +``` + +### ✅ Correct + +```ts +const foo = require('../package.json'); +``` + ## When Not To Use It If your project frequently uses older CommonJS `require`s, then this rule might not be applicable to you. diff --git a/packages/eslint-plugin/src/rules/no-require-imports.ts b/packages/eslint-plugin/src/rules/no-require-imports.ts index fbf4bc785717..c4f20e6c68f7 100644 --- a/packages/eslint-plugin/src/rules/no-require-imports.ts +++ b/packages/eslint-plugin/src/rules/no-require-imports.ts @@ -1,27 +1,59 @@ import type { TSESTree } from '@typescript-eslint/utils'; -import { ASTUtils } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils'; import { getScope } from '@typescript-eslint/utils/eslint-utils'; -import { createRule } from '../util'; +import * as util from '../util'; -export default createRule({ +type Options = [ + { + allow: string[]; + }, +]; +type MessageIds = 'noRequireImports'; + +export default util.createRule({ name: 'no-require-imports', meta: { type: 'problem', docs: { description: 'Disallow invocation of `require()`', }, - schema: [], + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { type: 'string' }, + description: 'Patterns of import paths to allow requiring from.', + }, + }, + additionalProperties: false, + }, + ], messages: { noRequireImports: 'A `require()` style import is forbidden.', }, }, - defaultOptions: [], - create(context) { + defaultOptions: [{ allow: [] }], + create(context, options) { + const allowPatterns = options[0].allow.map( + pattern => new RegExp(pattern, 'u'), + ); + function isImportPathAllowed(importPath: string): boolean { + return allowPatterns.some(pattern => importPath.match(pattern)); + } return { 'CallExpression[callee.name="require"]'( node: TSESTree.CallExpression, ): void { + if ( + node.arguments[0]?.type === AST_NODE_TYPES.Literal && + typeof node.arguments[0].value === 'string' && + isImportPathAllowed(node.arguments[0].value) + ) { + return; + } const variable = ASTUtils.findVariable(getScope(context), 'require'); // ignore non-global require usage as it's something user-land custom instead @@ -34,6 +66,13 @@ export default createRule({ } }, TSExternalModuleReference(node): void { + if ( + node.expression.type === AST_NODE_TYPES.Literal && + typeof node.expression.value === 'string' && + isImportPathAllowed(node.expression.value) + ) { + return; + } context.report({ node, messageId: 'noRequireImports', diff --git a/packages/eslint-plugin/src/rules/no-var-requires.ts b/packages/eslint-plugin/src/rules/no-var-requires.ts index 7ebf45250f05..3605904e655b 100644 --- a/packages/eslint-plugin/src/rules/no-var-requires.ts +++ b/packages/eslint-plugin/src/rules/no-var-requires.ts @@ -4,7 +4,11 @@ import { getScope } from '@typescript-eslint/utils/eslint-utils'; import { createRule } from '../util'; -type Options = []; +type Options = [ + { + allow: string[]; + }, +]; type MessageIds = 'noVarReqs'; export default createRule({ @@ -18,14 +22,39 @@ export default createRule({ messages: { noVarReqs: 'Require statement not part of import statement.', }, - schema: [], + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { type: 'string' }, + description: 'Patterns of import paths to allow requiring from.', + }, + }, + additionalProperties: false, + }, + ], }, - defaultOptions: [], - create(context) { + defaultOptions: [{ allow: [] }], + create(context, options) { + const allowPatterns = options[0].allow.map( + pattern => new RegExp(pattern, 'u'), + ); + function isImportPathAllowed(importPath: string): boolean { + return allowPatterns.some(pattern => importPath.match(pattern)); + } return { 'CallExpression[callee.name="require"]'( node: TSESTree.CallExpression, ): void { + if ( + node.arguments[0]?.type === AST_NODE_TYPES.Literal && + typeof node.arguments[0].value === 'string' && + isImportPathAllowed(node.arguments[0].value) + ) { + return; + } const parent = node.parent.type === AST_NODE_TYPES.ChainExpression ? node.parent.parent diff --git a/packages/eslint-plugin/tests/rules/no-require-imports.test.ts b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts index ff6cbd2a6032..42374ccffbf8 100644 --- a/packages/eslint-plugin/tests/rules/no-require-imports.test.ts +++ b/packages/eslint-plugin/tests/rules/no-require-imports.test.ts @@ -23,6 +23,30 @@ import { createRequire } from 'module'; const require = createRequire(); require('remark-preset-prettier'); `, + { + code: "const pkg = require('./package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "const pkg = require('../package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "const pkg = require('../packages/package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "import pkg = require('../packages/package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "import pkg = require('data.json');", + options: [{ allow: ['\\.json$'] }], + }, + { + code: "import pkg = require('some-package');", + options: [{ allow: ['^some-package$'] }], + }, ], invalid: [ { @@ -111,5 +135,58 @@ var lib5 = require?.('lib5'), }, ], }, + { + code: "const pkg = require('./package.json');", + errors: [ + { + line: 1, + column: 13, + messageId: 'noRequireImports', + }, + ], + }, + { + code: "const pkg = require('./package.jsonc');", + options: [{ allow: ['/package\\.json$'] }], + errors: [ + { + line: 1, + column: 13, + messageId: 'noRequireImports', + }, + ], + }, + { + code: "import pkg = require('./package.json');", + errors: [ + { + line: 1, + column: 14, + messageId: 'noRequireImports', + }, + ], + }, + { + code: "import pkg = require('./package.jsonc');", + options: [{ allow: ['/package\\.json$'] }], + errors: [ + { + line: 1, + column: 14, + messageId: 'noRequireImports', + }, + ], + }, + { + code: "import pkg = require('./package.json');", + options: [{ allow: ['^some-package$'] }], + errors: [ + { + line: 1, + column: 14, + messageId: 'noRequireImports', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-var-requires.test.ts b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts index 14ce51109941..718bcc46db31 100644 --- a/packages/eslint-plugin/tests/rules/no-var-requires.test.ts +++ b/packages/eslint-plugin/tests/rules/no-var-requires.test.ts @@ -16,6 +16,26 @@ import { createRequire } from 'module'; const require = createRequire('foo'); const json = require('./some.json'); `, + { + code: "const pkg = require('./package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "const pkg = require('../package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "const pkg = require('../packages/package.json');", + options: [{ allow: ['/package\\.json$'] }], + }, + { + code: "const pkg = require('data.json');", + options: [{ allow: ['\\.json$'] }], + }, + { + code: "const pkg = require('some-package');", + options: [{ allow: ['^some-package$'] }], + }, ], invalid: [ { @@ -157,5 +177,37 @@ configValidator.addSchema(require('./a.json')); }, ], }, + { + code: "const pkg = require('./package.json');", + errors: [ + { + line: 1, + column: 13, + messageId: 'noVarReqs', + }, + ], + }, + { + code: "const pkg = require('./package.jsonc');", + options: [{ allow: ['/package\\.json$'] }], + errors: [ + { + line: 1, + column: 13, + messageId: 'noVarReqs', + }, + ], + }, + { + code: "const pkg = require('./package.json');", + options: [{ allow: ['^some-package$'] }], + errors: [ + { + line: 1, + column: 13, + messageId: 'noVarReqs', + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot b/packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot index bdb7cb325381..7561bcbc4b3c 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot @@ -4,11 +4,30 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos " # SCHEMA: -[] +[ + { + "additionalProperties": false, + "properties": { + "allow": { + "description": "Patterns of import paths to allow requiring from.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } +] # TYPES: -/** No options declared */ -type Options = [];" +type Options = [ + { + /** Patterns of import paths to allow requiring from. */ + allow?: string[]; + }, +]; +" `; diff --git a/packages/eslint-plugin/tests/schema-snapshots/no-var-requires.shot b/packages/eslint-plugin/tests/schema-snapshots/no-var-requires.shot index 992833a20ae3..5271679f9bbb 100644 --- a/packages/eslint-plugin/tests/schema-snapshots/no-var-requires.shot +++ b/packages/eslint-plugin/tests/schema-snapshots/no-var-requires.shot @@ -4,11 +4,30 @@ exports[`Rule schemas should be convertible to TS types for documentation purpos " # SCHEMA: -[] +[ + { + "additionalProperties": false, + "properties": { + "allow": { + "description": "Patterns of import paths to allow requiring from.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } +] # TYPES: -/** No options declared */ -type Options = [];" +type Options = [ + { + /** Patterns of import paths to allow requiring from. */ + allow?: string[]; + }, +]; +" `; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy