From 3feb45d06e252530fe29369ec9db2f0b53035289 Mon Sep 17 00:00:00 2001 From: gantar Date: Tue, 2 Jun 2026 10:46:49 +0700 Subject: [PATCH] fix: transform baseUrl-only imports --- src/utils/resolve-path-update-node.ts | 20 ++++++++++-- test/projects/base-url-only/src/index.ts | 6 ++++ test/projects/base-url-only/src/local.ts | 1 + .../base-url-only/src/nested/value.ts | 1 + test/projects/base-url-only/tsconfig.json | 18 +++++++++++ test/tests/transformer/base-url-only.test.ts | 32 +++++++++++++++++++ 6 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test/projects/base-url-only/src/index.ts create mode 100644 test/projects/base-url-only/src/local.ts create mode 100644 test/projects/base-url-only/src/nested/value.ts create mode 100644 test/projects/base-url-only/tsconfig.json create mode 100644 test/tests/transformer/base-url-only.test.ts diff --git a/src/utils/resolve-path-update-node.ts b/src/utils/resolve-path-update-node.ts index e6a1dfe..26b0ba2 100755 --- a/src/utils/resolve-path-update-node.ts +++ b/src/utils/resolve-path-update-node.ts @@ -1,7 +1,7 @@ import type ts from "typescript"; import type { VisitorContext } from "../types.ts"; -import { isURL, maybeAddRelativeLocalPrefix } from "./general-utils.ts"; +import { isBaseDir, isURL, maybeAddRelativeLocalPrefix } from "./general-utils.ts"; import { resolveModuleName } from "./resolve-module-name.ts"; import { isModulePathsMatch } from "./ts-helpers.ts"; @@ -30,11 +30,13 @@ export function resolvePathAndUpdateNode( } /* Resolve Module */ - // Skip if no paths match found - if (!isModulePathsMatch(context, moduleName)) return node; + const isPathsMatch = isModulePathsMatch(context, moduleName); + const isBaseUrlOnlyMatch = !isPathsMatch && isBaseUrlOnlyModule(context, moduleName); + if (!isPathsMatch && !isBaseUrlOnlyMatch) return node; const res = resolveModuleName(context, moduleName); if (!res) return node; + if (isBaseUrlOnlyMatch && !isResolvedInBaseUrl(context, res.resolvedPath)) return node; const { outputPath, resolvedPath } = res; @@ -106,3 +108,15 @@ export function resolvePathAndUpdateNode( } } } + +function isBaseUrlOnlyModule(context: VisitorContext, moduleName: string): boolean { + return !!(!context.pathsPatterns && context.compilerOptions.baseUrl && moduleName[0] !== "." && !isURL(moduleName)); +} + +function isResolvedInBaseUrl(context: VisitorContext, resolvedPath: string | undefined): boolean { + const { baseUrl } = context.compilerOptions; + if (!baseUrl || !resolvedPath) return false; + + const { normalizePath } = context.tsInstance; + return isBaseDir(normalizePath(baseUrl), normalizePath(resolvedPath)); +} diff --git a/test/projects/base-url-only/src/index.ts b/test/projects/base-url-only/src/index.ts new file mode 100644 index 0000000..dfcf536 --- /dev/null +++ b/test/projects/base-url-only/src/index.ts @@ -0,0 +1,6 @@ +import { localValue } from "local"; +import { nestedValue } from "nested/value"; +import type { CompilerOptions } from "typescript"; + +export const result = localValue + nestedValue; +export type { CompilerOptions }; diff --git a/test/projects/base-url-only/src/local.ts b/test/projects/base-url-only/src/local.ts new file mode 100644 index 0000000..88032c6 --- /dev/null +++ b/test/projects/base-url-only/src/local.ts @@ -0,0 +1 @@ +export const localValue = 1; diff --git a/test/projects/base-url-only/src/nested/value.ts b/test/projects/base-url-only/src/nested/value.ts new file mode 100644 index 0000000..33e7878 --- /dev/null +++ b/test/projects/base-url-only/src/nested/value.ts @@ -0,0 +1 @@ +export const nestedValue = 2; diff --git a/test/projects/base-url-only/tsconfig.json b/test/projects/base-url-only/tsconfig.json new file mode 100644 index 0000000..4e70bf6 --- /dev/null +++ b/test/projects/base-url-only/tsconfig.json @@ -0,0 +1,18 @@ +{ + "include": ["src"], + + "compilerOptions": { + "target": "es5", + "module": "ESNext", + "outDir": "__built", + "moduleResolution": "node", + + "declaration": true, + "baseUrl": "./src", + + "plugins": [ + { "transform": "typescript-transform-paths" }, + { "transform": "typescript-transform-paths", "afterDeclarations": true } + ] + } +} diff --git a/test/tests/transformer/base-url-only.test.ts b/test/tests/transformer/base-url-only.test.ts new file mode 100644 index 0000000..e32047a --- /dev/null +++ b/test/tests/transformer/base-url-only.test.ts @@ -0,0 +1,32 @@ +import assert from "node:assert"; +import * as path from "node:path"; +import { before, describe, test } from "node:test"; + +import { projectsPaths, type ts, tsModules } from "../../config.ts"; +import { createTsProgram, getEmitResultFromProgram, type EmittedFiles } from "../../utils/index.ts"; + +describe(`Transformer -> baseUrl-only Tests`, () => { + const projectRoot = path.join(projectsPaths, "base-url-only"); + const tsConfigFile = path.join(projectRoot, "tsconfig.json"); + + for (const [s, tsInstance] of tsModules) { + describe(`TypeScript ${s}`, () => { + const indexFile = (tsInstance as typeof ts).normalizePath(path.join(projectRoot, "src/index.ts")); + let transformedFiles: EmittedFiles = {}; + + before(() => { + const programWithTransformer = createTsProgram({ tsInstance: tsInstance as typeof ts, tsConfigFile }); + transformedFiles = getEmitResultFromProgram(programWithTransformer); + }); + + test(`transforms local baseUrl imports`, () => { + assert.match(transformedFiles[indexFile].js, /from "\.\/local"/); + assert.match(transformedFiles[indexFile].js, /from "\.\/nested\/value"/); + }); + + test(`does not transform external package imports`, () => { + assert.match(transformedFiles[indexFile].dts, /from "typescript"/); + }); + }); + } +});