Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f7b5ca8

Browse filesBrowse files
authored
feat(eslint-plugin): [no-var-requires, no-require-imports] allow option (typescript-eslint#7710)
* feat(eslint-plugin): [no-var-requires, no-require-imports] allowPackageJson option * Use allow option * Update snap * Fix test
1 parent 1a8e0dc commit f7b5ca8
Copy full SHA for f7b5ca8

File tree

Expand file treeCollapse file tree

8 files changed

+295
-16
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+295
-16
lines changed

‎packages/eslint-plugin/docs/rules/no-require-imports.md

Copy file name to clipboardExpand all lines: packages/eslint-plugin/docs/rules/no-require-imports.md
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ import { lib2 } from 'lib2';
2828
import * as lib3 from 'lib3';
2929
```
3030

31+
## Options
32+
33+
### `allow`
34+
35+
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.
36+
37+
With `{allow: ['/package\\.json$']}`:
38+
39+
<!--tabs-->
40+
41+
### ❌ Incorrect
42+
43+
```ts
44+
console.log(require('../data.json').version);
45+
```
46+
47+
### ✅ Correct
48+
49+
```ts
50+
console.log(require('../package.json').version);
51+
```
52+
3153
## When Not To Use It
3254

3355
If your project frequently uses older CommonJS `require`s, then this rule might not be applicable to you.

‎packages/eslint-plugin/docs/rules/no-var-requires.md

Copy file name to clipboardExpand all lines: packages/eslint-plugin/docs/rules/no-var-requires.md
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ require('foo');
2828
import foo from 'foo';
2929
```
3030

31+
## Options
32+
33+
### `allow`
34+
35+
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.
36+
37+
With `{allow: ['/package\\.json$']}`:
38+
39+
<!--tabs-->
40+
41+
### ❌ Incorrect
42+
43+
```ts
44+
const foo = require('../data.json');
45+
```
46+
47+
### ✅ Correct
48+
49+
```ts
50+
const foo = require('../package.json');
51+
```
52+
3153
## When Not To Use It
3254

3355
If your project frequently uses older CommonJS `require`s, then this rule might not be applicable to you.

‎packages/eslint-plugin/src/rules/no-require-imports.ts

Copy file name to clipboardExpand all lines: packages/eslint-plugin/src/rules/no-require-imports.ts
+45-6Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,59 @@
11
import type { TSESTree } from '@typescript-eslint/utils';
2-
import { ASTUtils } from '@typescript-eslint/utils';
2+
import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';
33
import { getScope } from '@typescript-eslint/utils/eslint-utils';
44

5-
import { createRule } from '../util';
5+
import * as util from '../util';
66

7-
export default createRule({
7+
type Options = [
8+
{
9+
allow: string[];
10+
},
11+
];
12+
type MessageIds = 'noRequireImports';
13+
14+
export default util.createRule<Options, MessageIds>({
815
name: 'no-require-imports',
916
meta: {
1017
type: 'problem',
1118
docs: {
1219
description: 'Disallow invocation of `require()`',
1320
},
14-
schema: [],
21+
schema: [
22+
{
23+
type: 'object',
24+
properties: {
25+
allow: {
26+
type: 'array',
27+
items: { type: 'string' },
28+
description: 'Patterns of import paths to allow requiring from.',
29+
},
30+
},
31+
additionalProperties: false,
32+
},
33+
],
1534
messages: {
1635
noRequireImports: 'A `require()` style import is forbidden.',
1736
},
1837
},
19-
defaultOptions: [],
20-
create(context) {
38+
defaultOptions: [{ allow: [] }],
39+
create(context, options) {
40+
const allowPatterns = options[0].allow.map(
41+
pattern => new RegExp(pattern, 'u'),
42+
);
43+
function isImportPathAllowed(importPath: string): boolean {
44+
return allowPatterns.some(pattern => importPath.match(pattern));
45+
}
2146
return {
2247
'CallExpression[callee.name="require"]'(
2348
node: TSESTree.CallExpression,
2449
): void {
50+
if (
51+
node.arguments[0]?.type === AST_NODE_TYPES.Literal &&
52+
typeof node.arguments[0].value === 'string' &&
53+
isImportPathAllowed(node.arguments[0].value)
54+
) {
55+
return;
56+
}
2557
const variable = ASTUtils.findVariable(getScope(context), 'require');
2658

2759
// ignore non-global require usage as it's something user-land custom instead
@@ -34,6 +66,13 @@ export default createRule({
3466
}
3567
},
3668
TSExternalModuleReference(node): void {
69+
if (
70+
node.expression.type === AST_NODE_TYPES.Literal &&
71+
typeof node.expression.value === 'string' &&
72+
isImportPathAllowed(node.expression.value)
73+
) {
74+
return;
75+
}
3776
context.report({
3877
node,
3978
messageId: 'noRequireImports',

‎packages/eslint-plugin/src/rules/no-var-requires.ts

Copy file name to clipboardExpand all lines: packages/eslint-plugin/src/rules/no-var-requires.ts
+33-4Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { getScope } from '@typescript-eslint/utils/eslint-utils';
44

55
import { createRule } from '../util';
66

7-
type Options = [];
7+
type Options = [
8+
{
9+
allow: string[];
10+
},
11+
];
812
type MessageIds = 'noVarReqs';
913

1014
export default createRule<Options, MessageIds>({
@@ -18,14 +22,39 @@ export default createRule<Options, MessageIds>({
1822
messages: {
1923
noVarReqs: 'Require statement not part of import statement.',
2024
},
21-
schema: [],
25+
schema: [
26+
{
27+
type: 'object',
28+
properties: {
29+
allow: {
30+
type: 'array',
31+
items: { type: 'string' },
32+
description: 'Patterns of import paths to allow requiring from.',
33+
},
34+
},
35+
additionalProperties: false,
36+
},
37+
],
2238
},
23-
defaultOptions: [],
24-
create(context) {
39+
defaultOptions: [{ allow: [] }],
40+
create(context, options) {
41+
const allowPatterns = options[0].allow.map(
42+
pattern => new RegExp(pattern, 'u'),
43+
);
44+
function isImportPathAllowed(importPath: string): boolean {
45+
return allowPatterns.some(pattern => importPath.match(pattern));
46+
}
2547
return {
2648
'CallExpression[callee.name="require"]'(
2749
node: TSESTree.CallExpression,
2850
): void {
51+
if (
52+
node.arguments[0]?.type === AST_NODE_TYPES.Literal &&
53+
typeof node.arguments[0].value === 'string' &&
54+
isImportPathAllowed(node.arguments[0].value)
55+
) {
56+
return;
57+
}
2958
const parent =
3059
node.parent.type === AST_NODE_TYPES.ChainExpression
3160
? node.parent.parent

‎packages/eslint-plugin/tests/rules/no-require-imports.test.ts

Copy file name to clipboardExpand all lines: packages/eslint-plugin/tests/rules/no-require-imports.test.ts
+77Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ import { createRequire } from 'module';
2323
const require = createRequire();
2424
require('remark-preset-prettier');
2525
`,
26+
{
27+
code: "const pkg = require('./package.json');",
28+
options: [{ allow: ['/package\\.json$'] }],
29+
},
30+
{
31+
code: "const pkg = require('../package.json');",
32+
options: [{ allow: ['/package\\.json$'] }],
33+
},
34+
{
35+
code: "const pkg = require('../packages/package.json');",
36+
options: [{ allow: ['/package\\.json$'] }],
37+
},
38+
{
39+
code: "import pkg = require('../packages/package.json');",
40+
options: [{ allow: ['/package\\.json$'] }],
41+
},
42+
{
43+
code: "import pkg = require('data.json');",
44+
options: [{ allow: ['\\.json$'] }],
45+
},
46+
{
47+
code: "import pkg = require('some-package');",
48+
options: [{ allow: ['^some-package$'] }],
49+
},
2650
],
2751
invalid: [
2852
{
@@ -111,5 +135,58 @@ var lib5 = require?.('lib5'),
111135
},
112136
],
113137
},
138+
{
139+
code: "const pkg = require('./package.json');",
140+
errors: [
141+
{
142+
line: 1,
143+
column: 13,
144+
messageId: 'noRequireImports',
145+
},
146+
],
147+
},
148+
{
149+
code: "const pkg = require('./package.jsonc');",
150+
options: [{ allow: ['/package\\.json$'] }],
151+
errors: [
152+
{
153+
line: 1,
154+
column: 13,
155+
messageId: 'noRequireImports',
156+
},
157+
],
158+
},
159+
{
160+
code: "import pkg = require('./package.json');",
161+
errors: [
162+
{
163+
line: 1,
164+
column: 14,
165+
messageId: 'noRequireImports',
166+
},
167+
],
168+
},
169+
{
170+
code: "import pkg = require('./package.jsonc');",
171+
options: [{ allow: ['/package\\.json$'] }],
172+
errors: [
173+
{
174+
line: 1,
175+
column: 14,
176+
messageId: 'noRequireImports',
177+
},
178+
],
179+
},
180+
{
181+
code: "import pkg = require('./package.json');",
182+
options: [{ allow: ['^some-package$'] }],
183+
errors: [
184+
{
185+
line: 1,
186+
column: 14,
187+
messageId: 'noRequireImports',
188+
},
189+
],
190+
},
114191
],
115192
});

‎packages/eslint-plugin/tests/rules/no-var-requires.test.ts

Copy file name to clipboardExpand all lines: packages/eslint-plugin/tests/rules/no-var-requires.test.ts
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ import { createRequire } from 'module';
1616
const require = createRequire('foo');
1717
const json = require('./some.json');
1818
`,
19+
{
20+
code: "const pkg = require('./package.json');",
21+
options: [{ allow: ['/package\\.json$'] }],
22+
},
23+
{
24+
code: "const pkg = require('../package.json');",
25+
options: [{ allow: ['/package\\.json$'] }],
26+
},
27+
{
28+
code: "const pkg = require('../packages/package.json');",
29+
options: [{ allow: ['/package\\.json$'] }],
30+
},
31+
{
32+
code: "const pkg = require('data.json');",
33+
options: [{ allow: ['\\.json$'] }],
34+
},
35+
{
36+
code: "const pkg = require('some-package');",
37+
options: [{ allow: ['^some-package$'] }],
38+
},
1939
],
2040
invalid: [
2141
{
@@ -157,5 +177,37 @@ configValidator.addSchema(require('./a.json'));
157177
},
158178
],
159179
},
180+
{
181+
code: "const pkg = require('./package.json');",
182+
errors: [
183+
{
184+
line: 1,
185+
column: 13,
186+
messageId: 'noVarReqs',
187+
},
188+
],
189+
},
190+
{
191+
code: "const pkg = require('./package.jsonc');",
192+
options: [{ allow: ['/package\\.json$'] }],
193+
errors: [
194+
{
195+
line: 1,
196+
column: 13,
197+
messageId: 'noVarReqs',
198+
},
199+
],
200+
},
201+
{
202+
code: "const pkg = require('./package.json');",
203+
options: [{ allow: ['^some-package$'] }],
204+
errors: [
205+
{
206+
line: 1,
207+
column: 13,
208+
messageId: 'noVarReqs',
209+
},
210+
],
211+
},
160212
],
161213
});

‎packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot

Copy file name to clipboardExpand all lines: packages/eslint-plugin/tests/schema-snapshots/no-require-imports.shot
+22-3Lines changed: 22 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.