8000 v6: Cannot find module '@typescript-eslint/*' or its corresponding type declarations · Issue #7284 · typescript-eslint/typescript-eslint · GitHub
[go: up one dir, main page]

Skip to content

v6: Cannot find module '@typescript-eslint/*' or its corresponding type declarations #7284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JoshuaKGoldberg opened this issue Jul 21, 2023 · 19 comments
Labels
fix: user error issue was fixed by correcting the configuration / correcting the code working as intended Issues that are closed as they are working as intended

Comments

@JoshuaKGoldberg
Copy link
Member
JoshuaKGoldberg commented Jul 21, 2023

Overview

A handful of folks have mentioned that after upgrading to v6, trying to import from @typescript-eslint/* packages such as @typescript-eslint/utils causes a new TypeScript type error:

import { ESLintUtils } from '@typescript-eslint/utils';
Cannot find module '@typescript-eslint/utils' or its corresponding type declarations.

This is not a bug. We intentionally dropped support in v6 for Node <16 as those versions of node are EOL. v6 uses package.json exports - which requires Node 16+ and isn't supported by that older module resolution strategy.

To fix the issue, switch your TSConfig's module resolution to a newer version, most likely "bundler", "node16", or "nodenext". See the aka.ms/tsconfig "moduleResolution" docs for more information.

{
    "compilerOptions": {
        "moduleResolution": "bundler"
    }
}

See TypeScript's new modules reference docs for more information.

Issues that are duplicates of this one:

https://arethetypeswrong.github.io/?p=%40typescript-eslint%2Futils%406.0.0 also shows a nice table of which moduleResolution values we support. It's all of them except node10.

Keeping this issue open so it shows up more in searches.

@DanielDanielsson
Copy link
DanielDanielsson commented Aug 13, 2023

Sweet baby jesus, thank you so much! 🙏

Docs says it's "very likely" that you want to have these configurations for modern node projects:

"compilerOptions": {
    "moduleResolution": "nodenext",
    "module": "nodenext",
    //...
  }

And this solves the issue aswell.

@nbouvrette
Copy link

I spent a lot of time troubleshooting this so I am sharing this information if it can be useful for anyone else.

I am using eslint-plugin-local-rules to create custom rules without having to create packages which means that if I want to use @typescript-eslint/utils, changing the moduleResolution config in my Next.js project will break a lot of other package dependencies.

To work around this, I simply created a second tsconfig.json file in my eslint-local-rules directory with the following configuration:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "baseUrl": "./",
    "module": "esnext",
    "moduleResolution": "Bundler"
  },
  "include": ["**/*.ts"],
  "exclude": [],
  "ts-node": {
    "compilerOptions": {
      "module": "NodeNext"
    }
  }
}

And lastly in my .eslintrc.yaml I added my new tsconfig file in the parserOptions:

    parserOptions:
      project:
        - 'tsconfig.json'
        - 'eslint-local-rules/tsconfig.json'

@MichalBryxi
Copy link

Creating a custom rules package using typescript-eslint and bumped into Cannot find module '@typescript-eslint/utils' or its corresponding type declarations.. The fix from DanielDanielson above worked for me.

But I can't see in the official documentation mentioned anywhere a need to change anything in tsconfig.json. For the sake of better DX should the above page be enhanced with such information? Seems like a good spot as it's literally "Building Custom Rules" page.

@akwodkiewicz
Copy link
akwodkiewicz commented Aug 28, 2023

I have some unit tests for custom rules that I used to run with jest, but now it's not possible.

I'd gladly use nodenext/nodenext or Node16/Node16, but you cannot override this with a tsconfig.json file in your project, because ts-node is registered inside jest with a hardcoded "module": "CommonJS". See here:

https://github.com/SimenB/jest/blob/08ef1058175ba0cf3ad6749442fe88ac45524845/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L116-L123

So even if you change the module and moduleResolution to, for example, Node16, this is what you get when running jest:

TS5110: Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16'.

How should the jest configuration now look like? Does it mean we now have to build the whole "custom rules" project as ESM?

PS I know you're not responsible for TS + jest + Node interplay, but I failed to find any information on what to do now. Maybe the answer is not that tricky and you happen to know it. It's probably not only me having this issue, it was hard to find it on the web.

@JoshuaKGoldberg
Copy link
Member Author

because ts-node is registered inside jest with a hardcoded "module": "CommonJS".

Yeah, this seems like an issue on their end. I'd suggest filing an issue on ts-node linking back to this discussion.

We don't use ts-node in typescript-eslint and instead have gone with https://swc.rs/docs/usage/jest. We haven't wanted ts-node-style type type checking in our tests (it slows down tests) and SWC is a faster transpiler than Babel or ts-node (https://www.joshuakgoldberg.com/blog/jest-babel-to-swc).

@bradzacher
Copy link
Member

Side note that I believe that specific code path is only used if you use a jest.config.ts file.

@akwodkiewicz
Copy link

@JoshuaKGoldberg, thanks for the suggestion -- I went for the swc approach and it worked out of the box!

@AdamQuadmon
Copy link

setting moduleResolution does not solve the issue with @typescript-eslint/parser, am I missing something? should I wait for this #7273 to be merged or there are workarounds for this?

@JoshuaKGoldberg
Copy link
Member Author

@AdamQuadmon without any details on your codebase or errors you're seeing, I don't know what you mean by "the issue". Let's go with one of two actionable followups:

stianjensen added a commit to stianjensen/eslint-plugin-deprecation that referenced this issue Sep 12, 2023
This updates to a more modern moduleResolution, from the default of
node/node10.
Relevant typescript-eslint issue:
typescript-eslint/typescript-eslint#7284
stianjensen added a commit to stianjensen/eslint-plugin-deprecation that referenced this issue Sep 12, 2023
This updates to a more modern moduleResolution, from the default of
node/node10.
Relevant typescript-eslint issue:
typescript-eslint/typescript-eslint#7284
@GYuriy
Copy link
GYuriy commented Oct 8, 2023

Changing the module resolution in tsconfig worked for me in terms of satisfying TypeScript error, however ESLint itself is still yelling at me "Unable to resolve path to module '@typescript-eslint/utils'." (eslint import/no-unresolved)
Would appreciate the tip on how to solve this

@bradzacher
Copy link
Member

@GYuriy suggest just turning off that rule for TS files, tbh. It's duplicating checks that TS already does for you and it's logic requires separate configuration to even attempt to mirror the module resolution that TS uses. Also it does a lot of disk reads which makes it a pretty slow rule.

Whathecode added a commit to cph-cachet/carp.core-kotlin that referenced this issue Oct 22, 2023
Although there is a later version of `@typescript-eslint/typescript-estree` available, using any later version will require updating to a newer module resolution than "umd": typescript-eslint/typescript-eslint#7284
@dubzzz
Copy link
dubzzz commented Oct 24, 2023

I'm just wondering: why don't we add back the "types" field in the package.json of the projects? It seems that it would fix issues and unlock the bump of some other open-source projects current relying on @typescript-eslint/* internally.

For instance, I started to look at eslint-plugin-jest and adding the "types" would just make the move to v6 smoother.

The "types" would not be read, when bundler or nodenext is set. But each of them may require huge revamps of the existing codebase to be able to toggle them.

@bradzacher
Copy link
Member

We don't do it because it's an inaccurate representation of our package. It allows users to misconfigure their project and then access private implementation details that don't form part of our API.

node is (not very obviously) an alias for node10 which means that consumers using that setting are using module resolution logic which does not match the reality of what happens at runtime.
Our minimum required node version is v16 - hence node16 is the correct setting to model the resolution logic used at runtime.

Major version upgrades are the time for breaking changes and not all breaking changes are going to be easy for all codebases to update for. We do our best to minimise the impact but some changes are just hard.

As an example - in v9 eslint will remove some methods they have recently deprecated. For projects like us that support v7+ this will be really hard for us as we'll need to create utils to help support both legacy and current versions. That's on us and how we use eslint though - not on the eslint team to keep things around to make our lives easier.

Whathecode added a commit to cph-cachet/carp.core-kotlin that referenced this issue Nov 9, 2023
Although there is a later version of `@typescript-eslint/typescript-estree` available, using any later version will require updating to a newer module resolution than "umd": typescript-eslint/typescript-eslint#7284
phanect added a commit to phanect/eslint-plugin-tree-shakable that referenced this issue Dec 6, 2023
This is required to use `@typescript-eslint/utils` v6+.

See: typescript-eslint/typescript-eslint#7284
phanect added a commit to phanect/eslint-plugin-tree-shakable that referenced this issue Dec 6, 2023
This is required to use `@typescript-eslint/utils` v6+.

See: typescript-eslint/typescript-eslint#7284
phanect added a commit to phanect/eslint-plugin-tree-shakable that referenced this issue Dec 6, 2023
This is required to use `@typescript-eslint/utils` v6+.

See: typescript-eslint/typescript-eslint#7284
lesha1201 added a commit to lesha1201/eslint-plugin-testing-library that referenced this issue Dec 6, 2023
Because `@typescript-eslint` uses package.json `exports`, we had to
upgrade some dependencies which didn't support `exports` (see
typescript-eslint/typescript-eslint#7284).

Affected dependencies:
- `jest`
dj-256 added a commit to dorsale/dorsale that referenced this issue Dec 6, 2023
* Fix bug caused by @typescript-eslint update.
see typescript-eslint/typescript-eslint#7284 (comment)

* CI: bumps version to v0.0.21

* Try to do the bumping manually instead

* Replace gh-action-bump-version with basic script
* Use git-auto-commit-action to commit and push changes

---------

Co-authored-by: Automated Version Bump <gh-action-bump-version@users.noreply.github.com>
@JavaScriptBach
Copy link
Contributor

I have some unit tests for custom rules that I used to run with jest, but now it's not possible.

I'd gladly use nodenext/nodenext or Node16/Node16, but you cannot override this with a tsconfig.json file in your project, because ts-node is registered inside jest with a hardcoded "module": "CommonJS". See here:

https://github.com/SimenB/jest/blob/08ef1058175ba0cf3ad6749442fe88ac45524845/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L116-L123

So even if you change the module and moduleResolution to, for example, Node16, this is what you get when running jest:

TS5110: Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16'.

How should the jest configuration now look like? Does it mean we now have to build the whole "custom rules" project as ESM?

PS I know you're not responsible for TS + jest + Node interplay, but I failed to find any information on what to do now. Maybe the answer is not that tricky and you happen to know it. It's probably not only me having this issue, it was hard to find it on the web.

I have the same problem using jest + swc. My jest.config.ts is

module.exports = {
  transform: {
    "^.+\\.(t|j)sx?$": ["@swc/jest"],
  },
};

But when I try to run tests, I still get errors like

    Cannot find module '/workspaces/obsidian/node_modules/@typescript-eslint/parser' from '../node_modules/eslint/lib/rule-tester/rule-tester.js'

      at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/resolver.js:427:11)
      at runRuleForItem (../node_modules/eslint/lib/rule-tester/rule-tester.js:654:59)
      at testValidTemplate (../node_modules/eslint/lib/rule-tester/rule-tester.js:750:28)
      at Object.<anonymous> (../node_modules/eslint/lib/rule-tester/rule-tester.js:1032:29)

@akwodkiewicz @JoshuaKGoldberg I wonder if you have any pointers on how you got your swc+jest setup to work?

@akwodkiewicz
Copy link

@JoshuaKGoldberg, @JavaScriptBach replying to my own comment from half a year ago

Funnily enough, I totally forgot about this Jest + ts-node issue, and stumbled upon it again last month. So I reported a bug (jestjs/jest#14740) (without mentioning this issue) and proposed a fix: jestjs/jest#14739 (a quick mitigation of the bug is to use jest.config.js instead of jest.config.ts 🙃 )


@JavaScriptBach, trying to help you with swc, this is my jest.config.js (maybe usage of ".js" matters here as well? IDK)

module.exports = {
  testMatch: ['**/*.spec.ts'],
  transform: {
    '^.+\\.(t|j)s$': '@swc/jest',
  },
  coverageDirectory: '<rootDir>/../../../test-reports/eslint-plugin-unit-tests',
};

I did not have to pull off any additional tricks

@JavaScriptBach
Copy link
Contributor

Figured out my issue. I had to change the absolute path of parser when creating RuleTester after upgrading. e.g.

import { TSESLint } from "@typescript-eslint/utils";

const ruleTester = new TSESLint.RuleTester({
  <
F438
span class="pl-c1">parser: resolve(
    __dirname + "../../../../../node_modules/@typescript-eslint/parser",
  ),
});

to

const ruleTester = new TSESLint.RuleTester({
  parser: resolve(
    __dirname + "../../../../../node_modules/@typescript-eslint/parser/dist",
  ),
});

@dgoldstein0
Copy link

Just very painfully encountered this last week, per #8244. Figured I'd repeat my feedback from there: I have never seen a library that drops support for node <16 also start requiring moduleResolution: node16 - for every other package I've ever seen, dropping support for node <16 means that the new version may fail at runtime, not at typecheck time. So @typescript-eslint/ is definitely an outlier here - I've been using moduleResolution: node with 100s of other packages with no problems at all.

... so I have to ask - would it be possible to achieve your goals of hiding internal implementation details while also enabling moduleResolution: node to continue working? I suspect that as long as you don't have .d.ts present for your various private files, imports to them wouldn't resolve so with some effort you could avoid exposing your implementation details in the node10 resolution. that said... also understandable if you just don't want to spend the effort.

But clearly the reason this is such a common point of confusion is that @typescript-eslint is probably one of the few packages requiring node16/bundler resolution already.

@bradzacher
Copy link
Member
bradzacher commented Jan 17, 2024

@dgoldstein0 you cannot hide internals from node10 unless you physically bundle your declarations.
Anyone can import from internal files eg import type Foo from "module/dist/internal/Foo". There's no way around this by default because tsc spits out one .d.ts file per .ts source file.

People often accidentally import from these internal files because TS suggests auto-imports from those files.

The only way to hide these files from node10 is to remove them entirely - and the easiest way to do that is bundling. But that's an extra step that we as volunteer maintainers need to maintain purely to support building plugins against old versions of TS - which is a rarer usecase.

This is also a problem because it means that someone could accidentally write a runtime import that would typecheck fine, but would crash at runtime. This is bad!

With node16 we can declare our API surface in our package.json and have both node enforce it at runtime and typescript enforce it at typecheck time without any effort on our end.


But clearly the reason this is such a common point of confusion is that typescript-eslint is probably one of the few packages requiring node16/bundler resolution already.

The issue isn't our side - it's the typescript side! People don't realise that setting moduleResolution: "node" means node10 - they just think "I'm building for node so I'll use node".

This is why in the next version of TypeScript they're improving the error messages around this to help teach people that they're using the wrong module resolution.

@bradzacher
Copy link
Member

At this point it has been over 6 months since v6 has been released.
The paths to fix the issue have been documented.
We are not going to add back support for node10.

So I'm going to close this.

@bradzacher bradzacher unpinned this issue Jan 29, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
fix: user error issue was fixed by correcting the configuration / correcting the code working as intended Issues that are closed as they are working as intended
Projects
None yet
Development

No branches or pull requests

0