8000 @typescript-eslint can't parse TypeScript 5.x tsconfig.json file format when using PNPM · Issue #7499 · typescript-eslint/typescript-eslint · GitHub
[go: up one dir, main page]

Skip to content

@typescript-eslint can't parse TypeScript 5.x tsconfig.json file format when using PNPM #7499

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
4 tasks done
matthew-dean opened this issue Aug 17, 2023 · 15 comments
Closed
4 tasks done
Labels
awaiting response Issues waiting for a reply from the OP or another party enhancement New feature or request package: parser Issues related to @typescript-eslint/parser

Comments

@matthew-dean
Copy link
matthew-dean commented Aug 17, 2023

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Relevant Package

parser

Playground Link

No response

Repro Code

npm eslint --config .eslintrc.cjs .
// or 
yarn eslint --config .eslintrc.cjs .
// or
pnpm eslint --config .eslintrc.cjs .

ESLint Config

module.exports = {
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
}

ESLint Config

No response

tsconfig

{
  "extends": ["tsconfig.json"]
  "compilerOptions": {
    // ...
  }
}

Expected Result

Since microsoft/TypeScript#29118, ESLint should also be able to parse it and run normally.

Actual Result

Parsing error: Compiler option 'extends' requires a value of type string

Additional Info

This is a dupe of #6219, but it was closed without investigation, and it cannot be commented on. I also had to check I have [searched for related issues](https://github.com/typescript-eslint/typescript-eslint/issues?q=is%3Aissue+label%3Abug) and found none that matched my issue. because otherwise you can't submit an issue.

Work publicly started on this on or around January 20, 2021. This was announced as implemented in January 2023 and released on March 16, 2023.

Versions

package version
@typescript-eslint/eslint-plugin 6.4.0
@typescript-eslint/parser 6.4.0
TypeScript 5.0.3
ESLint 8.47.0
node 16.19.0
@matthew-dean matthew-dean added bug Something isn't working triage Waiting for team members to take a look labels Aug 17, 2023
@JoshuaKGoldberg
Copy link
Member

This was noted in #6380 and just never acted on because nobody's asked for it yet. I'm honestly surprised it took this long for someone to file an issue post-5.0 😄. Accepting PRs!

@JoshuaKGoldberg JoshuaKGoldberg added accepting prs Go ahead, send a pull request that resolves this issue enhancement New feature or request package: parser Issues related to @typescript-eslint/parser and removed triage Waiting for team members to take a look bug Something isn't working labels Aug 17, 2023
@bradzacher
Copy link
Member

I don't think that we manually parse tsconfigs - we pass them through to TS for parsing (which is why the 5.0 notes don't mention anything for us to do). I'm surprised that this errors.

Part of me thinks there might be a version mismatch.

@matthew-dean can you provide an isolated repro repo showing the error?

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed accepting prs Go ahead, send a pull request that resolves this issue labels Aug 17, 2023
@Zamiell
Copy link
Contributor
Zamiell commented Aug 26, 2023

Just chiming in to say that I use tsconfig extends with the array form in my monorepo in combination with most of the ESLint rules from this project, and I don't have any issues - everything just works exactly like it should. So it sounds like this issue might be probably something specific to his ESLint config.

@matthew-dean
Copy link
Author

Solution (TL;DR)

PNPM users need to add this to their package.json file (with whatever their local versions are):

  "resolutions": {
    "@typescript-eslint/eslint-plugin": "^6.5.0",
    "@typescript-eslint/parser": "^6.5.0",
    "typescript": "~5.2.2"
  }

Explanation

Okay, this was very difficult to track down, and I have not been able to replicate it, because it has to do with the way that PNPM resolves peer dependencies.

PNPM reasons that peer dependencies are related to the package that lists them as peers. And for whatever reason, in their great wisdom, the PNPM decided that, by default, peers that are do not match are auto-installed.

You are right that @typescript-eslint hands off parsing of tsconfig.json to TypeScript. What's missed though was that @typescript-eslint has a peerDependency of TypeScript below version 5. It's installed transitively because of this dependency, which lists its peerDependency on this line.

PNPM reasons that if @typescript-eslint needs ts-api-utils, and ts-api-utils needs TypeScript that matches a 4.x version, then any time @typescript-eslint needs to resolve TypeScript, then it MUST need TypeScript < 5. Therefore, the PNPM directory for @typescript-eslint/eslint-plugin contains a node_modules directory which contains typescript which is version 4.9.5.

PNPM's logic is not entirely without reasoning. If peer dependencies are not installed automatically, there's a good chance of failures at runtime, since the package that needs that peer is expecting that specific major version range, and that package would be un-tested with other ranges.

So, for all intents and purposes, in PNPM land, we can consider @typescript-eslint and all of its packages to be strongly bound to TypeScript 4.x. Except it isn't. Frustratingly, this behavior is not consistent, and sometimes @typescript-eslint will resolve TypeScript to a different version? Why? I assume because the @typescript-eslint express no direct opinion in the matter. When I upgraded from 6.4.0 to 6.5.0, even though the same ts-api-utils exists as a dependency, PNPM resolved it at the root to the general monorepo TypeScript version. (This is what makes is insanely difficult to replicate.)

Anyway, you could call it a PNPM bug, except PNPM insists it's correct behavior. You could ask people to not use PNPM, but it's pretty popular these days because of its speed. So, I guess the above solution is best? If you know the TypeScript version you want to use with this package, then you must "force" it via the resolutions prop.

@matthew-dean matthew-dean changed the title Support tsconfig.extends as array @typescript-eslint doesn't support TypeScript 5.x tsconfig.json file format Aug 28, 2023
@matthew-dean
Copy link
Author

We can probably close this since there is a workaround. Maybe worth documenting though?

@matthew-dean matthew-dean changed the title @typescript-eslint doesn't support TypeScript 5.x tsconfig.json file format @typescript-eslint can't parse TypeScript 5.x tsconfig.json file format when using PNPM Aug 28, 2023
@matthew-dean
Copy link
Author

Here's a comment I left on the PNPM issue:

I think what's missing is that in the case of something like @typescripe-eslint, what's missing is that the intent of the TypeScript-ESLint team is that TypeScript resolves to whatever the author's dependency version is, and not what a transitive dependency (of a @typescript-eslint package) has installed as a peer dependency. But there's no way (that I know of) to communicate that in package.json, is there?

@matthew-dean
Copy link
Author

Oh wait, so I noticed that ts-api-utils actually has a peer dependency of >=4.2.0, which should support any version of TypeScript 5.x+, but I think what's happening is that it's the same issue, where another peer of a peer somewhere has an explicit peer defined of 4.x. It's definitely something within the TypeScript-ESLint tree of dependencies / peer dependencies but I can't tell what. 🤔

@bradzacher
Copy link
Member

A long time ago in a galaxy far, far away the first version of our tool was built with typescript: *. It purposely did not include a version range because TS versions are funky to begin with, and the tools supported all versions anyways. We also didn't want to restrict people to just the versions we declared because by-and-large our tool would work with new versions too - but we often lagged behind the releases and thus didn't want to hold users back. So a runtime warning was added which told people to beware if they were doing something we hadn't tested - but otherwise gave the user free reign.

Some time after that the ecosystem started to build tooling on top of our tooling (eg create-react-app). The problem was that at the time we specified a peer dependency range of typescript: * which meant that users of such tools who did not use TS would get stuck with console logs at install time warning them they were missing a package. This warning was wrong as it was all optional but it annoyed people and they loudly complained at the tool owners and at us.

Our solution was to just remove the peer dependency specification. We already had runtime validation of the version and the entire system would break without it - so it was essentially superfluous.

Some time after that yarn2 introduced a feature that would enforce that packages do not access any other package unless it was explicitly declared as a dependency.

We, of course, failed this assertion and people could not use our tooling under new yarn. The workaround was for us to add this config to our packages which would tell package managers we did actually depend on TS but it was okay if it didn't exist.

"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},

This satisfied the constraints and made the ecosystem work and everyone was happy: users didn't get any warning logs on any version of any package manager because we didn't have typescript: *, and new package managers assumed we meant that thanks to peerDependenciesMeta and "just worked".

That's the history of why we do what we do and it's setup in this exact manner. It's been working for a good number of years - so if it's breaking now then that's a regression in pnpm that will need to be solved in some way or another.

@matthew-dean
8000 Copy link
Author

@bradzacher Oh don't get me wrong, I 100% concur this is an PNPM problem, and they have lots of weirdness around resolving peer dependencies in ways that you don't expect. But I wanted to document all that stuff in the off-chance someone came across this exact situation with a solution that should work for a PNPM setup. But feel free to close as not applicable here. I don't actually think there's anything you can do on your end in terms of package.json configuration.

@bradzacher
Copy link
Member

Sorry I was just dumping the entire history here as well so you can share it with the pnpm maintainer if needs be :)

I'm not saying we're infallible here btw - our setup unique and unconventional, even if it has its reasons for existing!

There might be something we can do to fix things - we're completely open to making changes as long as we don't cause logs for the CRA case or restrict users from new TS releases.

Last time we made a change it was the yarn lead maintainer telling us exactly how to fix things - so we would likely want a similarly knowledgeable individual to advise again :)

@zkochan
Copy link
zkochan commented Sep 1, 2023

This statement is not correct:

PNPM reasons that peer dependencies are related to the package that lists them as peers. And for whatever reason, in their great wisdom, the PNPM decided that, by default, peers that are do not match are auto-installed.

pnpm will resolve a peer dependency using any package that is found in the dependencies of a direct or indirect parent package. It doesn't matter if the found package doesn't match the range in the peerDependencies field. A warning will be printed but the package will not be auto-installed as it is found.

Also, @typescript-eslint is used in the pnpm workspace and I am not seeing any issues.

@bradzacher
Copy link
Member

If zoltan says it works then I'm inclined to believe that :)

it's probably local setup problems for users like outdated versions or something?

Either way I don't think there's anything we should action in our project - so closing this off.

@bradzacher bradzacher closed this as not planned Won't fix, can't repro, duplicate, stale Sep 1, 2023
@zkochan
Copy link
zkochan commented Sep 1, 2023

In any case, if you can create a sample repository that reproduces the issue, feel free to create a new issue in the pnpm repository and we'll look into it.

@matthew-dean
Copy link
Author

@zkochan Oh okay, so you're suggesting I was dealing with a PNPM bug of some kind, and this should have worked but didn't?

@zkochan
Copy link
zkochan commented Sep 7, 2023

That is a possibility. If you can submit an issue with steps to repro someone can check.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
awaiting response Issues waiting for a reply from the OP or another party enhancement New feature or request package: parser Issues related to @typescript-eslint/parser
Projects
None yet
Development

No branches or pull requests

5 participants
0