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!
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
Error Message:
ERROR ɵNotFound: NG0201: No provider found fora. 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:Thanks!