Search code examples
node.jstypescriptwebpackjestjsts-jest

Jest transformers aren't working with Typescript + Webpack + PEGJS


I have an NPM project:

{
  "scripts": {
    "test": "jest --config=jest.config.js"
  },
  "devDependencies": {
    "@types/pegjs": "0.10.3",
    "@types/jest": "29.1.1",
    "ts-loader": "9.3.1",
    "raw-loader": "4.0.2",
    "typescript": "4.8.3",
    "webpack": "5.74.0",
    "webpack-cli": "4.10.0",
    "jest": "29.1.2",
    "ts-jest": "29.0.3"
  },
  "dependencies": {
    "pegjs": "0.10.0"
  }
}

As you can see this project:

  • Has source files written in TypeScript.
  • Uses Webpack for bundling.
  • Uses PegJS.
  • Uses Jest for testing.

The structure:

myproj
|-src/
  |-grammar.pegjs
  |-index.ts
  |-index.test.ts
|-package.json
|-jest.config.js
|-transformer.js

Running tests

Jest will transpile .ts files into Javascript. As explained in the documentation, we need to handle cases where we are importing static assets using Webpack. In this case, index.ts has this:

import pegjsgrammar from grammar

Which needs handling. I am creating a transformer just for that transformer.js:

const fs = require("fs");

module.exports = {
    process(sourceText, sourcePath, options) {
        const fileContents = fs.readFileSync(sourcePath, { encoding: "utf8" }).toString();
        const json = JSON.stringify(fileContents).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');

        return {
            code: `module.exports = ${json};`,
        };
    },
};

And inside jest.config.js:

module.exports = {
    preset: "ts-jest",

    transform: {
      "\\.tsx?$": "ts-jest",
      "\\.jsx?$": "babel-jest",
      "\\.pegjs$": "<rootDir>/pegjs_jest_transformer.js"
    },

    moduleFileExtensions: ["ts", "js", "pegjs"],
  };
  

Errors

When running my tests, I get errors because the value in pegjsgrammar inside index.ts is undefined.


Solution

  • So, the problem is guaranteeing the interoperability between the different modules. To achieve that, modify the tsconfig.json to include this option:

    {
        "compilerOptions": {
            ...
            "esModuleInterop": true
        }
    }
    

    And it all works fine.