adrisons
adrisons

Reputation: 3723

Angular library bundle local dependency

I have two libraries: core and client.

Core is meant to be private and client is the one to be published. I want to include core inside client bundle (client uses core functions), so the final user does not need to manage core dependency.

¿How can I do this? Any help would be appreciated.

packages

packages
├── client
│   ├── ng-package.json
│   ├── node_modules
│   │   └── @lib
│   │       └── core -> ../../../core
│   ├── package-lock.json
│   ├── package.json
│   └── src
│       ├── lib
│       │   ├── client.ts
│       └── public-api.ts
└── core
    ├── ng-package.json
    ├── package-lock.json
    ├── package.json
    └── src
        ├── lib
        │   ├── core.ts
        └── public-api.ts

As you see, there is a symbolic in from packages/client/node_modules/@lib-core pointing to core. This makes that when I run the app in local environment it finds the reference to core. The problem is that after generating the build, there is no link.

core/package.json

{
  "dependencies": { "tslib": "^1.10.0" },
  "main": "src/public-api.ts",
  "name": "@lib/core",
  "scripts": {
    "build": "ng build core",
    "test": "ng test core"
  },
  "version": "0.0.1"
}

core/ng-package.json

{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/core",
  "lib": {
    "entryFile": "src/public-api.ts"
  }
}

client/package.json

{
  "bundledDependencies": ["@lib/core"],
  "dependencies": {
    "@lib/core": "^0.0.1",
    "tslib": "^1.10.0"
  },
  "main": "src/public-api.ts",
  "name": "@lib/client",
  "scripts": {
    "build": "ng build client",
    "test": "ng test client"
  },
  "version": "0.0.1"
}

client/ng-package.json

{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/client",
  "lib": {
    "entryFile": "src/public-api.ts",
    "umdModuleIds": {
      "@lib/core": "@lib/core"
    }
  },
  "whitelistedNonPeerDependencies": ["@lib/core"]
}

As you can see, I use npm bundledDependencies in client/package.json, as mentioned here. This works when running npm pack inside package/client, but I want to build with Angular first (so it generates javascript code and apply performance techniques). My intention is to pack after generating the build.

dist

After generating the bundle, I tried running npm install inside dist/client to see if the dependency could be installed and packed from there.

It throws the error 404 Not Found '@lib/core@^0.0.1' is not in the npm registry.

This is the tree of dist/client after build:

client
├── README.md
├── bundles
│   ├── lib-client.umd.js
│   ├── lib-client.umd.js.map
│   ├── lib-client.umd.min.js
│   └── lib-client.umd.min.js.map
├── esm2015
│   └── ...
├── esm5
│   └── ...
├── fesm2015
│   └── ...
├── fesm5
│   └── ...
├── lib
│   └── client.d.ts
├── lib-client.d.ts
├── package.json
└── public-api.d.ts

The dist/client/package.json has the same dependencies defined inside packages

  "bundledDependencies": [
    "@lib/core"
  ],
  "dependencies": {
    "@lib/core": "^0.0.1",
    "tslib": "^1.10.0"
  },

This are the imports in the bundles/lib-client.umd.js file:

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('@lib/core')) :
    typeof define === 'function' && define.amd ? define('@lib/client', ['exports', 'tslib', '@lib/core'], factory) :
    (global = global || self, factory((global['lib'] = global['lib'] || {}, global['lib']['client'] = {}), global.tslib, global['@lib/core']));
}(this, (function (exports, tslib, core) { 'use strict';

It looks that, instead of copying core code its referencing it as an external dependency.

Recap

The objective of this question is to generate a single bundle with the library to the user.


I also have another question on how to test this in ci: Lerna package import in CI

Upvotes: 7

Views: 4507

Answers (2)

Sandman
Sandman

Reputation: 1569

You can try with the following configurations.

In tsconfig.ts search for dist package:

"paths": {
      "@angular/*": ["./node_modules/@angular/*"],
      "@lib/core": [
        "../../../core/dist/@lib/core" 
      ]
    }

Still in tsconfig.ts (configurations to no have errors):

"angularCompilerOptions": {
  "fullTemplateTypeCheck": true,
  "strictInjectionParameters": true,
  "enableIvy": true,
  "importHelpers": true
}

In angular.json search for preserveSymlinks configuration:

projects.<project-name>.architect.build.options.preserveSymlinks: true

Added steps: Remove core library dependency from your client package.json. You not need it:

  "bundledDependencies": [
    ...
  ],
  "dependencies": {
    ...
  },

You can import every function of core library simply with:

import {...} from '@lib/core';

and they will bundled together when you'll run ng build on client library.

This setup worked to me, for your use case.

Upvotes: 4

vineeth pappu
vineeth pappu

Reputation: 542

Since your core is private and client has a direct dependency, you don't need to treat it as a local package. Instead directly access the core module using relative path.

i.e) remove the dependency from package.json and import the core dirctly using relative path in your client package.

eg:

Step 1: remove core dependency from client package.json

client/package.json

{
  "bundledDependencies": ["@lib/core"],
  "dependencies": {
    "tslib": "^1.10.0"
  },
  "main": "src/public-api.ts",
  "name": "@lib/client",
  "scripts": {
    "build": "ng build client",
    "test": "ng test client"
  },
  "version": "0.0.1"
}

Step 2: import core using relative path or alias name (using @)

Import core in your client.ts (inside client package inside src/lib)

packages/client/src/lib/client.ts

import core from "@lib/core"

Upvotes: 0

Related Questions