Reputation: 11369
I currently have an angular 2 application that is being built using webpack 2. I have it ignoring any angular or any third party libraries because my intent is to have those loaded/maintained separately in a different webpack bundle process (different webpack config than my app config). I want to do this so that those libraries can be updated without having to re-run webpack on my application, or any other applications that are using these libraries. I have this working using a webpack DLL currently, but I still have a problem where if I update that webpack DLL, the application still needs to be re-bundled to "re-sync" itself with the new module ids. With many applications pointing to this "core" bundle, you can imagine that would be tedious. I don't expect these to change often, but I would like to have the freedom of it.
When I run the application I get errors because my application code can't find things in the angular library such as "Output", "Input", ...etc. Does anyone know how I can either include angular, rxjs, zone, core-js, ...etc. in either script tags, separate webpack bundle, or some other means and have the application webpack bundle be able to know about those libraries and use them? Things like lodash work fine because they are loaded as a global variable, but with angular 2 I can't get that work the same way.
I have tried creating a webpack "vendor" bundle containing just the angular 2 library and its dependencies such as zone and rxjs. I have set the library
property on the output, and tried using libraryTarget
and the externals
property in the application webpack config. The bundles get created fine with only the specific code that is supposed to be in there, but the application can't find any of the modules in this "vendor" bundle. It seems that the import statements in the application code are unable to find the modules in this "vendor" bundle.
If anyone would like to see parts from my webpack config files, feel free ask.
Upvotes: 1
Views: 704
Reputation: 11369
I managed to figure this one out. For anyone looking to do the same thing, here is what needs to happen.
You need to add script tags for each of the umd modules from the angular libraries and its dependencies, or do what I did and create a gulp task to concatenate these libraries in the correct order and create one vendor file containing the code and reference that in a script tag.
Once you are loading the umd modules, you can then tell your webpack config to ignore specific modules and delegate them to a global variable using the externals
configuration property:
externals: [
{
'hammerjs': 'Hammer',
'lodash': '_',
'core-js/es6': 'core',
'core-js/es7/reflect': 'core',
'zone.js/dist/zone': 'Zone',
'@angular/core': 'ng.core',
'@angular/compiler': 'ng.compiler',
'@angular/common': 'ng.common',
'@angular/platform-browser': 'ng.platformBrowser',
'@angular/platform-browser-dynamic': 'ng.platformBrowserDynamic',
'@angular/http': 'ng.http',
'@angular/router': 'ng.router',
'@angular/forms': 'ng.forms',
'@angular/flex-layout': 'ng.flexLayout',
'@angular/material': 'ng.material'
},
function(context, request, callback) {
if (/^rxjs/.test(request)) {
return callback(null, 'Rx');
}
callback();
}
],
What this does is tell webpack that anytime it comes across one of these imports, use the global variable to reference it instead. The global variables exist now because of us using the umd files and loading those separately. The function in there is for rxjs and to use the Rx
global variable whenever it comes across an import statement that starts with "rxjs".
The umd file are listed below in case anyone doesn't know this already. These files are importable in various module loaders as well as accessible via a global variable:
// Third party JavaScript libs
'./node_modules/hammerjs/hammer.min.js', // Required by @angular/material
'./node_modules/lodash/lodash.min.js',
// Polyfills (required by @angular/*)
'./node_modules/core-js/client/shim.min.js',
'./node_modules/zone.js/dist/zone.min.js',
// RxJS
'./node_modules/rxjs/bundles/Rx.min.js', // Required by @angular/*
// Angular Framework (order matters)
'./node_modules/@angular/core/bundles/core.umd.min.js',
'./node_modules/@angular/common/bundles/common.umd.min.js',
'./node_modules/@angular/compiler/bundles/compiler.umd.min.js', // Don't need this if using AoT...
'./node_modules/@angular/platform-browser/bundles/platform-browser.umd.min.js', // Used for AoT
'./node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js', // Used for JiT
'./node_modules/@angular/http/bundles/http.umd.min.js',
'./node_modules/@angular/forms/bundles/forms.umd.min.js',
'./node_modules/@angular/router/bundles/router.umd.min.js',
// Third party Angular libraries
'./node_modules/@angular/flex-layout/bundles/flex-layout.umd.js',
'./node_modules/@angular/material/bundles/material.umd.js'
This process allows you to change the "vendor" javascript bundle without having to run webpack on every application that depends on it. Of course you would need to run webpack on the applications if they need new functionality, but to just update angular or any other external libraries without breaking changes, then this will work great. The only downside is that tree shaking is now not possible for the vendor code... But it is still available for the applications that are using webpack.
Upvotes: 0