josdejong / josdejong/mathjs

Can't use mathjs without package bundler

Open
#1,928 14 comments 1 reaction 0 assignees View on GitHub
feature help wanted
Dominant language
JavaScript
Stars
15.1k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Description

### The problem

I'm trying to use mathjs inside node, using ES modules, but without a package bundler (e.g. Webpack, Rollup).

When I do:

```javascript
import { evaluate } from 'mathjs';

console.log(evaluate('1+2'));
```

It throws:

```
import { evaluate } from 'mathjs';
^^^^^^^^
SyntaxError: The requested module 'mathjs' is expected to be of type CommonJS, which does not support named exports. CommonJS modules can be imported by importing the default export.
For example:
import pkg from 'mathjs';
const { evaluate } = pkg;
```

From what I can see, the `module` entry in `package.json` is only used for package bundlers. Without bundlers, node will look at the `main` entry, so it tries to load the CommonJS / UMD script.

Node does support mixing ES and CommonJS modules, but expects a single, default export.
Changing the import to this works, because it correctly loads the CommonJS module:

```javascript
import mathjs from 'mathjs';

console.log(mathjs.evaluate('1+2'));
```

So that's a kind of workaround, but it does mean that it's not using ES modules in an ES module project.

But the bigger issue is that it's now incompatible if I choose to use to use a bundler because the bundler will use the ES module version, which has no default import.
In particular, this causes my Jest tests to fail. In my test I have something like:

> TypeError: Cannot read property 'evaluate' of undefined

I think that [conditional exports](https://nodejs.org/api/esm.html#esm_conditional_exports) may be a solution. It seems to allow you to tell node which files to use for CommonJS `require` and which to use for ES `import`.

### Potential solution

I've not had a proper play with it, this seemed to work for me:

Change the `package.json` to this, to tell node to use the `esm` files for `import` statements, and `es5` files for `require`:
```json
{
"main": "./main/es5",
"module": "./main/esm",
"exports": {
"import": "./main/esm/index.js",
"require": "./main/es5/index.js"
},
// this is required, otherwise the `exports` doesn't work
"type": "module"
}
```

But because the `type` is now `module`, node will now complain when trying to use CommonJS.
To sort that, you can either rename all of the CommonJS files to have the `.cjs` extension, or to add a `package.json` in the root directories containing the CommonJS files (e.g. `main/es5/`, `lib/`), with just the following:

```json
{
"type": "commonjs"
}
```

I'm not sure if doing this has any adverse affects on other parts of the library though, but I'm happy to get a PR in for those changes.

I thought it seemed similar to #1766. Not quite the same issue, but possibly the same resolution, perhaps used with [Subpath Exports](https://nodejs.org/api/esm.html#esm_subpath_exports).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.