Skip to content

Add Yarn PnP module resolution support - #63919

Open
Guyllian Gomez (GGomez99) wants to merge 3 commits into
microsoft:mainfrom
GGomez99:guyllian.gomez/yarn-pnp
Open

Add Yarn PnP module resolution support#63919
Guyllian Gomez (GGomez99) wants to merge 3 commits into
microsoft:mainfrom
GGomez99:guyllian.gomez/yarn-pnp

Conversation

@GGomez99

@GGomez99 Guyllian Gomez (GGomez99) commented Aug 20, 2026

Copy link
Copy Markdown

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:

  • PnP VFS that handles virtual folders and zip files seamlessly, with caching and fallback to the original vfs if pnp is not available
  • Add PnP API and manifest handling, following the yarn PnP specification
  • Initialize the PnP API every time a Host is initialized for both build and LSP modes
  • Add PnP support when resolving modules in internal/module/resolver.go
  • Add PnP support for auto-imports and completion at internal/modulespecifiers/specifiers.go
  • Add PnP support for root types at internal/core/compileroptions.go
  • Handle zip paths when going to implementation with the LSP
  • Update the baseline testing framework to handle PnP when needed
  • PnP manifest auto-refresh by watching .pnp.cjs changes

Tests

  • Basic PnP setup
  • Types from transitive dependencies
  • Root types loading from PnP dependencies
  • Completion and autoimports

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>
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Aug 20, 2026
@GGomez99
Guyllian Gomez (GGomez99) marked this pull request as ready for review August 20, 2026 15:59
Copilot AI balanced review requested due to automatic review settings August 20, 2026 15:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 thread tsc/internal/module/resolver.go Outdated
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
}
Comment thread tsc/testdata/fixtures/pnp/test-expectations.json Outdated
…ests

Co-Authored-By: ejc3 <ejc3@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Yarn PnP

2 participants