Root directory in package.json
Asked Answered
H

7

95

My question concerns an existing library that I wish to publish as an NPM module. The library is already in use, and currently required via the local file system.

How can I specify the root directory of my module's files?

If I have a structure like:

.
├── package.json
├── src
|   ├── js
|   └────── lib
|   └───────── my
|   └───────────── thing.js
|   └───────────── that.js

How do I specify that the root of my module, and accessible files is src/js/lib/my/?

I would like to use as follows from an outside project:

var thing = require('my/thing'),
    that = require('my/that');

I saw the "files" property in package.json, is this the right way to go?

Habitforming answered 18/5, 2015 at 12:3 Comment(0)
C
50

UPDATE 2020

The issue suggesting mainDir is now closed. Instead there is a new field called exports which can be used almost like es import maps to map a folder to an export alias:

// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./my/": "./src/js/lib/my/"
  }
}
import thing from 'es-module-package/my/thing.js';
// Loads ./node_modules/es-module-package/src/js/lib/my/thing.js

As suggested in the issue linked in the original answer below it may be possible to map the root to a folder to access import thing from pkg/thing.js as so:

{
  "type": "module",
  "main": "./dist/index.js",
  "exports": {
    "./": "./src/js/lib/my/"
  }
}

Original Answer

For a native solution, see this node issue https://github.com/nodejs/node/issues/14970

The feature request suggests a mainDir field in the package.json next to main.

The more people that vote, the faster/more likely it will be implemented

Chuffy answered 29/1, 2018 at 10:1 Comment(5)
Unfortunately that issue was closed.Marsiella
Though docs related to node 12.14.0, unfortunately it doesn't work yet?? I use node 13.5.0Zindman
exports is great although it landed fairly recently. Apparently in node 12.16.0 without the --experimental-exports flag; refs: github.com/nodejs/node/pull/31691 and github.com/nodejs/node/pull/29867. TypeScript support is still work-in-progress; ref: github.com/microsoft/TypeScript/issues/33079Membership
Perfect, and the root folder access works too.Chronological
Node 16 deprecation warning: DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package. Update this package.json to use a subpath pattern like "./*".Suppressive
C
8

As the doc says:

The main field is a module ID that is the primary entry point to your program.

So you'll have something like "main": "src/js/lib/my/app.js" in your package.json file.

I would suggest you to create an app.js file and module.exports your different children. For example:

 module.exports.thing = require('./thing');
 module.exports.that = require('./that');

And use them like this:

var mylib = require('mylib')
  , thing = mylib.thing
  , that = mylib.that;
Contemplate answered 18/5, 2015 at 12:14 Comment(2)
My package is an already existing library, I can't alter the structure. I just need to NPMify my library.Habitforming
Why is adding entries to package.json not changing the lib, but adding an index.js is? This should be the accepted answer.Thermae
M
6

package.json is mainly a file used by npm to install and manage dependencies.

the require construct does not care a lot about package.json so you will not be able to use it to subvert the way require works and make it believe that packages are not where the require loading scheme expects them.

See the documentation on https://nodejs.org/api/modules.html and the loading scheme here: https://nodejs.org/api/modules.html#modules_all_together

you could maybe use the technique that the documentation calls 'Loading from the global folders' and define the NODE_PATH environment variable.

but I advise you to stick to a more standard way : - put your modules in a node_modules directory - or start your module hierarchy in the same directory where your app.js or index.js is located

Mchenry answered 18/5, 2015 at 12:22 Comment(1)
This is for a module that I want to publishHabitforming
D
4

Now this is ugly workaround and it does pollute the root of your package. But until Jordan's answer works, this feels like the way to achieve what you ask.

Just add a file in the root of your package for each of the modules you want to export using the require with slash notation. Such file will have the same name as the module being exported and it will simply reexport it.

.
├── package.json
├── thing.js       <--
├── that.js        <--
├── src
|   ├── js
|   └────── lib
|   └───────── my
|   └───────────── thing.js
|   └───────────── that.js

For example file ./thing.js will contain:

module.exports = require('./src/js/lib/my/thing');

And so you could require it as:

const thing = require('mypackage/thing');

Also as stated in the bug about adding mainDir property into package.json you can just temporarily copy your sources and the package.json file into one directory and publish from there.

Dart answered 18/4, 2018 at 17:43 Comment(0)
H
2

Another possibility is to use ECMAScript modules (ES modules), particularly the package exports field in your package.json file.

Given a package.json file with this config:

{
  "name": "my",
  "exports": {
    "./": "./src/js/lib/my/"
  }
}

You should be able to import modules from the library like:

import thing from 'my/thing'
import that from 'my/that'

This is enabled by default since node 13.0.0, but was behind the --experimental-exports flag from 12.13.0.

Note, that the ES Module spec is in the Stability:1 - Experimental stage and subject to change. I have no idea the extent to which this might be compatible with CommonJS modules.

Haggi answered 29/8, 2019 at 14:11 Comment(0)
D
0

Simply publish /install /link the folder intended to be the root

The natural way to achieve that, according to the npm approach, is to publish the folder which is to be the root. There are several ways to do that, depends on the final environment you want to work with:

  1. npm publish <folder> from your package repo to an npm registry and then install your package in other project as you install other packages. In your case it would be npm publish src/js/lib/my.
  2. npm install <folder> in some other project if you want to use your package only locally. In your case you go to the other project and run npm install relative/path/to/src/js/lib/my
  3. npm link your folder locally to node_modules in other project in case you'd like to have the changes in your original package reflected instantly in other project. In your case you first cd src/js/lib/my and run npm link and then go to the other project and run npm link my.

Prerequisite: in any case above, prior to the publish/install/link, you have to put in your my folder at least a proper package.json file. In your case, you have to have the package name defined in the package.json file as "name": "my". Typically you'll want there also some other files like README.md or LICENSE.

Remarks to methods 2 & 3

Typically there are problems with package dependencies when you use the package installed this way. To avoid it, first pack the package with npm pack dist and then install the package in the target project from the packed tarball, i.e. npm install path/to/package-tarball.tgz.

Automation example

You can automate the publishing process using the prepare script, combined with build script and "private": true field put in the package.json located in the root directory of your package repo. Here is an example with the dist folder as a package root:

  "private": true,
  "scripts": {
    "build": "rm -rf dist && webpack --mode=production && cat ./package.json | grep -v '\"private\":' > dist/package.json",
    "prepare": "npm run build"
  },

This way you won't publish the root folder ("private": true). And when you hit npm publish dist the automatically invoked prepare script will trigger dist folder cleanup (rm -rf dist), package build (webpack --mode=production) and copy of the package.jsonto the dist folder without the field "private": true (cat ./package.json | grep -v private > dist/package.json).

Discount answered 5/10, 2020 at 17:21 Comment(0)
C
-3

In webpack, you can specify resolve.alias like this:

{
  resolve: {
    alias: {
      'my': 'my/src'
    }
  }
}

or you can specify directions option in package.json

{
  directions: {
    'lib': 'src/lib'
  }
}
Curative answered 9/12, 2017 at 4:18 Comment(4)
Please site where in the documents on the internet this is available. I have never seen directions or any reference of it.Mariandi
@Mariandi Googling helps. First result is webpack docs: Resolve. Absolutely amazing answer. After trying to fix this the third time, finally came across this! :)Paulownia
directions doesn't exist in package.json documentationQuoin
I don't know where directions came from but now we have exports: nodejs.org/api/esm.html#esm_package_exportsPandorapandour

© 2022 - 2024 — McMap. All rights reserved.