Importing RRule from rrule package in TypeScript. Getting SyntaxError: The requested module 'rrule' is expected to be of type CommonJS

Viewed 1050

Update: Tried to create a minimal repro here: https://github.com/aioobe/node-issue-repro. It's very stripped down obviously, but it exhibits the same error message.

git clone git@github.com:aioobe/node-issue-repro.git
cd node-issue-repro
npm install
npx tsc && node build/index.js

In my TypeScript source code I have the following:

import { RRule } from 'rrule';

// ...

const oddDays = new RRule({
    freq: RRule.DAILY,
    bymonthday: [1, 3, 5]
});

When I run this, I get the following output:

(node:31434) ExperimentalWarning: The Node.js specifier resolution in ESM is experimental.
file:///home/aioobe/projects/test/server/database/storage.ts:10
import { RRule } from 'rrule';
         ^^^^^
SyntaxError: The requested module 'rrule' 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 'rrule';
const { RRule } = pkg;
    at ModuleJob._instantiate (internal/modules/esm/module_job.js:98:21)
    at ModuleJob.run (internal/modules/esm/module_job.js:137:5)
    at Loader.import (internal/modules/esm/loader.js:165:24)
    at Object.loadESM (internal/process/esm_loader.js:68:5)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! daily-challenge@0.1.0 start-server: `cross-env NODE_ENV=development node --loader ts-node/esm --es-module-specifier-resolution=node server/server.ts`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the daily-challenge@0.1.0 start-server script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/aioobe/.npm/_logs/2020-09-13T15_53_32_162Z-debug.log
npm run start-server exited with code 1
--> Sending SIGTERM to other processes..

In my package.json I have

{
    ...
    "dependencies": {
        ...
        "rrule": "^2.6.6",
        ...
    },
    ...
    "type": "module"
}

(I need "type": "module", otherwise I'm running into SyntaxError: Cannot use import statement outside a module.)

In my tsconfig.json I have

{
    "compilerOptions": {
        ...
        "allowJs": true,
        "esModuleInterop": true,
        "module": "esnext",
        "moduleResolution": "node",
        ...
    },
    ...
}

I've noted that in rrule.js the package.json does not have "type": "module", which according to this page means that it should be interpreted as CommonJS.

Honestly, I don't really know what this means for me. I've tried all kinds of syntax variations to include this package in my file (including the one suggested in the error message). I run into various errors, but the syntax posted above is the exact syntax used in the example code in the readme.

I use node version 14.9.0, npm version 6.14.8, typescript version 3.7.2 and ts-node 8.10.2. I'm happy to upgrade/downgrade, but I'm running into the same issue with node 12 unfortunately.

I use lots of other modules the above way without any problem.

3 Answers

The "rrule" module seems to declare an invalid index.d.ts.

It says export default RRule; but the default export is the entire module.

This is simple enough to work around:

import pkg from "rrule";
const { RRule } = pkg as any;

This behaves equivalently to:

import * as pkg from "rrule";
const { RRule } = pkg.default as any;

We need to use any here due to the fact TS thinks this is typeof RRule, even though if you print it you will see it's actually the whole module body. This is possibly an upstream bug, or a bug with the experimental ESM support.

Try replacing the import statement:

import { RRule } from 'rrule';

with:

import pkg from 'rrule';
const {RRule} = (pkg as any);

This worked on Node.js v14.11.0.


It is actually suggested in the error message that you've received:
(node:31434) ExperimentalWarning: The Node.js specifier resolution in ESM is experimental.
file:///home/aioobe/projects/test/server/database/storage.ts:10
import { RRule } from 'rrule';
         ^^^^^
SyntaxError: The requested module 'rrule' 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 'rrule';
const { RRule } = pkg;

Using commonjs as module code generation type fixes the issue. Following changes are required:

tsconfig.json

{
  "compilerOptions": {
    ...,
    "module": "commonjs",
    ...
}

package.json

{
  ...
  "type": "commonjs",
  ...
}

index.ts

import { RRule } from "rrule";
Related