Add Yarn PnP support#1966
Conversation
|
@microsoft-github-policy-service agree company="Datadog" |
|
I see my tests are not passing in CI, looking into it 🙇 |
| var ( | ||
| isPnpApiInitialized atomic.Uint32 | ||
| cachedPnpApi *PnpApi | ||
| pnpMu sync.Mutex | ||
| // testPnpCache stores per-goroutine PnP APIs for test isolation | ||
| // Key is goroutine ID (as int) | ||
| testPnpCache sync.Map // map[int]*PnpApi | ||
| ) | ||
|
|
||
| // getGoroutineID returns the current goroutine ID | ||
| // It is usually not recommended to work with goroutine IDs, but it is the most non-intrusive way to setup a parallel testing environment for PnP API | ||
| func getGoroutineID() int { | ||
| var buf [64]byte | ||
| n := runtime.Stack(buf[:], false) | ||
| idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] | ||
| id, _ := strconv.Atoi(idField) | ||
| return id | ||
| } | ||
|
|
There was a problem hiding this comment.
All of this global / goroutine local storage is something that won't work. If we are storing something, it needs to be attached to a Program, Project, a Host, etc, not global. Parsing out the goroutine ID is definitely a bad idea, especially given our LSP can handle multiple requests at the same time from multiple goroutines and so on.
There was a problem hiding this comment.
@jakebailey Thank you for having a first look at this PR!
Since your comments, we changed how we initialize the PnpApi by attaching it to the Host directly on initialization. The host can then provide it through host.PnpApi() which will return nil or the pnpapi instance if in a yarn project
|
|
||
| pnpApi := &PnpApi{fs: fs, url: filePath} | ||
|
|
||
| manifestData, err := pnpApi.findClosestPnpManifest() |
There was a problem hiding this comment.
I don't really understand this init. Surely one needs to be able to load multiple projects with differing pnp info at the same time? All of this info really does need to be handled differently.
There was a problem hiding this comment.
With the last changes mentioned in this comment, we initialize the PnP api with the Host. This means we assume that we have one yarn PnP project per host
I don't think we could initialize it on a smaller scope (like Project or Program) from looking at the changes we had to do to support PnP, but let me know what you think!
d70cc8a to
854be04
Compare
e92efeb to
e4a3c4b
Compare
36c114c to
9aa7f3e
Compare
* add pnpapi test * remove cycle dependency * add error handling * add error message * apply error message * add error bubbling * code clean up * remove useless test * add empty findBrokenPeerDependencies * early return * change function name * apply code review * merge duplicate logic * apply collect usage * apply codereview * apply fromConfig
440f2f8 to
478d4d9
Compare
478d4d9 to
f3c6477
Compare
a98f2f6 to
c343dca
Compare
e8885f5 to
4f9181b
Compare
## What's the problem this PR addresses? Add editor SDK support for oxc. This should enable optional editor integration with both oxfmt and oxlint. ## How did you fix it? Added oxlint, oxfmt and oxlint-tsgolint as supported integration and documented it. Oxlint and oxlint-tsgolint addition is a bit hacky and I don't know whether this is the right direction or not. Rationale can be read from the comment there. Loader option added to sdk setupEnv to prevent oxfmt tinypool from being broken due to failing to resolve tinypool/dist/process.js. I haven't tried this with vim yet since I don't use it so if anyone would kindly help test whether this will work on vim or not, thanks! ### oxlint type-aware feature (tsgolint) The biggest challenge here is the type-aware feature (tsgolint) support. The rust side of oxlint spawn tsgolint a bit different from how the oxlint itself is spawned: https://github.com/oxc-project/oxc/blob/d3dcf5bc9718ebb4839be27062b5d82da2118e2e/crates/oxc_linter/src/tsgolint.rs#L265-L270 Instead of using js entrypoint, it directly spawn executable so the wrapper approach is not directly applicable here. Luckily, there are some strategies that the oxc_linter uses to resolve tsgolint: - via PATH - via node_modules search - direct path via OXLINT_TSGOLINT_PATH (which is what oxc.path.tsgolint does basically) The only realistic strategy is to use PATH. For the sdks, it will generate a windows executable shim (cmd) that will basically just spawn tsgolint.js, else should fallback to tsgolint. Be noted however that this will not fix fundamental issue with typescript-go module resolution, which requires this to be merged first: - microsoft/typescript-go#1966 This PR just enable the usage of tsgo regardless of whether it can resolve with pnp or not in the type-checking. ### Reference Coc config: https://github.com/oxc-project/coc-oxc/blob/7a7f0d8f503d5cdb65da37232da6b865f159b42a/src/common.ts#L27-L36 Vscode oxc binary resolution reference: https://github.com/oxc-project/oxc-vscode/blob/main/client/findBinary.ts Oxlint-tsgolint executable resolution: https://github.com/oxc-project/oxc/blob/d3dcf5bc9718ebb4839be27062b5d82da2118e2e/crates/oxc_linter/src/tsgolint.rs#L1164-L1225 ## Checklist - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). - [x] I have set the packages that need to be released for my changes to be effective. - [x] I will check that all automated PR checks pass before the PR gets reviewed.
|
@RyanCavanaugh The issue is linked in the But perhaps you would like me to link it to this PR differently? |
|
I'd assume he wants it linked properly, not just a mention. To do this, you can add the following in the PR description: |
See: - https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-rc/ Ok, so this is a bit fiddly, so let me explain what's going on: - We used to have `tsc` from `typescript` (v6), written in JS, and `tsgo` from `@typescript/native-preview`, so they could both be installed at the same time. - With v7, the RC package takes over the `tsc` executable; it's now the Go implementation and there is no separate `tsgo`; so... - We change our global install to `7.0-rc`, and ditto for the in-project install. We no longer have a v6 install on disk. - Rename (the project-local) `bin/tsgo` to `bin/tsc`, and make it run `tsc` instead of `tsgo`. - We update our Neovim config to assume `tsc` is now the Go version instead of the JS version; but note that we still have to use the "tsgo" config from nvim-lspconfig, because that hasn't been renamed yet, it's just that we configure it to use the `tsc` executable now instead of `tsgo`. - The Go implementation still doesn't support Yarn PnP and the corresponding PR (microsoft/typescript-go#1966) is still open, so we maintain our hack wherein we use the `ts_ls` config from nvim-lspconfig to use typescript-language-server with a wired-in path to a Yarn-managed shim. This looks fragile, and it probably is, but it has been working for me ever since I added it a couple of months ago (in 011cee5, "feat(nvim): point ts_ls at Yarn PnP tsserver shim when available", 2026-05-12). - The Neovim config will use a repo-local `tsc` if one exists, but note that it assumes `tsc` is the Go implementation. So if you use it in a repo with v6 or older, it won't work, because v6 doesn't support `--lsp --stdio`. I'm not worried about this; I have only a few such repos, and I will update them next time I have to work in them. - Update references to `tsgo` in docs to reference `tsc` instead. - Note: the `_tsc` completions in the third-party zsh-completions repo still match the JS implementation instead of the Go one; presumably they will eventually update. I am not sure exactly where the command line options diverge, other than having seen that while v7 supports LSP via `tsc --lsp --stdio`, v6 does not. Finally, and most importantly, merely updating my dotfiles on other machines won't make all this magically start working: I will need to manually uninstall the old `typescript` package and install the new one: ``` npm uninstall -g typescript @typescript/native-preview npm install -g typescript@rc --min-release-age=0 ``` Change-Id: rmoqwxkqwoywkxrlwspqqmwwmktwqrvz
## Summary - Bound the generated TypeScript compatibility patch to TypeScript versions before 7.0.0, and committed the matching generated patch artifact so the compat patch generator stays reproducible. - Fixed optional patch handling so `optional!` patches fall back when a package no longer ships a target file (for example `lib/_tsc.js` in TypeScript 7 / TypeScript compatibility shims), instead of failing the install with `ENOENT`. - Added regression coverage for both direct `typescript@7.0.1-rc` installs and the TypeScript 7 side-by-side recommendation where the `typescript` ident aliases to `npm:@typescript/typescript6@^6.0.0`. - Stabilized the scoped `plugin-typescript` acceptance test by moving it from `@babel/traverse` (now reported by npm search metadata as having included types) to a fixture package that still exercises DefinitelyTyped scoped package insertion. - Added deferred version metadata for the changed workspaces. ## Test plan - `yarn workspace @yarnpkg/plugin-compat test:plugin-compat` - `yarn test:unit packages/plugin-patch` - `node ./scripts/run-yarn.js test:integration packages/acceptance-tests/pkg-tests-specs/sources/plugins/plugin-typescript.test.ts` - `yarn version check` ## Notes TypeScript 7 ships a native compiler package layout and no longer has the legacy JS compiler files patched by the existing PnP compatibility diff, such as `lib/_tsc.js`. Bounding the TypeScript compat patch makes this intent explicit, while the optional patch fallback fixes the broader bug that `optional!` patch failures caused by missing target files were still fatal. TypeScript 7 support should ship with microsoft/typescript-go#1966. Made with Cursor --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: John Doe <you@example.com> Co-authored-by: Maël Nison <mael.nison@mistral.ai>
|
What is a good way to try this PR in a yarn project? |
@proyectoramirez You can clone the repo and follow the How to build and run section of the contributing doc |
## Summary - Bound the generated TypeScript compatibility patch to TypeScript versions before 7.0.0, and committed the matching generated patch artifact so the compat patch generator stays reproducible. - Fixed optional patch handling so `optional!` patches fall back when a package no longer ships a target file (for example `lib/_tsc.js` in TypeScript 7 / TypeScript compatibility shims), instead of failing the install with `ENOENT`. - Added regression coverage for both direct `typescript@7.0.1-rc` installs and the TypeScript 7 side-by-side recommendation where the `typescript` ident aliases to `npm:@typescript/typescript6@^6.0.0`. - Stabilized the scoped `plugin-typescript` acceptance test by moving it from `@babel/traverse` (now reported by npm search metadata as having included types) to a fixture package that still exercises DefinitelyTyped scoped package insertion. - Added deferred version metadata for the changed workspaces. ## Test plan - `yarn workspace @yarnpkg/plugin-compat test:plugin-compat` - `yarn test:unit packages/plugin-patch` - `node ./scripts/run-yarn.js test:integration packages/acceptance-tests/pkg-tests-specs/sources/plugins/plugin-typescript.test.ts` - `yarn version check` ## Notes TypeScript 7 ships a native compiler package layout and no longer has the legacy JS compiler files patched by the existing PnP compatibility diff, such as `lib/_tsc.js`. Bounding the TypeScript compat patch makes this intent explicit, while the optional patch fallback fixes the broader bug that `optional!` patch failures caused by missing target files were still fatal. TypeScript 7 support should ship with microsoft/typescript-go#1966. Made with Cursor --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: John Doe <you@example.com> Co-authored-by: Maël Nison <mael.nison@mistral.ai>
Fix alias resolution and fallbackPool entry parsing
Closes #460
Co-authored with @gun-yu as a result of their changes (#1876) being merged in this PR
Motivation
This PR adds Plug'n'Play support natively to Typescript Go, following this issue: #460
It has been reviewed and supported by @arcanis, the lead maintainer of Yarn, and the original author of Yarn PnP.
Datadog has a frontend monorepo using yarn with over 6k packages, and seeing how TS Strada struggles with our current scaling, we decided to invest time in adding a native Yarn PnP support for Typescript Go.
This PnP implementation has been actively used in the IDE of more than 230 engineers at Datadog, and we're committed to fixing all issues reported to us.
Challenges
We did not integrate it in our CI yet as we still have several packages failing on build mode (most errors seem to be reported in the issues section of TS Corsa). Because the TS Corsa API is not available yet, we also couldn't integrate it properly with a fast lage setup unlike with the TS Strada API.
Changes
It's based on the main changes from the original yarn patch (microsoft/TypeScript@99f3e13) that the community has been maintaining for years throughout Typescript Strada updates, except that we implemented the official PnP specification so it doesn't depend on third-party code.
Implemented features:
Hostis initialized for both build and LSP modesinternal/module/resolver.gointernal/modulespecifiers/specifiers.gointernal/core/compileroptions.go.pnp.cjschangesTests