Skip to content

Conversation

@Teages
Copy link
Contributor

@Teages Teages commented Jan 18, 2025

resolves #281

Transpile vue template if the file have a typescript script block

@codecov
Copy link

codecov bot commented Jan 18, 2025

Codecov Report

Attention: Patch coverage is 90.67358% with 18 lines in your changes missing coverage. Please review.

Project coverage is 84.67%. Comparing base (9000888) to head (4e84ca9).
Report is 101 commits behind head on main.

Files with missing lines Patch % Lines
src/loaders/vue.ts 80.00% 10 Missing ⚠️
src/utils/vue.ts 94.40% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #289      +/-   ##
==========================================
+ Coverage   82.86%   84.67%   +1.81%     
==========================================
  Files          12       13       +1     
  Lines         852     1103     +251     
  Branches      133      235     +102     
==========================================
+ Hits          706      934     +228     
- Misses        144      167      +23     
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Teages Teages changed the title fix: transpile vue template fix(vue): transpile vue template Jan 18, 2025
@Teages Teages marked this pull request as ready for review January 18, 2025 12:58
@danielroe
Copy link
Member

i wonder if this will also fix a bug i haven't yet had time to report here

(can be reproduced in nuxt/image repo)

it was compiling nuxtimg component to reference properties in the template that were not exposed from setup

@Teages
Copy link
Contributor Author

Teages commented Jan 18, 2025

i wonder if this will also fix a bug i haven't yet had time to report here

(can be reproduced in nuxt/image repo)

I built it with @nuxt/[email protected] and I don't see any unusual.

The pr just remove ts syntax from vue template so it should has no effect to this.

it was compiling nuxtimg component to reference properties in the template that were not exposed from setup

Vue expose all props and variables defined in <script setup> to template by default, so it should be fine, or maybe I misunderstood what you meant.

Copy link
Member

you can replicate by running pnpm i -Dw @nuxt/[email protected] && pnpm dev:prepare && pnpm build && pnpm vitest run image

@Teages
Copy link
Contributor Author

Teages commented Jan 19, 2025

you can replicate by running pnpm i -Dw @nuxt/[email protected] && pnpm dev:prepare && pnpm build && pnpm vitest run image

I replicated it, minimal reproduction vue playground

<script>
import { defineComponent } from "vue";
export default defineComponent({
  setup() {
    const isServer = true;
    const __returned__ = { isServer };
    Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
    return __returned__;
  }
});
</script>

<template>
  {{ isServer || 'nothing here' }}
</template>

once I removed __isScriptSetup it works.

Then I found the description about __isScriptSetup: compileScript.ts

in non-inline mode, the __isScriptSetup: true flag is used by componentPublicInstance proxy to allow properties that start with $ or _

This is weird, according to the comments it should expose more stuff. But later I found this:

const hasSetupBinding = (state: Data, key: string) =>
  state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key)

It looks like we also need to compile the template and get these variables in $setup

@Teages
Copy link
Contributor Author

Teages commented Jan 19, 2025

Now it seems a bit of a dilemma:

  • $setup didn't expose to template (it only exist in render function), so we have to transpile vue template to render function, which defeats the purpose of mkdist. (Maybe this is acceptable?)
  • we can make our templater to transfer <script setup> only rather than merging them, but that is quite complex, as we need to deal with Reactive Props Destructure.

@Teages
Copy link
Contributor Author

Teages commented Jan 19, 2025

now <template> will be transpile into setup() as a render function so that it can visit those variables exposed from setup.

for files with no <script setup lang="ts">, we just remove ts syntax from <template>

As for point 2 in #289 (comment), I think make our own transpiler requires thousands lines of code to handling edge cases, it would be better to make it in upstream if we really want.

Tested with nuxt/image, now it passed all test.

build log
➜ nuxt-image git:(main) ✗ pnpm why mkdist
Legend: production dependency, optional only, dev only

@nuxt/[email protected] <root>\nuxt-image

devDependencies:
@nuxt/module-builder 1.0.0-alpha.1
└─┬ unbuild 3.3.1
  └── mkdist link:../unjs-mkdist
unbuild 3.3.1
└── mkdist link:../unjs-mkdist
➜ nuxt-image git:(main) pnpm dev:prepare; pnpm build; pnpm vitest run image

> @nuxt/[email protected] dev:prepare <root>\nuxt-image
> nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare docs

ℹ Stubbing image                                                                                                                                                                                                                                                                 01:22:13
ℹ Cleaning dist directory: ./dist                                                                                                                                                                                                                                                01:22:13
✔ Types generated in .nuxt                                                                                                                                                                                                                                                  nuxi 01:22:16
ℹ Using default Tailwind CSS file                                                                                                                                                                                                                               nuxt:tailwindcss 01:22:20
ℹ Nuxt Icon server bundle mode is set to local                                                                                                                                                                                                                                   01:22:26
✔ Nuxt Icon discovered local-installed 8 collections: carbon, heroicons, logos, ph, ri, simple-icons, tabler, vscode-icons                                                                                                                                                       01:22:27
✔ Types generated in docs/.nuxt                                                                                                                                                                                                                                             nuxi 01:22:29

> @nuxt/[email protected] build <root>\nuxt-image
> nuxt-module-build build

ℹ Building image                                                                                                                                                                                                                                                                 01:22:30
ℹ Cleaning dist directory: ./dist                                                                                                                                                                                                                                                01:22:30
✔ Build succeeded for image                                                                                                                                                                                                                                                      01:22:40
  dist/runtime (total size: 104 kB)                                                                                                                                                                                                                                               01:22:40
  └─ dist/runtime/components/NuxtImg.vue.d.ts (6.47 kB)
  └─ dist/runtime/composables.d.ts (85 B)
  └─ dist/runtime/utils/index.d.ts (1.23 kB)
  └─ dist/runtime/providers/aliyun.d.ts (102 B)
  └─ dist/runtime/providers/prepr/formatter.d.ts (71 B)
  └─ dist/runtime/composables.js (438 B)
  └─ dist/runtime/image.js (7.14 kB)
  └─ dist/runtime/image.d.ts (140 B)
  └─ dist/runtime/index.js (62 B)
  └─ dist/runtime/index.d.ts (62 B)
  └─ dist/runtime/ipx.d.ts (115 B)
  └─ dist/runtime/plugin.d.ts (159 B)
  └─ dist/runtime/plugin.js (158 B)
  └─ dist/runtime/ipx.js (1.02 kB)
  └─ dist/runtime/utils/index.js (3.47 kB)
  └─ dist/runtime/components/NuxtPicture.vue.d.ts (5.38 kB)
  └─ dist/runtime/components/NuxtImg.vue (5.22 kB)
  └─ dist/runtime/components/NuxtPicture.vue (5.25 kB)
  └─ dist/runtime/components/_base.d.ts (8.05 kB)
  └─ dist/runtime/components/_base.js (2.94 kB)
  └─ dist/runtime/utils/performance.d.ts (409 B)
  └─ dist/runtime/utils/meta.js (1.18 kB)
  └─ dist/runtime/utils/meta.d.ts (144 B)
  └─ dist/runtime/utils/performance.js (149 B)
  └─ dist/runtime/providers/aliyun.js (1.17 kB)
  └─ dist/runtime/providers/awsAmplify.d.ts (249 B)
  └─ dist/runtime/utils/prerender.d.ts (84 B)
  └─ dist/runtime/utils/prerender.js (503 B)
  └─ dist/runtime/providers/bunny.d.ts (204 B)
  └─ dist/runtime/providers/awsAmplify.js (2.24 kB)
  └─ dist/runtime/providers/caisy.d.ts (204 B)
  └─ dist/runtime/providers/cloudflare.js (962 B)
  └─ dist/runtime/providers/bunny.js (826 B)
  └─ dist/runtime/providers/cloudflare.d.ts (106 B)
  └─ dist/runtime/providers/cloudinary.d.ts (106 B)
  └─ dist/runtime/providers/cloudimage.d.ts (106 B)
  └─ dist/runtime/providers/caisy.js (474 B)
  └─ dist/runtime/providers/cloudimage.js (1.3 kB)
  └─ dist/runtime/providers/contentful.d.ts (204 B)
  └─ dist/runtime/providers/contentful.js (890 B)
  └─ dist/runtime/providers/directus.js (720 B)
  └─ dist/runtime/providers/cloudinary.js (2.78 kB)
  └─ dist/runtime/providers/directus.d.ts (204 B)
  └─ dist/runtime/providers/edgio.d.ts (106 B)
  └─ dist/runtime/providers/edgio.js (575 B)
  └─ dist/runtime/providers/fastly.d.ts (106 B)
  └─ dist/runtime/providers/glide.d.ts (106 B)
  └─ dist/runtime/providers/gumlet.d.ts (204 B)
  └─ dist/runtime/providers/fastly.js (572 B)
  └─ dist/runtime/providers/gumlet.js (1.83 kB)
  └─ dist/runtime/providers/glide.js (1.15 kB)
  └─ dist/runtime/providers/imageengine.js (1.06 kB)
  └─ dist/runtime/providers/hygraph.js (1.97 kB)
  └─ dist/runtime/providers/hygraph.d.ts (102 B)
  └─ dist/runtime/providers/imgix.d.ts (204 B)
  └─ dist/runtime/providers/imagekit.js (2.57 kB)
  └─ dist/runtime/providers/imageengine.d.ts (204 B)
  └─ dist/runtime/providers/imagekit.d.ts (106 B)
  └─ dist/runtime/providers/ipx.js (915 B)
  └─ dist/runtime/providers/imgix.js (4.62 kB)
  └─ dist/runtime/providers/ipx.d.ts (194 B)
  └─ dist/runtime/providers/ipxStatic.d.ts (26 B)
  └─ dist/runtime/providers/ipxStatic.js (26 B)
  └─ dist/runtime/providers/layer0.js (28 B)
  └─ dist/runtime/providers/layer0.d.ts (28 B)
  └─ dist/runtime/providers/netlifyImageCdn.d.ts (204 B)
  └─ dist/runtime/providers/netlifyImageCdn.js (1.12 kB)
  └─ dist/runtime/providers/none.js (44 B)
  └─ dist/runtime/providers/none.d.ts (106 B)
  └─ dist/runtime/providers/netlifyLargeMedia.d.ts (204 B)
  └─ dist/runtime/providers/netlifyLargeMedia.js (1.2 kB)
  └─ dist/runtime/providers/sanity.d.ts (106 B)
  └─ dist/runtime/providers/prismic.d.ts (170 B)
  └─ dist/runtime/providers/prismic.js (559 B)
  └─ dist/runtime/providers/sanity.js (2.81 kB)
  └─ dist/runtime/providers/sirv.d.ts (204 B)
  └─ dist/runtime/providers/sirv.js (3.39 kB)
  └─ dist/runtime/providers/storyblok.d.ts (106 B)
  └─ dist/runtime/providers/strapi5.d.ts (106 B)
  └─ dist/runtime/providers/strapi.js (408 B)
  └─ dist/runtime/providers/storyblok.js (993 B)
  └─ dist/runtime/providers/strapi.d.ts (151 B)
  └─ dist/runtime/providers/twicpics.js (1.61 kB)
  └─ dist/runtime/providers/strapi5.js (822 B)
  └─ dist/runtime/providers/unsplash.d.ts (173 B)
  └─ dist/runtime/providers/twicpics.d.ts (106 B)
  └─ dist/runtime/providers/vercel.d.ts (151 B)
  └─ dist/runtime/providers/uploadcare.js (1.31 kB)
  └─ dist/runtime/providers/unsplash.js (388 B)
  └─ dist/runtime/providers/uploadcare.d.ts (524 B)
  └─ dist/runtime/providers/prepr/formatter.js (103 B)
  └─ dist/runtime/providers/prepr/getImage.d.ts (331 B)
  └─ dist/runtime/providers/wagtail.d.ts (106 B)
  └─ dist/runtime/providers/vercel.js (1.14 kB)
  └─ dist/runtime/providers/wagtail.js (846 B)
  └─ dist/runtime/providers/weserv.js (2.27 kB)
  └─ dist/runtime/providers/weserv.d.ts (391 B)
  └─ dist/runtime/providers/prepr/index.d.ts (31 B)
  └─ dist/runtime/providers/prepr/index.js (31 B)
  └─ dist/runtime/providers/prepr/keyMap.d.ts (499 B)
  └─ dist/runtime/providers/prepr/getImage.js (865 B)
  └─ dist/runtime/providers/prepr/keyMap.js (116 B)
  └─ dist/runtime/providers/prepr/valueMap.d.ts (1.14 kB)
  └─ dist/runtime/providers/prepr/valueMap.js (173 B)
  dist/module.mjs (total size: 10.3 kB, chunk size: 10.3 kB, exports: default)                                                                                                                                                                                                    01:22:40  

Σ Total dist size (byte size): 123 kB
                                                                                                                                                                                                                                                                                  01:22:40  

 RUN  v2.1.8 <root>/nuxt-image

 ✓ test/unit/image.test.ts (23)
 ✓ test/unit/use-image.test.ts (4)

 Test Files  2 passed (2)
      Tests  26 passed | 1 skipped (27)
   Start at  01:22:44
   Duration  1.80s (transform 350ms, setup 727ms, collect 301ms, tests 86ms, environment 692ms, prepare 579ms)

➜ nuxt-image git:(main) 

@antony-k1208
Copy link

antony-k1208 commented Jan 22, 2025

Thanks so much for your great work! I've also stumbled upon this issue today and was really delighted to see it already being taken care of.

This seems to be breaking when using a prop with no value, which will normally imply true.
<BlogPost is-published />

The node.type is then 6 (an attribute) and calling search(node.value) breaks the code.

node.value is undefined and in the next handleNode call, you then try to access node.type which is not available.

@Teages
Copy link
Contributor Author

Teages commented Jan 22, 2025

@antony-k1208

You are right, the project didn't enable strict and I missed the type error. Now fixed and added a test for it.

@robinscholz
Copy link

We also ran into the problem Daniel described, for now this blocks us from updating mkdist past 1.6.0.

@robinscholz
Copy link

Ideally it’d be great to disable compilation entirely and simply copy the file as is. Not sure if this is already possible, but we definitely have a use case for it.

@Teages
Copy link
Contributor Author

Teages commented Jan 23, 2025

@robinscholz This pr also fixed Daniel's problem, see #289 (comment)

As for disabling compilation, you can set only the loader you need to transform in options, for example:

mkdist({ loaders: ['js', 'postcss', 'sass'] })

it will copy .vue and other files not supported by selected loader.

@robinscholz
Copy link

@Teages Omitting vue from the loader array also disables d.ts generation for vue files, correct? Is there any way to still have vue-tsc enabled without having to manually set it up?

@Teages
Copy link
Contributor Author

Teages commented Feb 8, 2025

@robinscholz I can't think of any reason why you would want to build it this way. But you can try this if you really want to.

mkdist({
  loaders: [
    'js',
    'sass',
    'postcss',
    (input, { loadFile }) => {
      // push a fake script block to call vue-tsc
      if (input.extension === '.vue') {
        loadFile({
          getContents: () => `export default {}`,
          path: `${input.path}.ts`,
          srcPath: `${input.srcPath}.ts`,
          extension: '.ts',
        })
      }
      
      return undefined
    },
  ],
})

I sincerely recommend you try building with this PR.

@danielroe can we have something like pkg.pr.new for mkdist?

@robinscholz
Copy link

@Teages building with your fix does indeed fix the problems we ran into. Wasn’t sure, since I needed to fix some other things as well 😅.

We ran mkdist v1.6 before and manually invoked vue-tsc, which I wanted to replicate (thus my question above). Happy to move forward with transpiling files for our build.

The repository/build I’m referring too is somewhat complicated in its structure: https://github.com/magicasaservice/vue-equipment/blob/main/packages/plugins/scripts/build.ts

@danielroe Would highly appreciate if this PR could get merged and published soon, since that would enable us to build directly with the npm mkdist package instead of the fork as a dependency. 🙂

@robinscholz
Copy link

@Teages

I’m running into a warning in our library about a missing template or rendering function.

This is the input:

<template>
  <primitive
    ref="elRef"
    :data-id="`${viewId}-trigger`"
    :data-active="view?.active"
    :data-disabled="mappedDisabled"
    :tabindex="mappedTabindex"
    :as-child="asChild"
    class="magic-menu-trigger"
    @click="onClick"
    @contextmenu="onClick"
    @mouseenter="onMouseenter"
  >
    <slot :view-active="view?.active" :trigger-disabled="mappedDisabled" />
  </primitive>
</template>

<script lang="ts" setup>
import { computed, inject, ref, toValue, watch } from 'vue'
import { Primitive } from '@maas/vue-primitive'
import { useMenuState } from '../composables/private/useMenuState'
import { useMenuView } from '../composables/private/useMenuView'
import { useMenuItem } from '../composables/private/useMenuItem'
import { useMenuTrigger } from '../composables/private/useMenuTrigger'
import {
  MagicMenuInstanceId,
  MagicMenuViewId,
  MagicMenuItemId,
} from '../symbols'

import type { Interaction } from '../types'
import { onKeyStroke } from '@vueuse/core'

interface MagicMenuTriggerProps {
  disabled?: boolean
  trigger?: Interaction[]
  asChild?: boolean
}

const { disabled, trigger } = defineProps<MagicMenuTriggerProps>()
const elRef = ref<InstanceType<typeof Primitive> | undefined>(undefined)

const instanceId = inject(MagicMenuInstanceId, undefined)
const viewId = inject(MagicMenuViewId, undefined)
const itemId = inject(MagicMenuItemId, undefined)

if (!instanceId) {
  throw new Error('MagicMenuTrigger must be nested inside MagicMenuProvider')
}

if (!viewId) {
  throw new Error('MagicMenuTrigger must be nested inside MagicMenuView')
}

const { getView, getRelativeViewIndex } = useMenuView(instanceId)
const view = getView(viewId)
const viewIndex = getRelativeViewIndex(viewId)

const { initializeState } = useMenuState(instanceId)
const state = initializeState()

const { getItem } = useMenuItem({ instanceId, viewId })
const item = getItem(itemId ?? '')

const mappedDisabled = computed(() => disabled ?? item?.disabled ?? false)

const mappedTrigger = computed<Interaction[]>(() => {
  if (trigger?.length) {
    return trigger
  }

  switch (state.options.mode) {
    case 'menubar':
      return view?.parent.item
        ? ['mouseenter', 'click']
        : state.active
        ? ['mouseenter', 'click']
        : ['click']
    case 'dropdown':
      return view?.parent.item ? ['mouseenter', 'click'] : ['click']
    case 'context':
      return view?.parent.item ? ['mouseenter', 'click'] : ['right-click']
    case 'navigation':
      return ['mouseenter']
    default:
      return []
  }
})

const mappedTabindex = computed(() => {
  if (viewIndex === 0 && state.options.mode !== 'context' && !itemId) {
    return 0
  } else {
    return undefined
  }
})

const { onMouseenter, onClick, onEnter } = useMenuTrigger({
  instanceId,
  viewId,
  itemId,
  mappedDisabled,
  mappedTrigger,
  elRef,
})

watch(
  () => view?.active,
  async (value) => {
    if (value) {
      await new Promise((resolve) => requestAnimationFrame(resolve))
      toValue(elRef)?.$el?.blur()
    }
  }
)

onKeyStroke('Enter', onEnter)
</script>

<style>
.magic-menu-trigger {
  cursor: var(--magic-menu-trigger-cursor, pointer);
}

.magic-menu-trigger.-disabled {
  pointer-events: none;
}
</style>

This is the compiled output:

<script>
import { defineComponent as _defineComponent } from "vue";
import { unref as _unref, renderSlot as _renderSlot, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue";
import { computed, inject, ref, toValue, watch } from "vue";
import { Primitive } from "@maas/vue-primitive";
import { useMenuState } from "../composables/private/useMenuState";
import { useMenuView } from "../composables/private/useMenuView";
import { useMenuItem } from "../composables/private/useMenuItem";
import { useMenuTrigger } from "../composables/private/useMenuTrigger";
import {
  MagicMenuInstanceId,
  MagicMenuViewId,
  MagicMenuItemId
} from "../symbols";
import { onKeyStroke } from "@vueuse/core";
export default /* @__PURE__ */ _defineComponent({
  __name: "MagicMenuTrigger",
  props: {
    disabled: { type: Boolean, required: false },
    trigger: { type: Array, required: false },
    asChild: { type: Boolean, required: false }
  },
  setup(__props) {
    const elRef = ref(void 0);
    const instanceId = inject(MagicMenuInstanceId, void 0);
    const viewId = inject(MagicMenuViewId, void 0);
    const itemId = inject(MagicMenuItemId, void 0);
    if (!instanceId) {
      throw new Error("MagicMenuTrigger must be nested inside MagicMenuProvider");
    }
    if (!viewId) {
      throw new Error("MagicMenuTrigger must be nested inside MagicMenuView");
    }
    const { getView, getRelativeViewIndex } = useMenuView(instanceId);
    const view = getView(viewId);
    const viewIndex = getRelativeViewIndex(viewId);
    const { initializeState } = useMenuState(instanceId);
    const state = initializeState();
    const { getItem } = useMenuItem({ instanceId, viewId });
    const item = getItem(itemId ?? "");
    const mappedDisabled = computed(() => __props.disabled ?? item?.disabled ?? false);
    const mappedTrigger = computed(() => {
      if (__props.trigger?.length) {
        return __props.trigger;
      }
      switch (state.options.mode) {
        case "menubar":
          return view?.parent.item ? ["mouseenter", "click"] : state.active ? ["mouseenter", "click"] : ["click"];
        case "dropdown":
          return view?.parent.item ? ["mouseenter", "click"] : ["click"];
        case "context":
          return view?.parent.item ? ["mouseenter", "click"] : ["right-click"];
        case "navigation":
          return ["mouseenter"];
        default:
          return [];
      }
    });
    const mappedTabindex = computed(() => {
      if (viewIndex === 0 && state.options.mode !== "context" && !itemId) {
        return 0;
      } else {
        return void 0;
      }
    });
    const { onMouseenter, onClick, onEnter } = useMenuTrigger({
      instanceId,
      viewId,
      itemId,
      mappedDisabled,
      mappedTrigger,
      elRef
    });
    watch(
      () => view?.active,
      async (value) => {
        if (value) {
          await new Promise((resolve) => requestAnimationFrame(resolve));
          toValue(elRef)?.$el?.blur();
        }
      }
    );
    onKeyStroke("Enter", onEnter);
    return (_ctx, _cache) => {
      return _openBlock(), _createBlock(_unref(Primitive), {
        ref_key: "elRef",
        ref: elRef,
        "data-id": `${_unref(viewId)}-trigger`,
        "data-active": _unref(view)?.active,
        "data-disabled": mappedDisabled.value,
        tabindex: mappedTabindex.value,
        "as-child": _ctx.asChild,
        class: "magic-menu-trigger",
        onClick: _unref(onClick),
        onContextmenu: _unref(onClick),
        onMouseenter: _unref(onMouseenter)
      }, {
        default: _withCtx(() => [
          _renderSlot(_ctx.$slots, "default", {
            viewActive: _unref(view)?.active,
            triggerDisabled: mappedDisabled.value
          })
        ]),
        _: 3
        /* FORWARDED */
      }, 8, ["data-id", "data-active", "data-disabled", "tabindex", "as-child", "onClick", "onContextmenu", "onMouseenter"]);
    };
  }
});
</script>

<style>
.magic-menu-trigger {
  cursor: var(--magic-menu-trigger-cursor, pointer);
}

.magic-menu-trigger.-disabled {
  pointer-events: none;
}
</style>

The component is still working, just maybe something to take a look at?

@Teages
Copy link
Contributor Author

Teages commented Feb 13, 2025

@robinscholz I think it is not a mkdist issue, see Usage with Render Functions, it already have a render function.

@Teages
Copy link
Contributor Author

Teages commented Mar 25, 2025

@danielroe I'd like to get your opinion on render functions: should we keep render functions, or work on transpiling <script setup> individually in the future?

PS: It has been a while, considering this issue broke the vue build of unbuild@^3, I hope you can take a look when you have time

@danielroe
Copy link
Member

I would like to work to keep .vue files as unchanged as possible, reverting some of the changes we made previously.

although this PR looks great I'm worried about the fragility of this approach in general. I would prefer an approach where we transpile only the TS features we have to do (such as the type-syntax for defineProps, defineEmits, etc.) and leave the rest for the vue compiler in the final project

@Teages
Copy link
Contributor Author

Teages commented Mar 25, 2025

I did some research, reactive props destructure make it really complex.

for example:

<script setup lang="ts">
const a = defineModel<number>()
const { b = 'hi' } = defineProps<{ b: string }>()

watch(() => b, { /* ... */ })
</script>

We need to process it into

<script setup>
const props = defineProps(['modelValue', 'b']) // no runtime types because I am lazy
const emits = defineEmits(['update:modelValue'])
const a = useModel(props, 'modelValue')

watch(() => props.b, { /* ... */ })
</script>

vue have already expose extractRuntimeProps and extractRuntimeEmits, but processPropsDestructure is not public.

I don't have much experience with AST, but it seems like a feasible approach.


I think I found a way to transpile without merge the two script block, I'll open a new PR later, but I'll keep this in case it was needed until I finish the new one.

@Teages
Copy link
Contributor Author

Teages commented Apr 4, 2025

continue on #300

@Teages Teages closed this Apr 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vue template is not transpiled

4 participants