I am trying to include external module (hosted in git/npm repository) as lazy-loaded module in my Angular application.
I am compiling my external module with ngc compiler:
node_modules/.bin/ngc -p tsconfig-aot.json
This is how my compiler config looks:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"baseUrl": "src",
"declaration": true,
"outDir": "./release/src"
},
"files": [
"./src/index.ts"
],
"angularCompilerOptions": {
"genDir": "release",
"skipTemplateCodegen": true,
"entryModule": "index#ExternalModule",
"skipMetadataEmit": false,
"strictMetadataEmit": true
}
}
And in my main app I am lazy loading given module:
RouterModule.forRoot([
{ path: '', component: HomeComponent, pathMatch: 'full'},
{ path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule'},
{ path: 'external', loadChildren: '@angular-universal-serverless/external-module/release#ExternalModule'}
])
For compilation purposes I am using @ngtools/webpack plugin.
The JIT compilation works without any problems, but AOT compilation gives me error:
ERROR in ./src/ngfactory lazy
Module not found: Error: Can't resolve '/path/to/my/project/angular-universal-serverless/src/ngfactory/node_modules/@angular-universal-serverless/external-module/release/src/index.js' in '/Users/mtreder/Documents/private/work/angular-universal-serverless/src/ngfactory'
@ ./src/ngfactory lazy
@ ./~/@angular/core/@angular/core.es5.js
@ ./src/main.server.aot.ts
So I decided to check what is the output from ngc
compiler (whic is called under the hood by the webpack plugin):
node_modules/.bin/ngc -p tsconfig.server.aot.json
And in fact, my module is missing in the /path/to/my/project/angular-universal-serverless/src/ngfactory/node_modules
catalog.
ls src/ngfactory/node_modules/
@angular @nguniversal @types idb ng-http-sw-proxy rxjs typescript-collections
How can I force ngc to place given modules in the ngfactory
output directory?
My main application can be found here: https://github.com/maciejtreder/angular-universal-serverless/tree/externalModule
And external module here: https://github.com/maciejtreder/angular-external-module
1) The first problem here is that AOT compiler doesn't compile your module(node_modules
folder is excluded by default), so you have to include it in files
option of your ts configs:
tsconfig.browser.json
tsconfig.server.json
tsconfig.server.aot.json
"files": [
"./node_modules/@angular-universal-serverless/external-module/release/src/externalComponent/external.module.d.ts"
],
"include": [
"./src/main.browser.ts",
"./src/app/lazy/lazy.module.ts",
"./src/app/httpProxy/http-proxy.module.ts"
]
We can't add it to includes
array because typescript will exclude it
Files included using "include" can be filtered using the "exclude" property
See more details in the doc
2) Then
\node_modules\@angular-universal-serverless\external-module\release\package.json should has typings
field like:
"name": "@angular-universal-serverless/external-module",
"main": "./src/index.js",
"typings": "./src/externalComponent/external.module.d.ts", <=== this one
We have to use external.module.d.ts
because angular doesn't create ngfactory
file for index.d.ts
while @ngtools/webpack plugin creates map for ContextElementDependency:
const factoryPath = lazyRoute.replace(/(\.d)?\.ts$/, '.ngfactory.ts');
// where lazyRoute === .../external-module/release/src/externalComponent/external.module.d.ts
const lr = path.relative(this.basePath, factoryPath);
this._lazyRoutes[k + '.ngfactory'] = path.join(this.genDir, lr);
If you don't want to change package.json
then change loadChildren
field:
{
path: 'external',
loadChildren: '@angular-universal-serverless/external-module/release/src/externalComponent/external.module#ExternalModule'
}