Files
5c9d8551c6 fix(turbopack): external module shouldn't wrap by esm when type as global (#82374)
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.

---------

Co-authored-by: Niklas Mischkulnig <[email protected]>
2025-08-06 11:28:28 -07:00

69 lines
1.8 KiB
TypeScript

/// <reference path="./types.d.ts" />
const jest = require('jest-circus')
const expectMod = require('expect')
function setupGlobals() {
globalThis.describe = jest.describe
globalThis.it = jest.it
globalThis.test = jest.test
// @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, {
value: 'Module',
})
return obj
}
}
const uncaughtExceptions: string[] = []
const unhandledRejections: string[] = []
process.on('uncaughtException', (e) => {
uncaughtExceptions.push(String(e))
})
process.on('unhandledRejection', (e) => {
unhandledRejections.push(String(e))
})
export default async function run() {
setupGlobals()
await import('TESTS')
let jestResult = await jest.run()
// Jest test results can contain references to arbitrary objects.
// Defensively remove circular references to avoid breaking our serialization protocol.
const seen = new Set()
jestResult = JSON.parse(
JSON.stringify(jestResult, (k, v) => {
if (v != null && typeof v === 'object') {
if (!seen.has(v)) {
seen.add(v)
return v
} else {
return 'CIRCULAR_REFERENCE_REMOVED'
}
}
return v
})
)
// Wait a full tick for unhandledRejection handlers to run -- a microtask is not sufficient.
await new Promise((resolve) => setTimeout(resolve, 0))
return {
jestResult,
uncaughtExceptions,
unhandledRejections,
}
}