single-spa-angular icon indicating copy to clipboard operation
single-spa-angular copied to clipboard

Angular 11 Shared Library - NullInjectorError: No provider for N_!

Open scottfwalter opened this issue 4 years ago • 12 comments

I am trying to get shared angular libraries to work but get this stack trace:

NullInjectorError: No provider for N_!
    at Ra.get (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:101406)
    at https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:109184
    at lc (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:109556)
    at rc.get (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:107393)
    at W_ (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:243001)
    at ew.bootstrapModule (https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js:2:245315)
    at Object.bootstrapFunction (http://localhost:4200/main.js:1904:179)
    at http://localhost:4200/main.js:5793:42
    at Generator.next (<anonymous>)
    at http://localhost:4200/main.js:5421:71

My import map looks like:

  <script type="systemjs-importmap">
    {
      "imports": {
        "@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
        "@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
        "@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
        "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
        "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js",
        "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js",
        "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
        "single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js",
        "@cxone/app1": "http://localhost:4200/main.js"
      }
    }
  </script>

Root config:

import { registerApplication, start } from "single-spa";

registerApplication({
  name: "app1",
  app: () =>
    System.import(
      "@cxone/app1"
    ),
    // activeWhen: () => true,
    activeWhen: ["/app1"]
});

registerApplication({
  name: "app2",
  app: () =>
    System.import(
      "http://localhost:4201/main.js"
    ),
  activeWhen: ["/app2"],
});

start({
  urlRerouteOnly: true,
});

My web app extra-webpack-config.js

const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default;
const webpackMerge = require("webpack-merge");
const SystemJSPublicPathWebpackPlugin = require("systemjs-webpack-interop/SystemJSPublicPathWebpackPlugin");

module.exports = (config, options) => {
  const singleSpaWebpackConfig = singleSpaAngularWebpack(config, options);

  singleSpaWebpackConfig.plugins.push(new SystemJSPublicPathWebpackPlugin({
    // ONLY NEEDED FOR WEBPACK 1-4. Not necessary for webpack@5
    systemjsModuleName: "@cxone/app1"
  }));


  singleSpaWebpackConfig.externals.push(
    "rxjs",
    "rxjs/operators",
    /^@angular\/.*/,
  );
  return singleSpaWebpackConfig;
};

This is what my network requests look like Screen Shot 2021-03-31 at 9 53 58 AM

scottfwalter avatar Mar 31 '21 14:03 scottfwalter

I have this exact same problem. Everything works fine if Angular is packed with the app, but after moving to shared system.js import, the bootstrap function fails.

lqc avatar Apr 08 '21 17:04 lqc

Based on my network traffic screenshot above. main.js is retrieved before any of the angular libraries. I wonder if that is part of the issue.

scottwalter-nice avatar Apr 12 '21 16:04 scottwalter-nice

I'm also unable to get a working example up and running with shared angular dependency due to this issue.

J-Yen avatar Apr 19 '21 13:04 J-Yen

I don't spend much time on single-spa-angular anymore, since I don't know or enjoy angular very much. If you can provide a repository demonstrating the issue, @arturovt may be able to investigate this.

internettrans avatar Apr 19 '21 18:04 internettrans

I think I made some progress. I made sure 'single-spa' and 'single-spa-angular' were in my child's app's webpack externals config. However, I now get this error: Unable to resolve bare specifier 'single-spa-angular/internals' .

I do have single-spa and single-spa-angular in my import map:

  <script type="systemjs-importmap">
    {
      "imports": {
        "@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
        "@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
        "@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
        "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
        "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js",
        "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js",
        "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
        "single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js",
        "@cxone/app1": "http://localhost:4200/main.js",
        "@cxone/app2": "http://localhost:4202/main.js"
      }
    }
  </script>

scottwalter-nice avatar Apr 19 '21 19:04 scottwalter-nice

Could anyone of you guys provide a minimal reproducible example?

arturovt avatar Apr 19 '21 19:04 arturovt

Here you go https://github.com/scottfwalter/spa-test

scottfwalter avatar Apr 19 '21 20:04 scottfwalter

Hi guys, I managed to get this working.

My setup is as follows:

In root-config:

<!-- this import map is for Angular + rxjs -->
<script type="systemjs-importmap" src="http://localhost:9200/import-map.json"></script>
<script type="systemjs-importmap">
{
  "imports": {
      "single-spa": "./js/single-spa.min.js",
      // ... put apps here
  }
}

Then I have a 'shared-deps' module where I repack Angular dependencies using Webpack and generate the import map:

entry: {
    '@angular__animations': './node_modules/@angular/animations/fesm2015/animations.js',
    '@angular__core': './node_modules/@angular/core/fesm2015/core.js',
    '@angular__common': './node_modules/@angular/common/fesm2015/common.js',
    '@angular__common__http': './node_modules/@angular/common/fesm2015/http.js',
    '@angular__platform-browser': './node_modules/@angular/platform-browser/fesm2015/platform-browser.js',
    '@angular__platform-browser-dynamic': './node_modules/@angular/platform-browser-dynamic/fesm2015/platform-browser-dynamic.js',
    '@angular__router': './node_modules/@angular/router/fesm2015/router.js',
    'rxjs': './node_modules/rxjs/_esm2015/index.js',
    'rxjs__operators': './node_modules/rxjs/_esm2015/operators/index.js',
  },
  output: {
    libraryTarget: 'system',
  },
  externals: [
    '@angular/animations',
    '@angular/common',
    '@angular/common/http',
    // '@angular/compiler',
    '@angular/core',
    '@angular/platform-browser',
    '@angular/platform-browser-dynamic',
    '@angular/router',
    'rxjs',
    'rxjs/operators',
    'zone.js',
  ],
  plugins: [
    new ImportMapPlugin({
      transformKeys: filename => {
        return filename.replace('__', '/').replace(/\.js$/, '');
      },
      fileName: 'import-map.json',
      baseUrl: './'
    }),
  ],

The key here is that you need to run ngcc in the shared-deps module, so that the FESM modules are Ivy compiled.

lqc avatar Apr 19 '21 20:04 lqc

Do you have a sample repo with this working that we can see?

scottwalter-nice avatar Apr 19 '21 21:04 scottwalter-nice

I extracted my solution and forked the repro case from @scottfwalter - https://github.com/lqc/spa-test

I used webpack just to check if it's possible, but I don't think you can generate reasonable packages for rx.js using webpack, so left that part untouched.

lqc avatar Apr 20 '21 09:04 lqc

@lqc Thank you for taking the time and creating that demo. It works!

scottfwalter avatar Apr 23 '21 16:04 scottfwalter

I think I made some progress. I made sure 'single-spa' and 'single-spa-angular' were in my child's app's webpack externals config. However, I now get this error: Unable to resolve bare specifier 'single-spa-angular/internals' .

I do have single-spa and single-spa-angular in my import map:

  <script type="systemjs-importmap">
    {
      "imports": {
        "@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
        "@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
        "@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
        "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
        "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs.min.js",
        "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/system/es2015/rxjs-operators.min.js",
        "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
        "single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.min.js",
        "@cxone/app1": "http://localhost:4200/main.js",
        "@cxone/app2": "http://localhost:4202/main.js"
      }
    }
  </script>

@scottwalter-nice When I faced this error, it was resolved for me by adding an entry for single-spa-angular/internals to the import map as follows:

<script type="systemjs-importmap">
    {
      "imports": {
        "single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js",
        "single-spa-angular/internals": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular-internals.umd.js",
        "single-spa-angular": "https://cdn.jsdelivr.net/npm/[email protected]/bundles/single-spa-angular.umd.js",
        "@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-core.min.js",
        "@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-common.min.js",
        "@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-router.min.js",
        "@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/ivy/angular-platform-browser.min.js",
        "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs.min.js",
        "rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs-operators.min.js"
      }
    }
  </script>

ajaykrishnan33 avatar Jun 16 '21 02:06 ajaykrishnan33

Closing this issue as stale. We also have published Angular packages in system format in esm-bundle repo (see single-spa documentation regarding this).

arturovt avatar Dec 29 '22 19:12 arturovt