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

[ESM] Jest crashes with request for ... is not yet fulfilled linking error #11434

Open
nicolo-ribaudo opened this issue May 22, 2021 · 12 comments

Comments

@nicolo-ribaudo
Copy link
Contributor

🐛 Bug Report

When two modules dynamically imported in parallel depend on the same file*, Jest crashes with a request for ... is not yet fulfilled error. The error is thrown by https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/src/module_wrap.cc#L535, so it's possible that this is a Node.js bug and not a Jest bug.

In my repository example below, I import two files in parallel (if I import them serially the test passes):
https://github.com/nicolo-ribaudo/babel/blob/31dac517b7ce0d504bda794075f0a88029cedd58/packages/babel-plugin-proposal-private-methods/test/repro.js#L6-L7

Those two files both depend on packages/babel-helper-create-class-features-plugin/lib/index.js. packages/babel-helper-create-class-features-plugin/lib/index.js depends on packages/babel-helper-create-class-features-plugin/lib/fields.js, and the error I get is about that file:

request for './fields.js' is not yet fulfilled

To Reproduce

Repository link: https://github.com/nicolo-ribaudo/babel/tree/jest-esm-request-not-yet-fulfilled-error

Steps to reproduce:

make bootstrap
node --experimental-vm-modules ./node_modules/.bin/jest babel-plugin-proposal-private-methods/test/repro

Expected behavior

It shouldn't crash

envinfo

  System:
    OS: Linux 5.8 Ubuntu 20.10 (Groovy Gorilla)
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Binaries:
    Node: 14.17.0 - /usr/local/bin/node
    Yarn: 2.4.1 - /usr/bin/yarn
    npm: 7.9.0 - ~/.npm-global/bin/npm
  npmPackages:
    jest: 27.0.0-next.11 => 27.0.0-next.11 

It also fails with Node.js 16.2.0

@nicolo-ribaudo
Copy link
Contributor Author

I know nothing about this other than what the GH search surfaced, but:

  • I think this error is linking error, dependency promises must be resolved on instantiate, just with a new error message
  • From the last messages in Node.js 15.9 regression with Jest ESM nodejs/node#37426, maybe the problem is that the two imports try to link()/evaluate() the same file at the same time?

@SimenB
Copy link
Member

SimenB commented May 22, 2021

Interesting find! The error is thrown when await the promise returned by module.link here: https://github.com/facebook/jest/blob/22267424a824310779bad3cafaae59485ce2a6c7/packages/jest-runtime/src/index.ts#L530. module.status at this point is unlinked, but the module which node says is "not yet fulfilled" is linking. I would have thought node just waited for modules in status linking to complete when it's returned from module.link callback, but maybe not?

I've tried for about an hour to understand why it goes boom without luck... Might need to ask in the node repo. I think we currently do only call link and evaluate on entry points and on dynamic imports, but I might be wrong 😛

Do you think you would be able to create a reproduction outside of the babel repo? I tried to add a failing test to this repo without success.

In https://github.com/facebook/jest/blob/22267424a824310779bad3cafaae59485ce2a6c7/e2e/native-esm/__tests__/native-esm.test.js

test('import same file in parallel indirectly', async () => {
  await Promise.all([import('../file1.js'), import('../file2.js')]);
});

and then both file1.js and file2.js being

import func from './sharedImport.js';

func();

and sharedImport.js

export default function func() {
  console.log('woop');
}

This passes instead of failing with the same error, though.

@nicolo-ribaudo
Copy link
Contributor Author

Sure, I can try 👍

@nicolo-ribaudo
Copy link
Contributor Author

nicolo-ribaudo commented May 23, 2021

@nicolo-ribaudo
Copy link
Contributor Author

Node throws if the promise isn't already resolved: https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/src/module_wrap.cc#L534

However, it seems in contrast with the idea behind promises: the "settled" status shouldn't be observed, a promise already resolved to x should behave exactly like a promise that will resolve to x.

@axmad386
Copy link

axmad386 commented Sep 5, 2021

same issue here, but I can't tell the detail. It's to complicated lol
I use polka (like express) and map the routes with middleware. I use Promise.all to map the routes and middleware. Some middleware import multer (and I mock it). This middleware is attached twice to the routes. I found that the first route is fine, but the second one is failed to run, and throw this error message
FATAL request for 'multer' is not yet fulfilled
{"code":"ERR_VM_MODULE_LINK_FAILURE"}
Something like multer module cannot load for the second time

It's only happen when I test my app with jest. In production is fine

this is the copy of my code

const runServices = async (routes, routeMiddleware) => {
    await Promise.all(["post", "get", "patch", "put", "delete"].map(async (method) => {
        await Promise.all((routes[method] || []).map(async (r) => {
            let service;
            let localMiddleware = [];
            try {
                const instance = await import(servicePath + r.service);
                service = instance.default || instance;
                (service.middleware || []).forEach((m, i) => {
                    let mInstance = m.default || m; //multer imported here
                    localMiddleware[i] = async (req, res, next) => {
                        try {
                            await mInstance(req, res, next);
                        }
                        catch (err) {
                            handleError(req, res, err);
                        }
                    };
                });
                router[method](routes.prefix + r.path, ...routeMiddleware, ...localMiddleware, async (req, res) => {
                    try {
                        await serviceExec(req, res, service);
                    }
                    catch (err) {
                        handleError(req, res, err);
                    }
                });
            }
            catch (err) {
                Log.fatal(err);
            }
        }));
    }));
};

System:
OS: Linux 5.11 elementary OS 6 Odin
CPU: (4) x64 Intel(R) Core(TM) i5-5300U CPU @ 2.30GHz
Binaries:
Node: 16.6.2 - ~/.nvm/versions/node/v16.6.2/bin/node
Yarn: 1.22.11 - ~/.nvm/versions/node/v16.6.2/bin/yarn
npm: 7.20.3 - ~/.nvm/versions/node/v16.6.2/bin/npm
npmPackages:
jest: ^27.1.0 => 27.1.0

@johonIngka
Copy link

Hi I have same issue with oas-tools getting 'request for ./middleware/index.js is not yet fulfilled' oas-tools/oas-tools#342
Any updates on this one?

@stevenfukase
Copy link

Same here. Having problems when using @oas-tools/core.

@SimenB
Copy link
Member

SimenB commented Nov 1, 2022

Again, this is a node (or rather, v8) bug linked above. Until that's fixed, no need to add any "+1"s to this issue

@juanda2222
Copy link

Is it possible to know when this issue is schedule to be resolved? or maybe a workaround?

@SimenB
Copy link
Member

SimenB commented Jan 31, 2023

You can track any of the issues linked in nodejs/node#37648

@fmaddenflx
Copy link

fmaddenflx commented Mar 6, 2023

I'm still hitting this issue. This has not yet been resolved?

In my situation I have code like the following:

async function proxyData(table: string, mock: boolean = false): Promise<PersistenceGateway | object> {
  let database: PersistenceGateway;
  if (config.db_mock) {
    return await import(`./gateways/${table}.local.inmemory.gateway`);
  } else {
    return await import(`./gateways/${table}.mysql.gateway`);
  }
}

It's failing in either the if or the else on the import. That dynamic import opens up a module who's first line is the following:

import log from '../logging.service';

And the error I'm getting is:

request for '../logging.service' is not yet fulfilled

coryasilva added a commit to coryasilva/open-api that referenced this issue Sep 10, 2024
When using jest this code gets transpiled to `require` and the file URL syntax of the import is not supported and breaks the testing environment.  This change only uses file URL import syntax on windows machines.  This fixes our tests and is still supported on linux and mac operating systems.

I think windows is definitely in the minority here and we should not optimize for it.  Furthermore, I believe using a UNC path would be a better solution outright but I cannot test because I don't have a window machine at hand.

Why not just use jest's `--experimental-vm-modules` flag? Glad you asked.  Because of this [jest bug](jestjs/jest#11434 (comment)) which is caused by this [node bug](nodejs/node#37648) which is ultimately caused by this [v8 bug](https://issues.chromium.org/issues/40784051)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants