Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
unional opened this issue Jun 7, 2023 · 5 comments · Fixed by #4129
Closed

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

unional opened this issue Jun 7, 2023 · 5 comments · Fixed by #4129
Labels

Comments

@unional
Copy link
Contributor

unional commented Jun 7, 2023

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"
  },
}
@unional
Copy link
Contributor Author

unional commented Jun 7, 2023

I tried to see if I can create a PR for this, but not really familiar with bazel.

@unional
Copy link
Contributor Author

unional commented Jun 7, 2023

This happens only to the latest version, as it adds the exports field without the type field.

e.g. @formatjs/intl 2.7.2 does not have exports field so it is working fine.

@marcovanharten-cpi
Copy link

Having the same issue indeed. I do not really know how to create a PR for this either

@unional
Copy link
Contributor Author

unional commented Jun 7, 2023

btw targeting es5 is not really necessary nowadays. If you update the target, then you don't even need tslib.

Actually, you probably don't even need that in the first place.
As long as the package is reasonably small, using tslib actually make your distribution larger, not smaller.

@MarcGodard
Copy link

Having this issue too, I think.

error during build:
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './types.js'
/node_modules/@formatjs/icu-messageformat-parser/package.json

pyrocat101 added a commit that referenced this issue Jun 12, 2023
…t-parser,@formatjs/intl-displaynames,@formatjs/intl-listformat,intl-messageformat,@formatjs/ecma402-abstract,@formatjs/intl-numberformat,@formatjs/icu-skeleton-parser): Revert esm conditional exports (#4129)

The ESM export chanegs are not properly tested and therefore broke in various scenarios.

Fixes #4128, #4127, #4126

This reverts commit e0d593c.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment