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

Respect package.json#exports when resolving plugins #14110

Merged

Conversation

nicolo-ribaudo
Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Jan 6, 2022

Q                       A
Fixed Issues?
Patch: Bug Fix? Yes
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

I had a demo code like this:

await babel.transformAsync(input, {
  configFile: false,
  plugins: [
    ["babel-plugin-polyfill-corejs3", {
      "method": "usage-pure"
    }],
  ],
})

and I noticed that it resolved to the CJS version of babel-plugin-polyfill-corejs3, even if @babel/core internally uses import() to load it.

This PR fixes that, by using the correct resolution algorithm for ESM. I used require.resolve for CJS and https://github.com/wooorm/import-meta-resolve for ESM (which is an exact copy of the experimental import.meta.resolve Node.js API).

Unfortunately that package is published as ESM, so we cannot directly depend on it. I added a gulp task to add it as a (.gitignored) file in our src folder, so that we can then compile it to CJS.

The vendored file has this MIT license + copyright (inlined in a comment)
/*
(The MIT License)

Copyright (c) 2021 Titus Wormer <mailto:tituswormer@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

---

This is a derivative work based on:
<https://github.com/nodejs/node>.
Which is licensed:

"""
Copyright Node.js contributors. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""

This license applies to parts of Node.js originating from the
https://github.com/joyent/node repository:

"""
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
*/
</details>

<a href="https://gitpod.io/#https://github.com/babel/babel/pull/14110"><img src="https://gitpod.io/button/open-in-gitpod.svg"/></a>

@nicolo-ribaudo nicolo-ribaudo added pkg: core PR: Bug Fix 🐛 labels Jan 6, 2022
}
return res.value;
}
async function resolveStandardizedNameForImport(
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept these two functions separated (rather than creating a gensync) because they are not simply a sync and an async version: the sync one is also used when Babel is called asynchronously.

@babel-bot
Copy link
Collaborator

@babel-bot babel-bot commented Jan 6, 2022

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/50740/

@JLHwung
Copy link
Contributor

@JLHwung JLHwung commented Jan 6, 2022

The author of import-meta-resolve lists the differences between the polyfill and import.meta.resolve. One of the notable differences is:

No support for CLI flags: --experimental-specifier-resolution, --experimental-json-modules, --experimental-wasm-modules, --experimental-policy, --input-type, --preserve-symlinks, --preserve-symlinks-main, nor --conditions work

I think we can first try the native import.meta.resolve, and then fallback to the polyfill. So early ESM adopters can experiment with the other import resolution options by providing --experimental-import-meta-resolve.

@nicolo-ribaudo nicolo-ribaudo force-pushed the resolve-plugin-preset-respect-exports branch 2 times, most recently from 70db994 to 0232e1f Compare Jan 6, 2022
@nicolo-ribaudo
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo commented Jan 6, 2022

The CI failure is unrelated.

@nicolo-ribaudo nicolo-ribaudo force-pushed the resolve-plugin-preset-respect-exports branch from 66229ba to ced9928 Compare Jan 7, 2022
@nicolo-ribaudo
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo commented Jan 8, 2022

I can reproduce the Jest failure locally, using node ./packages/jest-cli/bin/jest.js --color --config jest.config.ci.js -i packages in the jest repo after replacing all the @babel/core dependencies with the package from this patch.

The failure is that it segfaluts 😢

@nicolo-ribaudo nicolo-ribaudo marked this pull request as draft Jan 8, 2022
@nicolo-ribaudo
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo commented Jan 8, 2022

I hate it, it's nodejs/node#35889 for the millionth time. It's the dynamic import used to get the built-in import.meta.resolve that makes Node.js crash.

........PID 171352 received SIGSEGV for address: 0x10
/home/nicolo/Documenti/dev/jest/node_modules/segfault-handler/build/Release/segfault-handler.node(+0x3350)[0x7f426543a350]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x141f0)[0x7f42650931f0]
node[0xa15ec0]
node(_ZN2v88internal7Isolate38RunHostImportModuleDynamicallyCallbackENS0_6HandleINS0_6ScriptEEENS2_INS0_6ObjectEEENS0_11MaybeHandleIS5_EE+0xad)[0xe37d0d]
node(_ZN2v88internal25Runtime_DynamicImportCallEiPmPNS0_7IsolateE+0xba)[0x11f966a]
node[0x15e79d4]
[1]    171352 segmentation fault (core dumped)  node ./packages/jest-cli/bin/jest.js --color --config jest.config.ci.js -i

@SimenB Is there a way to detect if we are running in jest-runtime so that we can always fallback to the import.meta.resolve polyfill in that case? I would like to avoid detecting jest "in general", because we are using a different test runner that doesn't use vm.Script/importModuleDynamically and thus isn't affected by this V8 bug (btw, I'm happy to talk with you about this runner if you are interested. It has way less features than the default Jest one, but it unblocked our migration to native ESM).

@SimenB
Copy link
Contributor

@SimenB SimenB commented Jan 8, 2022

Currently, no. We could add something to import.meta, I guess... Would only work within a module, tho. Would that work for you? I'd like to avoid adding anything custom to e.g. require or some such. There is process.env.JEST_WORKER_ID, but anybody using jest-worker will have it, so will probably turn up false positives

btw, I'm happy to talk with you about this runner if you are interested. It has way less features than the default Jest one, but it unblocked our migration to native ESM

Definitely interested! Would you be able to open an issue over in the jest repo about it? If you meant a more private conversation that's possible as well, of course 🙂 Just DM me on twitter

@nicolo-ribaudo
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo commented Jan 8, 2022

Maybe I can check if Symbol.for("jest-native-now") in globalThis. Btw, why do you need to share the Symbol constructor with https://github.com/facebook/jest/blob/11d79ec096a25851124356095d60352f6ca2824e/packages/jest-util/src/installCommonGlobals.ts#L49?

Definitely interested! Would you be able to open an issue over in the jest repo about it? If you meant a more private conversation that's possible as well, of course slightly_smiling_face Just DM me on twitter

Sure, I'll DM you (I might then open an issue, but it might end up being a lot of work for nothing)!

@SimenB
Copy link
Contributor

@SimenB SimenB commented Jan 8, 2022

We load some of our libraries inside of the sandbox, and in order to have access to the "real" globals in cases where users do global.Symbol = undefined, global.Promise = undefined etc. We have a babel plugin which replaces all usage of these globals to ones via the Symbols: https://github.com/facebook/jest/blob/5d483ce07e450d7c1148ac961877ff0ac46f39a9/scripts/babel-plugin-jest-native-globals.js

@nicolo-ribaudo nicolo-ribaudo marked this pull request as ready for review Jan 8, 2022
@nicolo-ribaudo nicolo-ribaudo added this to In progress in Move to native ES modules via automation Jan 14, 2022
@nicolo-ribaudo nicolo-ribaudo force-pushed the resolve-plugin-preset-respect-exports branch 2 times, most recently from f29c2c4 to ac045cf Compare Jan 15, 2022
@nicolo-ribaudo nicolo-ribaudo merged commit e7c705a into babel:main Jan 16, 2022
27 of 28 checks passed
@nicolo-ribaudo nicolo-ribaudo deleted the resolve-plugin-preset-respect-exports branch Jan 16, 2022
Move to native ES modules automation moved this from In progress to Done Jan 16, 2022
@github-actions github-actions bot added the outdated label May 6, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated pkg: core PR: Bug Fix 🐛
Development

Successfully merging this pull request may close these issues.

None yet

5 participants