Skip to content

Unable to load formatjs in jest with ESM (Unexpected token 'export') #4128

@unional

Description

@unional

Which package?

Almost all @formatjs packages.

Describe the bug

When writing a library written in TypeSript, using jest and ts-jest,
we need to use extensionsToTreatAsEsm in indicates what files should be treated as ESM,
in order to use code such as import.meta

Here is an example:

// jest.config.js
/** @type {import('jest').Config} */
export default {
	extensionsToTreatAsEsm: ['.ts'],
	transform: {
		"^.+\\.(ts|tsx|cts|mts)$": [
			"ts-jest", {
				"isolatedModules": true,
				"useESM": true,
				"diagnostics": {
					"ignoreCodes": [
						151001
					]
				}
			}
		]
	}
}

The problem with that is then jest will rely on the type field of the nearest package.json to determine if the file should be processed as ESM or not.

The @formatjs packages does not specify such field. Using @formatjs/intl as an example:

{
  "main": "index.js",
  "module": "lib/index.js",
  "types": "index.d.ts",
  "exports": {
    ".": {
      "types": "./index.d.ts",
      "import": "./lib/index.js",
      "default": "./index.js"
    },
    "./package.json": "./package.json"
  },
}

This means ./lib/index.js (just like ./index.js) is treated as CJS thus getting `Unexpected token 'export' error:

.../node_modules/@formatjs/intl/lib/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export * from './src/types';

To Reproduce

Codesandbox URL

Reproducible Steps/Repo

Here is a repro:

https://github.com/repobuddy/repobuddy/tree/formatjs-issue/testcases/jsdom-formatjs

Checkout the jsdom-formatjs branch, run:

pnpm i

cd testcases/jsdom-formatjs

pnpm test

You can see the error.

Then, you can comment out extensionsToTreatAsEsm: ['.ts'] in the jest.config.js and run pnpm test again, which you can see the import.meta error:

.../repobuddy/jest/testcases/jsdom-formatjs/src/feature.spec.ts:10
    (0, globals_1.it)(`${(0, dirname_filename_esm_1.filename)(import.meta)} executed`, () => { });
                                                                     ^^^^

    SyntaxError: Cannot use 'import.meta' outside a module

Expected behavior

Able to run the code in such environment.

Screenshots

Desktop (please complete the following information):

  • OS: macOS

Additional context

The fix is to add type: module to a package.json under lib folder:

- package.json // keep as-is
- index.js
- lib
  - package.json // add this, containing `{ "type": "module" }`
  - index.js

Recommended solution

Optimally, I would recommend completely separating cjs and esm output

What I normally do is organize the output like this:

- package.json
- cjs
  - package.json // with `{}`, this will signal NodeJS to load code as CJS in this folder
  - index.js
- esm
  - index.js

and the package.json contains:

{
  "main": "./cjs/index.js",
  "module": "./esm/index.js",
  "types": "./cjs/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "types": "./esm/index.d.ts",
        "default": "./esm/index.js"
      },
      "require": {
        "types": "./cjs/index.d.ts",
        "default": "./cjs/index.js"
      },
      // optional
      "default": "./cjs/index.js"
    },
    "./package.json": "./package.json"
  },
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions