developit / microbundle

📦 Zero-configuration bundler for tiny modules.
https://npm.im/microbundle
MIT License
8.06k stars 361 forks source link

package.json "type": "module" / [ERR_REQUIRE_ESM]: Must use import to load ES Module #801

Closed aheissenberger closed 2 years ago

aheissenberger commented 3 years ago

Problem: microbundle does not correctly bundling packages where the package type is "type": "module" in package.json

when importing const appfaker = require('@ekzemplo/common') the bundled package into a project which is using "type": "commonjs" this error id thrown:

node:internal/modules/cjs/loader:1125
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
      ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /TST/ekzemplo/packages/ekzemplo-common/dist/index.js
require() of ES modules is not supported.
require() of /TST/ekzemplo/packages/ekzemplo-common/dist/index.js from /TST/ekzemplo/packages/ekzemplo-apievents/src/server.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /TST/ekzemplo/packages/ekzemplo-common/package.json.
...

this is the package.json of the imported package created with microbundle

{
  "name": "@ekzemplo/common",
  "version": "0.0.0",
  "source": "./src/index.js",
  "main": "./dist/index.js",
  "module": "./dist/index.module.mjs",
  "unpkg": "./dist/index.umd.js",
  "exports": {
    "import": "./dist/index.module.mjs",
    "require": "./dist/index.js"
  },
  "type": "module",
  "type":"commonjs",
  "license": "MIT",
  "scripts": {
    "start": "microbundle watch",
    "build": "microbundle build"
  },
  "dependencies": {
    "faker": "^5.3.1"
  }
}

manual fix I was able to fix the problem by renaming the file ./dist/index.js to ./dist/index.cjs and adapting main and require in the package.json after building:

{
  "name": "@ekzemplo/common",
  "version": "0.0.0",
  "source": "./src/index.js",
  "main": "./dist/index.cjs",
  "module": "./dist/index.module.mjs",
  "unpkg": "./dist/index.umd.js",
  "exports": {
    "import": "./dist/index.module.mjs",
    "require": "./dist/index.cjs"
  },
  "type": "module",
  "type":"commonjs",
  "license": "MIT",
  "scripts": {
    "start": "microbundle watch",
    "build": "microbundle build"
  },
  "dependencies": {
    "faker": "^5.3.1"
  }
}

but this will break the next build!! microbundle build by creating files which end with .cjs.js - e.g. ./dist/index.cjs.js

Question I am not an expert on the npm package format but would it be ok that in the case that the module type is "module" the ending of the generated files of main becomes .cjs or at least the names used in package.json are respected and not .js ist added if the name ends with .cjs

marvinhagemeister commented 3 years ago

I wonder if updating this regex is all that's needed to make that work.

aheissenberger commented 3 years ago

yes, cjsshoud be added to both places "(mjs|cjs|[tj]sx?)"

  if (options.multipleEntries) {
    let name = entry.match(/([\\/])index(\.(umd|cjs|es|m))?\.(mjs|cjs|[tj]sx?)$/) ? mainNoExtension : entry;
    mainNoExtension = path.resolve(path.dirname(mainNoExtension), path.basename(name));
  }

  mainNoExtension = mainNoExtension.replace(/(\.(umd|es|cjs|m))?\.(mjs|cjs|[tj]sx?)$/, '');

This only solves the problem of creating a bad base name - e.g. ./dist/index.cjs.module.js but still creates a index.js and ignores the extension in packages.json

By looking at the code I found that I can to set "cjs:main":"./dist/index.cjs", to override the wrong filename created by microbundle

I did add a fixture but running the tests gives me a couple of broken tests and a lot of snapshot updates. Is there something I missed in project setup?

I would suggest the following fixes: 1) extend the documentation to make it very clear that the tool expects code which uses es6 modules 2) the ending ".cjs" should be striped when creating the base name 2) for supporting "type":"module" which changes the interpretation of node regarding the ending ".js" the tool should use '.cjs' as the default extension for "mainsByFormat.cjs"

make-github-pseudonymous-again commented 3 years ago
  "type": "module",
  "type":"commonjs",

@aheissenberger Any reason why your sample package.json contains the type key twice?

aheissenberger commented 3 years ago
```json
  "type": "module",
  "type":"commonjs",

@aheissenberger Any reason why your sample package.json contains the type key twice?

no - this is a mistake - only "type": "module", is ok

rschristian commented 2 years ago

802 was merged so this should be good to close now