As of Angular 12, a single workspace, monolithic repository is not required but is the approach Angular takes and sometimes recommends. I've outlined and tested single and multiple workspace solutions below. The Angular team has recommended against referencing an unbuilt library many times, because the application builds are different than the libraries' [1][2].
Single Workspace
When a library and application are in the same workspace, add the watched library's output directory to the application's tsconfig paths. If the library has multiple entry points, create separate paths for the main and secondaries.
/* tsconfig.app.json */
"compilerOptions": {
"paths": {
"@my-scope/my-lib": ["dist/my-lib"],
"@my-scope/my-lib/*": ["dist/my-lib/*"]
}
}
Run the library's watch command.
ng build my-lib --configuration development --watch
Run the application's serve command.
ng serve app --configuration development
Multiple Workspaces
There are three solutions for developing a library with an application in a different workspace.
TSConfig Paths
Adding the library's output directory to the application's tsconfig paths, as demonstrated in the previous section, is the easiest solution. However, the catch is that additional paths for unwatched libraries may need to be added as well, particularly those with deep imports. These sometimes produce compile errors like the following, although, I've seen other obscure cases.
Module build failed (from .../ivy/index.js)
Error TS2339: Property 'X' does not exist on type 'typeof import("Y")
Error TS2305: Module '"X"' has no exported member 'Y'.
Always include Angular and then any that fail until successful.
/* tsconfig.app.json */
"compilerOptions": {
"paths": {
"@angular/*": ["node_modules/@angular/*"],
"@my-scope/my-lib": ["../MyWorkspace/dist/my-lib"],
"@my-scope/my-lib/*": ["../MyWorkspace/dist/my-lib/*"],
"@third-party-scope/third-party-lib": ["node_modules/@third-party-scope/third-party-lib"]
}
}
VSCode may require the library paths in tsconfig.json
.
Directory Reference
Enable the preserveSymlinks
build option in angular.json
.
Install the library using the output directory path to create a symlink. npm does not install peer dependencies when installing symlinks. Install these with install-peerdeps or manually. The following is a useful npm script.
"link:my-lib": "npm install ../MyWorkspace/dist/my-lib && install-peerdeps --only-peers --silent @my-scope/my-lib",
Run the library's watch and application's serve commands.
npm Link
Detailed Explanation
Use npm link
to create a symlink to the output directory.
> (cd MyWorkspace/dist/my-lib && npm link)
> (cd AppWorkspace && npm link @my-scope/my-lib)
Add tsconfig paths to tsconfig.json
. The necessary paths will be those of packages installed in both workspaces and the library itself if there internal imports of entry points. The build errors are clues, but it is recommended to start with Angular. Add paths until the build succeeds. Only one wildcard can be used in each path, so multiple paths will be needed for libraries with multiple entry points and directories.
"paths": {
"@angular/*": ["node_modules/@angular/*"],
"@angular/common/*": ["node_modules/@angular/common/*"],
"rxjs": ["node_modules/rxjs"],
"@third-party-scope/third-party-lib": ["node_modules/@third-party-scope/third-party-lib"]
}
Run the library's watch and application's serve commands. Note, running npm install
will replace the link.
Secondary Entry Points
Watching secondary entry points is not supported prior to Angular 14. It does work with tsconfig paths. The preferred workaround is to configure webpack to watch node_modules for the application build.
Install the custom webpack builder.
npm install -D @angular-builders/custom-webpack
Configure the build architect target. Create "linked" build and serve configurations.
/* angular.json */
"projects": {
"my-app": {
...
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
...
"configurations": {
...
"linked": {
... a development configuration
"customWebpackConfig": {
"path": "./webpack.config.js"
}
}
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"configurations": {
...
"linked": {
"browserTarget": "my-app:build:linked"
}
}
Remove node_modules
from the managed paths in the webpack configuration.
/* webpack.config.json */
module.exports = {
snapshot: { managedPaths: [] }
};
Alternatively, disable the build cache.
export NG_BUILD_CACHE=0
What is a workspace?
A workspace is the root directory structure and configuration files, like angular.json
and package.json
, which contains and manages projects (applications and libraries). A monolithic workspace contains all projects which share a single set of dependencies. Some Angulars prefer to organize projects into separate workspaces so that they can manage dependency versions separately. For more information, check out the documentation.