Skip to content

InjectionToken instance duplication causes NG0201 errors #128

Description

@zoroaster00

I am encountering an issue very similar to Issue #119, which traces back to a structural defect in how plugins are wired during the build process.

Environment

  • Angular: 21.2.17
  • "@angular-architects/native-federation-v4": "21.2.9", "@softarc/native-federation": "4.4.0", "@softarc/native-federation-orchestrator": "4.5.3"
  • es-module-shims: 2.8.4
shared: {
    ...shareAll(
      { singleton: true, strictVersion: true, requiredVersion: 'auto' },
    ),
  },

sharedMappings: mappingsFromWorkspace({ singleton: true, includeSecondaries: { resolveGlob: true } }).get(),

features: {
    ignoreUnusedDeps: true,
  },

Error Message: ERROR ɵNotFound: NG0201: No provider found for a. Source: t.

Analysis

By inspecting the compiled output, the root cause appears to be a missing plugin in the builder pipeline, which prevents esbuild from properly externalizing workspace library imports.

Here is the exact breakdown of the failure:

Externals are correctly registered: The workspace library (@a/b) is genuinely present in the remote application's externals list during the build.

Missing Plugin: However, angular-bundler.js—the pipeline responsible for compiling the remote's application source (including feature components referencing selectors from @a/b)—does not wire in the createSharedMappingsPlugin.

The Plugin's Role: The core responsibility of createSharedMappingsPlugin is intercepting module resolution. When esbuild resolves an import to a local file path covered by a sharedMappings directory (instead of the exact bare specifier '@a/b'), the plugin rewrites it to { path: '@a/b', external: true }.

Angular AOT Resolution: During Angular's AOT compilation of the WorkspaceLibModule scope (e.g., resolving a selector for the remote's template), it resolves the component via TypeScript's internal path resolution. This produces a JavaScript import that points to the actual resolved file path on disk, not necessarily the literal '@a/b' string.

Esbuild Fallback & Inlining: Because esbuild's native external: [...] option only performs exact string matching on bare specifiers, it misses this resolved-path import. Without the plugin to catch and rewrite it, esbuild has no way to know this module should be externalized. Consequently, it fully inlines the shared components/pipes into the remote's internal chunks (e.g., chunk-XXXX.js).

Broken Dependency Injection: This inlining completely disconnects the code from the host's canonical federated chunk (_a_b.js), breaking DI and provider registrations (like providers: [SharedPipe]) that live in the canonical WorkspaceLibModule.

In angular-bundler.js: it seems after applying the following locally it resolved the issue, but not sure if that is the right approach:

plugins: [
  compilerPlugin,
  ...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []),
  commonjsPlugin(),
  ...customPlugins,
],

Thanks!

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