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 /.
This commit is contained in:
Manuel Stahl
2026-03-07 22:29:39 +01:00
parent a65798c207
commit 3f4a0e7a83
3 changed files with 96 additions and 2 deletions
+82
View File
@@ -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)));
});
+1
View File
@@ -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",
+13 -2
View File
@@ -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<compat/typescript>":
version: 5.9.3
resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=8c6c40"
resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/6f7e53bf0d9702350deeb6f35e08b69cbc8b958c33e0ec77bdc0ad6a6c8e280f3959dcbfde6f5b0848bece57810696489deaaa53d75de3578ff255d168c1efbd
checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430
languageName: node
linkType: hard