I'm creating an Angular 2 application that can be dynamically injected into a running website. The point of this app is to be able to modify the website content visually.
Everything works as expected when the website in question does not also run using Angular 2. In particular, when the original website uses Angular 2 and Zone.js, my application also tries to load Zone.js but it crashes on error: Zone already loaded
. I'm using Webpack as a build system and I tried to solve the problem by splitting the build into 3 parts: manifest
, polyfill
, and app
. Manifest contains only the Webpack manifest, polyfill contains core-js
and zone.js
, and the rest is in the app
. There's also a fourth chunk called index
. This is the one placed into the website and it first checks if window.Zone
is defined. If it is, only manifest
and app
are added. Otherwise also polyfill
.
The problem is that when polyfill
chunk is not loaded, all modules in that chunk are missing from Webpack. So when the code reaches some import
from the app
chunk that requires either core-js
or Zone.js
(that's what I think is happening), I get this error:
TypeError: Cannot read property 'call' of undefined
at __webpack_require__ (manifest.js:55)
at Object.<anonymous> (app.js:15016)
at __webpack_require__ (manifest.js:55)
at Object.<anonymous> (app.js:65567)
at __webpack_require__ (manifest.js:55)
at Object.<anonymous> (app.js:65163)
at __webpack_require__ (manifest.js:55)
at Object.defineProperty.value (app.js:65137)
at __webpack_require__ (manifest.js:55)
at webpackJsonpCallback (manifest.js:26)
Somewhere in the manifest file:
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
QUESTION
How do I configure Webpack or Angular to not import zone.js
as a dependency but to use that one already registered on the window? I want to be able to conditionally load core-js
and zone.js
only if it's not yet loaded on the website.
UPDATE
I modified my build (Webpack) to only use a single bundle. In that bundle, I tried using Webpack's dynamic imports like this:
// import reflect metadata shim
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
// Angular and application imports
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ViewEncapsulation } from '@angular/core';
// load the editor in the next frame
requestAnimationFrame(() => {
if (window.Zone) {
bootstrap();
}
else {
import('zone.js/dist/zone') // <-- PROBLEM HERE
.then(bootstrap);
}
});
// bootstraps the editor
function bootstrap() {
// add the root component to the DOM
const root = document.createElement('efe-root');
document.body.appendChild(root);
// bootstrap the Angular app
platformBrowserDynamic()
.bootstrapModule(AppModule, {
defaultEncapsulation: ViewEncapsulation.Native
})
.then(() => console.debug('Exponea Free Editor has bootstrapped'));
}
I'm using Typescript and that requires definition files. The problem is that Zone.js
is an ambient dependency so I get an error about missing definition files when I use it like this. How do I solve this?