Search code examples
javascriptwebpackvuejs3vue-sfcvue-script-setup

How to define components with "require.context" in "<script setup>"?


How can I add components with require.context using ? The rfc for script setup shows this example. In the example, each component has to be imported.

In the Options API I used this code to collect all the components from a particular folder:

function getComponents() {
  const context = require.context('../icons', true, /Icon[A-Z]\w+\.vue$/)

  const res = {
    components: {},
    namesForValidator: [],
  }
  for (const key of context.keys()) {
    const match = key.match(/\/(\w+)\.\w+$/)
    if (match && match[1]) {
      const name = kebabCase(match[1])
      res.components[name] = context(key).default
      res.namesForValidator.push(name)
    }
  }
  return res
}

Then I add the resulting components object to the options object passed to defineComponent.

const { components, namesForValidator } = getIconsComponents()

export default defineComponent({
  components,

  // ...
})

All this works fine. But in the it's not clear to me how to make it work. Maybe there is (or is planned) some undocumented defineComponets compiler macros that would solve this problem?


Solution

  • Solved this problem with the code below.

    <template>
      <component :is="icon" />
    </template>
    
    <script lang="ts" setup>
    import { kebabCase } from '@/helpers'
    import { computed, toRefs } from 'vue'
    import type { DefineComponent } from 'vue'
    
    export interface IconComponentsList {
      components: Record<string, DefineComponent>
    }
    
    function getIconsComponents(): IconComponentsList {
      const context = require.context('../icons', true, /Icon[A-Z]\w+\.vue$/)
    
      const res = {
        components: {} as Record<string, DefineComponent>,
      }
      for (const key of context.keys()) {
        const match = key.match(/\/(\w+)\.\w+$/)
        if (match && match[1] && typeof match[1] === 'string') {
          const name = kebabCase(match[1].replace('Icon', ''))
          res.components[name] = context(key).default
        }
      }
      return res
    }
    
    const { components }: IconComponentsList = getIconsComponents()
    
    const { name } = toRefs(defineProps({
      name: {
        type: String,
        required: true,
      },
    }))
    
    const icon = computed(() => {
      return components[name]
    })
    </script>