Ensar Ishakoglu
Ensar Ishakoglu

Reputation: 163

Using ES Module packages in Azure Typescript Function

I am unable to use the package p-map in my Azure Function. I get the following error:

Worker failed to load function: 'serverless' with function id: '<id>'.
Result: Failure
Exception: Worker was unable to load function serverless: 'Error [ERR_REQUIRE_ESM]: require() of ES Module <es-module> from /usr/local/Cellar/azure-functions-core-tools@4/4.0.4483/workers/node/worker-bundle.js not supported.
Instead change the require of index.js in /usr/local/Cellar/azure-functions-core-tools@4/4.0.4483/workers/node/worker-bundle.js to a dynamic import() which is available in all CommonJS modules.'

The project is created by following the steps in Azure's documentation. The following is in the index.ts:

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import pMap from "p-map";
import got from "got";

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    const sites = [
        'https://avajs.dev',
        'https://github.com'
    ];

    const mapper = async site => {
        const {requestUrl} = await got.head(site);
        return requestUrl;
    };

    const result = await pMap(sites, mapper, {concurrency: 2});
    

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: result
    };

};

export default httpTrigger;

My tsconfig.json looks like following:

{
  "compilerOptions": {
    "module": "es2020",
    "target": "es2020",
    "outDir": "dist",
    "rootDir": ".",
    "sourceMap": true,
    "strict": false,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true
  }
}

Lastly, this is my package.json:

{
  "name": "azure-functions-test",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "prestart": "npm run build",
    "start": "func start",
    "test": "echo \"No tests yet...\""
  },
  "dependencies": {
    "got": "^12.0.4",
    "p-map": "^5.3.0"
  },
  "devDependencies": {
    "@azure/functions": "^3.0.0",
    "typescript": "^4.0.0"
  }
}

p-map is strictly an ES Module and cannot be used in CommonJS projects.

Am I missing something or is it just not possible to use ES Module packages in Azure Functions? Thanks in advance.

GitHub repository of aforementioned code to test things out locally: azure-functions-test

Upvotes: 4

Views: 2612

Answers (3)

Jonas M&#230;rsk
Jonas M&#230;rsk

Reputation: 295

I used the tsup bundler with no compromises (no ugly .js or .mjs extensions in source code, just pure ESM). I am using the Azure Functions v4 programming model, but i am sure a few tweaks would make it work for the previous programming model aswell (the one using function.json-files).

Here is what i did:

  1. Remove "type": "module" from package.json

  2. Install tsup

    npm i tsup -D
    
  3. Add tsup.config.ts

    import { defineConfig } from 'tsup'
    
    export default defineConfig({
        // Entry file(s) to start building from.
        entry: ['src/**/*.ts'],
        // To output .mjs files
        format: 'esm',
        // Optional: Include external packages inside the bundle. This was nescessary for a monorepo package in my case.
        noExternal: ['@monorepo/package'],
        // Optional: Empty dist directory before build
        clean: true,
    })
    
  4. Modify package.json

    {
        "main": "./dist/functions/*.mjs",
        "scripts": {
            "build": "tsup",
            ...
        },
        ...
    }
    

Upvotes: 0

praneetloke
praneetloke

Reputation: 1971

According to the docs on Node.js' website, specifying "type": "module" in your package.json should also indicate if Node should use ESM.

Here's what I did to get ESM working for my functions in my Function App:

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "moduleResolution": "Node",
    "outDir": "dist",
    "rootDir": ".",
    "sourceMap": true,
    "strict": false,
    "noImplicitAny": true,
    "noUnusedLocals": true
  }
}

Some relevant function app settings:

WEBSITE_NODE_DEFAULT_VERSION: ~16 (To run using Node.js 16 runtime) FUNCTIONS_EXTENSION_VERSION: ~4

Then make sure to import your ES-style exports with a .js extension if your original extension is .ts. So if you have a file called custom.ts that has exports, then you should import them as import { something } from "custom.js";.

And for any npm packages that use default exports (CommonJS style), you should import the package using its default export, i.e. import * as pkg from "pkg";.

Relevant links:

https://github.com/Azure/azure-functions-nodejs-worker/issues/104

https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=v2-v3-v4-export%2Cv2-v3-v4-done%2Cv2%2Cv2-log-custom-telemetry%2Cv2-accessing-request-and-response%2Cwindows-setting-the-node-version#ecmascript-modules (Note that this doc talks about importing your ESM exports with a .mjs extension but I didn't have to do that.)

Upvotes: 2

Ensar Ishakoglu
Ensar Ishakoglu

Reputation: 163

I resolved my issue by renaming my index.ts file to index.mts. This built a index.mjs file (after running npm run build) in my dist folder which fixed the issue.

One thing to note is that you also have to edit your function.json's scriptFile key so it uses your .mjs file instead of non-existing .js file.

Upvotes: 0

Related Questions