From 3a20b17bf4f8df8d85f587dc9705a7d6866ec6ce Mon Sep 17 00:00:00 2001 From: Tryggvi Gylfason Date: Sun, 28 Jan 2024 18:58:45 +0100 Subject: [PATCH] Support tsConfig paths aliases during move operations --- .../src/compiler/ast/module/SourceFile.ts | 21 +++++++++- .../ts-morph/src/tests/issues/1494tests.ts | 42 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 packages/ts-morph/src/tests/issues/1494tests.ts diff --git a/packages/ts-morph/src/compiler/ast/module/SourceFile.ts b/packages/ts-morph/src/compiler/ast/module/SourceFile.ts index def51545d..ccc31b572 100644 --- a/packages/ts-morph/src/compiler/ast/module/SourceFile.ts +++ b/packages/ts-morph/src/compiler/ast/module/SourceFile.ts @@ -1023,7 +1023,26 @@ export class SourceFile extends SourceFileBase { function updateStringLiteralReferences(nodeReferences: ReadonlyArray<[StringLiteral, SourceFile]>) { for (const [stringLiteral, sourceFile] of nodeReferences) { - if (ModuleUtils.isModuleSpecifierRelative(stringLiteral.getLiteralText())) + const compilerOptions = stringLiteral._context.compilerOptions.get() + const literalText = stringLiteral.getLiteralText(); + const paths = compilerOptions?.paths; + // See https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths + if (paths) { + // Check if the string literal is a path alias specifier + for (const [path, mappings] of Object.entries(paths)) { + const hasWildCard = path.endsWith('*'); + const [alias] = path.split('*'); + if (literalText.startsWith(alias) && hasWildCard) { + const pathAfterAlias = FileUtils.pathJoin( + stringLiteral._sourceFile.getDirectoryPath(), + stringLiteral._sourceFile.getRelativePathAsModuleSpecifierTo(sourceFile) + ); + stringLiteral.setLiteralValue(FileUtils.pathJoin(alias, pathAfterAlias)); + break; + } + } + } + if (ModuleUtils.isModuleSpecifierRelative(literalText)) stringLiteral.setLiteralValue(stringLiteral._sourceFile.getRelativePathAsModuleSpecifierTo(sourceFile)); } } diff --git a/packages/ts-morph/src/tests/issues/1494tests.ts b/packages/ts-morph/src/tests/issues/1494tests.ts new file mode 100644 index 000000000..4333ba873 --- /dev/null +++ b/packages/ts-morph/src/tests/issues/1494tests.ts @@ -0,0 +1,42 @@ +import { ts } from "@ts-morph/common"; +import { expect } from "chai"; +import { Project } from "../../Project"; + +describe("tests for my issue", () => { + it("should update specifiers with path aliases during move", () => { + const project = new Project({ + useInMemoryFileSystem: true, + compilerOptions: { + baseUrl: '.', + paths: { + 'Root/*': ['./*'] + } + } + }); + + const fileAtRoot = project.createSourceFile("a.ts", `export function foo() {}`); + const fileAtNestedFolder = project.createSourceFile("nested/deep/a.ts", `export function nested() {}`); + + const relImportRoot = project.createSourceFile("b.ts", `import { foo } from "./a";`); + const relImportFolder = project.createSourceFile("/b/b.ts", `import { foo } from "../a";`); + + const aliasImport = project.createSourceFile("c.ts", `import { foo } from "Root/a";`); + const aliasExport = project.createSourceFile("d.ts", `export { foo } from "Root/a";`); + const aliasImportFolder = project.createSourceFile("/c/c.ts", `import { foo } from "Root/a";`); + const aliasToNestedFolder = project.createSourceFile("/d/d.ts", `import { nested } from "Root/nested/deep/a";`); + + + const dir = project.createDirectory('./a'); + const deepDir = project.createDirectory('./nested/deep/deeper'); + fileAtRoot.moveToDirectory(dir) + fileAtNestedFolder.moveToDirectory(deepDir) + + expect(relImportRoot.getFullText()).to.equal(`import { foo } from "./a/a";`); + expect(relImportFolder.getFullText()).to.equal(`import { foo } from "../a/a";`); + + expect(aliasImport.getFullText()).to.equal(`import { foo } from "Root/a/a";`); + expect(aliasImportFolder.getFullText()).to.equal(`import { foo } from "Root/a/a";`); + expect(aliasToNestedFolder.getFullText()).to.equal(`import { nested } from "Root/nested/deep/deeper/a";`); + expect(aliasExport.getFullText()).to.equal(`export { foo } from "Root/a/a";`); + }); +});