diff --git a/packages/ptc-runtime/ptc-runtime-node/src/imports/vm-modules.ts b/packages/ptc-runtime/ptc-runtime-node/src/imports/vm-modules.ts index 059699439d..2c54b0a943 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/imports/vm-modules.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/imports/vm-modules.ts @@ -1,5 +1,6 @@ /** Adapt runner-local import decisions to Node's VM module callbacks. */ import * as vm from 'node:vm' +import { types } from 'node:util' import type { ImportAttributes } from 'node:module' import type { ProgramImportHandler } from '../evaluators/types.ts' @@ -56,7 +57,10 @@ export class VmModuleImportLoader { nativeImport ??= vm.compileFunction('return import(specifier, { with: attributes })', ['specifier', 'attributes'], { filename, importModuleDynamically: vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER, }) as NativeImport - return nativeImport(specifier, attributes) + const value = await nativeImport(specifier, attributes) + if (types.isModuleNamespaceObject(value)) return value + // A native then export can settle to any value; the VM callback still requires a module. + return this.module({ then(resolve: (result: unknown) => void) { resolve(value) } }) } // Node accepts namespace objects as well as vm.Module; the Node types declare only vm.Module. return load as vm.DynamicModuleLoader diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/imports.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/imports.spec.ts index 40e9317135..06b88f2ae9 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/imports.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/imports.spec.ts @@ -125,6 +125,14 @@ it.each(formats)('preserves JavaScript then-export assimilation in %s', async (f else expect(await e.expression('import("runner:then")')).toBe(42) }) +it.each(['default', 'redirect'] as const)('preserves native then-export results through Script %s loading', async (decision) => { + const specifier = 'data:text/javascript,export function then(resolve) { resolve({ answer() { return 42 } }) }' + const handler = vi.fn((): ProgramImportResult => decision === 'default' ? { type: 'default' } : { type: 'redirect', specifier }) + const e = evaluator('script', handler) + expect(await e.expression(`(await import(${JSON.stringify(decision === 'default' ? specifier : 'runner:then')})).answer()`)).toBe(42) + expect(handler).toHaveBeenCalledTimes(1) +}) + it('keeps async-function grammar and parameter semantics when imports are intercepted', async () => { const e = evaluator('function-body', () => ({ type: 'default' })) e.globals.answer = 42