Repository: babel/babel (TypeScript)

Issue:
[Bug]: `@babel/plugin-proposal-json-module` may output some ESM code (or even call `undefined` as if it's a function) to CJS outputs
### 💻

- [ ] Would you like to work on a fix?

### How are you using Babel?

@rollup/plugin-babel

### Input code

https://babel.dev/repl#?browsers=&build=&builtIns=false&corejs=false&spec=false&loose=false&code_lz=JYWwDg9gTgLgBAcwDYQEYEMkGc4DMoQhwBEyamWA9GRtgHQBWWEAdsXAO7AwAWcA3jACeYAKYAuEk1bEAvgCh5ogB6RYcACajc6AK5J4NCkA&debug=false&forceAllTransforms=false&modules=commonjs&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=unambiguous&lineWrap=true&presets=env%2Ctypescript&prettier=false&targets=Node-20.9&version=7.24.6&externalPlugins=%40babel%2Fplugin-proposal-json-modules%407.24.6&assumptions=%7B%7D

### Configuration file name

_No response_

### Configuration

```json
{
  "caller": {
    "name": "packemon"
  },
  "comments": true,
  "parserOpts": {
    "sourceType": "unambiguous",
    "strictMode": true
  },
  "plugins": [
    "C:\\Repos\\HaloSPV3\\hce.shared-config\\node_modules\\@babel\\plugin-syntax-import-attributes\\lib\\index.js",
    "@babel/plugin-proposal-json-modules"
  ],
  "presets": [
    [
      "C:\\Repos\\HaloSPV3\\hce.shared-config\\node_modules\\@babel\\preset-typescript\\lib\\index.js",
      {
        "allowDeclareFields": true,
        "onlyRemoveTypeImports": false,
        "optimizeConstEnums": true
      }
    ]
  ],
  "configFile": false,
  "babelrc": false
}
```

This is incomplete. Packemon conditionally adds more plugins and presets (including `@babel/preset-env`) before passing the config to rollup. The `SHOW_CONFIG_FOR` environment variable had no effect.


### Current and expected behavior

`@babel/plugin-proposal-json-module` may output some ESM code to CJS outputs, resulting in build errors.
- Top-level awaits in CommonJS
- import.meta in CommonJS

Fiddling with config values eventually caused the top-level await to be removed and the `const` was moved to the bottom of my imports group (as expected), but a `import.meta.resolve(...)` replace with `undefined(...)`.


### Environment

  System:
    OS: Windows 11 10.0.22621
  Binaries:
    Node: 22.2.0 - ~\scoop\apps\nodejs\current\node.EXE
    Yarn: 1.22.22 - ~\scoop\apps\nodejs\current\bin\yarn.CMD
    npm: 10.8.0 - ~\scoop\apps\nodejs\current\bin\npm.CMD
    pnpm: 9.1.2 - ~\scoop\apps\nodejs\current\bin\pnpm.CMD
  npmPackages:
    @babel/cli: ^7.24.6 => 7.24.6
    @babel/core: ^7.24.6 => 7.24.6
    @babel/eslint-parser: ^7.24.6 => 7.24.6
    @babel/helper-import-to-platform-api: ^7.24.6 => 7.24.6
    @babel/plugin-proposal-json-modules: ^7.24.6 => 7.24.6
    @babel/plugin-syntax-import-attributes: ^7.24.6 => 7.24.6
    eslint: ^8.57.0 => 8.57.0

---
Additionally, packemon's dependencies (

    "node_modules/packemon": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/packemon/-/packemon-4.1.1.tgz",
      "integrity": "sha512-HfT+CBM0LS+hMUS8ncvmtEz2G378AJuyBLnXWbJ0NxfkKXwKXEjHPVX64XoTDIhOupUYIiU5nH2LdOD1bi4ulw==",
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@babel/core": "^7.24.6",
        "@babel/plugin-external-helpers": "^7.24.6",
        "@babel/plugin-proposal-decorators": "^7.24.6",
        "@babel/plugin-proposal-json-modules": "^7.24.6",
        "@babel/plugin-syntax-import-attributes": "^7.24.6",
        "@babel/plugin-transform-runtime": "^7.24.6",
        "@babel/preset-env": "^7.24.6",
        "@babel/preset-flow": "^7.24.6",
        "@babel/preset-react": "^7.24.6",
        "@babel/preset-typescript": "^7.24.6",
        "@boost/cli": "^5.0.0",
        "@boost/common": "^5.0.0",
        "@boost/config": "^5.0.0",
        "@boost/debug": "^5.0.0",
        "@boost/event": "^5.0.0",
        "@boost/pipeline": "^5.0.0",
        "@boost/terminal": "^5.0.0",
        "@rollup/plugin-babel": "^6.0.4",
        "@rollup/plugin-commonjs": "^25.0.8",
        "@rollup/plugin-json": "^6.1.0",
        "@rollup/plugin-node-resolve": "^15.2.3",
        "@swc/core": "^1.5.7",
        "@swc/helpers": "^0.5.11",
        "babel-plugin-cjs-esm-interop": "^4.0.2",
        "babel-plugin-conditional-invariant": "^4.0.1",
        "babel-plugin-env-constants": "^4.0.1",
        "babel-preset-solid": "^1.8.17",
        "browserslist": "^4.23.0",
        "debug": "^4.3.4",
        "execa": "^9.1.0",
        "fast-glob": "^3.3.2",
        "filesize": "^10.1.2",
        "ink": "^5.0.0",
        "ink-progress-bar": "^3.0.0",
        "ink-spinner": "^5.0.0",
        "magic-string": "^0.30.10",
        "micromatch": "^4.0.7",
        "react": "^18.3.1",
        "resolve": "^1.22.8",
        "rollup": "^4.18.0",
        "rollup-plugin-node-externals": "^7.1.2",
        "rollup-plugin-polyfill-node": "^0.13.0",
        "semver": "^7.6.2",
        "spdx-license-list": "^6.9.0"
      },

### Possible solution

_No response_

### Additional context

This issue was previously noted and investigated at https://github.com/milesj/packemon/issues/251

Additionally, assigning a version string to `node` with a `patch` version will result in unexpected behavior.
```ts
import globals from "globals/globals.json" with {type: "json"}
```
```cjs
const globals = JSON.parse(node_fs.readFileSync(new URL(undefined("globals/globals.json"))));
// TypeError: undefined is not a function
```


Hints:
Hey @BinToss! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant [Slack community](https://babeljs.slack.com) that typically always has someone willing to help. You can sign-up [here](https://slack.babeljs.io/) for an invite.
What happens if you set `sourceType: "script"` rather than `unambiguous`? What I suspect is happening is that your input file is valid ESM, and Babel is interpreting it as ESM.
Oh yeah the REPL link you posted clearly shows that this is a Babel bug.
This is a hard problem: it's impossible to compile JSON imports to browser-compatible CommonJS, and we should probably just throw.
- We cannot compile it to `require("globals/globals.json")`, since the whole reason for the existence of `with { type: "json" }` is that it checks that the loaded file is JSON (and it should throw if, for example, it's a `globals/globals.json.js` file)
- It thus needs to be fetch-based, but the only way to have `fetch` at the top-level is with top-level await

In your specific case, you should _not_ compile ESM to CJS with Babel. Rollup works much better when it receives ESM code.
> This is a hard problem: it's impossible to compile JSON imports to browser-compatible CommonJS, and we should probably just throw.

Ah, right. Browsers prefer asynchronous code and using a browser's `fetch` would require wrapping it with `Promise.all` and exporting its promise. Lucky for me, I don't target browsers as my package's main export is a shareable configuration.

> In your specific case, you should not compile ESM to CJS with Babel. Rollup works much better when it receives ESM code.

Good news: I've been planning to switch my packages to ESM-only.

Bad news: Until I do that, I'll have to continue using the  Babel plugins present in the programmatically generated Babel config.
I use [Packemon](https://packemon.dev/docs/) for project management. When it's configured to target Node, it automatically adds `babel-plugin-cjs-esm-interop` to convert some ESM syntax to CJS (or vice versa, depending on source code). Most of the configuration is determined by a minimal configuration for Packemon configuration, which uses Rollup and @rollup/babel-plugin for building and packaging.

I'll investigate [babel-plugin-cjs-esm-interop](https://www.npmjs.com/package/babel-plugin-cjs-esm-interop) for the `undefined`-related issue I mentioned, but it's not documented as being able to transform `import.meta.resolve` calls...

Patch:
diff --git a/packages/babel-helper-import-to-platform-api/src/index.ts b/packages/babel-helper-import-to-platform-api/src/index.ts
--- a/packages/babel-helper-import-to-platform-api/src/index.ts
+++ b/packages/babel-helper-import-to-platform-api/src/index.ts
@@ -9,6 +9,7 @@ function imp(path: NodePath, name: string, module: string) {
 }
 
 export interface Pieces {
+  commonJS?: (require: t.Expression, specifier: t.Expression) => t.Expression;
   webFetch: (fetch: t.Expression) => t.Expression;
   nodeFsSync: (read: t.Expression) => t.Expression;
   nodeFsAsync: () => t.Expression;
@@ -39,6 +40,7 @@ export function importToPlatformApi(
     webSupportsIMR,
     nodeSupportsFsPromises,
   } = getSupport(targets);
+  const supportIsomorphicCJS = transformers.commonJS != null;
 
   let buildFetchAsync: (
     specifier: t.Expression,
@@ -48,21 +50,29 @@ export function importToPlatformApi(
 
   // "p" stands for pattern matching :)
   const p = ({
-    web: w,
-    node: n,
+    web: w = needsWebSupport,
+    node: n = needsNodeSupport,
     nodeFSP: nF = nodeSupportsFsPromises,
     webIMR: wI = webSupportsIMR,
     nodeIMR: nI = nodeSupportsIMR,
     toCJS: c = toCommonJS,
+    supportIsomorphicCJS: iC = supportIsomorphicCJS,
   }: {
-    web: boolean;
-    node: boolean;
+    web?: boolean;
+    node?: boolean;
     nodeFSP?: boolean;
     webIMR?: boolean;
     nodeIMR?: boolean;
     toCJS?: boolean;
-    preferSync?: boolean;
-  }) => +w + (+n << 1) + (+wI << 2) + (+nI << 3) + (+c << 4) + (+nF << 5);
+    supportIsomorphicCJS?: boolean;
+  }) =>
+    +w +
+    (+n << 1) +
+    (+wI << 2) +
+    (+nI << 3) +
+    (+c << 4) +
+    (+nF << 5) +
+    (+iC << 6);
 
   const readFileP = (fs: t.Expression, arg: t.Expression) => {
     if (nodeSupportsFsPromises) {
@@ -85,6 +95,10 @@ export function importToPlatformApi(
       toCJS: toCommonJS,
     })
   ) {
+    case p({ toCJS: true, supportIsomorphicCJS: true }):
+      buildFetchSync = specifier =>
+        transformers.commonJS(t.identifier("require"), specifier);
+      break;
     case p({ web: true, node: true }):
       buildFetchAsync = specifier => {
         const web = transformers.webFetch(
@@ -92,8 +106,15 @@ export function importToPlatformApi(
             (webSupportsIMR ? imr : imrWithFallback)(t.cloneNode(specifier)),
           ]),
         );
-        const node = nodeSupportsIMR
+        const node = supportIsomorphicCJS
           ? template.expression.ast`
+              import("module").then(module => ${transformers.commonJS(
+                template.expression.ast`module.createRequire(import.meta.url)`,
+                specifier,
+              )})
+            `
+          : nodeSupportsIMR
+            ? template.expression.ast`
                 import("fs").then(
                   fs => ${readFileP(
                     t.identifier("fs"),
@@ -101,7 +122,7 @@ export function importToPlatformApi(
                   )}
                 ).then(${transformers.nodeFsAsync()})
               `
-          : template.expression.ast`
+            : template.expression.ast`
                 Promise.all([import("fs"), import("module")])
                   .then(([fs, module]) =>
                     ${readFileP(
@@ -143,6 +164,20 @@ export function importToPlatformApi(
               .then(${transformers.nodeFsAsync()})
           `;
       break;
+    case p({
+      web: false,
+      node: true,
+      toCJS: false,
+      supportIsomorphicCJS: true,
+    }):
+      buildFetchSync = (specifier, path) =>
+        transformers.commonJS(
+          template.expression.ast`
+            ${imp(path, "createRequire", "module")}(import.meta.url)
+          `,
+          specifier,
+        );
+      break;
     case p({ web: false, node: true, toCJS: false, nodeIMR: true }):
       buildFetchSync = (specifier, path) =>
         transformers.nodeFsSync(template.expression.ast`
@@ -194,8 +229,21 @@ export function importToPlatformApi(
     }
   };
 
+  let buildFetch = buildFetchSync;
+  if (!buildFetchSync) {
+    if (toCommonJS) {
+      buildFetch = (specifier, path) => {
+        throw path.buildCodeFrameError(
+          "Cannot compile to CommonJS, since it would require top-level await.",
+        );
+      };
+    } else {
+      buildFetch = buildFetchAsync;
+    }
+  }
+
   return {
-    buildFetch: buildFetchSync || buildFetchAsync,
+    buildFetch,
     buildFetchAsync: buildFetchAsyncWrapped,
     needsAwait: !buildFetchSync,
   };
diff --git a/packages/babel-plugin-proposal-json-modules/src/index.ts b/packages/babel-plugin-proposal-json-modules/src/index.ts
--- a/packages/babel-plugin-proposal-json-modules/src/index.ts
+++ b/packages/babel-plugin-proposal-json-modules/src/index.ts
@@ -8,7 +8,11 @@ import {
   type Builders,
 } from "@babel/helper-import-to-platform-api";
 
-export default declare(api => {
+export interface Options {
+  uncheckedRequire: boolean;
+}
+
+export default declare((api, options: Options) => {
   const { types: t, template } = api;
   api.assertVersion(REQUIRED_VERSION("^7.22.0"));
 
@@ -18,6 +22,10 @@ export default declare(api => {
   let helperCJS: Builders;
 
   const transformers: Pieces = {
+    commonJS: options.uncheckedRequire
+      ? (require: t.Expression, specifier: t.Expression) =>
+          t.callExpression(require, [specifier])
+      : null,
     webFetch: (fetch: t.Expression) =>
       template.expression.ast`${fetch}.then(r => r.json())`,
     nodeFsSync: (read: t.Expression) =>
@@ -110,6 +118,7 @@ export default declare(api => {
           data.push({ id, fetch });
           decl.remove();
         }
+        if (data.length === 0) return;
 
         const decl = buildParallelStaticImports(data, helper.needsAwait);
         if (decl) path.unshiftContainer("body", decl);


Test patch:
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/input.js
@@ -0,0 +1,3 @@
+import j from "./x.json" with { type: "json" };
+someBody;
+import j2 from "./x2.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-multiple/options.json
@@ -0,0 +1,3 @@
+{
+  "throws": "Cannot compile to CommonJS, since it would require top-level await."
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/input.js
@@ -0,0 +1 @@
+import * as ns from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration-namespace/options.json
@@ -0,0 +1,3 @@
+{
+  "throws": "Cannot compile to CommonJS, since it would require top-level await."
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/input.js
@@ -0,0 +1 @@
+import j from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-declaration/options.json
@@ -0,0 +1,3 @@
+{
+  "throws": "Cannot compile to CommonJS, since it would require top-level await."
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/input.js
@@ -0,0 +1,2 @@
+// TODO: Not supported yet
+let promise = import("./x.json", { with: { type: "json" } });
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/output.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/output.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-expression/output.js
@@ -0,0 +1,8 @@
+"use strict";
+
+// TODO: Not supported yet
+let promise = import("./x.json", {
+  with: {
+    type: "json"
+  }
+});
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/input.js
@@ -0,0 +1 @@
+import j from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/import-meta-resolve-fallback/options.json
@@ -0,0 +1,4 @@
+{
+  "targets": { "firefox": "90" },
+  "throws": "Cannot compile to CommonJS, since it would require top-level await."
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/browser-cjs/options.json
@@ -0,0 +1,5 @@
+{
+  "sourceType": "module",
+  "targets": { "firefox": "110" },
+  "plugins": ["proposal-json-modules", "transform-modules-commonjs"]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/options.json
@@ -0,0 +1,8 @@
+{
+  "sourceType": "module",
+  "targets": { "firefox": "110", "node": "16" },
+  "plugins": [
+    ["proposal-json-modules", { "uncheckedRequire": true }],
+    "transform-modules-commonjs"
+  ]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/output.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/output.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-cjs/output.js
@@ -0,0 +1,3 @@
+"use strict";
+
+const x = require("./x.json");
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/options.json
@@ -0,0 +1,5 @@
+{
+  "sourceType": "module",
+  "targets": { "firefox": "110", "node": "16" },
+  "plugins": [["proposal-json-modules", { "uncheckedRequire": true }]]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/output.mjs b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/output.mjs
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-and-node-esm/output.mjs
@@ -0,0 +1 @@
+const x = await (typeof process === "object" && process.versions?.node ? import("module").then(module => module.createRequire(import.meta.url)("./x.json")) : fetch(import.meta.resolve("./x.json")).then(r => r.json()));
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/options.json
@@ -0,0 +1,8 @@
+{
+  "sourceType": "module",
+  "targets": { "firefox": "110" },
+  "plugins": [
+    ["proposal-json-modules", { "uncheckedRequire": true }],
+    "transform-modules-commonjs"
+  ]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/output.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/output.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-cjs/output.js
@@ -0,0 +1,3 @@
+"use strict";
+
+const x = require("./x.json");
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/options.json
@@ -0,0 +1,5 @@
+{
+  "sourceType": "module",
+  "targets": { "firefox": "110" },
+  "plugins": [["proposal-json-modules", { "uncheckedRequire": true }]]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/output.mjs b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/output.mjs
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/browser-esm/output.mjs
@@ -0,0 +1 @@
+const x = await fetch(import.meta.resolve("./x.json")).then(r => r.json());
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/options.json
@@ -0,0 +1,8 @@
+{
+  "sourceType": "module",
+  "targets": { "node": "16" },
+  "plugins": [
+    ["proposal-json-modules", { "uncheckedRequire": true }],
+    "transform-modules-commonjs"
+  ]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/output.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/output.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-cjs/output.js
@@ -0,0 +1,3 @@
+"use strict";
+
+const x = require("./x.json");
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/input.js b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/input.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/input.js
@@ -0,0 +1 @@
+import x from "./x.json" with { type: "json" };
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/options.json b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/options.json
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/options.json
@@ -0,0 +1,5 @@
+{
+  "sourceType": "module",
+  "targets": { "node": "16" },
+  "plugins": [["proposal-json-modules", { "uncheckedRequire": true }]]
+}
diff --git a/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/output.mjs b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/output.mjs
new file mode 100644
--- /dev/null
+++ b/packages/babel-plugin-proposal-json-modules/test/fixtures/uncheckedRequire/node-esm/output.mjs
@@ -0,0 +1,2 @@
+import { createRequire as _createRequire } from "module";
+const x = _createRequire(import.meta.url)("./x.json");