vuetifyjs / vuetify

🐉 Vue Component Framework
https://vuetifyjs.com
MIT License
39.94k stars 6.97k forks source link

[Feature Request] Volar Tags Support for Vuetify 2 #14798

Open douglasg14b opened 2 years ago

douglasg14b commented 2 years ago

Problem to solve

Currently with volar there are no tags for Vuetify, it seems to be suggested that this is the responsibility of the framework to provide these tags. Or the user to make sure that they have included every framework tag into a global components definitions, which seems not-ideal.

Proposed solution

I'm not entirely sure what actually needs to be done here?

The jist of it would be adding support so that all the Vuetify tags & bits show up in volar? Or a copy/paste block that users user.

Relevant:

https://github.com/johnsoncodehk/volar/issues/418

KaelWD commented 2 years ago

This was added in c3c98ba1d3fc347680e2362ca7384a942f0b4dd1

douglasg14b commented 2 years ago

@KaelWD Do you have a docs link that describes how this is supposed to work for v2? What release version is this in? I see there is a yarn command, but if we are not using yarn does that matter? Is this supposed to "just work"? If so, it does not appear to be.

That's great that this has already been handled, but not so much if how to utilize it is unknown.

Googlers will end up in this issue, having a description here is very helpful to the community that ends up at this point, as opposed to it being a dead end.

douglasg14b commented 2 years ago

Actually, looking again, this seems to only be for V3?

If so, this issue should be re-opened, as this is for V2 & Vue2.

KaelWD commented 2 years ago

Yes this is only v3, for v2 you need to use vetur instead.

kingyue737 commented 2 years ago

Yes this is only v3, for v2 you need to use vetur instead.

Vetur is no longer supported and there are many bugs left. Is it possible to also support volar in v2?

douglasg14b commented 2 years ago

Yes this is only v3, for v2 you need to use vetur instead.

Volar supports Vue 2, and Vetur is turning out to be a bit of a mess to use... Especially with typescript, and newer Vue 2 features. In addition to Pinia which also works on Vue 2.

Eventually Vue2 shops will be using Volar for Vue 2, and dropping Vetur.

KaelWD commented 2 years ago

possible to also support volar in v2

I don't know, volar requires shimming @vue/runtime-core which is vue 3 only. We also don't have typescript definitions for v2 components which would be a fair bit of work.

douglasg14b commented 2 years ago

@KaelWD

What would the process look like for this, and what would be required?

There are already some sort of integrated docs for V2, can those be transformed in some manner?

Or would this just be a slog through each component?

I asked because it may be possible for myself or a spare FE in my workplace to dedicate time to solving this problem if it's laid out and clear.


Can this issue be reopened? I'll modify the title to make it clear that this is a request for V2.

KaelWD commented 2 years ago

Vetur and web-types are generated here: https://github.com/vuetifyjs/vuetify/blob/91c135be/packages/api-generator/src/export.js

You may be able to do something similar to generate a .d.ts as well, I don't know how you'd get that to work in volar though judging by your comment in https://github.com/johnsoncodehk/volar/issues/418

douglasg14b commented 2 years ago

Thanks! I'm not entirely sure what that code does yet, but I'm sure I'll sort something out when circling back around.

As for this working or not with Volar in vue 2, I'll be testing more there, and circle back in that repo if this wasn't just an issue with what I was trying.

Request:

kingyue737 commented 2 years ago
  • Do you have a copy/paste example of the Vuetify 3 data interfaces output that can be used as a reference? This will be quite helpful.

https://unpkg.com/browse/vuetify@3.0.0-alpha.13/lib/index.d.ts

douglasg14b commented 2 years ago

Ah I see, just the import statements. After some more reading it may be the entire defineComponent({}) may have to be done for each component before the declaration of the GlobalComponents bit.

I imagine this could be generated to some degree using the existing stuff 🤔 I'll have to try later.

kingyue737 commented 2 years ago

I write a script to generate type declaration from web types. It works like a charm for me. Now volar can provide intellisense in template for vuetify2.

// ./scripts/vuetify-type.mjs

import { readFileSync } from 'fs'
const webTypes = JSON.parse(
  readFileSync('./node_modules/vuetify/dist/json/web-types.json')
)

const blackList = ['VFlex', 'VLayout'] // Components not to define in global

function convertType(typeStr) {
  switch (typeStr) {
    case 'array':
      return 'any[]'
    case 'function':
      return 'Function'
    case 'date':
      return 'Date'
    case 'regexp':
      return 'RegExp'
    default:
      return typeStr
  }
}

function getType(attrType) {
  if (typeof attrType === 'string') {
    return convertType(attrType)
  } else {
    return attrType.map((str) => convertType(str)).join('|')
  }
}

function getDescription(obj) {
  return obj.description ? `/** ${obj.description} */\n` : ''
}

function getSlotPropType(type) {
  return type
    .replaceAll('eventName', 'eventName: string')
    .replaceAll(':"', ':')
    .replaceAll('",', ',')
    .replaceAll('"}', '}')
    .replace(/\/\/.*/, '')
    .replaceAll('):', ')=>')
    .replace(/(aria-[a-z]*):/g, '"$1":')
    .replaceAll('Function', '(...args: any[]) => any')
    .replaceAll('object', '{ [key: keyof any]: any }')
}

function getSlotName(name) {
  if (name === 'header.<name>') {
    return '[name:`header.${string}`]'
  } else if (name === 'item.<name>') {
    return '[name:`item.${string}`]'
  }
  return `'${name}'`
}

const types = webTypes.contributions.html.tags
  .map((vm) =>
    !blackList.includes(vm.name)
      ? vm.name +
        ': DefineComponent<{' +
        vm.attributes
          .map(
            (attr) =>
              getDescription(attr) +
              `${attr.name.replace(/-./g, (x) =>
                x[1].toUpperCase()
              )}?: ${getType(attr.value.type)}`
          )
          .join('\n') +
        '}' +
        (vm.slots?.length
          ? ',{$scopedSlots: Readonly<{\n' +
            vm.slots
              .map(
                (slot) =>
                  getDescription(slot) +
                  `${getSlotName(slot.name)}:${
                    slot['vue-properties']
                      ? '(args: {' +
                        slot['vue-properties']
                          .map(
                            (prop) =>
                              prop.name + ':' + getSlotPropType(prop.type)
                          )
                          .join('\n') +
                        '}) => VNode[]'
                      : 'undefined'
                  }`
              )
              .join('\n') +
            '}>}\n'
          : '') +
        '>'
      : ''
  )
  .join('\n')

console.log(`
import type { DefineComponent, VNode } from '@vue/runtime-dom'
import type { DataTableHeader, DataOptions } from 'vuetify'
type eventHandler = Function

declare module '@vue/runtime-dom' {
  export interface GlobalComponents {
    ${types}
  }
}

export {}`
)

Output: image

Volar: image

You can add this script in package.json and run it once the web-types of vuetify 2 is updated.

"scripts": {
  /* ... */
  "type:vuetify": "node ./scripts/vuetify-type.mjs > ./src/vuetify2.d.ts"
},
npm run type:vuetify
kingyue737 commented 2 years ago

Hey @dschreij, I've updated my script. Now we can get types and intellisense of slots in Vuetify 2 components. Before running this script, Vuetify needs to be updated to v2.6.9 released today which fixed some errors in web-types #15472 #15478 #15480

Screenshot 2022-08-10 212442

For detailed settings, you can refer to my Vue 2.7 starter template https://github.com/kingyue737/vitify-admin

dschreij commented 2 years ago

@kingyue737 thanks for the update! I just ran your script and it works perfectly. Types for Vuetify all over the place :)

ascott18 commented 1 year ago

I packaged up @kingyue737's script and its output, along with a few corrections for presumably recent changes in Vuetify since 2.6.9, to make it easier to use.

NPM: https://www.npmjs.com/package/vuetify2-component-types GH: https://github.com/IntelliTect/vuetify2-component-types

GabrielHangor commented 1 year ago

Hey @dschreij, I've updated my script. Now we can get types and intellisense of slots in Vuetify 2 components. Before running this script, Vuetify needs to be updated to v2.6.9 released today which fixed some errors in web-types #15472 #15478 #15480

Screenshot 2022-08-10 212442

For detailed settings, you can refer to my Vue 2.7 starter template https://github.com/kingyue737/vitify-admin

Unfortunately the script is not working for me.... Throwing an error that type.replaceAll is not a function Any ideas why it might happen? I've tried it with vue 2.7 and Vuetify 2.6.15

GabrielHangor commented 1 year ago

Ok, putting this block in try/catch actually helped. Got all the types now :) Great!

function getSlotPropType(type) {
  try {
    return type
      .replaceAll('eventName', 'eventName: string')
      .replaceAll(':"', ':')
      .replaceAll('",', ',')
      .replaceAll('"}', '}')
      .replace(/\/\/.*/, '')
      .replaceAll('):', ')=>')
      .replace(/(aria-[a-z]*):/g, '"$1":')
      .replaceAll('Function', '(...args: any[]) => any')
      .replaceAll('object', '{ [key: keyof any]: any }');
  } catch (e) {
    console.log(e);
  }
}
johnleider commented 1 year ago

This is something that we would still like to do but will not be completed before the v2.7 release. Due to the nature of the issue, once resolved, we can simply release as part of a regular PATCH.

kingyue737 commented 1 year ago

I've updated the script to include types of emits of Vuetify2 components. Besides props and slots, now we can have autocompletion and type-check of v-on directives and event handlers in SFC template.

vuetify-event

You can also add the latest vuetify2-component-types to try this feature.

pnpm add vuetify2-component-types -D
GabrielHangor commented 4 months ago

After updating to the new version of Volar (Vue - Official 2.0.24), it is no longer working for me... Vue 2..7.16

MartinX3 commented 4 months ago

Vue 2 is end of life since 2024-01-01.

Vuetify 2 recieves only fixes for security vulnerabilities and critical bugs since 2023-07-05.

kingyue737 commented 4 months ago

@GabrielHangor I'm also using the latest vue extension (Vue - Official 2.0.24). It works fine image

Can you provide a minimal reproduction

GabrielHangor commented 4 months ago

@kingyue737 Sure, what info is required?

When switching to Vue Official 1.8.27 (not the old Volar) it is working as expected. I guess it can also be related to the fact that our codebase has lot's of class components. Components declared via defineComponent and setup works fine...