Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add exportsFields and conditionNames options #10953

Merged
merged 36 commits into from
Jun 17, 2020
Merged

Conversation

vankop
Copy link
Member

@vankop vankop commented May 25, 2020

What kind of change does this PR introduce?
closes #9509
exports field feature support

Did you add tests for your changes?

Yes

Does this PR introduce a breaking change?

No

What needs to be documented once your changes are merged?

Webpack now supports exports field entrypoint.

Regarding to conditional exports webpack core supports the following conditions:

  • "import" - matched when the package is loaded via import or import()
  • "require" - matched when the package is loaded via require()
  • "node" - matched when options.target is node or electron environment
  • "electron" - matched when options.target is electron environment
  • "browser" - matched when options.target is web environment
  • "worker" - matched when options.target === "webworker"
  • "development" - matched when options.mode === "development"
  • "webpack"
  • "default"

Added some parameters to options.resolve option:

  • options.resolve.conditionNames a list of exports field condition names, default list described above in "conditional exports" section
  • options.resolve.exportsFields a list of exports fields in description files, default is ["exports"]
  • options.resolve.byDependency extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".
    This option allows to define specific resolving to each dependency category, e.g. disallow extensions for esm:
module.exports = {
   resolve: {
        byDependency: {
              "esm": {
                     "extensions": []
              }
        }
   }
};

@webpack-bot
Copy link
Contributor

webpack-bot commented May 25, 2020

For maintainers only:

  • This needs to be documented (issue in webpack/webpack.js.org will be filed when merged)
  • This needs to be backported to webpack 4 (issue will be created when merged)

@vankop vankop marked this pull request as ready for review May 25, 2020 21:41
lib/config/defaults.js Outdated Show resolved Hide resolved
lib/NormalModuleFactory.js Outdated Show resolved Hide resolved
lib/Compilation.js Outdated Show resolved Hide resolved
lib/Dependency.js Outdated Show resolved Hide resolved
lib/dependencies/AMDRequireArrayDependency.js Outdated Show resolved Hide resolved
lib/NormalModuleFactory.js Outdated Show resolved Hide resolved
lib/NormalModuleFactory.js Outdated Show resolved Hide resolved
lib/config/defaults.js Outdated Show resolved Hide resolved
schemas/WebpackOptions.json Outdated Show resolved Hide resolved
schemas/WebpackOptions.json Outdated Show resolved Hide resolved
@webpack-bot
Copy link
Contributor

@vankop Thanks for your update.

I labeled the Pull Request so reviewers will review it again.

@sokra Please review the new changes.

- byDependency option
- create resolver per dependency category
@webpack-bot
Copy link
Contributor

Thank you for your pull request! The most important CI builds succeeded, we’ll review the pull request soon.

@sokra sokra merged commit ac0db5e into webpack:master Jun 17, 2020
@sokra
Copy link
Member

sokra commented Jun 17, 2020

Thanks

I hope it's fine...

@webpack-bot
Copy link
Contributor

I've created an issue to document this in webpack/webpack.js.org.

@vankop vankop deleted the exports-field branch June 18, 2020 06:09
@mikeal
Copy link

mikeal commented Jun 23, 2020

So, I’ve got a module that uses the export map in order to support require() (I’m using Node.js native ESM so I actually have to compile out a CJS version before publish).

Since compilers do not yet support export maps I’m still using the top level browser field for browser overlays, which means I’ve got an export map with no browser entries and a top level “browser” field.

I’m wondering if this is going to break my current setup or not. Does the top level browser field still win when you have an export map or does the export map take over?

To be clear, I’m fine with changing my configuration in order to upgrade, I’m using this early enough that I don’t think breaking me here is a reason to hold up this PR, I’m just trying to figure out what the change might need to be and what the rules are for which fields win.

@ljharb
Copy link

ljharb commented Jun 23, 2020

@mikeal i would expect that for max compat, you'd want to include both a toplevel "browser" field as well as a "browser" condition for each relevant entry point, and that they'd agree such that it didn't matter which one "won".

I would expect regardless, that the top level "browser" field would be a final transformation applied to the final "exports" resolution, and that they should thus be transparently compatible.

@alexander-akait
Copy link
Member

alexander-akait commented Jun 24, 2020

@mikeal

If both "exports" and "main" are defined, the "exports" field takes precedence over "main".

From Node.js docs.

So for browser we have a same logic. import wins over browser (for esm). Just note - browser can be in UMD/AMD/other format. But when developers use import some from 'module', we want to load ES modules -Node.js logic and tree shaking.

Many developers start to use exports field at a very early time.

@ctavan
Copy link

ctavan commented Jun 24, 2020

@mikeal I had a similar issue and this is what I ended up with:

  "main": "./dist/index.js",
  "exports": {
    ".": {
      "node": {
        "module": "./dist/esm-node/index.js",
        "require": "./dist/index.js",
        "import": "./wrapper.mjs"
      },
      "default": "./dist/esm-browser/index.js"
    },
    "./package.json": "./package.json"
  },
  "module": "./dist/esm-node/index.js",
  "browser": {
    "./dist/esm-node/index.js": "./dist/esm-browser/index.js"
  },

The different builds:

  • dist/index.js is a CommonJS build for Node.js
  • ./wrapper.mjs is an ESM wrapper around the CommonJS build for Node.js to avoid the dual package hazard
  • dist/esm-node/index.js is a pure ESM build for Node.js
  • dist/esm-browser/index.js is a pure ESM build for Browsers

How environments behave:

  • Recent Node.js will use exports.node.require if the module is require()d and exports.node.import if it is imported. Older Node.js will just use main.
  • webpack@4 will use module (for node target) + browser (for browser targets)
  • webpack@5 will use exports.node.module (for node target) and exports.default for all other cases (e.g. browser).
  • rollup behaves like webpack@4
  • jspm will use pkg.exports.default which works really well for modern browsers

This was the result of a longer discussion in uuidjs/uuid#462 if you are interested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Respect "exports" field in "package.json"
8 participants