Skip to content

Commit

Permalink
fix: add realpath to host to properly resolve monorepos (#332)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
agilgur5 authored May 30, 2022
1 parent d318e9a commit 8e58037
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 2 deletions.
9 changes: 7 additions & 2 deletions __tests__/host.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 8e58037

Please sign in to comment.