Skip to content

Commit

Permalink
feat(volar): support export props
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Jan 23, 2023
1 parent 7db855f commit 5e7a177
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .changeset/rich-brooms-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@vue-macros/volar': minor
'@vue-macros/nuxt': minor
---

add volar support of export props
3 changes: 2 additions & 1 deletion packages/nuxt/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export default defineNuxtModule<VueMacrosOptions>({
nuxt.options.typescript.tsConfig.vueCompilerOptions.plugins.push(
'@vue-macros/volar/define-model',
'@vue-macros/volar/define-props',
'@vue-macros/volar/define-slots'
'@vue-macros/volar/define-slots',
'@vue-macros/volar/export-props'
)

nuxt.options.vite.vue ||= {}
Expand Down
1 change: 1 addition & 0 deletions packages/volar/export-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/export-props').default
9 changes: 6 additions & 3 deletions packages/volar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
"./define-model": {
"require": "./define-model.js"
},
"./short-vmodel": {
"require": "./short-vmodel.js"
},
"./define-slots": {
"require": "./define-slots.js"
},
"./export-props": {
"require": "./export-props.js"
},
"./short-vmodel": {
"require": "./short-vmodel.js"
},
"./define-props": {
"require": "./define-props.js"
}
Expand Down
19 changes: 19 additions & 0 deletions packages/volar/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
import type { FileRangeCapabilities } from '@volar/language-core'
import type { Segment } from 'muggle-string'

export function getVueLibraryName(vueVersion: number) {
return vueVersion < 2.7 ? '@vue/runtime-dom' : 'vue'
}

export function addProps(
content: Segment<FileRangeCapabilities>[],
decl: Segment<FileRangeCapabilities>[],
vueLibName: string
) {
const idx = content.indexOf('setup() {\n')
if (idx === -1) return false
content.splice(idx, 0, ...['props: ({} as ', ...decl, '),\n'])

content.push(
`type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;\n`,
`type __VLS_TypePropsToRuntimeProps<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? { type: import('${vueLibName}').PropType<__VLS_NonUndefinedable<T[K]>> } : { type: import('${vueLibName}').PropType<T[K]>, required: true } };\n`
)
return true
}
2 changes: 1 addition & 1 deletion packages/volar/src/define-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function resolve({
const typeArg = getTypeArg(ts, sfc)
if (!typeArg) return

const vueVersion = vueCompilerOptions.target ?? 3
const vueVersion = vueCompilerOptions.target
const vueLibName = getVueLibraryName(vueVersion)
const unified =
(vueVersion < 3 && (vueCompilerOptions as any)?.defineModel?.unified) ??
Expand Down
98 changes: 98 additions & 0 deletions packages/volar/src/export-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { FileKind, FileRangeCapabilities } from '@volar/language-core'
import { addProps, getVueLibraryName } from './common'
import type { Segment } from 'muggle-string'
import type {
Sfc,
VueEmbeddedFile,
VueLanguagePlugin,
} from '@volar/vue-language-core'

function transform({
file,
sfc,
ts,
vueLibName,
}: {
file: VueEmbeddedFile
sfc: Sfc
ts: typeof import('typescript/lib/tsserverlibrary')
vueLibName: string
}) {
const idx = file?.content.indexOf('const __VLS_setup = async () => {\n')

const props: Record<string, boolean> = {}
const sources: [offset: number, content: string][] = []
let source = file.content[idx + 1][0]
let cursor = 0
for (const stmt of sfc.scriptSetupAst!.statements) {
if (!ts.isVariableStatement(stmt)) continue
const exportModifier = stmt.modifiers?.find(
(m) => m.kind === ts.SyntaxKind.ExportKeyword
)
if (!exportModifier) continue

const start = exportModifier.getFullStart()
const end = exportModifier.getEnd()
if (cursor > 0) {
sources.push([cursor, source.slice(0, start - cursor)])
}
source = source.slice(end - cursor)
cursor = end

for (const decl of stmt.declarationList.declarations) {
if (!ts.isIdentifier(decl.name)) continue
props[decl.name.text] = !!decl.initializer
}
}

if (sources.length > 0) {
sources.push([cursor, source])
file.content.splice(
idx + 1,
1,
...sources.map(([offset, content]): Segment<FileRangeCapabilities> => {
return [content, 'scriptSetup', offset, FileRangeCapabilities.full]
})
)
}

addProps(
file.content,
[
`__VLS_TypePropsToRuntimeProps<{
${Object.entries(props)
.map(([prop, optional]) => `${prop}${optional ? '?' : ''}: typeof ${prop}`)
.join(',\n')}
}>`,
],
vueLibName
)
}

const plugin: VueLanguagePlugin = ({
modules: { typescript: ts },
vueCompilerOptions,
}) => {
return {
name: 'vue-macros-export-props',
version: 1,
resolveEmbeddedFile(fileName, sfc, embeddedFile) {
if (
embeddedFile.kind !== FileKind.TypeScriptHostFile ||
!sfc.scriptSetup ||
!sfc.scriptSetupAst
)
return

const vueLibName = getVueLibraryName(vueCompilerOptions.target)

transform({
file: embeddedFile,
sfc,
vueLibName,
ts,
})
},
}
}
export default plugin
3 changes: 2 additions & 1 deletion playground/vue3/src/examples/export-props/child.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<!-- eslint-disable import/no-mutable-exports -->
<!-- eslint-disable vue/no-export-in-script-setup -->
<!-- eslint-disable @typescript-eslint/no-inferrable-types -->
<script setup lang="ts">
export let foo: string
export const bar = 10
export const bar: number = 10
const check = () => {
console.log(foo, bar)
Expand Down
2 changes: 1 addition & 1 deletion playground/vue3/src/examples/export-props/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import Child from './child.vue'
</script>

<template>
<Child foo="foo" bar="20" />
<Child foo="foo" />
</template>
3 changes: 2 additions & 1 deletion playground/vue3/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"@vue-macros/volar/define-model",
"@vue-macros/volar/define-props",
"@vue-macros/volar/short-vmodel",
"@vue-macros/volar/define-slots"
"@vue-macros/volar/define-slots",
"@vue-macros/volar/export-props"
],
"shortVmodel": {
"prefix": "$"
Expand Down

0 comments on commit 5e7a177

Please sign in to comment.