Summary
createDefaultMapFromNodeModules locates the bundled lib.*.d.ts with path.dirname(require.resolve("typescript")). When typescript resolves to a re-export wrapper instead of a normal install, that path is the wrapper's own lib/ directory, which does not contain the stdlib .d.ts files. The returned map is built without them, and the VFS later throws when the program asks for one.
This reproduces with the official TypeScript 7.0 side-by-side package, @typescript/typescript6.
Environment
@typescript/vfs 1.6.1 (the same code is in 1.6.4)
typescript aliased to @typescript/typescript6@6.0.2, per the TS 7.0 release notes, Running side-by-side with TypeScript 6.0: npm install -D typescript@npm:@typescript/typescript6
Repro
npm i -D typescript@npm:@typescript/typescript6
createDefaultMapFromNodeModules(compilerOptions) and build a program from the map.
Result:
TSVFS: A request was made for .../@typescript/typescript6/lib/lib.es2020.d.ts
but there wasn't a file found in the file map.
Root cause
@typescript/typescript6 is a thin re-export. Its entry file is:
// @typescript/typescript6/lib/typescript.js
module.exports = require('@typescript/old'); // = the real typescript@6.x, which ships the lib.*.d.ts
So the actual lib.*.d.ts live in the nested @typescript/old/lib/, not in @typescript/typescript6/lib/.
require.resolve("typescript") returns the wrapper's entry file (.../@typescript/typescript6/lib/typescript.js); it does not follow the runtime re-export. So in createDefaultMapFromNodeModules:
const lib = tsLibDirectory || path.dirname(require.resolve('typescript'));
// ...
const libFiles = fs.readdirSync(tsLibDirectory || path.dirname(require.resolve('typescript')));
lib points at the wrapper's lib/, which has typescript.js / tsc.js but none of the lib.*.d.ts. libFiles is therefore effectively empty and the default map is missing the stdlib.
Expected
The default map should include the stdlib lib.*.d.ts when typescript resolves through a re-export/alias, the same as for a plain install.
Suggested fix
Derive the lib directory from the compiler's getDefaultLibFilePath, which uses the executing file path and so follows the re-export to @typescript/old, instead of require.resolve:
const ts = _ts ?? require('typescript');
const libDir = tsLibDirectory ?? path.dirname(ts.getDefaultLibFilePath(compilerOptions));
This preserves the existing tsLibDirectory override, needs no change from callers (e.g. @ark/attest calls createDefaultMapFromNodeModules(compilerOptions) with no tsLibDirectory), and works for both plain and aliased/wrapped installs. Happy to open a PR.
Related
Summary
createDefaultMapFromNodeModuleslocates the bundledlib.*.d.tswithpath.dirname(require.resolve("typescript")). Whentypescriptresolves to a re-export wrapper instead of a normal install, that path is the wrapper's ownlib/directory, which does not contain the stdlib.d.tsfiles. The returned map is built without them, and the VFS later throws when the program asks for one.This reproduces with the official TypeScript 7.0 side-by-side package,
@typescript/typescript6.Environment
@typescript/vfs1.6.1 (the same code is in 1.6.4)typescriptaliased to@typescript/typescript6@6.0.2, per the TS 7.0 release notes, Running side-by-side with TypeScript 6.0:npm install -D typescript@npm:@typescript/typescript6Repro
npm i -D typescript@npm:@typescript/typescript6createDefaultMapFromNodeModules(compilerOptions)and build a program from the map.Result:
Root cause
@typescript/typescript6is a thin re-export. Its entry file is:So the actual
lib.*.d.tslive in the nested@typescript/old/lib/, not in@typescript/typescript6/lib/.require.resolve("typescript")returns the wrapper's entry file (.../@typescript/typescript6/lib/typescript.js); it does not follow the runtime re-export. So increateDefaultMapFromNodeModules:libpoints at the wrapper'slib/, which hastypescript.js/tsc.jsbut none of thelib.*.d.ts.libFilesis therefore effectively empty and the default map is missing the stdlib.Expected
The default map should include the stdlib
lib.*.d.tswhentypescriptresolves through a re-export/alias, the same as for a plain install.Suggested fix
Derive the lib directory from the compiler's
getDefaultLibFilePath, which uses the executing file path and so follows the re-export to@typescript/old, instead ofrequire.resolve:This preserves the existing
tsLibDirectoryoverride, needs no change from callers (e.g.@ark/attestcallscreateDefaultMapFromNodeModules(compilerOptions)with notsLibDirectory), and works for both plain and aliased/wrapped installs. Happy to open a PR.Related