single-spa-angular: Sharing Angular with Ivy throws: "Class constructor BrowserPlatformLocation cannot be invoked without 'new'"
While preparing for the upgrade to Angular 12, I tried to change our shared Angular 11 setup that is working with ViewEngine to Ivy activated like in the example at the angular-microfrontends repo.
However, if I keep sharing single-spa-angular in order to workaround the problem discussed at #288 (see), I receive the following error:
Uncaught TypeError: Class constructor BrowserPlatformLocation cannot be invoked without 'new'
at new SingleSpaPlatformLocation (extra-providers.ts:13)
If I, like the angular-microfrontends example, stop sharing single-spa-angular I get the same error as in #288:
Uncaught Error:
single-spa-angular: could not retrieve extra providers from the platform injector. Did you call platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule()
Demonstration
I’ll try to make a demo later. Meanwhile, my config looks like this:
Angular on the package.json:
"dependencies": {
"@angular/common": "~11.1.1",
"@angular/compiler": "~11.1.1",
"@angular/core": "~11.1.1",
"@angular/forms": "~11.1.1",
"@angular/platform-browser": "~11.1.1",
"@angular/platform-browser-dynamic": "~11.1.1",
"@angular/router": "~11.1.1",
"angular-i18next": "~10.2.0",
"i18next": "^20.3.1",
"rxjs": "^6.6.0",
"single-spa": "~5.8.0",
"single-spa-angular": "~4.9.0",
"zone.js": "~0.11.4"
},
The webpack config:
module.exports = (angularWebpackConfig, options) => {
const singleSpaWebpackConfig = singleSpaAngularWebpack(angularWebpackConfig, options);
singleSpaWebpackConfig.externals.push(
/^@apps\/*/,
/^single-spa$/,
/^single-spa-angular$/,
/^single-spa-angular\/internals$/,
/^rxjs$/,
/^rxjs\/operators$/,
/^@angular\/core$/,
/^@angular\/common$/,
/^@angular\/router$/,
/^@angular\/platform-browser$/,
/^zone\.js$/,
/^i18next$/,
);
return singleSpaWebpackConfig
}
Shared dependencies on importmap:
"@angular/core": "https://cdn.jsdelivr.net/npm/@esm-bundle/angular__core@11.1.1/system/es2015/ivy/angular-core.js",
"@angular/common": "https://cdn.jsdelivr.net/npm/@esm-bundle/angular__common@11.1.1/system/es2015/ivy/angular-common.js",
"@angular/router": "https://cdn.jsdelivr.net/npm/@esm-bundle/angular__router@11.1.1/system/es2015/ivy/angular-router.js",
"@angular/platform-browser": "https://cdn.jsdelivr.net/npm/@esm-bundle/angular__platform-browser@11.1.1/system/es2015/ivy/angular-platform-browser.js",
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.3/lib/umd/single-spa.dev.js",
"single-spa-angular": "https://cdn.jsdelivr.net/npm/single-spa-angular@4.9.2/bundles/single-spa-angular.umd.js",
"single-spa-angular/internals": "https://cdn.jsdelivr.net/npm/single-spa-angular@4.9.2/bundles/single-spa-angular-internals.umd.js",
"rxjs": "https://cdn.jsdelivr.net/npm/rxjs@6.6.6/bundles/rxjs.umd.js",
"rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs@6.6.6/system/es5/rxjs-operators.min.js",
"i18next": "https://unpkg.com/i18next/dist/umd/i18next.js",
"i18next-browser-languagedetector": "https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/4.2.0/i18nextBrowserLanguageDetector.js"
and main.single-spa.ts:
const lifecycles = singleSpaAngular({
bootstrapFunction: singleSpaProps => {
singleSpaPropsSubject.next(singleSpaProps);
i18next.loadNamespaces('login').then(() => {
i18next.addResourceBundle('en', 'login', resources.en, true, true);
i18next.addResourceBundle('es', 'login', resources.es, true, true);
});
return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule);
},
domElementGetter,
template: '<login-root />',
Router,
NgZone,
NavigationStart
});
Expected Behavior
Being able to share Angular 11-12 with Ivy enabled between multiple microfrontends like in the angular-microfrontends example.
Actual Behavior
App crashes on load with BrowserPlatformLocation error or it crashes when loading the second Angular app if single-spa-angular is not being shared.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 1
- Comments: 25 (14 by maintainers)
@OriolInvernonLlaneza Managed to somehow get it working in a hacky way, but wouldn’t want to use this for production since I dont really understand what else could go wrong with this:
I managed to isolate the “issue” to
ng-packagrwhich seems to be used to create thesingle-spa-angularbundles. The exact issue is in these lines:This seems to be explicitly transpiling the ES2015 UMD bundle to ES5.
In my local node_modules, I simply commented the
transforminng_packagr/src/lib/ng-package/entry-point/write-bundles.transform.ts, like this:After this, I built the
single-spa-angularrepo again and used the bundles generated from this in my demo code, which fixed the issue.However, I am not really sure it is a good idea to do this or if this will cause something else to break. I am okay with my code only supporting browsers that support ES2015, but not sure if some other library can potentially break because of this.
Was wondering whether to raise an issue in ng-packagr to make the transformation to ES5 optional, but then saw some comments over there in other issues saying they have deprecated support for UMD bundles and will soon be removing that from ng-packagr as well.
I then proceeded to check if SystemJS would work with fesm2015 bundles generated by ng-packagr, but apparently that is not supported either (I hope I am wrong about this).
@arturovt @joeldenning I know you guys must be busy, but your input on this would be super valuable. Any ideas about what could be done here?
Thanks in advance!
I found the same error as in #288 and here on the angular-microfrontends repo:
Assuming X is
BrowserPlatformLocation(same file, same line), I also get the same error while sharingsingle-spa-angular.Should I make a different issue on that repo?
@joeldenning I have added PRs for both 4.x as well as master branches. Please let me know if this works or if any changes are required.
I commented in the esm-bundle issue - I’d rather fix single-spa-angular than create an esm-bundle workaround for it.
@OriolInvernonLlaneza I think i understand why the umd bundles for Angular from unpkg seem to be working fine with single-spa-angular when ivy is disabled. The umd bundles seem to be compiled down to ES5 and the single-spa-angular umd bundle too is is compiled down to ES5, so the two were mutually compatible with each other.
This is not the case with the Ivy esm bundles since they are using ES2015 syntax. Maybe down leveling the esm bundles for Angular Ivy to ES5 will also solve the issue - not verified though.
However, this would probably also mean that all of my angular applications too would need to be downleveled to ES5. This is likely to impact my bundle sizes non-trivially, so would like to avoid this and use ES2015 itself.
I wasn’t using esm-bumdles, I was using umd bundles from unpkg. You can get all of them from there:
They are working for me in production right now.
Since ng-packagr doesn’t want to support es2015 umd bundles, perhaps it’s time to publish a systemjs version that is in es2015 format. I don’t think ng-packagr supports changing rollup’s
output.formatto"system", but that’s what we’d want. So we’d probably need to write our own rollup configuration to do this. I think I’d prefer that approach over usingpatch-packageto manually modify ng-packagr. @ajaykrishnan33 are you interested in giving that a shot? I’m willing to merge #374 and #375 as a last resort if needed.I don’t spend much time maintaining single-spa-angular and do not know angular or ng-packagr very well, but my understanding is that this is just a matter of ng-packagr and/or typescript configuration. It should not be compiling tjhe SingleSpaPlatformLocation class down to es5, but keeping it as an es class. The single-spa-angular project is not supposed to publish any es5 bundles anymore (same goes for Angular itself), so we don’t have the es2015 or esm folders for differential loading. This file should not have
var SingleSpaPlatformLocationbut ratherclass SingleSpaPlatformLocation, and it’s ng-packagr/typescript that control that. I’d be happy to review a pull request fixing it.Thanks @OriolInvernonLlaneza
Just posting another screenshot from the generated
single-spa-angular.umd.jscode which shows that theSingleSpaPlatformLocationclass got compiled down to ES5 syntax.Yesterday looking back at the problems I had when we shared Angular for the first time (v8), I found that I had the very same issues and got help from StackOverflow. See:
That time I had to import
rxjsfrom an UMD bundle andrxjs/operatorsassystemfrom theesm-bundleand it worked. (I have no idea why both assystemdidn’t work).I am facing the same issue too. We are currently evaluating whether single-spa is a good fit for our usecase, but we dont want to be restricted from using the Angular Ivy compiler or from using future versions of Angular because of this choice.
We really like single-spa and would like to use it. Sharing components and services between applications is an important capability for us. Hence it would be great if this issue could be resolved since it is directly blocking us from adopting single-spa.