node.jsibm-cloudcommonjsibm-cloud-functionsesmodules

Can I use ES modules in IBM Cloud Functions (node.js) or is only Common JS supported?


When I create a Node.js v16 function action from a zip file containing a simple estest.js:

function main(params) {
    return { message: 'Hello World' };
}

and package.json containing "type": "module" I get:

{
  "error": "Initialization has failed due to: Error [ERR_REQUIRE_ESM]: require() of ES Module /nodejsAction/1AfjbP59/estest.js from /nodejsAction/runner.js not supported.estest.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains \"type\": \"module\" which declares all .js files in that package scope as ES modules.\nInstead rename estest.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change \"type\": \"module\" to \"type\": \"commonjs\" in /nodejsAction/1AfjbP59/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).\n\n    at eval (eval at <anonymous> (/nodejsAction/runner.js:51:31), <anonymous>:1:1)\n    at /nodejsAction/runner.js:51:31"
}

I have not found any IBM Cloud documentation which states that only the legacy Common JS style is supported.

EDIT:

Action created using:

ibmcloud fn action create test/estest estest.zip --kind nodejs:16

Solution

  • After some more research I've concluded the action itself can't be an ES Module.

    I tried renaming it to estest.mjs (without "type":"module" in package.json) which gave:

    {"error": "Initialization has failed due to: Error [ERR_REQUIRE_ESM]: require() of ES Module /nodejsAction/azrfHRlh/estest.mjs not supported.\nInstead change the require of /nodejsAction/azrfHRlh/estest.mjs to a dynamic import() which is available in all CommonJS modules.\n    at eval (eval at <anonymous> (/nodejsAction/runner.js:51:31), <anonymous>:1:1)\n    at /nodejsAction/runner.js:51:31"}
    

    which I believe is due to this require() in runner.js (the IBM Cloud / OpenWhisk implementation which tries to load my estest.mjs ES Module):

    //  The module to require.
    let whatToRequire = index !== undefined ? path.join(moduleDir, index) : moduleDir;
    let handler = eval('require("' + whatToRequire + '").' + main);
    

    require() does not support ES modules: https://nodejs.org/api/esm.html#esm_require.

    Solution

    As the error message suggests, the solution was to create a CommonJS wrapper (estest.cjs) which does a dynamic import() of my ES module estest.mjs and use "main": "estest.cjs" in package.json:

    estest.cjs
    async function main(params) {
        const { mjstest } = await import('./estest.mjs');
        return mjstest(params);
    }
    
    exports.main = main;
    
    estest.mjs
    export const mjstest = (params) => {
        return { message: `Hello ${params.name || 'World'}` };
    }
    
    package.json
    {
        "name": "estest",
        "version": "1.0.0",
        "main": "estest.cjs"
    }