Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Package.json with multiple entrypoints

Tags:

node.js

npm

I have a library which was used as follows

import { Foo } from '@me/core';
const foo = new Foo();

The package.json of that library looks like

"name": "@me/core",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
...

With dist/index.js its entrypoint. Now, however, I would provide an import for NodeJs projects only and one for web projects. Ideally I would have something like this for NodeJs

import { Foo } from '@me/core/nodejs';

and if you're working on a web-project you could do

import { Foo } from '@me/core/web';

My understanding is that both @me/core/nodejs and @me/core/web will be different NPM packages this way, which is not what I want. I want it to be in 1 npm package.

I tried to changed the library's index.ts file, from

export * from './foo';

into

import * as nodejs from './nodejs';
import * as web from './web';

export { web, nodejs };

This actually works, but I have to use it now (in a NODEJS project) as follows

import { nodejs } from '@me/core';

const foo = new nodejs.Foo();

Is there maybe a way to import this such that I don't need the nodejs everytime?

As you can see I'm not so sure what I should do here so any help would be appreciated!

UPDATE: Based on the suggestions by @Klaycon I see the following error:

enter image description here

like image 218
Jeanluca Scaljeri Avatar asked Jul 23 '20 15:07

Jeanluca Scaljeri


People also ask

Can we have 2 package json?

If you use the two-package. json project structure, you'll only have your devDependencies in your development package. json and your dependencies in your app package.

Should I have multiple package json?

In terms of keeping things simple, it is easier to only have to maintain a single package. json file, but there is nothing inherently wrong with multiple package. json files within a repo. Some companies do use mono-repos, for which it would make total sense to have multiple package.

What is entry point in package json?

In order to publish a package on npm, it is important to define the package entry points in the project's package. json file. The entry point is the path to the file that should be loaded when users import a package through import Package from "package-name" or const Package = require("package-name") .

How do module exports work?

All functions related to a single module are contained in a file. Module exports are the instructions that tell Node. js which bits of code (functions, objects, strings, etc.) to export from a given file so that other files are allowed to access the exported code.


2 Answers

As you're using ECMAScript modules, please refer to node.js docs on package entry points:

In a package’s package.json file, two fields can define entry points for a package: "main" and "exports". The "main" field is supported in all versions of Node.js, but its capabilities are limited: it only defines the main entry point of the package.
The "exports" field provides an alternative to "main" where the package main entry point can be defined while also encapsulating the package, preventing any other entry points besides those defined in "exports". This encapsulation allows module authors to define a public interface for their package.

So, in your package.json, you could define exports like this:

  "main": "dist/index.js",
  "exports": {
    ".": "dist/index.js",
    "./nodejs": "dist/nodejs",
    "./web": "dist/web",
  }
like image 169
Klaycon Avatar answered Sep 25 '22 18:09

Klaycon


The accepted answer, using the exports field, is one way to do it, but not the only way -- and as you've found, isn't currently supported by Typescript.

Look at the @angular/material package, which works similarly to what you have in mind. In @angular/material/package.json, you've got a module field that points to an empty JS file, and a typings field that points to an empty .d.ts file. But you've also got @angular/material/button/package.json which points (indirectly) to the implementation of MatButton (and a bunch of other stuff). When you import {MatButton} from "@angular/material/button", it resolves this package file, looks at its module and typings fields, and uses those to resolve the runtime code and exported types, respectively.

You could do the same thing: @me/core/package.json would have module and typings fields that point to empty files; @me/core/node/package.json would point to code for your Node-specific Foo class, and @me/core/web/package.json would point to code for your browser version of Foo.

like image 33
Coderer Avatar answered Sep 23 '22 18:09

Coderer