aspect-build/aspect-cli

[Bug]: tsconfig paths are not expanded correctly for relative paths

Closed this issue · 2 comments

What happened?

Relative paths in nested (not root) tsconfig.json files are not expanded and resolved correctly, but are missing the actual relative path to the project. The below explanation will provide a little bit more context.

Version

Development (host) and target OS/architectures:

Output of bazel --version: aspect 5.5.2 / custom built from main

Version of the Aspect rules, or other relevant rules from your
WORKSPACE or MODULE.bazel file:

Language(s) and/or frameworks involved:

typescript

How to reproduce

No response

Any other information?

I encountered some issues while trying to use aspect cli with configure. The problem occurs within a nested file structure with different tsconfig.json files e.g. at <project-root>/frontend/app1/tsconfig.json and relative paths configs.

Firstly the config file will be read and interpreted correctly like described in #396. But when I have the paths configured like so ("@/*": ["./src/*"]):

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

Like shown in the previous snippet, the paths config is relative. When using the cli those imports using @ cannot be resolved. Concretely the path will be expanded like the following: @/app/test -> src/app/test while normal dependencies should be formed like frontend/app1/src/app/test to be resolved correctly.

I got it working with the following hack, which simply returns the actual relative path of the tsconfig file and expands all paths found to be relative to it with the passed path:

diff --git a/gazelle/js/typescript/config.go b/gazelle/js/typescript/config.go
index 04d1b24..3678a98 100644
--- a/gazelle/js/typescript/config.go
+++ b/gazelle/js/typescript/config.go
@@ -70,10 +70,10 @@ func (tc *TsWorkspace) IsWithinTsRoot(f string) bool {
 }

 func (tc *TsWorkspace) ExpandPaths(from, f string) []string {
-       _, c := tc.getConfig(from)
+       rel, c := tc.getConfig(from)
        if c == nil {
                return []string{}
        }

-       return c.ExpandPaths(from, f)
+       return c.ExpandPaths(rel, from, f)
 }
diff --git a/gazelle/js/typescript/tsconfig.go b/gazelle/js/typescript/tsconfig.go
index 81666b4..0aabb60 100644
--- a/gazelle/js/typescript/tsconfig.go
+++ b/gazelle/js/typescript/tsconfig.go
@@ -7,6 +7,7 @@ import (
        "sort"
        "strings"

+       . "aspect.build/cli/gazelle/common/log"
        "github.com/msolo/jsonr"
        "github.com/sirupsen/logrus"
 )
@@ -69,6 +70,9 @@ func parseTsConfigJSON(cm *TsConfigMap, root, configDir string, tsconfigContent
                return nil, err
        }

+       BazelLog.Debugf("tsconfig.json: %v", c)
+       BazelLog.Debugf("root: %s, configDir: %s", root, configDir)
+
        var baseConfig *TsConfig
        if c.Extends != "" {
                base, err := parseTsConfigJSONFile(cm, root, path.Join(configDir, path.Dir(c.Extends)), path.Base(c.Extends))
@@ -144,7 +148,7 @@ func parseTsConfigJSON(cm *TsConfigMap, root, configDir string, tsconfigContent
 //
 // Path matching algorithm based on ESBuild implementation
 // Inspired by: https://github.com/evanw/esbuild/blob/deb93e92267a96575a6e434ff18421f4ef0605e4/internal/resolver/resolver.go#L1831-L1945
-func (c TsConfig) ExpandPaths(from, p string) []string {
+func (c TsConfig) ExpandPaths(rel, from, p string) []string {
        pathMap := c.Paths.Map

        possible := make([]string, 0)
@@ -183,6 +187,10 @@ func (c TsConfig) ExpandPaths(from, p string) []string {
                        matchedText := p[len(m.prefix) : len(p)-len(m.suffix)]
                        mappedPath := strings.Replace(originalPath, "*", matchedText, 1)

+                       if strings.HasPrefix(mappedPath, "./") {
+                               mappedPath = path.Join(rel, mappedPath[2:])
+                       }
+
                        mappedPath = path.Clean(mappedPath)

                        possible = append(possible, path.Join(c.Paths.Rel, mappedPath))

@joelMuehlena thanks for the detailed bug report. It looks like you already tried your fix out? Want to open a PR with that fix (along with some tests)?

Yes, I can do that.