From 3f4a0e7a831dcab75547122f617a853c23f36832 Mon Sep 17 00:00:00 2001 From: Manuel Stahl Date: Sat, 7 Mar 2026 22:29:39 +0100 Subject: [PATCH] Patch TypeScript PnP resolution for VSCode SDK Add a ts-pnp-backed sdk.user.cjs hook so the Yarn TypeScript SDK can resolve package exports and type references under Plug'n'Play. The hook is anchored to the workspace instead of process.cwd(), which fixes VSCode tsserver startup when the server process starts from /. --- .yarn/sdks/typescript/lib/sdk.user.cjs | 82 ++++++++++++++++++++++++++ package.json | 1 + yarn.lock | 15 ++++- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 .yarn/sdks/typescript/lib/sdk.user.cjs diff --git a/.yarn/sdks/typescript/lib/sdk.user.cjs b/.yarn/sdks/typescript/lib/sdk.user.cjs new file mode 100644 index 0000000..b3922b3 --- /dev/null +++ b/.yarn/sdks/typescript/lib/sdk.user.cjs @@ -0,0 +1,82 @@ +const {createRequire} = require(`module`); +const fs = require(`fs`); +const path = require(`path`); + +const workspaceRoot = path.resolve(__dirname, `../../../../`); +const workspaceRequire = createRequire(path.join(workspaceRoot, `package.json`)); +const {resolveModuleName: resolvePnPModuleName} = workspaceRequire(`ts-pnp`); +const debugLogPath = path.join(workspaceRoot, `.yarn/sdks/typescript/sdk-user.log`); + +const wrappedExports = new WeakMap(); + +function debugLog(...args) { + try { + fs.appendFileSync(debugLogPath, `${new Date().toISOString()} ${args.join(` `)}\n`); + } catch {} +} + +function wrapResolver(target, property) { + const original = target[property]; + if (typeof original !== `function`) { + return original; + } + + return function patchedResolver(request, issuer, compilerOptions, host, ...rest) { + let result; + try { + result = original.call(this, request, issuer, compilerOptions, host, ...rest); + } catch (error) { + debugLog(`original-${property}-throw`, request, issuer, error && (error.stack || error.message || String(error))); + throw error; + } + + if (result?.resolvedModule || result?.resolvedTypeReferenceDirective) { + return result; + } + + try { + return resolvePnPModuleName( + request, + issuer, + compilerOptions, + host, + (nextRequest, nextIssuer, nextOptions, nextHost) => + original.call(this, nextRequest, nextIssuer, nextOptions, nextHost, ...rest) + ); + } catch (error) { + debugLog(`pnp-${property}-throw`, request, issuer, error && (error.stack || error.message || String(error))); + throw error; + } + }; +} + +module.exports = exports => { + if (!exports || (typeof exports !== `object` && typeof exports !== `function`)) { + return exports; + } + + if (wrappedExports.has(exports)) { + return wrappedExports.get(exports); + } + + const proxy = new Proxy(exports, { + get(target, property, receiver) { + if (property === `resolveModuleName` || property === `resolveTypeReferenceDirective`) { + return wrapResolver(target, property); + } + + return Reflect.get(target, property, receiver); + }, + }); + + wrappedExports.set(exports, proxy); + return proxy; +}; + +process.on(`uncaughtException`, error => { + debugLog(`uncaughtException`, error && (error.stack || error.message || String(error))); +}); + +process.on(`unhandledRejection`, error => { + debugLog(`unhandledRejection`, error && (error.stack || error.message || String(error))); +}); diff --git a/package.json b/package.json index 1802789..56ed158 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "prettier": "^3.8.1", "react-test-renderer": "^19.2.4", "ts-node": "^10.9.2", + "ts-pnp": "^1.2.0", "typescript": "^5.9.3", "typescript-eslint": "^8.56.1", "vite": "^7.3.1", diff --git a/yarn.lock b/yarn.lock index ffa0454..cafbb74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7101,6 +7101,7 @@ __metadata: react-router-dom: "npm:^7.13.1" react-test-renderer: "npm:^19.2.4" ts-node: "npm:^10.9.2" + ts-pnp: "npm:^1.2.0" typescript: "npm:^5.9.3" typescript-eslint: "npm:^8.56.1" vite: "npm:^7.3.1" @@ -7224,6 +7225,16 @@ __metadata: languageName: node linkType: hard +"ts-pnp@npm:^1.2.0": + version: 1.2.0 + resolution: "ts-pnp@npm:1.2.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ff32b4f810f9d99f676d70fe2c0e327cb6c812214bd4fc7135870b039f9e85a85b2c20f8fe030d9bd36e9598a12faa391f10aecb95df624b92f1af6bd47dc397 + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -7398,11 +7409,11 @@ __metadata: "typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": version: 5.9.3 - resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=8c6c40" + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/6f7e53bf0d9702350deeb6f35e08b69cbc8b958c33e0ec77bdc0ad6a6c8e280f3959dcbfde6f5b0848bece57810696489deaaa53d75de3578ff255d168c1efbd + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 languageName: node linkType: hard