简体   繁体   中英

Cannot omit PrimeNG dependencies using Webpack externals in Angular app

I'm trying to build an Angular library with a custom Webpack config, where I specify externals setting to exclude certain dependencies from the compilation output. And everything works fine, all externals are actually excluded... except for PrimeNG!

Note: instead of ng-packagr, I am using Angular 8 with a custom builder ngx-build-plus:browser , but I guess it's irrelevant in this case. I configured it so that it builds the library correctly. And it does, except for the mentioned issue.

I'm running the build with this command: ng build --extra-webpack-config libs/web-templates/extra-webpack.config.js --project web-templates --single-bundle .

Here is how my imports look in the main library module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TreeTableModule } from 'primeng/treetable';
import { MultiSelectModule } from 'primeng/multiselect';

I also tried different paths for importing primeng modules, but it didn't help:

import { TreeTableModule } from 'primeng/components/treetable/treetable';
import { MultiSelectModule } from 'primeng/components/multiselect/multiselect';

In both cases I used this extra-webpack.config.js :

module.exports = {
  output: {
    libraryTarget: 'umd' // super-important
  },
  externals: {
    "rxjs": "rxjs",
    "rxjs/operators": "rxjs/operators",
    "@angular/core": "@angular/core",
    "@angular/common": "@angular/common",
    "@angular/forms": "@angular/forms",
    "@angular/platform-browser": "@angular/platform-browser",
    "file-saver": "file-saver",
    "lodash": "lodash",
    "moment": "moment",
    // 1) for the case with longer paths to primeng
    "primeng/components/treetable/treetable": "primeng/components/treetable/treetable",
    "primeng/components/multiselect/multiselect": "primeng/components/multiselect/multiselect",
    // 2) for the case with normal paths to primeng
    "primeng/treetable": "primeng/treetable",
    "primeng/multiselect": "primeng/multiselect",
    // that's just for importing some TypeScript interfaces, not compiled actually, so can be removed
    "primeng/api": "primeng/api"
  }
};

As a result, compiled output doesn't contain @angular/core , rxjs/operators and all other dependencies, except PrimeNG. For example, here is a piece of the output with rxjs/operators dependency:

/***/ "rxjs/operators":
/*!*********************************!*\
  !*** external "rxjs/operators" ***!
  \*********************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_rxjs_operators__;

/***/ })

/******/ });
});

And that's it, rxjs operators are successfully excluded from the output, as well as angular source code, moment JS and lodash.

However, it doesn't happen for primeng TreeTableModule and MultiselectModule no matter what I do. The output includes all their sources. I tried aliasing their paths in typescript config, tried using short and full paths (shown above).

I even tried using relative paths - no changes:

  // part of externals config
    "../../../../node_modules/primeng/components/treetable/treetable": "../../../../node_modules/primeng/components/treetable/treetable",
    "../../../../node_modules/primeng/components/multiselect/multiselect": "../../../../node_modules/primeng/components/multiselect/multiselect",
    "node_modules/primeng/components/treetable/treetable": "node_modules/primeng/components/treetable/treetable",
    "node_modules/primeng/multiselect": "node_modules/primeng/multiselect",
    "node_modules/primeng/treetable": "node_modules/primeng/treetable"

There are no other places where I import PrimeNG. I use it exactly the same as Angular dependencies. I thought it could be because of the @ or / character in the dependency name, but the same settings work perfectly fine for rxjs/operators .

I'm not worried about how to integrate it with the host application (I already did, successfully), I'm only worried about the compilation output. It is 1.5 MB (unminified) because it includes a lot of PrimeNG code. The main question is:

Why does it work for other dependencies, and doesn't work for PrimeNG dependencies under exactly the same conditions?

If relevant, here is the beginning of the output file (UMD module) for the webpack config mentioned above. An interesting observation: it includes full paths to primeng components (eg primeng/components/multiselect/multiselect ), but doesn't include short paths like primeng/multiselect , although they are also listed in webpack externals. But in any case, the sources of primeng are always present in the output.

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory(require("@angular/common"), require("@angular/core"), require("@angular/forms"), require("@angular/platform-browser"), require("file-saver"), require("lodash"), require("moment"), require("primeng/components/multiselect/multiselect"), require("primeng/components/treetable/treetable"), require("rxjs"), require("rxjs/operators"));
    else if(typeof define === 'function' && define.amd)
        define(["@angular/common", "@angular/core", "@angular/forms", "@angular/platform-browser", "file-saver", "lodash", "moment", "primeng/components/multiselect/multiselect", "primeng/components/treetable/treetable", "rxjs", "rxjs/operators"], factory);
    else {
        var a = typeof exports === 'object' ? factory(require("@angular/common"), require("@angular/core"), require("@angular/forms"), require("@angular/platform-browser"), require("file-saver"), require("lodash"), require("moment"), require("primeng/components/multiselect/multiselect"), require("primeng/components/treetable/treetable"), require("rxjs"), require("rxjs/operators")) : factory(root["@angular/common"], root["@angular/core"], root["@angular/forms"], root["@angular/platform-browser"], root["file-saver"], root["lodash"], root["moment"], root["primeng/components/multiselect/multiselect"], root["primeng/components/treetable/treetable"], root["rxjs"], root["rxjs/operators"]);
        for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
    }
})(window, function(__WEBPACK_EXTERNAL_MODULE__angular_common__, __WEBPACK_EXTERNAL_MODULE__angular_core__, __WEBPACK_EXTERNAL_MODULE__angular_forms__, __WEBPACK_EXTERNAL_MODULE__angular_platform_browser__, __WEBPACK_EXTERNAL_MODULE_file_saver__, __WEBPACK_EXTERNAL_MODULE_lodash__, __WEBPACK_EXTERNAL_MODULE_moment__, __WEBPACK_EXTERNAL_MODULE_primeng_components_multiselect_multiselect__, __WEBPACK_EXTERNAL_MODULE_primeng_components_treetable_treetable__, __WEBPACK_EXTERNAL_MODULE_rxjs__, __WEBPACK_EXTERNAL_MODULE_rxjs_operators__) {
return /******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
//  ... the rest of the file

UPDATE:

I managed to exclude primeng externals by using regexp in webpack config. But it's not very practical.

  // webpack config:
  externals: [{
    // ... my previous externals object
  }, /primeng/]

As a result, compiled output contains this:

    else if(typeof define === 'function' && define.amd)
        define([
"../../../../../../../node_modules/primeng/components/multiselect/multiselect.ngfactory", 
"../../../../../../../node_modules/primeng/components/treetable/treetable.ngfactory", 
"@angular/common", "@angular/core", "@angular/forms", "@angular/platform-browser", "file-saver", "lodash", "moment", 
"primeng/components/common/shared",
 "primeng/components/dropdown/dropdown", 
"primeng/components/multiselect/multiselect", 
"primeng/components/paginator/paginator", 
"primeng/components/tooltip/tooltip", 
"primeng/components/treetable/treetable",
 "rxjs", "rxjs/operators"], factory);

So it appears that a lot of other depedencies are added there: "primeng/components/paginator/paginator", "primeng/components/tooltip/tooltip", "../../../../../../../node_modules/primeng/components/treetable/treetable.ngfactory", etc. The host application will hardly be able to provide those dependencies (especially .ngfactory)...

But why all those dependencies appear? In my source code, I only use primeng/treetable and primeng/multiselect imports, nothing else. How does it even work, can anybody tell me?

Try narrowing the scope of the regex:

/^primeng.*$/

It worked for me

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM