Repository: vercel/next.js
Pull request: fix(turbopack): external module shouldn't wrap by esm when type as global

When use externalType: `global` or `script` under utoopack(turbopack only use esm or cjs external): like externalType is global , and the external package is react.

The generate external module code like:

```ts
263: ((__turbopack_context__) => {
"use strict";

const mod = globalThis["react"];

__turbopack_context__.n(mod);
}),
```

The real function of runtime code `__turbopack_context__.n`  is `exportNamespace`, its code as follows:

```ts
module.exports = module.namespaceObject = namespace
```

And when we import the module as following:

```ts
import react from "react";

console.log(react.version);
```

it will be bundled as:

```ts
var __TURBOPACK__imported__module__263__ = __turbopack_context__.i(263);

console.log(__TURBOPACK__imported__module__263__["default"]);
```

The `__turbopack_context__i` is esmImport, it will return the module when the module contains `namespaceObject` without `interopEsm`:

```ts
function esmImport() {
  const module = getOrInstantiateModuleFromParent(id, this.m)

  // any ES module has to have `module.namespaceObject` defined.
  if (module.namespaceObject) return module.namespaceObject

  return interopEsm();
}
```

So the following case if we use a  react umd module(which doesn't have a `default export`) as our external package, code `console.log(__TURBOPACK__imported__module__263__["default"]);` will output `undefined`(which webpack can output the right version).

I just change export wrapper the externalType: global and script here(which now only consumed by utoopack), so the esmImport will execute `interopEsm` function here to make our code work fine. 

We test the case under utoopack and here is our pr: https://github.com/umijs/mako/pull/2097/files.  


Changed files:
filename: turbopack/crates/turbopack-ecmascript/src/references/external_module.rs
patch: @@ -28,8 +28,8 @@ use crate::{
     },
     references::async_module::{AsyncModule, OptionAsyncModule},
     runtime_functions::{
-        TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_EXTERNAL_IMPORT, TURBOPACK_EXTERNAL_REQUIRE,
-        TURBOPACK_LOAD_BY_URL,
+        TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_EXPORT_VALUE, TURBOPACK_EXTERNAL_IMPORT,
+        TURBOPACK_EXTERNAL_REQUIRE, TURBOPACK_LOAD_BY_URL,
     },
     utils::StringifyJs,
 };
@@ -193,8 +193,12 @@ impl CachedExternalModule {
 
         if self.external_type == CachedExternalType::CommonJs {
             writeln!(code, "module.exports = mod;")?;
-        } else {
+        } else if self.external_type == CachedExternalType::EcmaScriptViaImport
+            || self.external_type == CachedExternalType::EcmaScriptViaRequire
+        {
             writeln!(code, "{TURBOPACK_EXPORT_NAMESPACE}(mod);")?;
+        } else {
+            writeln!(code, "{TURBOPACK_EXPORT_VALUE}(mod);")?;
         }
 
         Ok(EcmascriptModuleContent {

filename: turbopack/crates/turbopack-tests/js/jest-entry.ts
patch: @@ -10,6 +10,10 @@ function setupGlobals() {
   // @ts-ignore Property 'expect' does not exist on type 'typeof globalThis'
   globalThis.expect = expectMod.expect
 
+  // Set up global external values for testing
+  // @ts-ignore Property 'testGlobalExternalValue' does not exist on type 'typeof globalThis'
+  globalThis.testGlobalExternalValue = { bar: '11' }
+
   // From https://github.com/webpack/webpack/blob/9fcaa243573005d6fdece9a3f8d89a0e8b399613/test/TestCases.template.js#L422
   globalThis.nsObj = function nsObj(obj) {
     Object.defineProperty(obj, Symbol.toStringTag, {

filename: turbopack/crates/turbopack-tests/tests/execution.rs
patch: @@ -377,6 +377,11 @@ async fn run_test_operation(prepared_test: ResolvedVc<PreparedTest>) -> Result<V
         ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Untraced)
             .resolved_cell(),
     );
+    import_map.insert_exact_alias(
+        rcstr!("testGlobalExternalValue"),
+        ImportMapping::External(None, ExternalType::Global, ExternalTraced::Untraced)
+            .resolved_cell(),
+    );
 
     let remove_unused_exports = options.remove_unused_exports.unwrap_or(true);
 

filename: turbopack/crates/turbopack-tests/tests/execution/turbopack/basic/global-external/input/index.js
patch: @@ -0,0 +1,6 @@
+import foo from 'testGlobalExternalValue'
+
+it('should access global external values', () => {
+  expect(foo).toEqual({ bar: '11' })
+  expect(foo.bar).toBe('11')
+})