Add Yarn PnP module resolution support - #63919
Open
Guyllian Gomez (GGomez99) wants to merge 3 commits into
Open
Conversation
Ports the Yarn Plug'n'Play (PnP) support developed on guyllian.gomez/yarn-pnp in typescript-go into this repo's layout, adding a pnp resolver and vfs overlay so tsc/lsp can resolve modules through .pnp.cjs/.pnp.data.json manifests without node_modules. - internal/pnp: parses PnP manifests and resolves package/dependency locations - internal/vfs/pnpvfs: read-through vfs.FS overlay that exposes zip-packed dependencies (zip:/path/to.zip/inner/path) as regular files - module resolver, autoimport, and project/session wiring to plug the PnP resolver and vfs into the existing module resolution and LSP pipelines - new compiler/fourslash test fixtures and baselines under tsc/testdata covering PnP resolution scenarios - packages/vscode-typescript/src/client.ts: register the "zip" URI scheme in the LSP client's documentSelector so the extension attaches to files served from zip-packed PnP dependencies Ported and squashed from the original branch history to reconcile with divergent upstream refactors (module rename to github.com/microsoft/TypeScript/tsc, auto-import infrastructure rewrite) that landed in this repo's main before this port. Original work also includes contributions from valentinmelusson (fix package IDs for Yarn PnP submodule imports) and gun-yu (add pnp error handling and test). Co-Authored-By: valentinmelusson <valentin.melusson@datadoghq.com> Co-Authored-By: gun-yu <163322737+gun-yu@users.noreply.github.com>
Guyllian Gomez (GGomez99)
marked this pull request as ready for review
August 20, 2026 15:59
4 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
Adds native Yarn Plug’n’Play support across the compiler, language server, module resolution, and virtual filesystem.
Changes:
- Implements PnP manifest parsing, dependency resolution, type roots, and auto-imports.
- Adds zip/virtual-path filesystem support and manifest refresh handling.
- Adds compiler, resolver, VFS, and fourslash coverage with baselines.
Reviewed changes
Copilot reviewed 84 out of 88 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
tsc/testdata/tests/cases/compiler/pnpTypeRootsResolution.ts |
Tests PnP type resolution. |
tsc/testdata/tests/cases/compiler/pnpTransitiveDependencies.ts |
Tests transitive dependency isolation. |
tsc/testdata/tests/cases/compiler/pnpSimpleTest.ts |
Tests basic PnP imports. |
tsc/testdata/tests/cases/compiler/pnpDeclarationEmitWorkspace.ts |
Tests workspace declaration emit. |
tsc/testdata/fixtures/pnp/test-expectations.json |
Defines PnP resolver scenarios. |
tsc/testdata/baselines/reference/compiler/pnpTypeRootsResolution.types |
Type baseline. |
tsc/testdata/baselines/reference/compiler/pnpTypeRootsResolution.symbols |
Symbol baseline. |
tsc/testdata/baselines/reference/compiler/pnpTypeRootsResolution.js |
Emit baseline. |
tsc/testdata/baselines/reference/compiler/pnpTransitiveDependencies.types |
Type baseline. |
tsc/testdata/baselines/reference/compiler/pnpTransitiveDependencies.symbols |
Symbol baseline. |
tsc/testdata/baselines/reference/compiler/pnpTransitiveDependencies.js |
Emit baseline. |
tsc/testdata/baselines/reference/compiler/pnpTransitiveDependencies.errors.txt |
Diagnostic baseline. |
tsc/testdata/baselines/reference/compiler/pnpSimpleTest.types |
Type baseline. |
tsc/testdata/baselines/reference/compiler/pnpSimpleTest.symbols |
Symbol baseline. |
tsc/testdata/baselines/reference/compiler/pnpSimpleTest.js |
Emit baseline. |
tsc/testdata/baselines/reference/compiler/pnpDeclarationEmitWorkspace.types |
Type baseline. |
tsc/testdata/baselines/reference/compiler/pnpDeclarationEmitWorkspace.symbols |
Symbol baseline. |
tsc/testdata/baselines/reference/compiler/pnpDeclarationEmitWorkspace.js |
Emit baseline. |
tsc/internal/vfs/pnpvfs/pnpvfs.go |
Implements PnP zip and virtual-path VFS. |
tsc/internal/vfs/pnpvfs/pnpvfs_test.go |
Tests the PnP VFS. |
tsc/internal/vfs/cachedvfs/cachedvfs.go |
Propagates cache clearing. |
tsc/internal/tspath/path.go |
Detects zip paths. |
tsc/internal/tsoptions/tsoptionstest/vfsparseconfighost.go |
Enables PnP in config tests. |
tsc/internal/tsoptions/tsconfigparsing.go |
Exposes PnP through config hosts. |
tsc/internal/tsoptions/contentmappers_test.go |
Updates test host contracts. |
tsc/internal/transpile/transpile.go |
Updates compiler-host construction. |
tsc/internal/transformers/tstransforms/importelision_test.go |
Updates fake program contract. |
tsc/internal/testutil/tsbaseline/js_emit_baseline.go |
Preserves PnP loader fixtures. |
tsc/internal/testutil/harnessutil/harnessutil.go |
Initializes PnP in compiler tests. |
tsc/internal/project/snapshot.go |
Carries and refreshes PnP state. |
tsc/internal/project/session.go |
Wires PnP into sessions and watches. |
tsc/internal/project/projectcollectionbuilder.go |
Builds PnP manifest watchers. |
tsc/internal/project/project.go |
Stores project PnP watchers. |
tsc/internal/project/overlayfs.go |
Invalidates snapshots after installs. |
tsc/internal/project/filechange.go |
Adds a PnP install change kind. |
tsc/internal/project/extendedconfigcache_test.go |
Updates PnP-aware config tests. |
tsc/internal/project/configfileregistrybuilder.go |
Exposes PnP during config parsing. |
tsc/internal/project/compilerhost.go |
Exposes PnP from project hosts. |
tsc/internal/project/autoimport.go |
Carries PnP into auto-import cloning. |
tsc/internal/pnp/pnpapi.go |
Implements the PnP resolution API. |
tsc/internal/pnp/pnpapi_test.go |
Tests manifest loading and resolution. |
tsc/internal/pnp/pnp.go |
Initializes and identifies PnP loaders. |
tsc/internal/pnp/manifestparser.go |
Parses serialized PnP manifests. |
tsc/internal/modulespecifiers/types.go |
Extends specifier host contracts. |
tsc/internal/modulespecifiers/specifiers.go |
Generates PnP package specifiers. |
tsc/internal/modulespecifiers/specifiers_test.go |
Updates specifier test hosts. |
tsc/internal/module/types.go |
Extends module resolution hosts. |
tsc/internal/module/resolver.go |
Adds PnP module and type resolution. |
tsc/internal/module/resolver_test.go |
Tests PnP package IDs. |
tsc/internal/lsp/server.go |
Initializes PnP for LSP sessions. |
tsc/internal/lsp/lsproto/lsp.go |
Converts zip document URIs. |
tsc/internal/ls/lsconv/converters.go |
Emits zip document URIs. |
tsc/internal/ls/lsconv/converters_test.go |
Tests zip URI conversion. |
tsc/internal/ls/host.go |
Adds PnP to language-service hosts. |
tsc/internal/ls/findallreferences_test.go |
Updates compiler-host construction. |
tsc/internal/ls/file_rename_test.go |
Updates test host contracts. |
tsc/internal/ls/autoimport/util.go |
Uses PnP for auto-import resolution. |
tsc/internal/ls/autoimport/registry.go |
Indexes importable PnP packages. |
tsc/internal/ls/autoimport/aliasresolver.go |
Updates checker host contracts. |
tsc/internal/ls/autoimport/aliasresolver_crash_test.go |
Updates fake clone hosts. |
tsc/internal/fourslash/tests/pnpAutoImportCompletion_test.go |
Tests PnP auto-import completions. |
tsc/internal/execute/watcher.go |
Passes PnP into watch builds. |
tsc/internal/execute/tsctests/sys.go |
Adds PnP-aware test systems. |
tsc/internal/execute/tsc/extendedconfigcache_test.go |
Updates config test hosts. |
tsc/internal/execute/tsc/emit_test.go |
Updates test systems and hosts. |
tsc/internal/execute/tsc/compile.go |
Adds PnP to system contracts. |
tsc/internal/execute/tsc.go |
Passes PnP into compilation. |
tsc/internal/execute/incremental/buildinfo_contentmapper_test.go |
Updates incremental test hosts. |
tsc/internal/execute/build/orchestrator.go |
Passes PnP into build orchestration. |
tsc/internal/execute/build/host.go |
Exposes PnP from build hosts. |
tsc/internal/execute/build/compilerHost.go |
Exposes PnP from compiler hosts. |
tsc/internal/diagnostics/loc_generated.go |
Updates generated import ordering. |
tsc/internal/diagnostics/extraDiagnosticMessages.json |
Adds PnP diagnostics. |
tsc/internal/diagnostics/diagnostics_generated.go |
Generates PnP diagnostic definitions. |
tsc/internal/core/compileroptions.go |
Refactors type-root discovery. |
tsc/internal/compiler/projectreferencedtsfakinghost.go |
Forwards PnP through reference hosts. |
tsc/internal/compiler/program.go |
Exposes PnP from programs. |
tsc/internal/compiler/program_test.go |
Updates compiler-host calls. |
tsc/internal/compiler/host.go |
Stores PnP on compiler hosts. |
tsc/internal/compiler/emitHost.go |
Exposes PnP during emit. |
tsc/internal/compiler/emit_test.go |
Updates compiler-host calls. |
tsc/internal/compiler/contentmapper_test.go |
Updates compiler-host calls. |
tsc/internal/checker/checker_test.go |
Updates compiler-host calls. |
tsc/internal/api/server.go |
Initializes PnP for API sessions. |
tsc/cmd/tsc/sys.go |
Initializes PnP for the CLI. |
packages/vscode-typescript/src/client.ts |
Registers the zip URI scheme. |
Files not reviewed (2)
- tsc/internal/diagnostics/diagnostics_generated.go: Generated file
- tsc/internal/diagnostics/loc_generated.go: Generated file
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+382
to
+395
| func (p *PnpApi) AppendPnpTypeRoots(nmTypes []string, currentDirectory string, compilerOptions *core.CompilerOptions, nmFromConfig bool) ([]string, bool) { | ||
| baseDir := compilerOptions.GetBaseDirFromOptions(currentDirectory) | ||
| pnpTypes := p.GetPnpTypeRoots(baseDir) | ||
|
|
||
| if len(nmTypes) > 0 { | ||
| return append(nmTypes, pnpTypes...), nmFromConfig | ||
| } | ||
|
|
||
| if len(pnpTypes) > 0 { | ||
| return pnpTypes, false | ||
| } | ||
|
|
||
| return nil, false | ||
| } |
Comment on lines
+786
to
+788
| if isAlias && dep.AliasName == toLocator.Name && dep.Reference == toLocator.Reference { | ||
| pnpPackageName = toLocator.Name | ||
| break |
Comment on lines
+213
to
+214
| data.packageRegistryMap[ident][reference] = packageInfo | ||
| data.addPackageToTrie(ident, reference, packageInfo) |
| return err | ||
| } | ||
|
|
||
| p.manifest = newData |
Comment on lines
+1732
to
+1735
| pnpApi := pnp.InitPnpApi(s.fs, cwd) | ||
| if pnpApi != nil { | ||
| s.fs = pnpvfs.From(s.fs) | ||
| } |
Comment on lines
+161
to
+171
| cachedReader, ok := pnpFS.cachedZipReadersMap[zipPath] | ||
| if ok { | ||
| usedReader = cachedReader | ||
| } else { | ||
| zipReader, err := zip.OpenReader(zipPath) | ||
| if err != nil { | ||
| return pnpFS.fs, path, "" | ||
| } | ||
|
|
||
| usedReader = zipReader | ||
| pnpFS.cachedZipReadersMap[zipPath] = usedReader |
Comment on lines
+356
to
+361
| var prefix string | ||
| if tspath.IsZipPath(fileName) { | ||
| prefix = "zip:" | ||
| } else { | ||
| prefix = "file:" | ||
| } |
Comment on lines
+99
to
+102
| func (pnpFS *pnpFS) UseCaseSensitiveFileNames() bool { | ||
| // pnp fs is always case sensitive | ||
| return true | ||
| } |
…ests Co-Authored-By: ejc3 <ejc3@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #63769
See original PR: microsoft/typescript-go#1966
Git history has been lost as replaying all commits would have generated a huge amount of unnecessary conflicts
Co-authored with gun-yu (@gun-yu), EJ (@ejc3) and Valentin MELUSSON (@valentinmelusson), original commits were lost during the move from the old repo
Motivation
This PR adds Plug'n'Play support natively to Typescript Go, following this issue: #63769
It has been reviewed and supported by Maël Nison (@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 is being actively used by all engineers at Datadog, including in IDEs, by agents, and it's currently being beta-tested in CI with a custom lage setup.
Changes
It's based on the main changes from the original yarn patch (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