npm package: @mapbox/esbuild-jest
Description: Jest transformer and preset for transpiling using ESBuild.
Latest version: 1.1.0

# `@mapbox/esbuild-jest`

> **NOTE**: This is a fork of [esbuild-jest](https://github.com/aelbore/esbuild-jest) with some incompatible differences.

Our Jest preset and transformer for compiling files with [ESBuild](https://esbuild.github.io/) and generating accurate coverage.

📦 `npm install --save-dev @mapbox/esbuild-jest esbuild`

esbuild-jest is not an officially supported Mapbox product, and is developed on a best-effort basis. Issues filed from third-party contributors may not be triaged in a timely manner.

## Transforms

In your Jest configuration file:

```json
{
  "transform": {
    "^.+\\.(tsx?|jsx?)$": "@mapbox/esbuild-jest"
  }
}
```

You can also specify ESBuild compatible options like so:

```json
{
  "transform": {
    "^.+\\.(tsx?|jsx?|css)$": [
      "@mapbox/esbuild-jest",
      {
        "loader": {
          ".css": "text"
        }
      }
    ]
  }
}
```

## Preset 

If you'd like to follow Mapbox conventions, you can use our Jest preset.

In your Jest configuration file:

```json
{
  "preset": "@mapbox/esbuild-jest"
}
```

In this configuration, `.js`, `.jsx`, `.ts`, and `.tsx` files are transformed by ESBuild. `.css` files are loaded as plaintext.

Coverage is also enabled with high (80%) thresholds.

### Directory structure

Our Jest preset expects files to be laid out in a certain way:

- `src/` - source files for coverage in this directory.
- `test/` - test files in this directory, with a suffix of either `.test` or `.spec`.
  * Example: `index.test.ts`

## How do I use jest.mock()?

> https://github.com/evanw/esbuild/issues/412

`@mapbox/esbuild-jest` may require code changes in two ways:
1. Calls to `jest.mock()` are **not** hoisted to the top of the file.
2. ESBuild marks ESM imports as read-only when converting to CommonJS, which is in line with the ECMAScript specification, but breaks certain calls to `jest.spyOn`.

There are multiple ways around this.

### Option 1

Stub the entire module in a **separate** file that is ESM imported before other files.

`test_utils/myMock.ts`:

```
jest.mock('../../src/myFile', () => {

})
```

`myFile.test.ts`: 

```
// Important that these are first.
import './test_utils/myMock.ts';

import { myFunction } from '../src/myFile';

// ...
```

### Option 2

Use the following workaround below that "allows" a module to be mocked.

> Sourced from: https://github.com/evanw/esbuild/issues/412#issuecomment-723047255

```typescript
export interface HasSpyOn {
  spyOn(): jest.SpyInstance;
}

export function enableSpyOn<T extends Function>(fn: T): T & HasSpyOn {
  if ('process' in globalThis && globalThis.process.env.NODE_ENV === 'test') {
    let name = fn.name,
      obj = { [name]: fn };
    (fn as any) = function (this: any) {
      return obj[name].apply(this, arguments);
    };
    (fn as any).spyOn = () => jest.spyOn(obj, name);
  }
  return fn as any;
}
```

```typescript
// file.ts
import { enableSpyOn } from 'helpers/enableSpyOn';
export const fn = enableSpyOn(function fn() {});

// file_test.ts
import { fn } from './file';
let spy = fn.spyOn();
```

# License

esbuild-jest is provided under the terms of the [MIT License](https://github.com/mapbox/esbuild-jest/blob/main/LICENSE)