From 158c4bb056417828833088508b43e6917d65d23f Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 9 Jun 2023 17:48:06 +0200 Subject: [PATCH] fix(mocker): respect namespace import when hoisting vi.mock (#3547) --- packages/vitest/src/node/hoistMocks.ts | 26 +++++++++++++++++--------- test/core/test/injector-mock.test.ts | 13 +++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/vitest/src/node/hoistMocks.ts b/packages/vitest/src/node/hoistMocks.ts index a3d9a2aed391..515329bfa4e1 100644 --- a/packages/vitest/src/node/hoistMocks.ts +++ b/packages/vitest/src/node/hoistMocks.ts @@ -1,5 +1,5 @@ import MagicString from 'magic-string' -import type { CallExpression, Identifier, ImportDeclaration, VariableDeclaration, Node as _Node } from 'estree' +import type { CallExpression, Identifier, ImportDeclaration, ImportNamespaceSpecifier, VariableDeclaration, Node as _Node } from 'estree' import { findNodeAround, simple as simpleWalk } from 'acorn-walk' import type { AcornNode } from 'rollup' @@ -24,11 +24,6 @@ function isIdentifier(node: any): node is Positioned { } function transformImportSpecifiers(node: ImportDeclaration) { - const specifiers = node.specifiers - - if (specifiers.length === 1 && specifiers[0].type === 'ImportNamespaceSpecifier') - return specifiers[0].local.name - const dynamicImports = node.specifiers.map((specifier) => { if (specifier.type === 'ImportDefaultSpecifier') return `default: ${specifier.local.name}` @@ -86,12 +81,25 @@ export function hoistMocks(code: string, id: string, parse: (code: string, optio const transformImportDeclaration = (node: ImportDeclaration) => { const source = node.source.value as string + const namespace = node.specifiers.find(specifier => specifier.type === 'ImportNamespaceSpecifier') as ImportNamespaceSpecifier | undefined + + let code = '' + if (namespace) + code += `const ${namespace.local.name} = await import('${source}')\n` + // if we don't hijack ESM and process this file, then we definetly have mocks, // so we need to transform imports into dynamic ones, so "vi.mock" can be executed before const specifiers = transformImportSpecifiers(node) - const code = specifiers - ? `const ${specifiers} = await import('${source}')\n` - : `await import('${source}')\n` + + if (specifiers) { + if (namespace) + code += `const ${specifiers} = ${namespace.local.name}\n` + else + code += `const ${specifiers} = await import('${source}')\n` + } + else if (!namespace) { + code += `await import('${source}')\n` + } return code } diff --git a/test/core/test/injector-mock.test.ts b/test/core/test/injector-mock.test.ts index 2ee3fb32a4e0..a1764c42c5ff 100644 --- a/test/core/test/injector-mock.test.ts +++ b/test/core/test/injector-mock.test.ts @@ -58,3 +58,16 @@ test('always hoists all imports but they are under mocks', () => { const { someValue2 } = await import('./path2.js')" `) }) + +test('correctly mocks namespaced', () => { + expect(hoistSimpleCode(` + import { vi } from 'vitest' + import add, * as AddModule from '../src/add' + vi.mock('../src/add', () => {}) + `)).toMatchInlineSnapshot(` + "const { vi } = await import('vitest') + vi.mock('../src/add', () => {}) + const AddModule = await import('../src/add') + const { default: add } = AddModule" + `) +})