Dynamic import() ES module error in Node.js prebuild script

4 replies
Last updated: Nov 8, 2021
Dynamic imports are supposed to work in Node right?
prebuild.js:

(async function () {
  const createClient = require("next-sanity");
  const { config } = await import("../config.js");
})();
throws an error:

Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.

...

export const config = {
^^^^^^

SyntaxError: Unexpected token 'export'
The Node docs clearly state that "Dynamic import() is supported in both CommonJS and ES modules. In CommonJS modules it can be used to load ES modules." -
https://nodejs.org/api/esm.html#import-expressions
AI Update

This is actually a Node.js module system issue, not specifically a Sanity problem, but I can help clarify what's happening here.

You're correct that Node.js supports dynamic import() in CommonJS files - but there's a critical detail: the file you're importing (../config.js) must be recognized as an ES module by Node.js.

The error message is telling you that config.js contains ES module syntax (export), but Node.js is trying to parse it as CommonJS because:

  1. Your package.json doesn't have "type": "module", AND
  2. The file doesn't have a .mjs extension

Solutions:

Option 1: Make config.js recognizable as ESM

  • Rename config.js to config.mjs, OR
  • Add "type": "module" to your package.json

Option 2: Keep everything CommonJS If prebuild.js needs to stay as CommonJS (.js without "type": "module"), convert config.js to use CommonJS exports:

// config.js
const config = {
  // your config
};

module.exports = { config };

Then in prebuild.js:

// prebuild.js
(async function () {
  const { createClient } = require("next-sanity");
  const { config } = require("../config.js");
})();

Option 3: Make prebuild.js an ES module Rename to prebuild.mjs and use ESM throughout:

// prebuild.mjs
import { createClient } from "next-sanity";
import { config } from "../config.js";

The Node.js docs are correct - dynamic import() works in CommonJS, but the target file still needs to be identifiable as an ES module through file extension or package.json configuration. Node.js can't determine module type purely from file contents when using .js extension without package.json hints.

Show original thread
4 replies
I think because dynamic import(…) can be used for both CJS & ES module, Node needs a hint on which module type to use. That’s why it suggested changing the file extention from js -> mjs. Node treats all
.js
as CJS &
.mjs
as ES module.
If you add a
type: "module"
to package.json, it’ll do the reverse where all
js
files are treated as ES Module & you’d have to mark CJS files as
.cjs
Hey User, I had tried changing the prebuild.js script and all its dependencies to ES modules, both by adding a package.json with the
"type": "module"
line to the parent dir as well as by changing the extensions to
.mjs
. This caused a heap of webpack errors when Next tried to build or HMR the site, since it also uses these files, and I guess it didn't like that. For now I just copied and pasted the Sanity config object into the prebuild script. It works and now I'm able to fetch global site data, store it in a JSON file, and use it in the Layout component.
Ah things get complicated when bundlers are involved… I think a possible solution is to create a directory for shared files in esm format (but keep the
.js
ext), then add a empty package.json with
type: module
in it. Hopefully both Node & webpack can understand that.
I’m glad that typescript sorted these stuff out for me so I don’t have to deal with them, as long as all my files are
.ts
Yep, that’s what I did. Node was ok with it but webpack wasn’t. I should get more into typescript. 😀

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?