How to distribute an es6 package with submodules - javascript

I am trying to publish a package on npm. I'm not sure how to phrase what I am trying to do..maybe packaging "sub modules"?
The abbreviated directory structure is:
- package.json
- src/
- a.js
- b.js
- dist/
- a.js
- b.js
My build script uses Babel to transpile to the dist directory with the same modules as in src/. I would like
consumers to import functions from the a.js module like so:
import {foo} from "mypackage/a";
not
import {foo} from "mypackage/dist/a";
If I add an index.js to mypackage/ and export a I can do
import {a} from "mypackage"
but that's not what I want...
What is the standard way to publish packages so "sub modules" can be imported like this or can someone point me to a repo that does this kind of thing?

If I understand, there should be two options that you can choose.
First, let me try to explain how Node sub-modules with a slash are handled by Node.
When you import the module with a slash, Node will try to look in the root folder for a file with the name that is specified after the slash. In your case, it will be a.js. In case nothing is found, Node will try to look for the directory named a containing the file index.js. If no file is found, nothing is imported.
So back to your problem, you can either create file a.js in your root folder containing the following export:
export * from './dist/a';
// The traditional export should look like this, in case I have wrong ES6 export
module.exports = require('./dist/a')
or change the structure of your project a bit, so files a and b are located in their specific directories.
For example:
- package.json
- src/
- a.js
- b.js
- a/
- index.js
- b/
- index.js

Related

VSCode not showing correct import suggestions for a local linked package

VSCode is not offering import suggestions correctly for a package that was linked locally (using npm link).
Normally when I want to use a class from a package, I'd write Foo and press tab to automatically add the import for myModule/Foo.js. But in my current setup, I am not getting import suggestions for myModule at all.
My folder structure looks like this:
project
|- server (node package)
| |- node_modules/
| |- src/
| |- package.json
|- myModule (node package)
|- src/
|- package.json
All packages are of the module type (es6 import/exports).
"type": "module"
The myModule package uses a subpath export to hide the src folder:
"exports": {
"./*": "./src/*"
}
Then I used npm link in project/myModule, followed by npm link myModule in project/server.
I can then use myModule in server/src/index.js as follows:
import Foo from "myModule/Foo.js"
import Four from "myModule/Bar/Four.js"
At this point, Node is happy. Unfortunately, VSCode is not. When using Foo or Bar and using the IntelliSense suggestion to automatically add the import, I get the following result:
No import suggestions at all with the setup described above.
VSCode adding an import through a relative path (E.g: ../../myModule/src/Foo.js) if I remove the subpath export from myModule.
I tried adding a jsconfig.json with a path alias like: "myModule/*": ["./node_modules/myModule/src/*"] or "myModule/*": ["./node_modules/myModule/*"] but unfortunately that didn't make a difference.
What can I do to have working import suggestions again in VSCode?
Placing a jsconfig.json in project/ with "myModule/*": ["./myModule/src/*"] as a path alias has made VSCode happy again.
This may present other challenges further down the line, as I'm now specifying this alias for every package in project/. Other suggestions are still very much welcome, as well as an explanation why jsconfig.json in project/server/ was not working as expected.

How does webpack pick a relative path inside node_modules ? does it reference package.json at all?

When i do npm install react-slick, i get the following in my node_modules folder:
Now in my react application in a src/index.js file, when i do the following:
import Slider from "react-slick";
How does webpack know where to pick slider from ? will it at all look for some clue or definition inside node_modules/react-slick/package.json at all ?
Edit :- so here is the package.json file for webpack, when i import Slider from 'react-slick' , does it resolve to dist or lib ? and which file does it pick then and why ?
Well, the simple walkthrough of it will be as below:
Simple Walkthrough
If you carefully look at the node_modules/react-slick/package.json there is a property named main. Something like this:
{
"name": "react-slick",
"main": "index.js"
}
It will tell the Webpack which file is the entry file of the whole package (It's usually referred to index.js). All the necessary exports for the package lies in this file, so Webpack will only look for those exports and will import what you looking for. In this particular case, there should be a default export for the Slider that you using right now. So the index.js is probably something like this:
// index.js
var slider = require('./lib/slider'); // Usually all the main modules are lies under lib folder.
// other imports ...
module.exports = slider;
Difference between lib and dist
Usually, the dist folder is for shipping a UMD that a user can use if they aren't using package management. The lib folder is what package.json, main property points to, and users that install your package using npm will consume that directly. The only use of the lib as opposed to src is to transform your source using babel and Webpack to be more generally compatible since most build processes don't run babel transforms on packages in node_modules.
Webpack uses aliases to target node_modules using a shorthand.
Example #1:
import 'xyz'
/abc/node_modules/xyz/index.js
Example #2:
import 'xyz/file.js'
/abc/node_modules/xyz/file.js
Once it targets the correct folder in node_modules, it follows the rules written in the package itself (manifest, package.json)
You can also define your own aliases as such:
webpack.config.js
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
xyz$: path.resolve(__dirname, 'path/to/file.js')
}
}
};
And then can be used as import xyz from $xyz

How do NPM Package imports resolve?

I have a monorepo with lerna, yarn workspaces, and the following structure:
- packages
- a_webpack
- src
- index.ts
- dist
- main.js
- main.css
- b_tsc
- src
- indes.ts
- dist
- index.js
both packages a_webpack and b_tsc are to be consumed by another package c.
on b_tsc i run tsc to compile into its dist folder.
on a_webpack i run webpack to do the same
I mainly use webpack, because I can get a separate .css file in the dist that can be imported
When I import b_tsc in package c like:
import { something } from 'b_tsc'
everything works as expected.
Also when I do:
import 'b_tsc/dist/main.css'
that is working.
However when i try:
import { something } from 'a_webpack'
I'm getting:
Module not found: Can't resolve 'a_webpack'
Question
Even if I change the output of webpack to generate dist/index.js, it doesn't work. What am I doing wrong here?
General Question
When importing like seen above, how does the compiler know it needs to look inside dist/main.js or any other entry point within that package?
Figured it out:
The entrypoint is specified in the package.jsons main property.
That solved the import issue for me.

Where do I export function in my npm package so developers can import them?

I am trying to create an npm package for others to use. Currently, my project looks like this:
lib/
forms/
Button.js
...
layouts/
Row.js
...
node_modules/
src/
forms/
Button.tsx
...
layouts/
Row.tsx
...
package.json
...
package.json specifies that only lib/**/* should be published. An npm script compiles my *.tsx files into the *.js files in the lib/ directory.
Ideally, after someone installs my package, they would be able to import Button like this:
import {Button} from "#whiterook6/components/forms"
But the way the import works currently is if it looks like this:
import {Button} from "#whiterook6/components/lib/forms/Button"
How and where do I put my index.js files, and what content goes inside them, so that they
reference the lib/**/*.js files properly, instead of referencing src/ files, since those won't be published;
allow other devs to only include the bits they want (so the total compiled project size remains small); and
have a "nice" import statement?
Also, what is the correct export syntax I should use?

Submodules in Browserify

/foo
/bar.js
/foobar.js
/index.js
In node.js if you a require a directory (require('foo')), it would look into that directory and find an index.js file and return whatever exports I have in that file, so I can just bundle up the contents of the directory in an index.js file. Therefore, I dont have to require bar and foobar separately if index.js already includes them.
However this approach doesn't work with browserify. It seems like only thing browserify understands is relative paths.
/star
/star.js
/starfoo.js
/index.js
/foo
/bar.js
/foobar.js
/index.js
In other words I want to separate my project into submodules, call require on a directory as if I am calling require on a dependency. For example in the star.js file I want to be able to require('foo') and get the exports of bar.js and foobar.js (as long as /foo/index.js is importing bar.js and foobar.js)
edit:
Looking at the react source code, i think what i am describing is possible
https://github.com/facebook/react/blob/master/src/isomorphic/ReactIsomorphic.js
In this file they call require on React-Children in line 14.
var ReactChildren = require('ReactChildren');
However react children is couple directories deeper.
https://github.com/facebook/react/blob/master/src/isomorphic/children/ReactChildren.js
Where is this mapping defined?
There isn't a way to specify a base directory because that's not how node modules work. If you want non-relative paths, use the node_modules directory. If you want require('foo') to work from any directory, just make a symlink from your project root:
ln -s foo node_modules/foo

Categories