Skip to content

Angular adapter does not pass the configured tsconfig to esbuild BuildOptions #98

Description

@oparicio-mmc

Summary

createAngularEsbuildContext receives and normalizes the Angular tsConfigPath, and passes it to the Angular compiler plugin, but it does not pass that path to the esbuild.BuildOptions object used by esbuild.context.

As a result, esbuild's own resolver does not honor baseUrl/paths from the configured Angular tsconfig for imports that esbuild resolves outside the Angular compiler program. Builds then depend on esbuild's automatic discovery of a file named exactly tsconfig.json, which does not cover common Angular/Nx layouts using tsconfig.app.json and tsconfig.lib.json.

This is the v4 equivalent of angular-architects/module-federation-plugin#848.

Affected version

  • native-federation/angular-adapter v22.0.6
  • Current main: 2221011563edb2ea5b0afa1003e6895b54e9e356

The failure was reproduced against the historical Angular adapter in @angular-architects/native-federation 21.2.5. Source inspection shows the same omission in v22.0.6.

Reproduction shape

Use an Angular/Nx workspace with:

  1. An application configured with tsConfig: apps/example/tsconfig.app.json.

  2. Path mappings in a base config, for example:

    {
      "compilerOptions": {
        "baseUrl": ".",
        "paths": {
          "example/contracts": ["libs/example/contracts/src/index.ts"]
        }
      }
    }
  3. A workspace library with tsconfig.lib.json but no ancestor file named exactly tsconfig.json containing the same mappings.

  4. A transitive value import from that library to example/contracts.

  5. A Native Federation production build.

The federation artefact build can fail with:

Could not resolve "example/contracts"

Adding a conveniently placed tsconfig.json, or patching the adapter to pass the configured tsconfig to esbuild, makes the same build succeed.

Root cause

src/tools/esbuild/angular-bundler.ts already normalizes the configured path:

tsConfigPath = path.join(workspaceRoot, tsConfigPath);

It also passes it to CompilerPluginOptions:

const pluginOptions: CompilerPluginOptions = {
  tsconfig: tsConfigPath,
  // ...
};

However, the separate config: esbuild.BuildOptions passed to esbuild.context(config) has no tsconfig property. Angular's compiler plugin uses onLoad, while static import resolution still goes through esbuild, so configuring only the compiler plugin is insufficient.

Proposed fix

Pass the already-normalized absolute path to esbuild as well:

 const config: esbuild.BuildOptions = {
   // ...
   resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
+  tsconfig: tsConfigPath,
 };

Suggested regression test

  • Capture/mock the argument passed to esbuild.context.
  • Assert that BuildOptions.tsconfig equals the normalized absolute tsConfigPath.
  • Ideally include a fixture where an application uses tsconfig.app.json, a library uses tsconfig.lib.json, and a transitive value import relies on a root path mapping.

I can prepare a focused PR if this approach is accepted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions