Skip to content

Commit

Permalink
feat(eslint-plugin): [no-var-requires, no-require-imports] allowPacka…
Browse files Browse the repository at this point in the history
…geJson option
  • Loading branch information
Josh-Cena committed Oct 1, 2023
1 parent 4a6d841 commit cc3c317
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 9 deletions.
22 changes: 22 additions & 0 deletions packages/eslint-plugin/docs/rules/no-require-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ import { lib2 } from 'lib2';
import * as lib3 from 'lib3';
```

## Options

### `allowPackageJson`

When this is set to `true`, the rule will allow `require` imports that import `package.json` files. 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.

With `{allowPackageJson: true}`:

<!--tabs-->

### ❌ Incorrect

```ts
console.log(require('../data.json').version);
```

### ✅ Correct

```ts
console.log(require('../package.json').version);
```

## When Not To Use It

If you don't care about using newer module syntax, then you will not need this rule.
Expand Down
22 changes: 22 additions & 0 deletions packages/eslint-plugin/docs/rules/no-var-requires.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ require('foo');
import foo from 'foo';
```

## Options

### `allowPackageJson`

When this is set to `true`, the rule will allow `require` variables that import `package.json` files. 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.

With `{allowPackageJson: true}`:

<!--tabs-->

### ❌ Incorrect

```ts
const foo = require('../data.json');
```

### ✅ Correct

```ts
const foo = require('../package.json');
```

## When Not To Use It

If you don't care about using newer module syntax, then you will not need this rule.
Expand Down
50 changes: 45 additions & 5 deletions packages/eslint-plugin/src/rules/no-require-imports.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,60 @@
import type { TSESTree } from '@typescript-eslint/utils';
import { ASTUtils } from '@typescript-eslint/utils';
import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';

import * as util from '../util';

export default util.createRule({
type Options = [
{
allowPackageJson?: boolean;
},
];
type MessageIds = 'noRequireImports';

export default util.createRule<Options, MessageIds>({
name: 'no-require-imports',
meta: {
type: 'problem',
docs: {
description: 'Disallow invocation of `require()`',
},
schema: [],
schema: [
{
type: 'object',
properties: {
allowPackageJson: {
type: 'boolean',
},
},
},
],
messages: {
noRequireImports: 'A `require()` style import is forbidden.',
},
},
defaultOptions: [],
create(context) {
defaultOptions: [{ allowPackageJson: false }],
create(context, options) {
function isPackageJsonImport(
specifier: TSESTree.Node | undefined,
): boolean {
if (!specifier) {
return false;
}
return (
specifier.type === AST_NODE_TYPES.Literal &&
typeof specifier.value === 'string' &&
specifier.value.endsWith('/package.json')
);
}
return {
'CallExpression[callee.name="require"]'(
node: TSESTree.CallExpression,
): void {
if (
options[0].allowPackageJson &&
isPackageJsonImport(node.arguments[0])
) {
return;
}
const variable = ASTUtils.findVariable(context.getScope(), 'require');

// ignore non-global require usage as it's something user-land custom instead
Expand All @@ -33,6 +67,12 @@ export default util.createRule({
}
},
TSExternalModuleReference(node): void {
if (
options[0].allowPackageJson &&
isPackageJsonImport(node.expression)
) {
return;
}
context.report({
node,
messageId: 'noRequireImports',
Expand Down
29 changes: 25 additions & 4 deletions packages/eslint-plugin/src/rules/no-var-requires.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { AST_NODE_TYPES, ASTUtils } from '@typescript-eslint/utils';

import * as util from '../util';

type Options = [];
type Options = [
{
allowPackageJson?: boolean;
},
];
type MessageIds = 'noVarReqs';

export default util.createRule<Options, MessageIds>({
Expand All @@ -17,14 +21,31 @@ export default util.createRule<Options, MessageIds>({
messages: {
noVarReqs: 'Require statement not part of import statement.',
},
schema: [],
schema: [
{
type: 'object',
properties: {
allowPackageJson: {
type: 'boolean',
},
},
},
],
},
defaultOptions: [],
create(context) {
defaultOptions: [{ allowPackageJson: false }],
create(context, options) {
return {
'CallExpression[callee.name="require"]'(
node: TSESTree.CallExpression,
): void {
if (
options[0].allowPackageJson &&
node.arguments[0]?.type === AST_NODE_TYPES.Literal &&
typeof node.arguments[0].value === 'string' &&
node.arguments[0].value.endsWith('/package.json')
) {
return;
}
const parent =
node.parent?.type === AST_NODE_TYPES.ChainExpression
? node.parent.parent
Expand Down
58 changes: 58 additions & 0 deletions packages/eslint-plugin/tests/rules/no-require-imports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ import { createRequire } from 'module';
const require = createRequire();
require('remark-preset-prettier');
`,
{
code: "const pkg = require('./package.json');",
options: [{ allowPackageJson: true }],
},
{
code: "const pkg = require('../package.json');",
options: [{ allowPackageJson: true }],
},
{
code: "const pkg = require('../packages/package.json');",
options: [{ allowPackageJson: true }],
},
{
code: "import pkg = require('../packages/package.json');",
options: [{ allowPackageJson: true }],
},
],
invalid: [
{
Expand Down Expand Up @@ -111,5 +127,47 @@ var lib5 = require?.('lib5'),
},
],
},
{
code: "const pkg = require('./package.json');",
errors: [
{
line: 1,
column: 13,
messageId: 'noRequireImports',
},
],
},
{
code: "const pkg = require('./package.jsonc');",
options: [{ allowPackageJson: true }],
errors: [
{
line: 1,
column: 13,
messageId: 'noRequireImports',
},
],
},
{
code: "import pkg = require('./package.json');",
errors: [
{
line: 1,
column: 13,
messageId: 'noRequireImports',
},
],
},
{
code: "import pkg = require('./package.jsonc');",
options: [{ allowPackageJson: true }],
errors: [
{
line: 1,
column: 14,
messageId: 'noRequireImports',
},
],
},
],
});
33 changes: 33 additions & 0 deletions packages/eslint-plugin/tests/rules/no-var-requires.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ import { createRequire } from 'module';
const require = createRequire('foo');
const json = require('./some.json');
`,
{
code: "const pkg = require('./package.json');",
options: [{ allowPackageJson: true }],
},
{
code: "const pkg = require('../package.json');",
options: [{ allowPackageJson: true }],
},
{
code: "const pkg = require('../packages/package.json');",
options: [{ allowPackageJson: true }],
},
],
invalid: [
{
Expand Down Expand Up @@ -157,5 +169,26 @@ 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: [{ allowPackageJson: true }],
errors: [
{
line: 1,
column: 13,
messageId: 'noVarReqs',
},
],
},
],
});

0 comments on commit cc3c317

Please sign in to comment.