16

I have a module I want to publish to npm. I have found some "solutions" that are 4+ years old, examples using babel 5.x, and other problems that made the examples not work as shown.

Ideally I want to write my code using es6 and build/transpile with babel such that it can be imported with require() in scripts or import in modules.

Right now, here's my (sample) module that shows what I've tried.

// index.js
export default function speak () {
  console.log("Hello, World!");
}
// .babelrc
{
  "comments":false,
  "presets": [
      ["@babel/env", {"modules": "commonjs"}]
  ],
  "plugins": [
    "@babel/plugin-transform-modules-commonjs",
    "add-module-exports"
  ]
}
// package.json
{
  "name": "foo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "babel index.js -d dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.10.5",
    "@babel/core": "^7.10.5",
    "@babel/plugin-transform-modules-commonjs": "^7.10.4",
    "@babel/preset-env": "^7.10.4",
    "babel-plugin-add-module-exports": "^1.0.2"
  }
}

And, finally, a demo script (demo.js)

// demo.js
const speak = require('./dist/index.js');
speak();

When I do npm run build and then node demo.js, it works as expected:

Hello, World!

I would also like to be able to have a module (add "type":"module" to package.json) and then use a demo.js file this way:

import speak from './dist/index.js';
speak();

However, I get this an error that a default export isn't available:

import speak from './dist/index.js';
       ^^^^^
SyntaxError: The requested module './dist/index.js' does not provide an export named 'default'

I don't really care what the answer is, I'd just like to know what the best practices are. Should I just export as ES6? Should I just require commonjs? Is there a way of transpiling with two available targets?

Note:

  • node v14.5.0
  • npm v6.14.6
  • @babel/core v7.10.5
Sir Robert
  • 4,686
  • 7
  • 41
  • 57

1 Answers1

2

You can use a bundler like webpack or rollup in combination with babel. They provide options to compile to multiple targets. Normally any library code is compiled to below targets:

  1. ESM or MJS (Ecmascript modules)
  2. UMD (Universal Modules)

You can also compile to CJS (CommonJS module) or IIFE (Immediately invoked function expression).

UMD and ESM are pretty much standard these days (esp. UMD because it is combination of IIFE, CJS and AMD).

You can explore official documentation for Webpack or Rollup. However, I have created a tool which you can use to achieve the output you are looking for. https://www.npmjs.com/package/rollup-boilerplate

You can check it out, play around with it, hack it. I think it can be good starting point. You can checkout this article to get started: https://medium.com/@contactsachinsingh/setting-up-a-rollup-project-under-two-minutes-fc838be02d0e

Sachin Singh
  • 898
  • 1
  • 8
  • 17
  • Thanks-- this is helpful. I was under the impression that rollup and webpack were both for targeting browsers. Is that not the case? – Sir Robert Jul 27 '20 at 19:02
  • 1
    No! That's not the case. Rollup and Webpack's purpose is to just bundle your code which can be consumed in some fashion. It doesn't matter if you are working on client side project or server side. – Sachin Singh Jul 27 '20 at 19:08
  • 1
    Ok, this is a HUGE help (especially your rollup-boilerplate module, thank you!). I'll play with rollup and try to figure it out. When I `require` the module it works fine, but when I import it, I have to import from the ./dist/umd/index.js' path within the module. Any ideas about how to deal with that? – Sir Robert Jul 27 '20 at 21:05
  • You are using ``@babel/plugin-transform-modules-commonjs`` which transform your code to CommonJS which works with ``require``. Two things you can do: Remove this plugin and use ``--experimental-modules`` flag. E.g. ``node index.js --experimental-modules``, or use a bundler. – Sachin Singh Jul 28 '20 at 06:54
  • Ideally you should publish your code in UMD and ESM formats, specify ``main`` and ``module`` paths in ``package.json``, and you're good. CommonJS module system will pick the UMD version (``main`` path). For web you can use ESM (``module`` path). You can also use UMD version for web but that doesn't work with `` – Sachin Singh Jul 28 '20 at 06:54