phau-heinan
phau-heinan

Reputation: 91

Cannot find module '@middy/core' when running jest with typescript and esm

hello I am running typescript with esm and jest and I have a problem with middy:

I had this test:

import { APIGatewayEvent, Context } from 'aws-lambda';
import { lambda } from '../index.js';

describe('get-by-id handler', () => {
  test('Should not find id', async () => {
    const x = jest.fn();
    x();
    expect(x).toHaveBeenCalled();
  });
});

which worked fine with the configurations I supplied below and npm run test command. howerver when I changed it to look like this:

import { APIGatewayEvent, Context } from 'aws-lambda';
import { lambda } from '../index.js';

describe('get-by-id handler', () => {
  test('Should not find id', async () => {
    const res = await lambda({
    pathParameters: { id: 'id' },
    } as unknown as APIGatewayEvent, {} as Context);
    console.log(res);

    const x = jest.fn();
    x();
    expect(x).toHaveBeenCalled();
  });
});

I get this error

jest -c jest.config.cjs

FAIL  app/src/handlers/get-by-id/tests/index.test.ts
● Test suite failed to run

    app/src/handlers/get-by-id/index.ts:4:19 - error TS2307: Cannot find module '@middy/core' or its corresponding type declarations.
    
    4 import middy from '@middy/core';
                        ~~~~~~~~~~~~~

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        11.409 s, estimated 13 s
Ran all test suites.

here is the config: tsconfig.json:

{
  "extends": "./node_modules/@tsconfig/node20/tsconfig.json",
  "compilerOptions": {
    "declaration": true,
    "outDir": "./dist/",
    "noUnusedParameters": false,
    "noPropertyAccessFromIndexSignature": false,
    "resolveJsonModule": true
  },
}

jest.config.cjs:

module.exports = {
    preset: 'ts-jest',
    testEnvironment: 'node',
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
    moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.js$': '$1',
    },
};

package.json:

{
  "type": "module",
  "main": "index.js",
  "scripts": {
    "build": "npm run clean && npm run copy-files && npm run tsc",
    "copy-files": "copyfiles -e \"!{*.md,package?(-lock).json}\" \"**/*.*\" dist",
    "clean": "rimraf ./dist",
    "tsc": "tsc -p tsconfig.build.json",
    "deploy": "cdktf deploy",
    "lint": "npx eslint . --ext .ts,.tsx",
    "test": "jest -c jest.config.cjs",
    "coverage": "jest -c jest-cov.config.cjs"
  },
  "license": "ISC",
  "devDependencies": {
    "@cdktf/provider-aws": "^18.0.6",
    "@tsconfig/node20": "^20.1.2",
    "@types/aws-lambda": "^8.10.129",
    "@types/jest": "^29.5.10",
    "@types/node": "^20.10.0",
    "@typescript-eslint/eslint-plugin": "^6.13.0",
    "@typescript-eslint/parser": "^6.13.0",
    "cdktf": "^0.19.1",
    "constructs": "^10.3.0",
    "copyfiles": "^2.4.1",
    "eslint": "^8.54.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-config-airbnb-typescript": "^17.1.0",
    "eslint-plugin-import": "^2.29.0",
    "eslint-plugin-require-extensions": "^0.1.3",
    "jest": "^29.7.0",
    "rimraf": "^5.0.5",
    "ts-jest": "^29.1.1",
    "ts-node": "^10.9.1",
    "typescript": "^5.3.2"
  },
  "dependencies": {
    "@aws-sdk/client-dynamodb": "^3.460.0",
    "@aws-sdk/client-secrets-manager": "^3.460.0",
    "@aws-sdk/lib-dynamodb": "^3.460.0",
    "@middy/core": "^5.0.3",
    "axios": "^1.6.2",
    "dotenv": "^16.3.1"
  }
}

handler:

import type { APIGatewayEvent } from 'aws-lambda';
import middy from '@middy/core';
const handler = async (event: APIGatewayEvent) => {
    return {
        statusCode: 200,
    };
};

// eslint-disable-next-line import/prefer-default-export
export const lambda = middy().handler(handler);

running on node 20.9.0

what I have tried so far step by step:

  1. changing test script in packge.json to this "NODE_OPTIONS=--experimental-vm-modules jest -c jest.config.cjs" as mentioned here result was the same

  2. changing jest.config.cjs to this:

module.exports = {
    preset: 'ts-jest/presets/default-esm',
    testEnvironment: 'node',
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
    moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.js$': '$1',
    },
    transform: {
        // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
        // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
        '^.+\\.tsx?$': [
            'ts-jest',
            {
                useESM: true,
            },
        ],
    },
};

as mentioned here result was the same

  1. changing jest.config.cjs to this:
const esModules = ["@middy"].join("|")
module.exports = {
    preset: 'ts-jest/presets/default-esm',
    testEnvironment: 'node',
    testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts?)$',
    moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.js$': '$1',
    },
    transform: {
        '^.+\.ts?$': [
            'ts-jest',
            {
                useESM: true,
            },
        ],
    },
    transformIgnorePatterns: [`node_modules/(?!${esModules})`],
};

as mentioned here result was the same

  1. changing tsconfig.json to this:
{
  "compilerOptions": {
    "incremental": true,
    "target": "es2020",
    "module": "es2020",
    "declaration": true,
    "sourceMap": true,
    "composite": true,
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "preserveConstEnums": true,
    "resolveJsonModule": true,
    "allowJs": true,
    "rootDir": ".",
    "outDir": "lib"
  },
  "include": ["src/**/*", "tests/**/*"],
  "exclude": ["node_modules"]
}

as mentioned here got this error:

ReferenceError: jest is not defined

       9 |         console.log(res);
      10 |
    > 11 |         const x = jest.fn();
         |                   ^
      12 |         x();
      13 |         expect(x).toHaveBeenCalled();
      14 |     });

      at Object.<anonymous> (app/src/handlers/get-by-id/tests/index.test.ts:11:19)

Upvotes: 9

Views: 1931

Answers (1)

Barnab&#225;s Molnar
Barnab&#225;s Molnar

Reputation: 96

If someone still has the same issue, I have found a solution. This is a common issue when working with ESM (ECMAScript Modules) in Jest. The error occurs because @middy/core is using ES modules (import syntax), but Jest is configured to handle CommonJS modules by default. in jest.config you should have something like this :

{

...other configs,

"transformIgnorePatterns": [
"/node_modules/(?!(@middy/core)/)"
],

"moduleNameMapper": {
   "^@middy/core$": "/node_modules/@middy/core"
},

"transform": {
   "^.+\.tsx?$": [
      "ts-jest",
       {
         "tsconfig": "tsconfig.dev.json",
         "useESM": true
       }
   ],
   "^.+\.jsx?$": "babel-jest"
 }
}

after that, you will need two packages in devdep:

@babel/preset-env: "^7.x.x", babel-jest: "^29.x.x"

then you will need a babel.config.js file with this content:

module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
  ],
};

Upvotes: 0

Related Questions