diff --git a/src/plugins/hydration.ts b/src/plugins/hydration.ts index 8cdeea5..9a2bbe2 100644 --- a/src/plugins/hydration.ts +++ b/src/plugins/hydration.ts @@ -1,7 +1,7 @@ import { genImport } from 'knitwork' import MagicString from 'magic-string' import { resolve } from 'node:path' -import { parseSync, type ImportDeclaration } from 'oxc-parser' +import { parseSync, type ImportDeclaration, type ImportDeclarationSpecifier, type ImportSpecifier } from 'oxc-parser' import { createUnplugin } from 'unplugin' import { distDir } from '../dirs' @@ -31,23 +31,31 @@ export const InjectHydrationPlugin = createUnplugin(() => { const hasDefineComponent = DEFINE_COMPONENT_RE.test(code) const hasDefineNuxtComponent = DEFINE_NUXT_COMPONENT_RE.test(code) - const defineComponentImport = findImportSpecifier(imports as ImportDeclaration[], 'defineComponent', ['vue', '#imports']) + const defineComponentImport = findImportSpecifier( + imports, + 'defineComponent', + ['vue', '#imports'], + (specifier, nextSpecifier) => { + m.remove( + specifier.start, + nextSpecifier?.start ?? specifier.end, + ) + }, + ) const defineComponentAlias = defineComponentImport?.local.name || 'defineComponent' - if (defineComponentImport) { - m.remove( - defineComponentImport.start, - defineComponentImport.end, - ) - } - const defineNuxtComponentImport = findImportSpecifier(imports as ImportDeclaration[], 'defineNuxtComponent', ['#app/composables/component', '#imports', '#app', 'nuxt/app']) + const defineNuxtComponentImport = findImportSpecifier( + imports, + 'defineNuxtComponent', + ['#app/composables/component', '#imports', '#app', 'nuxt/app'], + (specifier, next) => { + m.remove( + specifier.start, + next?.start ?? specifier.end, + ) + }, + ) const defineNuxtComponentAlias = defineNuxtComponentImport?.local.name || 'defineNuxtComponent' - if (defineNuxtComponentImport) { - m.remove( - defineNuxtComponentImport.start, - defineNuxtComponentImport.end, - ) - } const importsToAdd = new Set([ hasDefineComponent @@ -121,11 +129,24 @@ export const InjectHydrationPlugin = createUnplugin(() => { /** * Finds an import specifier for a given imported name from specified package names. */ -function findImportSpecifier(importDecl: ImportDeclaration[], importedName: string, pkgNames: string | string[]) { +function findImportSpecifier( + importDecl: ImportDeclaration[], + importedName: string, + pkgNames: string | string[], + callback?: (specifier: ImportSpecifier, nextSpecifier?: ImportDeclarationSpecifier) => void, +) { const names = Array.isArray(pkgNames) ? pkgNames : [pkgNames] - return importDecl.find(imp => names.includes(imp.source.value))?.specifiers.find((specifier) => { - return specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier' && specifier.imported.name === importedName - }) + 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 + } + } } function normalizePath(path: string) { diff --git a/test/unit/hydration/vite-plugin.test.ts b/test/unit/hydration/vite-plugin.test.ts index 458e00f..1cb3b49 100644 --- a/test/unit/hydration/vite-plugin.test.ts +++ b/test/unit/hydration/vite-plugin.test.ts @@ -92,6 +92,13 @@ describe('InjectHydrationPlugin', () => { expect(result.code).not.toContain('import { defineNuxtComponent } from \'#imports\'') }) + it('should replace defineComponent with correct offsets', async () => { + const code = `import { useModel, defineComponent, createVNode } from '#imports'\n${exportDefineComponent}` + const result = await modifyImportPluginTransform(code, 'test.ts') + expect(result.code).toContain(importDefineComponent.trim()) + expect(result.code).toContain('import { useModel, createVNode } from \'#imports\'') + }) + it('should inject import if defineComponent is used but not imported', async () => { const code = `${exportDefineComponent}` const result = await modifyImportPluginTransform(code, 'test.ts')