unplugin / unplugin-vue-components

📲 On-demand components auto importing for Vue
https://www.npmjs.com/package/unplugin-vue-components
MIT License
3.77k stars 349 forks source link

Components are resolved in dev mode but not build mode (with VitePress) #801

Open dwgray opened 2 weeks ago

dwgray commented 2 weeks ago

Describe the bug

I'm working on a documentation site using VitePress and a custom theme. I'd like to use unplugin-vue-components to automatically register both the components from the library I'm documenting and the samples and helper components in the documentation build. I have successfully set this up so that it works in dev mode, but it is silently failing in build mode.

I've tracked this down to an issue with using <script setup lang='ts'>

The link to the repro below shows the failing behavior using a simple VitePress site and unplugin-vue-components

Components are not resolved if:

Components are resolved if:

When debugging the broken behavior, I find that the transform method of the unplugin-vue-component plugin is not seeing the code that it can transform (the code it gets doesn't have any instances of _resolveComponent* in it - see my 9/20 update below for details). So the issue may be upstream from unplugin-vue-component, but I'm not familiar enough with VitePress or Vue.js SSR to have an idea if they are the culprits.

Expected: image

Broken (not the missing "this is just testing if a component is resolved" and the styling on the alert): image


History, in case it helps track this down.

I'm working on a documentation site using VitePress and a custom theme. I'd like to use unplugin-vue-components to automatically register both the components from the library I'm documenting and the samples and helper components in the documentation build. I have successfully set this up so that it works in dev mode, but it is silently failing in build mode.

I have this working for another site that uses Vite directly (not VitePress) without issue. Is there something extra that needs to be done to get rollup to recognize the plugins? Or is it possible that the relative paths are different for rollup? If so is there a way to diagnose this? I'm not seeing either VitePress or Rollup providing a verbose flag, but maybe there is something else to help me drill down?

  vite: {
    plugins: [
      Icons(),
      Components({
        globs: ['components/*.vue', 'docs/**/demo/*.vue'],
        dts: true,
        include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
        resolvers: [BootstrapVueNextResolver()],
      }),
    ],
  },

Full file: https://github.com/bootstrap-vue-next/bootstrap-vue-next/blob/main/apps/docs/.vitepress/config.mts

Running pnpm dev renders all of the components and examples. Running pnpm build followed by pnpm preview does not render the components, it just leave the tags in place. e.g. there is a ComponentSidebar component that is resolved in dev mode but fails in build mode: https://bootstrap-vue-next.github.io/bootstrap-vue-next/docs/components/breadcrumb.html

image

Update: Sorry, this probably should have been a discussion rather than a bug report, but I don't think I can move it at this point.

I am working on a minimal repo. I started out by creating a vanilla VitePress site and then started adding the things we're using. I am at the point where I would expect the issue to reproduce - the configuration for my Component() in Vite Plugins is identical, and I'm consuming custom autogenerated components and bootstrap-vue-next components, but the issue doesn't reproduce. https://github.com/dwgray/vitepress-unplugin

When I have cycles for this again, I'll continue working towards an actual minimal repro, but I'm hoping that someone can point me to how to diagnose this kind of problem (e.g., verbose mode at some layer in this stack or some other way to debug that rollup is reading my configuration and acting on it).

Update 9/20/24:

I am still unable to get this down to a simple repro despite significant effort. However, I was able to get a debugger attached and find some interesting behavior that I suspect is relevant. The code that is being passed to the transform hook is radically different between the working and the broken case. I think this may indicate something going on with how SSR is working in VitePress, but I'm pretty new to this toolset, so that's a pretty wild guess.

Here's the line I'm looking at: https://github.com/unplugin/unplugin-vue-components/blob/22a8364a57ccd21b34e131667429320e10c0eba6/src/core/unplugin.ts#L44

For one of my affected files breadcrumbs.md all calls have an id of C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md.

In the broken (build) case, I'm seeing two calls.

1st call:

import _sfc_main from "C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md?vue&type=script&setup=true&lang.ts";
export * from "C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md?vue&type=script&setup=true&lang.ts";
export default _sfc_main;

2nd call:

import _sfc_main from "C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md?vue&type=script&setup=true&lang.ts";
export * from "C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md?vue&type=script&setup=true&lang.ts";
import { useSSRContext as __vite_useSSRContext } from "vue";
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
  const ssrContext = __vite_useSSRContext();
  (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("docs/components/breadcrumb.md");
  return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
export default _sfc_main;

In the working (dev and rollup for my simple non-repro) case, I'm seeing one call:

import { defineComponent as _defineComponent } from "vue";
import { data } from "../../data/components/breadcrumb.data";
export const __pageData = JSON.parse(\'{"title":"Breadcrumb","description":"","frontmatter":{},"headers":[],"relativePath":"docs/components/breadcrumb.md","filePath":"docs/components/breadcrumb.md"}\');
const __default__ = { name: "docs/components/breadcrumb.md" };
const _sfc_main = /* @__PURE__ */ _defineComponent({
  ...__default__,
  setup(__props, { expose: __expose }) {
    __expose();
    const __returned__ = { __pageData, get data() {
      return data;
    } };
    Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
    return __returned__;
  }
});
import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue";
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_ComponentSidebar = _resolveComponent("ComponentSidebar");
  const _component_BreadcrumbOverview = _resolveComponent("BreadcrumbOverview");
  const _component_HighlightCard = _resolveComponent("HighlightCard");
  const _component_BreadcrumbAsArray = _resolveComponent("BreadcrumbAsArray");
  const _component_BreadcrumbManual = _resolveComponent("BreadcrumbManual");
  const _component_BreadcrumbSlots = _resolveComponent("BreadcrumbSlots");
  const _component_ComponentReference = _resolveComponent("ComponentReference");
  return _openBlock(), _createElementBlock("div", null, [
  ...
  ]);
}
_sfc_main.__hmrId = "e1842bb7";
typeof __VUE_HMR_RUNTIME__ !== "undefined" && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main);
import.meta.hot.accept((mod) => {
  if (!mod) return;
  const { default: updated, _rerender_only } = mod;
  if (_rerender_only) {
    __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render);
  } else {
    __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated);
  }
});
import _export_sfc from "\\0plugin-vue:export-helper";
export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__file", "C:/Projects/bootstrap-vue-next/apps/docs/src/docs/components/breadcrumb.md"]]);

It's pretty obvious why unplugin-vue-components isn't doing anything in the broken case - there are no component references to resolve! But that means something in the build process isn't giving you the file code actually contains the _resolveComponent* references. So I'm pretty sure this is the problem, I just can't find anything that could be causing vite/vitepress/rollup to behave differently in the broken case than the working case.

working-unplugin.txt

Thanks in advance for any help or direction on this.

Reproduction

https://github.com/bootstrap-vue-next/bootstrap-vue-next/blob/main/apps/docs/.vitepress/config.mts

System Info

System:
    OS: Windows 11 10.0.22631
    CPU: (16) x64 Intel(R) Core(TM) i9-10885H CPU @ 2.40GHz
    Memory: 8.66 GB / 31.75 GB
  Binaries:
    Node: 20.16.0 - C:\Program Files\nodejs\node.EXE
    npm: 10.8.1 - C:\Program Files\nodejs\npm.CMD
    pnpm: 9.5.0 - C:\Program Files\nodejs\pnpm.CMD
  Browsers:
    Edge: Chromium (127.0.2651.74)
    Internet Explorer: 11.0.22621.3527

Used Package Manager

pnpm

Validations