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

[Feature]: Support package imports in jest-resolve #12270

Closed
anthony-redFox opened this issue Jan 27, 2022 · 21 comments · Fixed by #13705
Closed

[Feature]: Support package imports in jest-resolve #12270

anthony-redFox opened this issue Jan 27, 2022 · 21 comments · Fixed by #13705

Comments

@anthony-redFox
Copy link

🚀 Feature Proposal

Node Imports in packages.json

Motivation

https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#imports

Example

No response

Pitch

similar as #9771

@renemroger
Copy link

Is there a current work around for the imports feature in jest?

@SimenB
Copy link
Member

SimenB commented Feb 7, 2022

No, but once we have exports I guess imports shouldn't be too hard to build on top of it

@KermanX
Copy link

KermanX commented Feb 7, 2022

I think this is not only a feature, but a bug.

Projects using some popular packages, such as chalk cannot use jest because of this bug...

An example is #12309.

@antongolub
Copy link
Contributor

antongolub commented Feb 10, 2022

If anyone is looking for a workaround for now: moduleNameMapper

  "moduleNameMapper": {
    "chalk": "chalk/source/index.js",
    "#ansi-styles": "chalk/source/vendor/ansi-styles/index.js",
    "#supports-color": "chalk/source/vendor/supports-color/index.js"
  },

https://github.com/qiwi/libdefkit/blob/master/jest.config.json#L22

@SimenB
Copy link
Member

SimenB commented Feb 10, 2022

Once a module implementing the algorithm exists (e.g. lukeed/resolve.exports#14) we should be good to go for adding support here.

@github-actions
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

@Twipped
Copy link

Twipped commented Apr 7, 2022

I've been wrestling with this problem today. I thought I could address it by adding a resolver that uses enhanced-resolve, but apparently adding a resolver (which can only be commonjs?) breaks ESM processing, and suddenly every ESM dependency I use is unparsable.

@SimenB
Copy link
Member

SimenB commented Apr 7, 2022

Plugging in a resolver should work fine, but it needs to support all cases. You could choose to e.g. look for # as first char and delegate (default resolver is injected as argument), but regardless I'd assume it works fine?

@jlarmstrongiv
Copy link

jlarmstrongiv commented Apr 27, 2022

This solution works for me:

  moduleNameMapper: {
    chalk: require.resolve("chalk/source/index.js"),
    "#ansi-styles": require.resolve("ansi-styles/index.js"),
    "#supports-color": require.resolve("supports-color/index.js"),
  },

This solution works for me:

  moduleNameMapper: {
    chalk: require.resolve("chalk"),
    "#ansi-styles": path.join(
      require.resolve("chalk").split("chalk")[0],
      "chalk/source/vendor/ansi-styles/index.js",
    ),
    "#supports-color": path.join(
      require.resolve("chalk").split("chalk")[0],
      "chalk/source/vendor/supports-color/index.js",
    ),
  },

shawnyu5 added a commit to shawnyu5/fragments that referenced this issue May 24, 2022
the aws-jwt-verify module currently needs to be set to 2.1.3 vs. 3.x due
to a awslabs/aws-jwt-verify#66 in how it jestjs/jest#12270. Until this
is fixed upstream, avoid using the latest version.
@mizdra
Copy link

mizdra commented Jun 13, 2022

#12270 (comment) does not work in applications where chalk v4 (without imports) and chalk v5 (with imports) coexist.

So I use the workaround of using jest's default resolver whenever possible. This also works for applications with both chalk v4 and chalk v5.

// jest.config.mjs

import { dirname, join } from 'path';
import { fileURLToPath } from 'url';

const dir = join(dirname(fileURLToPath(import.meta.url)));

const jestConfig = {
  // ...
  resolver: join(dir, 'src/test-util/jest/resolver.cjs'),
};
export default jestConfig;
// src/test-util/jest/resolver.cjs
// @ts-check

const { dirname, resolve } = require('path');
const resolveFrom = require('resolve-from');

/**
 * @typedef {{
 * basedir: string;
 * conditions?: Array<string>;
 * defaultResolver: (path: string, options: ResolverOptions) => string;
 * extensions?: Array<string>;
 * moduleDirectory?: Array<string>;
 * paths?: Array<string>;
 * packageFilter?: (pkg: any, file: string, dir: string) => any;
 * pathFilter?: (pkg: any, path: string, relativePath: string) => string;
 * rootDir?: string;
 * }} ResolverOptions
 * */

/** @type {(path: string, options: ResolverOptions) => string} */
module.exports = (path, options) => {
  // workaround for https://github.com/facebook/jest/issues/12270
  if (path === '#ansi-styles' || path === '#supports-color') {
    const chalkRoot = resolve(dirname(resolveFrom(options.basedir, 'chalk')), '../');
    const subPkgName = path.slice(1);
    return `${chalkRoot}/source/vendor/${subPkgName}/index.js`;
  }
  return options.defaultResolver(path, options);
};

from: https://github.com/mizdra/eslint-interactive/blob/c6383db807ec31b6329b744ce5d2b2897adf085e/src/test-util/jest/resolver.cjs

@BlackGlory
Copy link
Contributor

BlackGlory commented Jun 13, 2022

import resolve from 'enhanced-resolve'
import { SyncResolver } from 'jest-resolve'

const resolver: SyncResolver = (path, options) => {
  const { defaultResolver } = options

  try {
    return defaultResolver(path, options)
  } catch (e) {
    const result = resolve.sync(options.basedir, path)
    if (result) {
      return result
    } else {
      throw e
    }
  }
}

export default resolver

@ottokruse
Copy link

ottokruse commented Jun 13, 2022

As more options to solve this pile up here, I'd like to point out that using Jest to test code that has dependencies that have package imports (a.k.a subpath imports) should ideally just work, without doing any config or setting up resolvers.

Concur with @KermanX that this is not a feature request, but a bug.

Are FB engineers working on Jest, or are we solely waiting on a PR from the community?

@jlarmstrongiv
Copy link

@ottokruse community PR https://engineering.fb.com/2022/05/11/open-source/jest-openjs-foundation/

@Jaid
Copy link

Jaid commented Jun 14, 2022

Fixed it for me by just switching from the default runner to https://github.com/nicolo-ribaudo/jest-light-runner, which disables most of Jest's “magic” features I don't need anyways.

@Maxim-Mazurok
Copy link
Contributor

Maxim-Mazurok commented Jun 27, 2022

I couldn't figure out how to use @BlackGlory script, so I did this:

resolver.cjs:

const resolve = require("enhanced-resolve");

const resolver = (path, options) => {
  const { defaultResolver } = options;

  try {
    return defaultResolver(path, options);
  } catch (e) {
    const result = resolve.sync(options.basedir, path);
    if (result) {
      return result;
    } else {
      throw e;
    }
  }
};

module.exports = resolver;

jest.config.ts:

export default {
  preset: "ts-jest/presets/default-esm",
  globals: {
    "ts-jest": {
      useESM: true,
    },
  },
  resolver: "./resolver.cjs",
};

@mxxk
Copy link

mxxk commented Jul 25, 2022

To add to #12270 (comment) and #12270 (comment), here's an alternative implementation of the custom resolver which relies on the node:module core module instead of enhanced-resolve:

const nativeModule = require('node:module');

function resolver(module, options) {
  const { basedir, defaultResolver } = options;
  try {
    return defaultResolver(module, options);
  } catch (error) {
    return nativeModule.createRequire(basedir).resolve(module);
  }
}

module.exports = resolver;

Note: Since .resolve() already throws an error if the module cannot be resolved, there is no need to check its return value and rethrow the error caught from defaultResolver().


Presumably, at some point when Jest starts importing resolver as an ES module, the above can be rewritten as

import url from 'node:url';

export default async function resolver(path, options) {
  const { basedir, defaultResolver } = options;
  try {
    return defaultResolver(path, options);
  } catch (error) {
    return await import.meta.resolve(path, url.pathToFileURL(basedir));
  }
}

This relies on import.meta.resolve(), which at the time of this writing the latest version of Node 18 still requires --experimental-import-meta-resolve to use.

@nujarum
Copy link

nujarum commented Aug 28, 2022

This relies on import.meta.resolve(), which at the time of this writing the latest version of Node 18 still requires --experimental-import-meta-resolve to use.

I have previously released a package resolve-esm to call import.meta.resolve() without the experimental flag.
This should work on all current Node.js LTS versions (v14, v16, v18).

I will be happy if this is of any help.

@seanfuture
Copy link

If helpful, until this issue is closed, the following works for the sole case of resolving chalk in a Jest-enabled Next.js project. No need to map the chalk reference itself:

moduleNameMapper: {
    '#ansi-styles': 'ansi-styles/index.js',
    '#supports-color': 'supports-color/index.js',
}

char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 5, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 11, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 16, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 17, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 18, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 23, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
char0n added a commit to swagger-api/swagger-js that referenced this issue Jan 23, 2023
Node.js =12.20.0 added support for exports field.
Node.js =12.19.0 added support for imports field.

Both of these fields are used by integrated ApiDOM.

Jest@29.3.0 doesn't yet support package.json imports
field. Next version will, more info in:
jestjs/jest#12270.

Refs #2744
@SimenB
Copy link
Member

SimenB commented Jan 24, 2023

https://github.com/facebook/jest/releases/tag/v29.4.0

@Maxim-Mazurok
Copy link
Contributor

Can confirm this is working now, thank you! I can remove my resolver.cjs workaround now, yay!

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.