Home > Net >  How to print a friendly error when node runtime does not support ecmascript modules?
How to print a friendly error when node runtime does not support ecmascript modules?

Time:10-07

I've written a node.js command line application that uses ECMAScript modules, top-level await, and nullish coalescing operators so it requires at least node 14.

Older node.js versions can't even parse the script and throw a SyntaxException before evaluating anything. I want to print a friendly error telling users to upgrade to a supported version but I can't get around syntax errors.

#!/usr/bin/env node
# entry.cjs
if (process.version.split('.', 1)[0].slice(1) < 14) {
  console.log('too old')
  process.exit(1)
}
import './index.js'
$ node8 entry.cjs
import './index.js'
^^^^^^

SyntaxError: Unexpected token import

If I switch to require('./index.js') then it fails with modern runtimes because you cannot require() an ES module.

$ node16 entry.cjs
entry.cjs:6
require('./index.js')
^

Error [ERR_REQUIRE_ESM]: require() of ES Module index.js from entry.cjs not supported.
Instead change the require of index.js in entry.cjs to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (entry.cjs:6:1) {
  code: 'ERR_REQUIRE_ESM'
}

I thought an dynamic import expression would work because it would only be evaluated after parsing and after the version check, but that is a reserved keyword even in node 8

$ node8 entry.cjs
node entry.cjs
entry.cjs:6
import('./index.js')
^^^^^^

SyntaxError: Unexpected token import

CodePudding user response:

I believe this can be solved by creating a second .cjs file.

  1. file1.cjs does the version check, and if it didn't fail require file2.cjs
  2. file2.cjs does the dynamic import.

CodePudding user response:

With NPM

You could define a package.json file and have your start command check the node version first, and then run your script

Example from another SO Q&A:

// package.json
{
  //...
  "scripts": {
    "start": "node check-version.js && node index.js"
  },
  //...
}

// check-version.js
const MIN_VERSION = 14;
const nodeVersion = process.version.replace(/^v/, '');
const [ nodeMajorVersion ] = nodeVersion.split('.');

if (nodeMajorVersion < MIN_VERSION) {
  console.warn(`node version ${nodeVersion} is incompatible with this module.`, `Expected version >= ${MIN_VERSION}`);
  process.exit(1);
}

Then npm start would gracefully fail if the version is lower than required and run as expected if not. (You could define another name for the script, i.e.: npm run entry)


Without NPM

If you do not wish to use npm/yarn, you could achieve the same result via .sh:

  1. Create entry.sh
#!/bin/sh
node check-version.js && node index.js
  1. chmod 755 entry.sh
  2. ./entry.sh
  • Related