microbundle icon indicating copy to clipboard operation
microbundle copied to clipboard

Support Node conditional exports

Open Ayc0 opened this issue 4 years ago • 9 comments

In node 13, there is a new option available: exports.

As this will be the new standard for defining packages available for node/browsers/etc, it could be nice to use it instead/addition of the module, unpkg etc. fields in the current package.json. Webpack also plans to support it in the v5: https://github.com/webpack/webpack/issues/9509

Current API:

{
  "source": "src/foo.js",           // Your source file (same as 1st arg to microbundle)
  "main": "dist/foo.js",            // output path for CommonJS/Node
  "module": "dist/foo.module.js",   // output path for JS Modules
  "unpkg": "dist/foo.umd.js",       // optional, for unpkg.com
  "scripts": {
    "build": "microbundle",         // uses "source" and "main" as input and output paths by default
    "dev": "microbundle watch"
  }
}

New API:

{
  "source": "src/foo.js",           // Your source file (same as 1st arg to microbundle)
  "main": "dist/foo.js",            // output path for CommonJS/Node
  "exports": { 
    "import": "dist/foo.module.js", // output path for JS Modules
    "browser": "dist/foo.umd.js",   // optional, for unpkg.com
  },
  "scripts": {
    "build": "microbundle",         // uses "source" and "main" as input and output paths by default
    "dev": "microbundle watch"
  }
}

Ayc0 avatar Apr 23 '20 14:04 Ayc0

Hiya @Ayc0 - thanks for opening an issue for this, it's been on my mind a lot. I have an implementation that should be able to be airlifted into Microbundle, hopefully I'll get some time this week or next.

developit avatar Apr 23 '20 14:04 developit

any update on this? I think solving this will also be an alternative solution for https://github.com/developit/microbundle/issues/50, since exports is a way to specify multiple entries with multiple exported formats.

gingur avatar Mar 11 '21 19:03 gingur

I think this is now supported: image

Ayc0 avatar Mar 12 '21 14:03 Ayc0

The example provided is subpath exports, I think the desired functionality would be conditional exports to handle the same conditions main, module, ect handle but on a per entry level. To be fair, I haven't tested this format to see if microbundle properly handles it.

gingur avatar Mar 12 '21 15:03 gingur

I do not think microbundle currently (v0.15.0) properly handles this. Here is a replication case, using arcgis-rest-fetch, which specifically uses conditional exports (see the note in the README). Here is demo case:

Replication Case

  1. Set up a project:
mkdir demo-case
cd demo-case
  1. Create package.json:
{
  "name": "foo",
  "type": "module",
  "source": "src/foo.js",
  "exports": {
    "require": "./dist/foo.cjs",
    "default": "./dist/foo.modern.js"
  },
  "main": "./dist/foo.cjs",
  "module": "./dist/foo.module.js",
  "unpkg": "./dist/foo.umd.js",
  "scripts": {
    "build": "microbundle  --compress=false",
    "dev": "microbundle watch"
  }
}

(per Installation)

  1. Install microbundle and arcgis-rest-fetch:
npm i -D microbundle
npm i -D @esri/arcgis-rest-fetch
  1. Create file src/foo.js:
import { getFetch } from "@esri/arcgis-rest-fetch";

export const gf = () => {
	return getFetch();
}
  1. Run: npm run build

    • Expected: contents of dist/foo.cjs should contain the getFetch() from the https://github.com/Esri/arcgis-rest-js/blob/main/packages/arcgis-rest-fetch/node-ponyfill.js since it's defined in exports.require.
    • Actual: contents of dist/foo.cjs contains the getFetch() from https://github.com/Esri/arcgis-rest-js/blob/main/packages/arcgis-rest-fetch/browser-ponyfill.js

More Info

Some links to other implementations: https://github.com/parcel-bundler/parcel/issues/4155#issuecomment-756457121

gavinr avatar May 01 '22 02:05 gavinr

@gavinr I think there's a bit of confusion here between Microbundle consuming "exports" and outputting bundles that line up with user-defined "exports". I believe this issue is for the latter.

Regardless, unless you're inlining the dependency (which you haven't indicated), Microbundle shouldn't alter the behavior there at all.

If you are inlining the dependency, then Microbundle chooses the bundle that fits your current environment (your build process): as you're authoring in ESM (using import) you inline the "module" entry from that package's "exports", which is correct behavior.

If your users need to consume a package differently based on their current environment (Node vs Browser, for example), then that package should be a dependency, not inlined. That way your users will still consume that package as it intended.

rschristian avatar May 01 '22 03:05 rschristian

Thank you for the quick reply. I think you’re saying that my replication case is off topic from the original issue here? If so, my apologies and I can move it to a separate issue.

Message ID: @.***>

gavinr avatar May 01 '22 03:05 gavinr

Yes, it's different to the original topic (we weren't instructing users to use "exports" in their packages at the time of this issue), though hopefully I've answered your issue in the above comment.

Sorry, circled back and did some late edits.

rschristian avatar May 01 '22 03:05 rschristian

If you need to consume a package differently based on the current environment of your own build output, that package should be a dependency, not inlined

That seems to be the correct solution. I'll mark my responses as off-topic so others won't be confused in the future. Thanks.

gavinr avatar May 01 '22 04:05 gavinr