Use ESM with node & shadow-cljs

Hello there!

I have to admit that I fail at requiring a ESM module in our node js app - The goal is to render parts of our apps on the server side. We have three builds:

  1. dev using :target :node-script - This is the only target I tested so far, but the solution should work for all three
  2. app using :target :node-library - Will be deployed to a lambda function
  3. test using :target :node-test

And the usage looks a bit like this:

(ns my-app
    [remark-breaks :default remark-breaks]

(defn handler []
  [:> react-markdown {:remark-plugins [remark-breaks
                      :components [...]
                      :children [...]}]

And I’m getting this error

SHADOW import error /.../proj/target/cljs-runtime/shadow.js.shim.module$remark_breaks.js

  /* ignore this, look at stacktrace */, require, module, __filename, __dirname);
Error [ERR_REQUIRE_ESM]: require() of ES Module /.../proj/node_modules/remark-breaks/index.js from /.../proj/target/dev.js not supported.
Instead change the require of index.js in /.../projr/target/dev.js to a dynamic import() which is available in all CommonJS modules.

I’m confused whether I need to change the targets or somehow adjust the require statement.

Any help would be welcome.


Try :target :esm. More info here.

For examples of :target :esm, check out:

There are also reference docs these days.

The limitation of not being able to require ESM from commonjs (ie. :node-script) is coming from node and shadow-cljs can’t do anything to change that. If a package you want to use is only provided as ESM code then your code must become ESM as well more or less. You can use dynamic import as well, but IMHO thats harder than just using ESM.

Hello again!

Thank you for your suggestions, they’re probably the better & cleaner solution than the one I chose, but changing the project to esm was opening many other issues I honestly didn’t understood.

I used fix-esm (promotes itself has hacky and introduced quite a few dependencies into tho project), but then I could just do:

(ns ;...
  (require ["fix-esm" :as fix-esm]))

(def remark-breaks (-> fix-esm (.require "remark-breaks") .-default))

This I could just use as the (require [remark-breaks :default remark-breaks])