I am trying to create an npm package that will have a local and a global (-g) installation option, but I am confused at the difference between the four directories (src vs lib and the purpose of bin).
Before, I would have used the src directory, and have webpack transpile and bundle (ts-loader + babel-loader) to the dist directory (with dist being hidden in gitignore). However, from what I looked at online, I should have instead bundle to lib directory, and manually create a bin directory that points to lib for executable cli (or global) packages?
Can someone tell me if my thought is correct? Should lib be added to gitignore, or should it be committed? What should I do about the bin directory? Is there a resource that you can point to for learning more about this?
I tried searching online for help with creating npm packages and searched through npmjs.com too, but I could not figure out what to do.
I also tried taking a quick look on github at what other projects did, but all I am able to derive so far is that the bin directory should include 1 index.js file that imports the main js file from the lib directory.
Thanks!
It doesn't really matter much when it comes to directory structure. Further, I would highly recommend that you use just the compiler Babel or TypeScript and generate required bundle (CJS or ESM). This way, you easily gets to preserve your source code directory structure.
If you cannot just use the compiler, then prefer Rollup over Webpack as it is excellent when it comes to bundling for libraries. The ESM output is still experiment with Webpack (as of version 5.xx).
If you still decide to go ahead with Webpack, then this is what I would do. For a given directory structure:
<ROOT>
|-- src
| |-- app (actual library)
| | |-- index.js
| |-- cli (cli related code)
| | |-- index.js
And my content is:
// Library barrel file: src/app/index.js
export function main() {
console.log('Hello from library');
}
// CLI barrel file: src/cli/index.js
import { main } from '../app/index';
function cli() {
main();
}
cli();
// webpack.config.js
module.exports = {
mode: 'production',
entry: {
'cli/index': './src/bin/cli.js',
'library/index': './src/app/index.js'
},
output: {
filename: '[name].js',
library: {
type: 'commonjs'
}
},
externals: {
'../app/index': {
commonjs: '../app/index',
},
},
// USE ONLY IF LIBRARY TARGET IS MODULE
// experiments: {
// outputModule: true,
// }
// Rest of the configuration...
};
By default, it would create a dist
folder with directory structure like this:
<ROOT>
|-- dist
| |-- cli
| | |-- index.js
| |-- library
| | |-- index.js
The, I can make use of package.json files bin
and main
field to respective compiles files:
{
"name": "my-lib",
"version": "1.0.0",
"main": "dist/library/index.js",
"types": "dist/library/index.d.js",
"bin": "dist/cli/index.js"
}
In above webpack configuration, note the use of externals
which matches all the imports I have used. The default behavior of webpack is to always bundle and thus without this externals
configuration, you would end up with library getting bundled twice for two entry points - one for cli and another as a library. (This is where Rollup helps greatly.) Further, you will have to do this for literally every third-party module that you imports or use webpack-node-externals
Few things to note:
my-library
and my-libary-cli
. The my-libary-cli
package can specify my-library
as a peerDependency
. bin
and main
field. The folder structure doesn't matter.tsc
. Webpack won't do that for you. exports
field instead of main
field. So, I would prefer having a barrel pattern instead of allowing multiple sub imports. May things get wrong. TypeScript, Jest setup, etc. on consumer side.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.