Tips for implementing absolute import paths in a library project

In my workspace, I have a library with two projects: one for the library itself and another for a test application.

├── projects
    ├── midi-app
    └── midi-lib

Within the workspace's tsconfig.json file, I set up paths for @app and @lib:

"paths": {
  "@app/*": ["projects/midi-app/src/app/*"],
  "@lib/*": ["projects/midi-lib/src/lib/*"],
  "midi-lib": [
    "dist/midi-lib"
  ],
  "midi-lib/*": [
    "dist/midi-lib/*"
  ]
}

In addition, there is a

projects/midi-lib/tsconfig.lib.json
file that extends the main tsconfig.json settings:

"extends": "../../tsconfig.json",

The library has a public-api.ts file which includes:

export * from './lib/midi-lib.module';

I can successfully use this library in the test application. However, when I try importing it as a Node module into a separate client application within a different workspace, I encounter numerous errors related to unknown paths like Can't resolve '@lib/...'

How can I make sure the library paths are accessible in a client application? Or how can I handle the translation of these paths during the library packaging process?

Additionally, I am curious why the extension is not the other way around - why doesn't the main tsconfig.json file extend the

projects/midi-lib/tsconfig.lib.json
file instead?

Here is my approach to packaging and using the library:

To package the library, I add the following scripts to the parent package.json file:

"copy-license": "cp ./LICENSE.md ./dist/midi-lib",
"copy-readme": "cp ./README.md ./dist/midi-lib",
"copy-files": "npm run copy-license && npm run copy-readme",
"build-lib": "ng build midi-lib",
"npm-pack": "cd dist/midi-lib && npm pack",
"package": "npm run build-lib && npm run copy-files && npm run npm-pack",

After adding these scripts, I run the command: npm run package

Then, I install the dependency by running:

npm install ../midi-lib/dist/midi-lib/midi-lib-0.0.1.tgz

And import the module in the application module. In the app.module.ts file:

import { MidiLibModule } from 'midi-lib';
@NgModule({
  imports: [
    MidiLibModule

Lastly, I insert the component in a template:

<midi-midi-lib></midi-midi-lib>

Upon installing the library in a client application, I notice various .d.ts files under the node_modules/midi-lib directories:

├── bundles
├── esm2015
│   └── lib
│       ├── device
│       ├── keyboard
│   ...
│   └── upload

One example is the lib/service/melody.service.d.ts file:

import { SoundtrackStore } from '@lib/store/soundtrack-store';
import { ParseService } from '@lib/service/parse.service';
import { CommonService } from './common.service';
export declare class MelodyService {
    private soundtrackStore;
    private parseService;
    private commonService;
    constructor(soundtrackStore: SoundtrackStore, parseService: ParseService, commonService: CommonService);
    addSomeMelodies(): void;
    private addSoundtrack;
    private generateNotes;
}

This file references the @lib path mapping, which is not recognized in the client application.

I also tried using the baseUrl property as a workaround, but it didn't solve the issue since the baseUrl value was not specified during library installation.

Why does running npm run package to package the library not resolve the paths mappings?

Answer №1

The establishment of the paths mapping in your tsconfig.json is specifically for compile-time purposes only. This does not impact the code generated by the TypeScript compiler itself. This could be the reason why you are experiencing an error at runtime. A suggestion was made to the TypeScript project, proposing that tsc should automatically translate module paths in the emitted code to align with the established mapping under paths. The response from the TS devs indicated that tsc is functioning as intended and the solution would require configuring a module loader that can perform a similar mapping at runtime as defined by paths.


Based on the details provided regarding your situation, here's what I believe you should consider doing:

If midi-app is primarily a test application without distribution requirements, you should be able to continue using the existing paths mapping without encountering issues. (Since there have been no mentioned problems while running this app, it appears that your tools are handling the runtime issue seamlessly.)

For midi-lib, it might be best to avoid relying on mappings set by paths and opt for relative paths instead. This approach ensures smoother consumption by others since this library is designed for external use. Consumers utilizing Webpack, Rollup, SystemJS, and similar tools will need to configure mappings specific to your library alongside their configurations.

Furthermore, the complexity of required configuration could escalate depending on how and where consumers utilize your library. While a global mapping may suffice when your package is the sole instance requiring such a mapping, scenarios involving multiple libraries necessitate intricate configurations to designate distinct mappings accordingly.

While the discussion has focused on module resolution during bundling or runtime loading, another consideration involves establishing specialized configurations within tsc compilation files which include .d.ts files.

By leveraging relative paths in your code, consumers of your library won't face challenges implementing unique configurations tailored to accommodate your library's requirements.


An exception to this strategy may apply if your intention is to publish your library as midi-lib. In such cases, modifying your paths map to reference midi-lib/* rather than @lib/* could be advantageous:

"midi-lib/*": ["projects/midi-lib/src/*"],

(Take note that the @ symbol holds no significance in relation to TypeScript. If your package warrants installation within a scope like @midi-project/midi-lib, ensure your tsconfig.json mapping includes the appropriate scope: "@midi-project/midi-lib/*": ...)

The objective is to establish a mapping enabling imports within your project just as consumers would import individual modules from your project. For instance, if a consumer fetches the ParseService module through

import { ParseService } from "midi-lib/lib/service/parse.service"
, prioritize employing the same import format wherever applicable. Ensuring uniform path usage between compile time and runtime (or bundling) operations streamlines functionality. While the translation from paths occurs during compile time via tsc, Node's module resolution algorithm (or its equivalents such as Webpack or Rollup) handle translations during runtime or bundling.

The reduction in manual efforts varies based on selected naming conventions and your library's structure.


In theory, a post ng build step could potentially substitute occurrences of @lib in module names within the output generated by ng build. However, challenges exist:

  1. The process isn't a simple tool activation; familiarity with tools such as rollup along with custom configuration creation is essential.

  2. Ideally, a readily available tool capable of transforming the necessary .d.ts files doesn't currently exist, necessitating personalized tool development.

  3. Adapting Angular AOT compilation metadata affected by the switch requires custom interventions due to current tool unavailability for such modifications.

  4. Potential complications emerge from future Angular version updates altering AOT compilation metadata formats or introducing new metadata types that demand adjustments. Personal encounters reflect similar upheavals following Angular upgrades affecting AOT compilation metadata procedures.

Answer №2

Like others mentioned before, typescript does not alter your @app and @lib imports. I have encountered the same issue while attempting to utilize absolute paths in a library package. The solution lies in preparing your library for publication using rollup or a similar tool.

Rollup offers numerous plugins, and while I won't delve into the complete setup process, what you require is a plugin that will rewrite your imports. It appears that this particular plugin accomplishes that: https://github.com/bitshiftza/rollup-plugin-ts-paths

In addition to this plugin, the rest of your rollup configuration will likely involve utilizing rollup-plugin-node-resolve, rollup-plugin-commonjs, and a typescript plugin (rollup-plugin-typescript), or alternatively exploring the newer approach of using babel. You can refer to various tutorials as there are prominent libraries coded in typescript that use rollup for code packaging (such as React).

Wishing you productive coding sessions!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating data types from the name of the route in vue-router route[x]

I am attempting to generate route names based on the routes defined in the Vue Router. My goal is to utilize a helper function called findRouteByName() to locate a specific route. However, I encountered an issue when trying to define the parameters of the ...

The condition in a Typescript function that checks for strings will never evaluate to true

I encountered a strange issue with a TypeScript condition in a function. Here is my current code, where the parameters are passed from outside: getLevel(validation: string, status: string): string { let card = ""; if (validation == &qu ...

Hold off until the RxJS dispatch is resolved

I am working on integrating a "next step" feature into my Angular 6 webapp. When the user clicks the "next step" button, the frontend triggers an action to update the database with the data in the store, another action to retrieve processed data from a Spr ...

Unable to locate the module from my personal library in Typescript

The Query Why is my ng2-orm package not importing or being recognized by vscode when I try to import Config from 'ng2-orm'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser&a ...

Having trouble implementing the ternary operator (?) in TypeScript within a Next.js project

Trying to implement a ternary operator condition within my onClick event in a Next.js application. However, encountering an error indicating that the condition is not returning any value. Seeking assistance with resolving this issue... https://i.stack.img ...

Error message displayed indicating script not found after executing yarn tsc within a Docker container

Using docker, I successfully built my project by running yarn tsc. However, when I executed docker run -p88:5011 accounts2 I encountered the following error: PM2 error: Error: Script not found: /home/accounts/dist/server/index.js. This error occurs despit ...

There is a gap found in the span element in Angular, but this gap is not seen in

Comparing two component templates, one in AngularJS and the other in modern Angular: <span ng-repeat="status in statusInformation.statusList | filter : whereFilter" class="show-on-hover"> <span class="app-icon ap ...

What is the best way to determine the specific type of a value that is part of a union type?

Utilizing @azure/msal-react and @azure/msal-browser for implementing authentication in a React project with Typescript. The issue arises when TypeScript identifies event.payload as type EventPayload, but does not allow checking the exact type (e.g. Authen ...

How to retrieve a value from a base64-decoded string in Angular 6?

I successfully decoded a Base64 string using the xml2js library and obtained the following XML value: <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="293" height="102" viewBox="0 0 293 102" xmlns="http://www.w3.org/2000/svg" ...

Encountering a TypeScript React issue with passing objects to context in code

Within my project, there is a context provider that acts as an object containing various properties: <Provider value={ { scaleNum: scaleNum, // number scaleLet: scaleLet, // string ...

Tips for retrieving the return value from a function with an error handling callback

I am having an issue with my function that is supposed to return data or throw an error from a JSON web token custom function. The problem I am facing is that the data returned from the signer part of the function is not being assigned to the token const a ...

Trouble with the Ngx-Captcha feature

I am currently utilizing https://www.npmjs.com/package/ngx-captcha/v/11.0.0. <ngx-recaptcha2 #captchaElem [siteKey]="'6Leh1ZIjAAAAAG8g0BuncTRT-VMjh3Y7HblZ9XSZ'" (success)="handleSuccess($event)" [useGlobalDomain]="fals ...

Swap out the traditional for loop with a LINQ query utilizing the any method

In my TypeScript code, I have the following snippet: public executeTest(test: Test): void { const testFilters: Record<string> = getTestFilters(); let isTestingRequired: boolean = false; for (let i: number = 0; i < testFilters.leng ...

Unidentified Angular NgRX action

After building my Angular application using NgRX, I integrated actions, reducers, services, and effects. However, when trying to access the action in the component code, I encountered the following error: Error: Property 'GetEmployees' does no ...

Troubleshooting: Issues with CORS in Django and Angular for POST requests

My Angular4 application (currently running on http://127.0.0.1:4200 development server) needs to communicate with a Django REST backend on the web. The backend is hosted on HTTPS (via Apache server directing requests to a gunicorn server on an internal por ...

What is the process for creating a unique Vee-Validate rule in TypeScript?

I am in the process of developing a unique VeeValidate rule for my VueJS component written in TypeScript. This rule is designed to validate two fields simultaneously, following the guidelines outlined in VeeValidate - Cross Field Validation. Here is a snip ...

Modify the [src] attribute of an image dynamically

I have a component that contains a list of records. export class HomeComponent implements OnInit { public wonders: WonderModel[] = []; constructor(private ms: ModelService){ ms.wonderService.getWonders(); this.wonders = ms.wonder ...

What could be causing the media queries to not take effect at a max-width of 425px?

When I try to apply media queries for max-width 768px and max-width 425px, I noticed that the styles intended for max-width 768px are also being applied when the width is 425px. This results in the al-articles-tags-chip-list-container having a width of 600 ...

typescript in conjunction with nested destructuring

ES6 has definitely made coding more efficient by reducing the number of lines, but relying solely on typescript for everything may not be the best approach. If I were to implement type checking for arguments that have been destructed multiple levels deep, ...

Restricting types does not appear to be effective when it comes to properties that are related

I am working with a specific type that looks like this: type Props = { type: 'foo'; value: string; } | { type: 'baz'; value: number; }; However, when using a switch statement with the type property in TypeScript, the program in ...