Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to let users import from subfolders of my NPM package

I want to let users import from the subfolders of my TypeScript NPM package. For example, if the directory structure of the TypeScript code is as follows,

- lib
- src
  - server
  - react

users of my package should be able to import from subfolders as package-name/react, package-name/server etc.

My tsconfig.json is as follows.

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

I could do this when "outDir" was set to the project root, but then the file structure is messy. Can someone point me a way out to do this (importing from submodules) while preserving "outDir": "./lib"? Helpful answers are highly appreciated. Thanks in advance.

like image 574
Pavindu Avatar asked Apr 14 '21 19:04

Pavindu


People also ask

Where are npm dependent packages?

You can use https://www.npmjs.com/package/npm-dependents to find dependents that are not installed. In the meantime, you may want to just use the "Dependents" tab on the individual npm project pages.


3 Answers

That is possible with an entry in the package.json file called exports:

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

You can read more about it here: https://nodejs.org/api/packages.html#packages_package_entry_points

But be careful:

Warning: Introducing the "exports" field prevents consumers of a package from using any entry points that are not defined, including the package.json (e.g. require('your-package/package.json'). This will likely be a breaking change.


EDIT: It seems like TypeScript ignores the field, maybe it helps when you add types like this:

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

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

What I did

I copied your project setup and changed something:

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 this to package.json:

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

react.d.ts

export * from "./lib/react";

server.d.ts

export * from "./lib/server";

That worked for me. Now I could import "mypackage/server" for example somewhere else. Maybe you didn't split up the definition files to multiple files, but try to apply these changes and see if it works.

like image 169
blaumeise20 Avatar answered Oct 16 '22 09:10

blaumeise20


You can copy your package.json file into your dist/lib folder as part of the publish process to avoid a messy repository structure. See here for an example. As a downside, yarn link might be more complicated and this approach might not work for yarn workspaces.

Alternatively, you can use the exports key in package.json once TypeScript implements support for it.

like image 39
Henning Avatar answered Oct 16 '22 10:10

Henning


Add below exports entry in package.json

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

After the entry you can import from within lib subfolder or file within lib folder but for subfolder within lib folder there need to be index.js file which would be imported.

Structure after typescript compilation that would work:

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

Note: slash(/) at the end of the entry are important for subpath export.

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');
}

change to 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: I used node version 14.4.0 and tsc version 4.2.4

like image 44
Chandan Avatar answered Oct 16 '22 11:10

Chandan