Description
I am taking the below information from the following StackOverflow question: http://stackoverflow.com/questions/42537138/angular2-cli-tree-shaking-removing-dynamically-created-ngmodule (I am the author)
OS?
any
Versions.
@angular/cli: 1.0.0-rc.1
node: 6.10.0
os: linux x64
@angular/common: 2.4.9
@angular/compiler: 2.4.9
@angular/core: 2.4.9
@angular/forms: 2.4.9
@angular/http: 2.4.9
@angular/platform-browser: 2.4.9
@angular/platform-browser-dynamic: 2.4.9
@angular/router: 3.4.9
@angular/upgrade: 2.4.9
@angular/cli: 1.0.0-rc.1
@angular/compiler-cli: 2.4.9
typescript: 2.1.6
Repro steps.
Compiling my app with a non-production environment works just fine. However, when I run ng build --prod -e prod
the transpiling/compiling process removes all dynamically created modules.
The log given by the failure.
Immediately when trying to load a dynamically created component:
EXCEPTION: No NgModule metadata found for 'e'.
ORIGINAL STACKTRACE:
main.dc05ae9….bundle.js:formatted:4731
Error: No NgModule metadata found for 'e'.
at f (vendor.c18e6df….bundle.js:formatted:76051)
at t.resolve (vendor.c18e6df….bundle.js:formatted:20624)
at t.getNgModuleMetadata (vendor.c18e6df….bundle.js:formatted:20169)
at t._loadModules (vendor.c18e6df….bundle.js:formatted:40474)
at t._compileModuleAndAllComponents (vendor.c18e6df….bundle.js:formatted:40462)
at t.compileModuleAndAllComponentsSync (vendor.c18e6df….bundle.js:formatted:40436)
at e.createComponentFactory (main.dc05ae9….bundle.js:formatted:4789)
Mention any other details that might be useful.
My dynamic component builder (the problem is in the createComponentModule
function which dynamically creates a new module):
@Injectable()
export class DynamicTypeBuilder {
constructor() {
}
private _cacheOfFactories: {[templateKey: string]: ComponentFactory<any>} = {};
private compiler: Compiler = new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
public createComponentFactory<COMPONENT_TYPE>(type: any, template: string, additionalModules: any[] = []): Observable<ComponentFactory<COMPONENT_TYPE>> {
let factory = this._cacheOfFactories[template];
if (factory) {
return Observable.of(factory);
}
// unknown template ... let's create a Type for it
let module = this.createComponentModule(type, additionalModules);
// compiles and adds the created factory to the cache
return Observable.of(this.compiler.compileModuleAndAllComponentsSync(module))
.map((moduleWithFactories: ModuleWithComponentFactories<COMPONENT_TYPE>) => {
factory = moduleWithFactories.componentFactories.find(value => value.componentType == type);
this._cacheOfFactories[template] = factory;
return factory;
});
}
protected createComponentModule(componentType: any, additionalModules: any[]): Type<any> {
@NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
BrowserModule,
PipesModule,
...additionalModules
],
declarations: [
componentType
],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
})
class RuntimeComponentModule {
}
return RuntimeComponentModule;
}
}
And its transpiled/compiled result:
var _ = function() {
function e() {
this._cacheOfFactories = {},
this.compiler = new i.a([{
useDebug: !1,
useJit: !0
}]).createCompiler()
}
return e.prototype.createComponentFactory = function(e, t, n) {
var i = this;
var _ = this._cacheOfFactories[t];
if (_)
r.Observable.of(_);
var a = this.createComponentModule(e, n);
return r.Observable.of(this.compiler.compileModuleAndAllComponentsSync(a)).map(function(n) {
return _ = n.componentFactories.find(function(t) {
return t.componentType == e
}),
i._cacheOfFactories[t] = _,
_
})
}
,
e.prototype.createComponentModule = function(e, t) {
var n = function() {
function e() {}
return e
}();
return n
}
,
e.ctorParameters = function() {
return []
}
,
e
}()
The 'e' in the error message is the function e()
from createComponentModule
which, as you can see, is empty even though is should contain the @NgModule
content.
This is the transpiled/compiled content with a non-production setting, still containing all the information about the removed NgModule:
var DynamicTypeBuilder = (function () {
function DynamicTypeBuilder() {
// since this object is a singleton, we can have the cache and the compiler here
this._cacheOfFactories = {};
this.compiler = new __WEBPACK_IMPORTED_MODULE_1__angular_compiler__["a" /* JitCompilerFactory */]([{ useDebug: false, useJit: true }]).createCompiler();
}
DynamicTypeBuilder.prototype.createComponentFactory = function (type, template, additionalModules) {
var _this = this;
if (additionalModules === void 0) { additionalModules = []; }
var factory = this._cacheOfFactories[template];
if (factory) {
return __WEBPACK_IMPORTED_MODULE_2_rxjs__["Observable"].of(factory);
}
var module = this.createComponentModule(type, additionalModules);
return __WEBPACK_IMPORTED_MODULE_2_rxjs__["Observable"].of(this.compiler.compileModuleAndAllComponentsSync(module))
.map(function (moduleWithFactories) {
factory = moduleWithFactories.componentFactories.find(function (value) { return value.componentType == type; });
_this._cacheOfFactories[template] = factory;
return factory;
});
};
DynamicTypeBuilder.prototype.createComponentModule = function (componentType, additionalModules) {
var RuntimeComponentModule = (function () {
function RuntimeComponentModule() {
}
return RuntimeComponentModule;
}());
RuntimeComponentModule = __decorate([
__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__angular_core__["NgModule"])({
imports: [
__WEBPACK_IMPORTED_MODULE_3__angular_forms__["FormsModule"],
__WEBPACK_IMPORTED_MODULE_3__angular_forms__["ReactiveFormsModule"],
__WEBPACK_IMPORTED_MODULE_4__angular_platform_browser__["BrowserModule"],
__WEBPACK_IMPORTED_MODULE_5_app_modules_pipes_module__["a" /* PipesModule */]
].concat(additionalModules),
declarations: [
componentType
],
schemas: [__WEBPACK_IMPORTED_MODULE_0__angular_core__["CUSTOM_ELEMENTS_SCHEMA"]]
})
], RuntimeComponentModule);
// a module for just this Type
return RuntimeComponentModule;
};
return DynamicTypeBuilder;
}());
DynamicTypeBuilder = __decorate([
__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__angular_core__["Injectable"])(),
__metadata("design:paramtypes", [])
], DynamicTypeBuilder);
I assume tree shaking removes this dynamically created NgModule.
How can I create a new NgModule like this and still use the production mode of Angular CLI?