Skip to content

Fix importModuleSpecifier: "project-relative" ignored when tsconfig paths are configured#3953

Merged
RyanCavanaugh merged 3 commits into
mainfrom
copilot/fix-ts-native-import-module-specifier
May 19, 2026
Merged

Fix importModuleSpecifier: "project-relative" ignored when tsconfig paths are configured#3953
RyanCavanaugh merged 3 commits into
mainfrom
copilot/fix-ts-native-import-module-specifier

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 18, 2026

With project-relative and a tsconfig defining paths aliases, intra-project auto-imports were emitting the alias (e.g. @utils/helper) instead of a relative path (e.g. ../utils/helper).

Two regressions in getLocalModuleSpecifier (internal/modulespecifiers/specifiers.go) vs. the TS reference, both inside the RelativePreferenceExternalNonRelative branch:

  • projectDirectory included the config filename. Used compilerOptions.ConfigFilePath directly instead of its directory, so sourceIsInternal/targetIsInternal were always false (no file path starts with …/tsconfig.json). This silently disabled the tsconfig-boundary check that decides when crossing the project root should prefer the alias.
  • Conditional return relativePath. After the boundary checks confirm an intra-package import, TS returns relativePath unconditionally; the port gated it on len(fromPackageJsonImports) > 0 and otherwise fell through to the "fewer components wins" tiebreaker, which always chose the short paths alias over a deeper relative path.

Changes

  • Compute projectDirectory as tspath.GetDirectoryPath(compilerOptions.ConfigFilePath).
  • Drop the fromPackageJsonImports gate and return relativePath unconditionally at the end of the ExternalNonRelative block.
  • Switch the source/target internal classification from strings.HasPrefix to tspath.Path.ContainsPath, which enforces a separator boundary so siblings like /project vs /project2 aren't conflated.
  • Add fourslash regression TestImportModuleSpecifierPreferenceProjectRelativeWithPaths reproducing the issue scenario (tsconfig with paths, intra-project import, project-relative).

The pre-existing TestImportNameCodeFix_externalNonRelateive2 was baselining the buggy behavior; with the projectDirectory fix the boundary check now fires correctly and its existing assertions (alias for cross-boundary, relative for intra-package) hold without changes.

Copilot AI changed the title [WIP] Fix import module specifier issue in TypeScript extension Fix importModuleSpecifier: "project-relative" ignored when tsconfig paths are configured May 18, 2026
Copilot AI requested a review from jakebailey May 18, 2026 14:57
Copy link
Copy Markdown
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a bug? Not sure why projectDirectory would be the config file path, which is a filename, not a dir name.

@jakebailey jakebailey marked this pull request as ready for review May 18, 2026 15:32
Copilot AI review requested due to automatic review settings May 18, 2026 15:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in module specifier selection so that ImportModuleSpecifierPreferenceProjectRelative correctly prefers relative paths for intra-project auto-imports even when tsconfig.json paths aliases exist.

Changes:

  • Compute projectDirectory from the directory containing compilerOptions.ConfigFilePath (not the config filename itself).
  • Always return the computed relativePath for intra-package imports in the RelativePreferenceExternalNonRelative branch.
  • Add a new fourslash regression test + baseline for project-relative with paths aliases.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/modulespecifiers/specifiers.go Fixes project directory detection and ensures intra-package imports return relative specifiers under ExternalNonRelative.
internal/fourslash/tests/importModuleSpecifierPreference_test.go Adds regression test covering project-relative with paths aliases.
testdata/baselines/reference/fourslash/autoImports/importModuleSpecifierPreferenceProjectRelativeWithPaths.baseline.md Adds expected baseline showing a relative import is chosen.

var projectDirectory tspath.Path
if len(compilerOptions.ConfigFilePath) > 0 {
projectDirectory = tspath.ToPath(compilerOptions.ConfigFilePath, host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames())
projectDirectory = tspath.ToPath(tspath.GetDirectoryPath(compilerOptions.ConfigFilePath), host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot consider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched the boundary check from strings.HasPrefix to tspath.Path.ContainsPath in be9152f, which has a proper separator-boundary check so /project vs /project2 aren't conflated. The TS reference uses plain startsWith here so it has the same latent issue, but since both projectDirectory and the source/module paths are already-normalized tspath.Path values, Path.ContainsPath is the natural fit on the Go side.

Copilot AI requested a review from jakebailey May 18, 2026 16:19
@RyanCavanaugh RyanCavanaugh enabled auto-merge May 19, 2026 16:53
@RyanCavanaugh RyanCavanaugh added this pull request to the merge queue May 19, 2026
Merged via the queue into main with commit 10ff693 May 19, 2026
21 checks passed
@RyanCavanaugh RyanCavanaugh deleted the copilot/fix-ts-native-import-module-specifier branch May 19, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

importModuleSpecifier: "project-relative" is ignored by TS Native extension (forces tsconfig paths instead of relative imports)

4 participants