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

add support for .cjs .mjs and .js with package.json type="module" #4873

Closed

Conversation

jantimon
Copy link
Contributor

@jantimon jantimon commented Jan 8, 2025

When using Node.js projects with "type": "module" in package.json, the relay compiler currently fails to load configuration files due to incompatibilities between ESM and CommonJS

See #4060 (comment)

This pr handle all three module format scenarios:

  • Native ES modules (.mjs)
  • CommonJS modules (.cjs)
  • Regular .js files that respect the package.json "type" field

It works by switching from Node require() to dynamic import() and using the --input-type=module flag

I also added a new test and verified that this approach works for Node 18.0.0

fixes #4060

@facebook-github-bot
Copy link
Contributor

Hi @jantimon!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@captbaritone
Copy link
Contributor

I believe the Relay config must also be able to be read by the Babel Transform which would presumably need an equivalent change? This is further complicated by the fact that there are alternative transform implementations which may also try to read the config. If they do (maybe you could check?) we would probably need to either also update them or at least update their (and our) documentation to callout the incompatability.

@jantimon
Copy link
Contributor Author

Thanks for your feedback - I've took a quick look into the Babel Plugin and it looks like it is not possible to add it to babel

You probably know that Babel and its plugins must be CommonJS and synchronous by design - so we can't use import or import() to import a esm relay config.

However Next.js (which uses @swc/plugin-relay) is able to consume both relay config formats CJS and ESM:
https://nextjs.org/docs/pages/api-reference/config/next-config-js#ecmascript-modules

import relayConfig from './relay.config.mjs';
export default {
  compiler: {
    relay: relayConfig
  }
};

I believe your idea to update the babel documentation is the best option

@jantimon
Copy link
Contributor Author

I added a note for babel-plugin-relay - could you please take a look?

@captbaritone
Copy link
Contributor

This makes sense to me, and the large number of upvotes indicates others feel similarly. One question: how does Babel fail if you use .mjs? Is it obvious what you need to change or is it cryptic?

@facebook-github-bot
Copy link
Contributor

@captbaritone has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@jantimon
Copy link
Contributor Author

I created a minimal webpack / relay / babel project and got a understandable error message:

[webpack-cli] Failed to load './minimal-relay-app/webpack.config.js' config
[webpack-cli] ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and
'./minimal-relay-app/package.json' contains "type": "module". To treat it as a CommonJS script,
rename it to use the '.cjs' file extension.

For details please take a look at the full outputs:

Full outputs

package.json - with "type": "module"

$ webpack
[webpack-cli] Failed to load './minimal-relay-app/webpack.config.js' config
[webpack-cli] ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and './minimal-relay-app/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
    at file://minimal-relay-app/webpack.config.js:1:14
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:431:15)
    at async WebpackCLI.tryRequireThenImport (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:232:34)
    at async loadConfigByPath (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:1406:27)
    at async WebpackCLI.loadConfig (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:1515:38)
    at async WebpackCLI.createCompiler (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:1781:22)
    at async WebpackCLI.runWebpack (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:1877:20)
    at async Command.<anonymous> (./minimal-relay-app/node_modules/webpack-cli/lib/webpack-cli.js:944:21)
error Command failed with exit code 2.

package.json - no type:

yarn build
yarn run v1.22.19
warning package.json: No license field
$ webpack
asset bundle.js 1.91 MiB [compared for emit] (name: main)
runtime modules 1.25 KiB 6 modules
modules by path ./node_modules/ 1.72 MiB
  modules by path ./node_modules/relay-runtime/ 428 KiB 100 modules
  modules by path ./node_modules/react-relay/ 197 KiB 49 modules
  modules by path ./node_modules/@babel/runtime/ 22.5 KiB 26 modules
  modules by path ./node_modules/react-dom/ 1010 KiB 3 modules
  modules by path ./node_modules/fbjs/lib/*.js 5.29 KiB 3 modules
  modules by path ./node_modules/react/ 85.7 KiB 2 modules
  modules by path ./node_modules/scheduler/ 17.3 KiB 2 modules
  ./node_modules/invariant/browser.js 1.36 KiB [built] [code generated]
modules by path ./src/ 4.15 KiB
  ./src/index.js 1.34 KiB [built] [code generated]
  ./src/RelayEnvironment.js 1.39 KiB [built] [code generated]
  ./src/__generated__/srcFixedQuery.graphql.js 1.43 KiB [built] [code generated]
webpack 5.97.1 compiled successfully in 781 ms
✨  Done in 1.40s.

There is only one downside - the proposed solution from the error message To treat it as a CommonJS script, rename it to use the '.cjs' file extension. does not work as the relay babel plugin does not look for .cjs

@jantimon
Copy link
Contributor Author

I added also support for .cjs in packages/babel-plugin-relay/BabelPluginRelay.js

Now users can follow the proposed solution of the error and rename their config to .cjs inside a "type"="module" package

@facebook-github-bot
Copy link
Contributor

@captbaritone merged this pull request in 08e45d7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to use relay.config.js with ESM module
3 participants