diff --git a/src/plugins/hydration.ts b/src/plugins/hydration.ts index 061be3cd..ab8ccd1a 100644 --- a/src/plugins/hydration.ts +++ b/src/plugins/hydration.ts @@ -127,6 +127,13 @@ export const InjectHydrationPlugin = createUnplugin(() => { /** * Finds an import specifier for a given imported name from specified package names. + * Searches through all matching import declarations since there can be multiple imports from the same package. + * + * Like + * ```ts + * import { ref } from 'vue' + * import { defineComponent } from 'vue' + * ``` */ function findImportSpecifier( importDecl: ImportDeclaration[], @@ -135,16 +142,18 @@ function findImportSpecifier( callback?: (specifier: ImportSpecifier, nextSpecifier?: ImportDeclarationSpecifier) => void, ) { const names = Array.isArray(pkgNames) ? pkgNames : [pkgNames] - const decl = importDecl.find(imp => names.includes(imp.source.value)) - if (!decl) { - return - } - for (let i = 0; i < decl.specifiers.length; i++) { - const specifier = decl.specifiers[i]! - if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier' && specifier.imported.name === importedName) { - callback?.(specifier, decl.specifiers[i + 1]) - return specifier - } + + const allSpecifiers = importDecl + .filter(imp => names.includes(imp.source.value)) + .flatMap(decl => decl.specifiers.map((spec, i) => ({ spec, next: decl.specifiers[i + 1] }))) + + const match = allSpecifiers.find(({ spec }) => + spec.type === 'ImportSpecifier' && spec.imported.type === 'Identifier' && spec.imported.name === importedName, + ) + + if (match) { + callback?.(match.spec as ImportSpecifier, match.next) + return match.spec as ImportSpecifier } } diff --git a/test/unit/hydration/vite-plugin.test.ts b/test/unit/hydration/vite-plugin.test.ts index 1cb3b49a..a3bf396f 100644 --- a/test/unit/hydration/vite-plugin.test.ts +++ b/test/unit/hydration/vite-plugin.test.ts @@ -130,6 +130,25 @@ describe('InjectHydrationPlugin', () => { )) expect(result.code).not.toContain(`import { defineNuxtComponent as _defineNuxtComponent } from '#app'`) }) + + it('should find defineComponent when split across multiple import declarations', async () => { + const code = `import { ref } from 'vue'\nimport { defineComponent } from 'vue'\n${exportDefineComponent}` + const result = await modifyImportPluginTransform(code, 'test.ts') + expect(result.code).toContain(importDefineComponent.trim()) + expect(result.code).not.toContain(`import { defineComponent } from 'vue'`) + expect(result.code).toContain(`import { ref } from 'vue'`) + }) + + it('should find aliased defineComponent when split across multiple import declarations', async () => { + const code = `import { ref } from 'vue'\nimport { defineComponent as _defineComponent } from 'vue'\nexport default _defineComponent({})` + const result = await modifyImportPluginTransform(code, 'test.ts') + expect(result.code).toContain(genImport( + '@nuxt/hints/runtime/hydration/component', + [{ name: 'defineComponent', as: '_defineComponent' }], + )) + expect(result.code).not.toContain(`import { defineComponent as _defineComponent } from 'vue'`) + expect(result.code).toContain(`import { ref } from 'vue'`) + }) }) describe('inject-hydration-composable', () => {