22

I have a issue with npm and the main field. I see the documentation and as of my understanding I point main to be a different entry point than ./index.js. I already tested the package where all dist files are inside the root folder. I ignore src and test during pack phase using .npmignore but I did not like the point that building and packing the project to verify the structure pul all my files into the package root folder. So i changed the output to be dist instead.

If i use npm pack and extract the file I get the following structure:

/
dist
  -- index.js
  -- moduleA
    -- index.js
package.json
README.md

So for so good. But now I am forced to import it as follows:

import {moduleA} from "myNpmModule/dist/moduleA";

But I dont want to have the dist folder in my import. So I set main in package.json

"main": "dist/index.js"

But still it does not work and only works if I import with dist. I use npm 3.10.7 and node 6.7.0.

Can anyone help?

Regards

jonas.hartwig
  • 857
  • 3
  • 8
  • 19

3 Answers3

18

It's hard to tell for sure not knowing the contents of your main index.js and moduleA but it's usually done in a way that you don't import any specific file, but rather the directory containing the package.json - like:

import {moduleA} from "myNpmModule";

Now, the index.js referenced as "main" in package.json should import the rest of the modules, and export them as its own module.exports properties.

For example, in dist/index.js:

import {moduleA} from "./moduleA";
module.exports.moduleA = moduleA;

and in your main code:

import {moduleA} from "myNpmModule";

Something like that - with possible differences to suits your own module's structure.

Actually I wrote a module that automatically does something like that, importing modules in subdirectories and exporting them as properties. I haven't put it on npm because it was for my own use, when I publish it to npm I'll update this answer.

Update

Here is a working example of what I described above - with import changed to require() to avoid the need for a transpilation step.

Module

A module following my advice from this answer:

Project structure:

dist
  -- index.js
  -- moduleA
    -- index.js
package.json
moduleA.js

dist/index.js contents:

var {moduleA} = require('./moduleA');
module.exports.moduleA = moduleA;

dist/moduleA/index.js contents:

module.exports.moduleA = {
  info: 'This is what dist/moduleA/index.js exports as moduleA'
};

package.json contents:

{
  "name": "nested-project-structure-example",
  "version": "0.0.1",
  "description": "An example for a Stack Overflow answer",
  "main": "dist/index.js",
  "scripts": {
    "test": "node test.js"
  },
  // ...
}

moduleA.js contents:

module.exports = require('./dist/moduleA');

Usage

A project that uses this module:

It can be imported like this:

Version 1
var {moduleA} = require('nested-project-structure-example');
console.error(moduleA.info);

This imports the dist/ModuleA/index.js via the dist/index.js file referenced in package.json. See test1.js for a working example.

Version 2
var {moduleA} = require('nested-project-structure-example/dist/moduleA');
console.error(moduleA.info);

This imports the dist/ModuleA/index.js directly knowing the internal path including dist. See test2.js for a working example.

Version 3
var {moduleA} = require('nested-project-structure-example/moduleA');
console.error(moduleA.info);

This imports the dist/ModuleA/index.js via the moduleA.js file in the main project directory. That way doesn't need to know the internal project organization - dist path is not needed. See test3.js for a working example.

The whole content of the moduleA.js in the project is:

module.exports = require('./dist/moduleA');

Without having such a file in your project's root directory you will not be able to import the moduleA without either including the dist in your path or importing it directly via the main js file of your project included in package.json (dist/index.js in this case).

Those are 3 ways to achieve the goal of your question, two of which don't include the dist in the code that imports the module. I hope it answers your question.

Those are the only options that you have without splitting your module into a set of completely separate modules, each distributed separately.

rsp
  • 107,747
  • 29
  • 201
  • 177
  • thanks for your response. Actually my structure is like you mentioned except that i dont have any "root" index.js. instead i want to replace the root index.js with the wone from dist/index.js with does all the module.exports.moduleA = moduleA. my how was that main would bypass the index.js and just point it to a different file. – jonas.hartwig Sep 28 '16 at 13:01
  • @jonas.hartwig See my updated answer. It includes a working example of a structure that I was explaining, even with a module published to npm and travis tests. It works and it has a file structure that you described, with imports and exports organized how I originally explained in my answer. – rsp Sep 29 '16 at 12:13
  • Hi, you code works and this works for me in my code as well, however it is not quite what I am looking for. Taking your example I would want to avoid importing the root index.js and the whole structure of the package with all modules. What I want to do instead is to just import info. So in your example you import the whole package. and in the second line of your example you access info. I want to import info directly. But that does not seem to work if you do not access the projects file structure and the index.js of that sub module directly. e.g var moduleA = require("my-package/moduleA"). – jonas.hartwig Sep 30 '16 at 06:13
  • @jonas.hartwig Ok, I see what you're trying to do. You have 3 options here: (1) require the root index.js with the whole structure only to get one part, (2) require "my-package/dist/moduleA" including the "dist" part, (3) add a small one-line moduleA.js file in your package directory and require "my-package/moduleA" without a "dist" part. See my updated answer for all 3 options explained. – rsp Sep 30 '16 at 14:39
  • @jonas.hartwig So, did the updated answer work for you? – rsp Oct 02 '16 at 15:42
  • What could you advise in the following case? I wrote a JS library using ES6 modules, and it includes a Webpack configuration which builds the library creating a UMD-compatible version of it if you run `npm run build`. Which file should I specify in the `main` field of `package.json`? `dist/my-library.js` is the UMD version (not minified), but is only available after the build (not committed to Git repo) and not right after you run `npm install my-library`. Should I commit `dist/` or is it OK to keep it out from Git? (I wouldn't like to commit the transpiled UMD version if possible) – tonix Nov 20 '19 at 23:04
1

So here is what I understand of how this works. I am not 100% sure it that is true. I gained this insight from plain observation and reasoning rather from actual seeing this in a doc. assumption 1 (package.json):

{... "name": "my-package", "main": "dist/index.js", "scripts": { "build": "babel src --out-dir dist", "prepublish": "npm run build" }... }

assumption 2 (package structure):

/
  -- dist
    -- moduleA
      -- index.js
        -- moduleAA
          -- index.js
    -- moduleB
      -- index.js

doing the above you get:

var myPackage = require("my-package");
var moduleAA = myPackage.moduleA.moduleAA;
// or in short
var moduleAA = require("my-package").moduleA.moduleAA;

however it seems like that:

import moduleA from "my-package/moduleA/moduleAA";

is not equivalent to the statement using require above. What you could do instead id:

import { moduleA } from "my-pakage";
const moduleAA = moduleA.moduleAA;

assuming you still want to have the direct import from moduleAA with the above given project structure you would need to do:

import moduleAA from "my-package/dist/moduleA/moduleAA";

So here is my conclusion and how i understand this.

... from "my-package/dist/moduleA/moduleAA"; does not look through the project structure from a JS/npm point of view (what is exported) but instead it looks at the file structure of the package as soon as you use a / in the from phrase.

Which means that if you use

import { moduleA } from "my-pakage";

it will actually import all exports from dist/index.js but if you import "my-package/moduleA" it actually looks inside the package if the path "/moduleA" exists. In the above case that is not true. If we omit the dist folder and move the structure into the package root that statement would actually work the way you would expect.

So no one can ask why I want to have this stuff in dist folder? It is easy to ignore in git. As I understand the best practise using node you use the "prepublish" step to actually build your package. which means if you make a fresh checkout of the code and run "npm install" which executes "npm run prepublish" by design it spams the folder structure with the packaged and transformed files.

After playing with this a few hours I gave up and just accept that "npm install" would potentially spam my folders. As an alternative I could not define prepublish in my package.json and just run "npm run build" prior to "npm publish". ("scripts": { "build": "babel src --out-dir ." })

jonas.hartwig
  • 857
  • 3
  • 8
  • 19
0

This is an old question and maybe there is something better now but here is how I did it

add this to your scripts in package.json

  "scripts": {
     "link:publish": "tsc && cp package.json dist && cp README.md dist && cp tsconfig.json dist && cd dist && npm publish",
  },

basically coppy things in the dist folder to publish from there. a bit hacky but in my case necessary

Ced
  • 15,847
  • 14
  • 87
  • 146