language-tools icon indicating copy to clipboard operation
language-tools copied to clipboard

Is it possible to isolate custom blocks from the rest of the SFC?

Open UfukUstali opened this issue 1 year ago • 0 comments

I am currently working on a PR for a nuxt module called server-block-nuxt by @Hebilicious which will get multiple server blocks inside of the SFC to work.

<server lang="ts">
import db from "db";

export const GET = defineEventHandler(() => {
//            ^? Cannot redeclare block-scoped variable 'GET'. ts(2451)
  return db.query("hello");
});
</server>

<server lang="ts">
export const GET = defineEventHandler(() => {
//            ^? Cannot redeclare block-scoped variable 'GET'. ts(2451)
  return { message: "world" };
});
</server>

<script lang="ts" setup>
const { data } = await useFetch("/api/about");
const GET = "GET";
//     ^? Cannot redeclare block-scoped variable 'GET'. ts(2451)
db.query("hello")
//^? does exist as a type but at runtime it isn't here
</script>

<template>
  <div>
    <p>{{ GET }}</p>
  </div>
</template>

But if you look at the above example I am faced with a typescript error and the error is not just limited to custom blocks because it appears just the same in script setup.

Another problem is if you look at the top server block we import db from "db" but this import is also made available inside of the script setup only as a type as the server blocks are removed by a vite plugins transform option during run/build-time

There is already a VueLanguagePlugin required to get auto-imports and types to work inside the custom blocks if the desired functionality can be achieved through a plugin it is also a possibility but I couldn't find any documentation on plugin authoring.

This is the current plugin.

import type { VueLanguagePlugin } from "@vue/language-core"

const plugin: VueLanguagePlugin = () => {
  return {
    name: "sfc-server-volar",
    version: 1,
    resolveEmbeddedFile(fileName, sfc, embeddedFile) {
      if (embeddedFile.fileName.replace(fileName, "").match(/^\.(js|ts|jsx|tsx)$/)) {
        for (const block of sfc.customBlocks) {
          const content = embeddedFile.content
          if (block.type === "server") {
            content.push([
              block.content, // text to add
              block.name, // source
              0, // content offset in source
              {
                // language capabilities to enable in this segment
                hover: true,
                references: true,
                definition: true,
                diagnostic: true,
                rename: true,
                completion: true,
                semanticTokens: true
              }
            ])
          }
        }
      }
    }
  }
}

export default plugin

My current plan is to manipulate the block.content inside of the plugin before pushing it to embededFile.content like so:

Given:

import db from "db";
export const GET = defineEventHandler(() => {
  return db.query("hello");
});
export const POST = defineEventHandler(() => {
  return db.query("goodbye");
});

Modified:

import db from "db"; // 2. hoist the imports out of the block
{ // 1. put the declarations inside of a block to isolate them
  const GET = defineEventHandler(() => {
// ^? 3. delete export string
    return db.query("hello");
  });
  const POST = defineEventHandler(() => {
// ^? 3. delete export string
    return db.query("goodbye");
  });
}

This wouldn't however solve the issue of imports of one block being also avalible inside of the others but just the "Cannot redeclare block-scoped variable 'GET'. ts(2451)". Which I am actually fine with since there is nothing sensitive being leaked, we only lose the type safety to some degree.

Conclusion:

If the above proposed solution is an acceptable one I will go through with it for the time being.

And I would like to request an official way of enabling/disabling this kind of behaviour inside of the plugin api to address the problems that could not be solved by simply manipulating the content. If there is one and I am not aware of it I would like to learn about it.

UfukUstali avatar Nov 29 '23 19:11 UfukUstali