Skip to content

Commit

Permalink
add support for .cjs .mjs and .js with package.json type="module" (#4873
Browse files Browse the repository at this point in the history
)

Summary:
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

Pull Request resolved: #4873

Reviewed By: lynnshaoyu

Differential Revision: D68588063

Pulled By: captbaritone

fbshipit-source-id: 992583b86d993c6e31c6b57f1e8ea8af07dbf4cc
  • Loading branch information
jantimon authored and facebook-github-bot committed Jan 28, 2025
1 parent 6165081 commit 08e45d7
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 3 deletions.
1 change: 1 addition & 0 deletions compiler/crates/js-config-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ where
LoaderSource::Js(format!(".{}.rc.cjs", name)),
LoaderSource::Js(format!("{}.config.js", name)),
LoaderSource::Js(format!("{}.config.cjs", name)),
LoaderSource::Js(format!("{}.config.mjs", name)),
],
)
}
3 changes: 2 additions & 1 deletion compiler/crates/js-config-loader/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ pub struct JsLoader;
impl<T: for<'de> Deserialize<'de> + 'static> Loader<T> for JsLoader {
fn load(&self, path: &Path) -> Result<Option<T>, ErrorCode> {
let output = Command::new("node")
.arg("--input-type=module")
.arg("-e")
.arg(r#"process.stdout.write(JSON.stringify(require(process.argv[1])))"#)
.arg(r#"process.stdout.write(JSON.stringify((await import(process.argv[1])).default))"#)
.arg(path)
.output()
.expect("failed to execute process. Make sure you have Node installed.");
Expand Down
19 changes: 19 additions & 0 deletions compiler/crates/js-config-loader/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ fn config_js() {
assert_eq!(config.value.name, "correct");
}

#[test]
fn config_mjs() {
let dir = tempdir().unwrap();
let dir_d = dir.path().join("a/b/c/d");
let dir_f = dir.path().join("a/b/c/d/e/f");
create_dir_all(&dir_f).unwrap();

std::fs::write(
dir_d.join("foo.config.mjs"),
r#"
export default { name: "correct" };
"#,
)
.unwrap();

let config = search::<TestConfig>("foo", &dir_f).unwrap().unwrap();
assert_eq!(config.value.name, "correct");
}

#[test]
fn config_js_invalid_js() {
let dir = tempdir().unwrap();
Expand Down
7 changes: 5 additions & 2 deletions compiler/crates/relay-compiler/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,17 @@ impl Config {
}

pub fn load(config_path: PathBuf) -> Result<Self> {
let loader = if config_path.extension() == Some(OsStr::new("js")) {
let loader = if config_path.extension() == Some(OsStr::new("js"))
|| config_path.extension() == Some(OsStr::new("cjs"))
|| config_path.extension() == Some(OsStr::new("mjs"))
{
LoaderSource::Js(config_path.display().to_string())
} else if config_path.extension() == Some(OsStr::new("json")) {
LoaderSource::Json(config_path.display().to_string())
} else {
return Err(Error::ConfigError {
details: format!(
"Invalid file extension. Expected `.js` or `.json`. Provided file \"{}\".",
"Invalid file extension. Expected `.js`, `.cjs`, `.mjs` or `.json`. Provided file \"{}\".",
config_path.display()
),
});
Expand Down
3 changes: 3 additions & 0 deletions packages/babel-plugin-relay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const fragment = require('__generated__/User_fragment.graphql');
project (i.e. in the same folder as the `package.json` file).
- The `package.json` file contains a `"relay"` key.

> [!IMPORTANT]
> This Babel plugin does not support `relay.config.mjs` or `package.json` with `"type": "module"`.
### Supported configuration options for `babel-plugin-relay`

- `artifactDirectory` A specific directory to output all artifacts to. When
Expand Down

0 comments on commit 08e45d7

Please sign in to comment.