'Is there a better way to build an Angular monorepo app with libraries?
I have several apps in my Angular monorepo project. Also there are about 5 libraries I've written to use across the apps.
What I want to know is how to better build/architect these libraries.
The ins are follows:
- Libraries are for internal usage only (meaning no publishing or using in other apps but those in
projects
folder) - Libraries have different dependencies like
lodash
andRxJs
- One library can import another inside itself
What I've done so far:
- Specified the
umdModuleIds
in each library'sng-package.json
. - Specified
peerDependencies
on external libraries likelodash
andRxJs
- Set up my app build which has
prebuild
with about 5 commandsng build lib-name
combined via "&&" - I import Lodash in next way
import { cloneDeep } from 'lodash'
Now I see that my main.js
chunk is much bigger than it was before extracting some services/components/functions into external libraries. Now main.js
's size on prod build is 2.1 Mb which in my opinion is too big.
Also, I'm not sure whether it's worth making 3 different builds of each library (UMD, FESM2015, FESM5).
I import libraries from dist folder as it recommended in docs following next form import { LibService } from 'lib'
.
Solution 1:[1]
Nrwl tools, developed by Angular core contributors, specializes in enterprise architectures, including mono repositories.
The Nrwl nx-examples is a great resource to get started.
I started by using nx
to build a new project. In the end, my project structure ended up as follows:
platform-directory/
|
---apps/
| |
| ---app1/
| |
| ---app2/
|
---library1/
| |
| ---src/
|
---library2/
| |
| ---src/
|
---angular.json
|
---package.json
|
---README.md
|
---tsconfig.json
tsconfig.json
The top level tsconfig.json
should contain the bulk of the global configuration for the apps and libraries as well as paths
shortcuts if desired.
Path shortcuts may be configured as follows:
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"baseUrl": "./",
"declaration": false,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"module": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"target": "es6",
"lib": [
"es2018",
"dom"
],
"paths": {
"@app1*": [
"./apps/app1/src/app*"
],
"@lib1/package1": [
"./lib1/src/package1/public_api.ts"
],
"@lib1/package2": [
"./lib1/src/package2/public_api.ts"
],
...
}
Library Imports
In the applications, library code may be imported directly from the library sources, such as:
import { MyLibraryComponent } from '@lib1/package1'
Since you are not publishing the libraries, there is no need to build them. When you run the Angular compiler on your application code, the library code will be automatically included and optimized as needed.
IMPORTANT: Within each library, do not import files using the path shortcuts, since this causes hard-to-debug circular dependencies. For example, within lib2
it is okay to use:
import { MyLibraryComponent } from '@lib1/package1'
However, if this import were used within lib1
, it would create a circular dependency.
As a side note, each app will have a tsconfig.app.json
and tsconfig.spec.json
such as the following:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc/apps/app1"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
Solution 2:[2]
Specifically regarding your concern with bundle size: I suspect Lodash is a major culprit there. Try using lodash-es
instead, and importing from only packages you need. E.g.
import cloneDeep from 'lodash-es/cloneDeep';
That should reduce the amount of lodash
in your bundle substantially, but it still won't be nearly as small as it could be for most use cases. I made a library specifically for this reason called micro-dash
. For example it includes cloneDeep
, which as the docs say adds 397 bytes
to your bundle (roughly - it depends on multiple factors), whereas the lodash version adds 12,289 bytes
.
Ultimately, though, to troubleshoot an oversized bundle you should see exactly how much each library is adding to it. That is the realm of source-map-explorer
. Absolutely run that on your final prod bundles, and address the worst offenders first!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Eric Simonton |