Skip to content

vueDirectives: directive imports are skipped after an earlier transform leaves a directive unresolved #578

Open

Description

@NgoQuocViet2001

Environment

  • unimport 7.0.2 (same code on main at bbd0776)
  • @vue/compiler-sfc 3.5.42
  • Node 24

Reproduction

import { compileTemplate } from '@vue/compiler-sfc'
import { createUnimport } from 'unimport'

const { code } = compileTemplate({
  id: 'a.vue',
  filename: 'a.vue',
  source: '<div v-awesome-directive v-missing></div>',
  compilerOptions: { hoistStatic: false },
})

const ctx = createUnimport({
  presets: [{
    from: './directives/awesome-directive.ts',
    imports: [{ name: 'default', meta: { vueDirective: true } }],
  }],
  addons: { vueDirectives: true },
})

const first = (await ctx.injectImports(code, 'a.vue')).code
const second = (await ctx.injectImports(code, 'a.vue')).code
console.log(first === second) // false

Describe the bug

Expected: both calls return the same code, with import _directive_awesome_directive from './directives/awesome-directive.ts' and no _resolveDirective("awesome-directive") line.

Actual: the second call returns the input unchanged, so const _directive_awesome_directive = _resolveDirective("awesome-directive") stays and Vue cannot resolve the directive.

A different file transformed afterwards can end up worse. With a second preset { from: './directives/focus.ts', imports: [{ name: 'vFocus', meta: { vueDirective: true } }] }, transforming <div v-awesome-directive v-focus></div> after the file above gives:

import { vFocus as _directive_focus } from './directives/focus.ts';import { withDirectives as _withDirectives, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache) {
  const _directive_awesome_directive = _resolveDirective("awesome-directive")
  ...

resolveDirective is no longer imported but is still called, so rendering throws _resolveDirective is not defined.

RE_DIRECTIVE in src/addons/vue-directives.ts has the g flag, and the "any directive left?" check uses RE_DIRECTIVE.test(s.toString()) (it was .match() before 62a9ede). test() leaves lastIndex set, and the next s.original.matchAll(RE_DIRECTIVE) starts from that offset.

Additional context

The current inline snapshots for "single named directive" and "single mixed directives" in test/vue-directives.test.ts record this output (they changed in 429d84a). Running vitest run test/vue-directives.test.ts -t "single named directive" on its own fails its snapshot because the import is then injected correctly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions