Search code examples
javascriptangulartypescriptmonorepopnpm

How to import local monorepo typescript package in Angular apps?


I'm trying to integrate an Angular project into a PNPM workspace monorepo that mainly contains react app. Here is the directory structure

|-apps
|  |-react-app
|  |  |-package.json
|  |
|  |-angular-app (new)
|    |-package.json
|
|
|-packages
|  |-shared
|     |-index.ts
|     |-package.json
|
|-package.json
|-pnpm-workspace.yaml

Lets say the shared package has the name @my-app/shared and it has "main": "index.ts".

In the react-app, I can install this local package and directly use it in my code like the following

import { someValue } from '@my-app/shared';

With some adjustment to the webpack config, the react-app is able to include the shared module in the bundle correctly.

However, I couldn't figure out how to do it in an angular app. The package can install correctly and everything, but when I run the development server, seems that it doesn't include the @my-app/shared into the bundle

import { value } from '@my-app/shared';
console.log('xxx', value);

enter image description here

When I inspected the bundle, seems that the angular compiler does not bundle the module. Instead it just shows the placeholder

enter image description here

How do you import local package in Angular then? I believe there needs to be some config added to the angular.json but I couldn't find any resource. All of the sources out there points to Angular Library through ng generate library, which isn't what I'm looking for since @my-app/shared just a simple typescript utils functions.


Solution

  • I had the same issue, I investigated why, here's what I found ⤵️

    It's basically due to typescript not emitting any ts files stored in node_modules (you can check it here).

    When angular is compiling your application, it compiles files using the typescript compiler: since typescript detects the file as an external library, it is excluded, so nothing is emitted for this file.

    Why it works with your react app:

    I suppose, you had to set allowTsInNodeModules to true right? The ts-loader is solving this issue in the webpack plugin, but @angular/cli does not implement any workaround.

    Why it works with paths:

    When typescript analyzes the list of files to compile, it does not check by looking at a specific node_modules pattern in the file path:

    • Instead, it maintains a currentNodeModulesDepth variable (see here).
    • When we use paths, the currentNodeModulesDepth is seen equal to zero, as typescript found a mapping and, so, did not start to look at node_modules.
    • So in this case, it is correctly included in the final output.

    Note that you can simplify your workaround with:

    "paths": {
      "@my-app/*": ["../../packages/*"]
    }
    

    So, you don't have to list all dependencies, you can use * as a placeholder.