volarjs / services

MIT License
134 stars 29 forks source link

✨ FR: Json schema for custom block #10

Closed kingyue737 closed 1 year ago

kingyue737 commented 2 years ago

Some popular libraries like vue-i18n, unplugin-vue-router, vite-plugin-pages and @nuxtjs/router-extras allow to define a custom json block in SFC, eg. <route>, <i18n>. With support of json schema, users can profit auto-completion or validation in those custom blocks.

Author of volar thinks it's easy to implement😀

kingyue737 commented 1 year ago

Hi @johnsoncodehk , I'm learning how to write a volar plugin. I try to specify a Json schema for a specific custom block:

/** @type {import('@volar/vue-language-core').VueLanguagePlugin} */

const schemaUrl = 'https://json.schemastore.org/prettierrc.json'
const blockType = 'route'

const plugin = () => {
  return {
    name: 'example-volar-plugin-json-schema',
    version: 1,

    getEmbeddedFileNames(fileName, sfc) {
      const names = []
      for (let i = 0; i < sfc.customBlocks.length; i++) {
        const customBlock = sfc.customBlocks[i]
        names.push(
          fileName +
            '.customBlock_' +
            customBlock.type +
            '_' +
            i +
            '.' +
            customBlock.lang
        )
      }
      return names
    },

    resolveEmbeddedFile(fileName, sfc, embeddedFile) {
      if (embeddedFile.fileName.replace(fileName, '').match(/^\.(jsx|tsx)$/)) {
        for (const block of sfc.customBlocks) {
          if (block.type === blockType) {
            try {
              block.content = block.content.replace(
                '{',
                `{"$schema":"${schemaUrl}",`
              )
            } catch {
              /* continue regardless of error */
            }
          }
        }
      }
      const match = embeddedFile.fileName.match(
        /^(.*)\.customBlock_([^_]+)_(\d+)\.([^.]+)$/
      )
      if (match) {
        embeddedFile.content[0][2] = -`"$schema":"${schemaUrl}",`.length
      }
    },
  }
}
module.exports = plugin

I don't know if my thinking is correct. It seems that autocompletion, hover and validation, they all work!

image

But when I hover on a property, tip shows twice:

image

The second problem is that the relative path of Json schema in a SFC custom block is related to root of the disk instead of workspace: image

Maybe we need to implement resolveRelativePath like https://github.com/microsoft/vscode-json-languageservice/blob/e73082b16fe2c7c09ac24a35956efab1e88c699b/src/test/jsonSchemaTestSuite.test.ts#L28 in the built-in json plugin of volar?

Any guidance would be appreciated!

johnsoncodehk commented 1 year ago

Hi @kingyue737, you don't need to use VueLanguagePlugin, please try https://github.com/johnsoncodehk/volar-plugins/tree/master/packages/vue-json.