Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions src/plugins/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[],
Expand All @@ -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
}
}

Expand Down
19 changes: 19 additions & 0 deletions test/unit/hydration/vite-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Loading