From 8e580375e102b627ff35648d0d62ba2d048825ed Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Mon, 30 May 2022 12:10:48 -0400 Subject: [PATCH] fix: add `realpath` to host to properly resolve monorepos (#332) * fix: add `realpath` to host to properly resolve monorepos - tested this in a pnpm repo with symlinked deps and it worked there, so I believe this fixes all pnpm issues - it may also fix some Lerna issues if they were due to symlinks, but I didn't check those - not sure about others, e.g. Rush, Yarn workspaces, Yarn PnP - I figured out this was needed by staring at the TS source code and then I found this line: https://github.com/microsoft/TypeScript/blob/67673f324dd5f9398bb53fd16bf75efd155c32e7/src/compiler/moduleNameResolver.ts#L1412 - it expects `host.realpath` to be implemented for TS's `realPath` to work correctly, otherwise it just returns the path with no transformation (i.e. the path to the symlink instead of the realpath) - this is not documented _anywhere_ and we were hitting this when calling `getEmitOutput`, before even using `moduleNameResolver` - so I just tried implementing it... and it worked! - notably, the other Rollup TS plugins don't implement this either??? - not sure how they don't error on this?? - note that I added a `!` as `realpath` doesn't have to be implemented on `ts.sys`... but it is in the default implementation (see comment) - I originally had a ternary with `fs.realpathSync` if it didn't exist but that is literally what the default implementation uses - can add this back in the future if it becomes an issue * fix realpath test on windows --- __tests__/host.spec.ts | 9 +++++++-- src/host.ts | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/__tests__/host.spec.ts b/__tests__/host.spec.ts index 21ce7178..7248f034 100644 --- a/__tests__/host.spec.ts +++ b/__tests__/host.spec.ts @@ -2,7 +2,7 @@ import { afterAll, beforeAll, test, expect, jest } from "@jest/globals"; import * as ts from "typescript"; import * as path from "path"; import { normalizePath as normalize } from "@rollup/pluginutils"; -import { remove, ensureDir, writeFile } from "fs-extra"; +import { remove, ensureDir, writeFile, ensureSymlink } from "fs-extra"; import { setTypescriptModule } from "../src/tsproxy"; import { LanguageServiceHost } from "../src/host"; @@ -18,12 +18,14 @@ const unaryFuncSnap = { text: unaryFunc }; const local = (x: string) => normalize(path.resolve(__dirname, x)); const testDir = local("__temp/host"); const testFile = `${testDir}/file.ts`; +const linkedTestFile = `${testDir}/link.ts`; const nonExistent = `${testDir}/this-does-not-exist.ts`; afterAll(() => remove(testDir)); beforeAll(async () => { await ensureDir(testDir); await writeFile(testFile, unaryFunc, "utf8"); + await ensureSymlink(testFile, linkedTestFile); }); test("LanguageServiceHost", async () => { @@ -55,10 +57,13 @@ test("LanguageServiceHost", async () => { expect(host.directoryExists(nonExistent)).toBeFalsy(); expect(host.fileExists(nonExistent)).toBeFalsy(); expect(host.fileExists(testFile)).toBeTruthy(); - expect(host.readDirectory(testDir)).toEqual([testFile]); + expect(host.readDirectory(testDir)).toEqual([testFile, linkedTestFile]); expect(host.readFile(nonExistent)).toBeFalsy(); expect(host.readFile(testFile)).toEqual(unaryFunc); expect(host.useCaseSensitiveFileNames()).toBe(process.platform === "linux"); + // test realpath w/ symlinks. this returns a host path, so expect path.normalize() + expect(host.realpath(testFile)).toEqual(path.normalize(testFile)); + expect(host.realpath(linkedTestFile)).toEqual(path.normalize(testFile)); // test misc functionality expect(host.getCompilationSettings()).toEqual(testOpts); diff --git a/src/host.ts b/src/host.ts index 6c1d4c55..a0831635 100644 --- a/src/host.ts +++ b/src/host.ts @@ -104,6 +104,11 @@ export class LanguageServiceHost implements tsTypes.LanguageServiceHost return tsModule.sys.fileExists(path); } + public realpath(path: string): string + { + return tsModule.sys.realpath!(path); // this exists in the default implementation: https://github.com/microsoft/TypeScript/blob/ab2523bbe0352d4486f67b73473d2143ad64d03d/src/compiler/sys.ts#L1288 + } + public getTypeRootsVersion(): number { return 0;