vuejs / vitepress

Vite & Vue powered static site generator.
https://vitepress.dev
MIT License
11.48k stars 1.86k forks source link

Build fails due to custom directive missing an SSR Transform #92

Closed VinceG closed 1 year ago

VinceG commented 3 years ago

Describe the bug

Adding a simple directive inside a markdown file or an SFC throws an error during the build. Seems like ClientOnly component does not exist yet in VitePress.

Custom directive is missing corresponding SSR transform and will be ignored.

To Reproduce

<div v-test>test</div>

<script>
export default {
  directives: {
    test: {
      mounted(el, binding) {
        console.log(el)
      }
    }
  }
}
</script>

Expected behavior Build should work even though an SSR implementation for the custom directive doesn't exist.

System Info

Additional context None.

kiaking commented 3 years ago

Hi could you elaborate a bit more on this use case? Why would you paste such code into md file?

VinceG commented 3 years ago

@kiaking Of Course. I have the following component written in Vue 3 https://github.com/VinceG/vue-click-away

As part of the component i wrote the documentation in vitepress, if you look at the Gif you'll see the example usage and demo within vitepress. I am trying to deploy it into gh-pages (or anything else really) and while running the production build it fails with the above error message.

The markdown file for the demo looks like this:

https://raw.githubusercontent.com/VinceG/vue-click-away/master/docs/index.md

But the actual code that is failing is located here: https://github.com/VinceG/vue-click-away/blob/master/index.js

I don't think the markdown file has anything to do with it, vitepress is not able to build for production when there are directives in the codebase. I gave a simple reproduction case above to replicate this issue with a new vitepress setup.

I hope this helps.

EDIT: in Vuepress we have component that probably solves this issue, vitepress however, does not have it.

kiaking commented 3 years ago

Oh I see. I haven't checked, but does VuePress support this feature...!? I think you should register that component, and then use it inside the markdown.

// .vitepress/theme/index.js

import DefaultTheme from 'vitepress/dist/client/theme-default'
import VueClickAwayExample from './components/VueClickAwayExample.vue'

export default {
  ...DefaultTheme,
  enhanceApp({ app }) {
    app.component('VueClickAwayExample', VueClickAwayExample)
  }
}
<!-- index.md -->

# Vue Click Away

<VueClickAwayExample />
VinceG commented 3 years ago

@kiaking Hmm, well using it isn't the problem. it's building for production. once i run yarn build it throws the above error

kiaking commented 3 years ago

Thanks for the answer! I've confirmed. Yap, it is failing on build time due to the error you described.

I'm not too familiar with the SSR with directives so I have to learn things before we can fix this 😅

yyx990803 commented 3 years ago

This is not really a bug, since you do need to provide the corresponding directive transforms to ensure you are rendering correct HTML during SSR. However some directives like click away don't really render any props, so it makes sense to change this to a warning instead of an error in @vue/compiler-ssr.

That said, a client only wrapper component is fairly simple:

import { ref, onMounted } from 'vue'
const ClientOnly = {
  setup(_, { slots }) {
    const show = ref(false)
    onMounted(() => { show.value = true })
    return () => show.value && slots.default ? slots.default() : null
  }
}
VinceG commented 3 years ago

This is not really a bug, since you do need to provide the corresponding directive transforms to ensure you are rendering correct HTML during SSR. However some directives like click away don't really render any props, so it makes sense to change this to a warning instead of an error in @vue/compiler-ssr.

That said, a client only wrapper component is fairly simple:

import { ref, onMounted } from 'vue'
const ClientOnly = {
  setup(_, { slots }) {
    const show = ref(false)
    onMounted(() => { show.value = true })
    return () => show.value && slots.default ? slots.default() : null
  }
}

@yyx990803 thank you. Any idea how to write those directive transforms? I wasn’t able to find any documentation regarding this.

Also, are there plans to add client only component for vitepress like the equivalent in vuepress?

yyx990803 commented 3 years ago

v3 SSR docs are not fully complete yet, so for now you'll have to read the types here: https://github.com/vuejs/vue-next/blob/master/packages/compiler-core/src/transform.ts#L56

ClientOnly is implemented in 8809d2d and will be available in the next release.

kiaking commented 3 years ago

OK cool, since ClientOnly component is here, maybe we should just document to use it when you have component that uses directive option.

Tahul commented 3 years ago

If anyone lands here by looking on how to update his directive to make it SSR compatible (which is my case), I advise you to take a look at the native directives code from vue-next repository.

Starting at v-show, which is here: https://github.com/vuejs/vue-next/blob/master/packages/runtime-dom/src/directives/vShow.ts

Tahul commented 3 years ago

Update:

Even after implementing the getSSRProps on my own directive, I can't get the directive to work with VitePress.

I still get the Custom directive is missing corresponding SSR transform and will be ignored. error.

The same happens when wrapping my directive with a <ClientOnly /> component, which makes me think this might be a bug ?

Even when trying to add an absolute return value in the getSSRProps hook, Vitepress does not seem to handle it.

I tried it this way, so I could debug when the hook is called, and it doesn't seem to be the case.

getSSRProps({ value }) {
  return { style: { backgroundColor: 'blue' } }
}

Maybe this is a wrong implementation on my side, but I think this is closely related to this issue.

If you want me to open another issue for this discussion, please let me know.

Otherwise we might want to switch this back to a bug because the <ClientOnly /> workaround should stay a workaround and not the encouraged method to fix this when you have access to the directive code.

daief commented 3 years ago

Update:

Even after implementing the getSSRProps on my own directive, I can't get the directive to work with VitePress.

I still get the Custom directive is missing corresponding SSR transform and will be ignored. error.

The same happens when wrapping my directive with a <ClientOnly /> component, which makes me think this might be a bug ?

Even when trying to add an absolute return value in the getSSRProps hook, Vitepress does not seem to handle it.

I tried it this way, so I could debug when the hook is called, and it doesn't seem to be the case.

getSSRProps({ value }) {
  return { style: { backgroundColor: 'blue' } }
}

Maybe this is a wrong implementation on my side, but I think this is closely related to this issue.

If you want me to open another issue for this discussion, please let me know.

Otherwise we might want to switch this back to a bug because the <ClientOnly /> workaround should stay a workaround and not the encouraged method to fix this when you have access to the directive code.

Because there should be a directive transforms.

According to this, I find the way to provide the directive transforms to resolve this error.

We can pass directiveTransforms option via vue-loader.

// webpack config
module.exports = {
  module: {
    // ...
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: "vue-loader",
            options: {
              compilerOptions: {
                directiveTransforms: {
                  // <div v-custom-directive />
                  "custom-directive": () => ({
                    props: [],
                  }),
                },
              },
            },
          },
        ],
      },
      // ...
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

Or pass it via compile function.

import { compile } from "@vue/compiler-dom";

const { code } = compile(`<p v-custom-directive></p>`, {
  // ...
  directiveTransforms: {
    "custom-directive": () => ({
      props: [],
    }),
  }, 
});

I got the same error when I use renderToString of @vue/server-renderer with a custom directive and I am not familiar with vitepress.

Hope this helps you. @Tahul

maxerbox commented 3 years ago

For vite, see https://github.com/vuejs/vue-next/issues/3298#issuecomment-785687683

emerxom commented 2 years ago

I don't know what I am doing wrong but using the ClientOnly don't fix the build problem for me. I had to remove all custom directives for it to build

antfu commented 2 years ago

A temporary fix:

https://github.com/vueuse/patch-vue-directive-ssr

emerxom commented 2 years ago

A temporary fix:

https://github.com/vueuse/patch-vue-directive-ssr

thanks @antfu It worked... It was a mistake I made that prevent it from working before.

murilolivorato commented 2 years ago

I cant understand how vue directives works in VUE 3 . It has 3 weeks that I am trying a solution for that . @yyx990803 help us =),

I didn't found a documentation about that . A SPA DIRECTIVE IS LIKE THAT -

  app.directive('color', {
    mounted (el, binding) {
      el.style.color = 'purple'
    }
  })

HOW COULD I DO IT IN SSR ? -

export default (dir) => {
  return {
    props: []
  }
}

I don't understand how can I access this props

productdevbook commented 2 years ago

A temporary fix:

https://github.com/vueuse/patch-vue-directive-ssr

thank you fixed. I hope there is a permanent solution.

drewcook commented 2 years ago

This is still an annoying bug. For reference, this is a bug in vuepress@2.0.0-beta.27 as well, which isn't the latest version at time of posting this, but is definitely newer than these comments and original post here. As in a lot of front end development, we have to install a separate patch package as a workaround, which is great, but is not ideal.

Is this issue going to be fixed or on the roadmap at all?

brc-dd commented 1 year ago

This is not reproducible in VitePress alpha with the below code:

# Hello World

<div v-test>test</div>

<script setup>
const vTest = {
  mounted: (el) => {
    console.log(el)
  }
}
</script>

I am closing this issue for now. If you think that there is still an issue, please let us know.

mrweiner commented 1 year ago

I'm aware that this is closed, but does https://github.com/vueuse/patch-vue-directive-ssr still work for anybody? I've run into this error a few times with userland packages (such as https://github.com/PNKBizz/vue-mutation-observer) and it doesn't seem to help.

brc-dd commented 1 year ago

@mrweiner I don't think that mutation-observer package supports vue@3