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

3

6

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 [email protected]: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! [email protected] 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 [email protected] 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.

Evaporite answered 13/9, 2020 at 16:7 Comment(6)
Could you please provide minimal environment with issue reproduced, I tried on simple typescript demo environment but couldn't reproduce it on stackblitz: stackblitz.com/edit/typescript-pwn3loVasya
Minimal repro uploaded to gitub repo. git clone [email protected]:aioobe/node-issue-repro.git, cd node-issue-repro, npm install, npx tsc && node build/index.js.Evaporite
updating import statement to import RRule from 'rrule'; or import { default as RRule } from 'rrule'; seems to be working.Vasya
Seems to be a known issue on node: github.com/nodejs/node/issues/32137. Let me know if the fix works for you and I can add it as an answer.Vasya
@DipenShah, error goes away, but it doesn't seem to work. I get undefined for RRule.DAILY for example, and UnhandledPromiseRejectionWarning: TypeError: RRule is not a constructorEvaporite
It's strange, I can see value for RRule but RRule.DAILY is undefined.Vasya
D
7

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.

Deedee answered 21/9, 2020 at 22:35 Comment(1)
I'm really glad you put the "This behaves equivalently to:" piece of code. In my project running locally the regular import works, but when I build a Docker container, it only works when using import * as etc. So thank you!Dotation
V
1

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";
Vasya answered 15/9, 2020 at 23:13 Comment(6)
When I make this change, I run into (node:5064) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. /home/aioobe/projects/dc/server/server.ts:1 import express from 'express'; I will try to update the minimal repro to reflect this too.Evaporite
If I google this error, all I come up with is answers like these: https://mcmap.net/q/67988/-how-can-i-use-an-es6-import-in-node-js-duplicate that basically say "you must user "type": "module".Evaporite
Also, "module": "commonjs" immediately gets reverted to "module": "esnext" when I launch my application. In the logs it says The following changes are being made to your tsconfig.json file: - compilerOptions.module must be esnext (for import() and import/export)Evaporite
Seems like you project has some config which is forcing esnext module generator. May be you are using nextjs github.com/vercel/next.js/issues/7361Vasya
@Evaporite any luck so far?Vasya
I don't use nextjs to the extent of my knowledge. I still have not managed to strip down my main project to reproduce the issue described in my last comment. :-/ Still trying though.Evaporite
S
0

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;
Spaceless answered 15/9, 2020 at 22:3 Comment(1)
I've tried that. (Actually mentioned that in my post.) I can try again and let you know what error I got...Evaporite

© 2022 - 2024 — McMap. All rights reserved.