Subscribe on changes!

script-setup dismisses local component starting with lowercase in dev

avatar
Aug 18th 2021

Version

3.2.1

Reproduction link

https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuICA8c3R5bGVkLWxvZ2luIC8XG48L3RlbXBsYXRlPlxuXG48c2NyaXB0IHNldHVwPlxuICBpbXBvcnQgc3R5bGVkTG9naW4gZnJvbSBcIi4vQ29tcC52dWVcIlxuPC9zY3JpcHQIiwiQ29tcC52dWUiOiI8dGVtcGxhdGUXG5IRUxMT1xuPC90ZW1wbGF0ZT4ifQ==

Steps to reproduce

This can't be demonstrated in SFC playground because it only happens in dev-mode, not build-mode.

  1. In script-setup, import a local component starting with a lowercase, e.g. styledLogin.
  2. Use <styled-login> in template.
  3. Test the page in dev mode (not build)

For reference, here's the case I've used locally:

<template>
<styled-login value="test" />
</template>

<script setup lang="ts">
  import { styledLogin } from "@/Components/Formatters/user";
</script>

What is expected?

Works

What is actually happening?

styled-login is undefined.

In dev this happens because it's been dropped and was not included in $setup.

My local test case is compiled to this:

import { createHotContext as __vite__createHotContext } from "/@vite/client";import.meta.hot = __vite__createHotContext("/Modules/Dev/bug.vue");import { defineComponent as _defineComponent } from "/node_modules/.vite/vue.js?v=c5083fb6";
const _sfc_main = _defineComponent({
  setup(__props, { expose }) {
    expose();
// ----- NOTICE: styledLogin is not included in $setup
    const __returned__ = {};
    Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
    return __returned__;
  }
});

import { openBlock as _openBlock, createBlock as _createBlock } from "/node_modules/.vite/vue.js?v=c5083fb6"

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
// ------ FAILS because $setup["styledLogin"] is undefined
  return (_openBlock(), _createBlock($setup["styledLogin"], { value: "test" }))
}

import block0 from "/Modules/Dev/bug.vue?vue&type=route&index=0&lang.route"
if (typeof block0 === 'function') block0(_sfc_main)

_sfc_main.render = _sfc_render
_sfc_main.__file = "bug.vue"
_sfc_main.__hmrId = "f489aed8"
typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main)
export const _rerender_only = true
import.meta.hot.accept(({ default: updated, _rerender_only }) => {
  if (_rerender_only) {
    __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render)
  } else {
    __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated)
  }
})
export default _sfc_main

This can be worked-around by Pascal-casing the imported component: StyledLogin works.

Codegen for build is different and doesn't have the problem, here's what the SFC explorer generates:

/* Analyzed bindings: {
  "styledLogin": "setup-const"
} */
import { openBlock as _openBlock, createBlock as _createBlock } from "vue"

import styledLogin from "./Comp.vue"

const __sfc__ = {
  setup(__props) {

  
return (_ctx, _cache) => {
  return (_openBlock(), _createBlock(styledLogin))
}
}

}
__sfc__.__file = "App.vue"
export default __sfc__