Tips for enabling users to import from subdirectories within my NPM package

Is there a way to allow users to import from subfolders of my TypeScript NPM package? For instance, if the TypeScript code is structured like this:

- lib
- src
  - server
  - react

Users should be able to import from the subfolders as package-name/react, package-name/server, etc.

My tsconfig.json configuration looks like this:

{
    "compilerOptions": {
      "target": "es5",
      "module": "commonjs",
      "declaration": true,
      "outDir": "./lib",
      "strict": true,
      "jsx": "react",
      "esModuleInterop": true
    },
    "include": ["src"],
    "exclude": ["node_modules", "**/__tests__/*"]
  }

I was able to achieve this when setting "outDir" to the project root, but it made the file structure messy. Can anyone suggest a solution for importing from submodules while keeping "outDir": "./lib"? Any helpful responses would be greatly appreciated. Thank you in advance.

Answer №1

To enable a specific entry point in the package.json file, you can utilize the exports field:

"exports": {
  "./server": "./lib/server",
  "./react": "./lib/react"
},

For more details, refer to: https://nodejs.org/api/packages.html#packages_package_entry_points

However, it's important to note that:

Warning: Implementing the "exports" field may restrict consumers from using undefined entry points, such as the package.json (e.g. require('your-package/package.json'). This could potentially cause disruptions.


UPDATE: TypeScript seems to overlook this field, but incorporating types like this might be helpful:

// server.d.ts
export * from "./lib/server";

// react.d.ts
export * from "./lib/react";

My Approach

I replicated your project structure and made modifications:

src/
  server.ts
  react.ts
lib/
  (generated output)
  server.d.ts
  server.js
  react.d.ts
  react.js
package.json
tsconfig.json
react.d.ts
server.d.ts

Add the following lines to package.json:

"exports": {
  "./server": "./lib/server",
  "./react": "./lib/react"
},

react.d.ts

export * from "./lib/react";

server.d.ts

export * from "./lib/server";

This approach worked for me. Now I was able to import "mypackage/server" in another context. If you haven't split the definition files yet, consider implementing these changes to test their effectiveness.

Answer №2

To implement the following functionality, insert the exports entry below in your package.json file:

"exports": {
  "./": "./lib/"
},

Once the entry is added, you will be able to import from the lib subfolder or any file within the lib folder. However, for a subfolder within the lib folder, there must be an index.js file which will handle the imports.

The structure after typescript compilation that would work is as follows:

- lib
- src
  - server.js
  - react.js
- lib
- src
  - server
    - index.js
  - react
    - index.js

Note: Do ensure that the presence of a slash(/) at the end of the entry since it is crucial for subpath export to function properly.

Follow these example steps:

mkdir -p mod1/src/api mod2/src
cd mod1
npm init -y

mod1/tsconfig.json

{
    "compilerOptions": {
      "target": "es5",
      "module": "commonjs",
      "declaration": true,
      "outDir": "./lib",
      "strict": true,
      "jsx": "react",
      "esModuleInterop": true
    },
    "include": ["src"],
    "exclude": ["node_modules", "**/__tests__/*"]
}

mod1/package.json

{
  "name": "mod1",
  "version": "1.0.0",
  "description": "",
  "exports": {
    "./": "./lib/"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "MIT"
}

mod1/src/api/index.ts

export const api = function() : void {
  console.log('api');
}

mod1/src/react.ts

export const react = function() : void {
  console.log('react');
}

mod1/src/server.ts

export const server = function() : void {
  console.log('server');
}

Navigate to the mod2 directory

cd ../mod2
npm init -y
npm install ../mod1

mod2/src/index.js

const lib = require('mod1/server');
const { react } = require('mod1/react');
const { api } = require('mod1/api');
lib.server();
react();
api();

Note: The process was tested using node version 14.4.0 and tsc version 4.2.4

Answer №3

To streamline your repository structure during the publish process, consider moving your package.json file into the dist/lib folder. Take a look here for an example of how this can be done. Keep in mind that this approach may complicate yarn link and may not be compatible with yarn workspaces.

Another option is to utilize the exports key in package.json once support for it is implemented in TypeScript – see this link for more information.

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

Delay the execution until all promises inside the for loop are resolved in Angular 7 using Typescript

I am currently working on a project using Angular 7. I have a function that contains a promise which saves the result in an array as shown below: appendImage(item){ this.imageCompress.compressFile(item, 50, 50).then( result => { this.compressedI ...

What could be causing the error within the 'react-code-blocks' library?

Currently, I am involved in a project that utilizes Typescript and React. However, I have encountered an issue while working with the 'react-code-blocks' library. The structure of my container component is as follows: interface ICodeBlockProps { ...

Tips for resolving the error: finding the right loader for handling specific file types in React hooks

data = [{ img: '01d' }, { img: '02d' }] data && data.map((item) => ( <img src={require(`./icons/${item['img']}.svg`).default} /> )) I am facing an issue with the message Error: Module parse failed: U ...

Creating a Universal Resolver in Angular (2+) - A Step-by-Step Guide

I have a vision to develop an ultra-generic API Resolver for my application. The goal is to have all "GET" requests, with potential extension to other verbs in the future, utilize this resolver. I aim to pass the URL and request verb to the resolver, allow ...

The Angular material checkbox has a mind of its own, deciding to uncheck

I am having an issue with a list displayed as checkboxes using angular-material (Angular 7). Below, I will provide the code snippets for both the .html and .ts files. Every time I click on a checkbox, it gets checked but then immediately becomes unchecked ...

What could be causing an error with NextJS's getStaticPaths when running next build?

When attempting to use Next.js's SSG with getStaticPaths and getStaticProps, everything worked fine in development. However, upon running the build command, an error was thrown: A required parameter (id) was not provided as a string in getStaticPath ...

The absence of a function implementation right after the declaration within a TypeScript class is a common issue that needs

I received a handwritten array to populate a table for my class, however I am now fetching this array's content from a JSON during the ngOnInit phase and it is not structured in the way I require. Therefore, I am attempting to create a function that ...

Angular 2 - Issue: Parameters provided do not correspond to any signature of call target

I'm encountering the following error message: "error TS2346: Supplied parameters do not match any signature of call target." This occurs when attempting to reject a promise, but I believe the code adheres to the required signatures. Any suggestions on ...

What is the best way to integrate Next.js with Strapi (or the other way around)?

My goal is to develop an application utilizing Next.js for the frontend, fetching data from a Strapi API hosted on the same server. The plan is to have Strapi handle API and admin routes, while Next.js manages all other routes. I intend to use fetch in Nex ...

## We have encountered a problem with node-fibers ##

## Encountering a Problem with `node-fibers` ## `\node_modules\synchronize\node_modules\fibers\bin\win32-x64-57\fibers.node` is not found. When I attempt to start my node server using the command 'node server.js&apo ...

tips for accessing dynamic key pair value data in Angular

I am facing an issue where I cannot retrieve the dynamic key pair value from the dynamic JSON. Below is my JSON: var d = { "pexels-photo.jpeg": { "information": "laptop", "desc": { "mimetype": "image/jpeg", "id" ...

Exploring through objects extensively and expanding all their values within Angular

I am in need of searching for a specific value within an object graph. Once this value is found, I want to set the 'expanded' property to true on that particular object, as well as on all containing objects up the object graph. For example, give ...

Looking for a solution to resolve the issue "ERROR TypeError: Cannot set property 'id' of undefined"?

Whenever I attempt to call getHistoryData() from the HTML, an error message "ERROR TypeError: Cannot set property 'id' of undefined" appears. export class Data { id : string ; fromTime : any ; toTime : any ; deviceType : string ...

The error message received when attempting to install Copay using npm is: unable to execute in working directory %s %s (working directory

While attempting to install copay bitcopay, I ran into an error when executing sudo npm install. $ sudo npm install Password: npm WARN lifecycle <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1e7d716e7f675e2c3029302e">[emai ...

Utilize the identical function within the reducer for numerous mat-slide-toggle and checkboxes in component.html

I'm currently diving into the world of Angular and ngrx while tackling a project focused on enabling users to create forms. In this project, users can add various form elements (such as labels, text fields, dropdown menus, checkboxes, etc.) from a sid ...

Angular Error: Potential security risk detected in resource URL context due to unsafe value being used

Hey there, I'm looking to display a dynamic pdf file. Initially, I encountered a CORS error, but managed to resolve it by using DOM Sanitizer. However, now I'm facing an issue with unsafe URLs. Any assistance would be greatly appreciated. Below ...

Error encountered while attempting to load SWC binary for win32/ia32 in a Next JS application

Upon installing a Next.js app using the command npx create-next-app@latest, I encountered an error while running the app. Can anyone explain why this error occurred and provide a solution? PS D:\New folder\my-app> npm run dev [email pr ...

An Axios error message indicates ERR_NETWORK and ERR_EMPTY_RESPONSE

When I initiate a Patch Request from the frontend, it takes approximately 30-40 seconds for the backend to resolve. const handleSendClick = (data: any) => { const requiredLanguages = Array.isArray(data.required_languages) ? data.required_langu ...

Leveraging Fastify's preHandler middleware functionality

Implementing a middleware to validate user authentication before accessing the specified route. Encountering an issue where tokenService inside tokenController is showing as undefined when passing tokenController.authUser as a middleware. However, the met ...

Is it possible to implement a redirect in Angular's Resolve Navigation Guard when an error is encountered from a resolved promise?

I have integrated Angularfire into my Angular project and am utilizing the authentication feature. Everything is functioning properly, however, my Resolve Navigation Guard is preventing the activation of the component in case of an error during the resolve ...