diff --git a/packages/compiler/src/frontend/lowering/lowerer.ts b/packages/compiler/src/frontend/lowering/lowerer.ts index 7a2bfd9e7..747e396af 100644 --- a/packages/compiler/src/frontend/lowering/lowerer.ts +++ b/packages/compiler/src/frontend/lowering/lowerer.ts @@ -181,7 +181,7 @@ export { newFnCtx, type FnCtx } from "./function-context.js"; * - Lexical scoping is resolved here: locals get function-unique ids * ("x.0", "x.1" for shadowing); the IR is scope-flat. */ -import { dirname, resolve } from "node:path"; +import { dirname, relative, resolve } from "node:path"; import { bindingSource } from "../binding-source.js"; import { tsgoPath } from "../dts-paths.js"; import * as ts from "../ts7/adapter.js"; @@ -3192,12 +3192,14 @@ export class Lowerer { errorPropertyDispatchRevision: string | null = null; /** Structural source positions (structural-positions.ts) for names that * stay the same when unrelated code moves. */ - /** Default library files are named by their base name: two checkers - * install them in different directories. */ + /** Files are named by their path relative to the entry's directory, so + * names do not depend on where the program sits, and default library + * files by their base name: two checkers install them in different + * directories. */ readonly positions = new StructuralPositions((sf) => this.program.isSourceFileDefaultLibrary(sf) ? `lib:${sf.fileName.slice(sf.fileName.lastIndexOf("/") + 1)}` - : sf.fileName, + : relative(dirname(this.entry.fileName), sf.fileName), ); /** The outermost node whose token span is exactly `loc`, or null. */ diff --git a/packages/compiler/test/stable-names.test.ts b/packages/compiler/test/stable-names.test.ts index 720472556..c82db039d 100644 --- a/packages/compiler/test/stable-names.test.ts +++ b/packages/compiler/test/stable-names.test.ts @@ -110,3 +110,10 @@ console.log(box.value); expect(locals).toHaveLength(2); expect(locals[0]!.name).not.toBe(locals[1]!.name); }); + +test("names do not depend on the directory the program sits in", async () => { + // Each call lowers in its own temporary directory. + const [first] = await lower(recursive); + const [second] = await lower(recursive); + expect(names(second!)).toEqual(names(first!)); +});