Skip to content

Commit

Permalink
feat(volar): add
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed May 23, 2023
1 parent cfa1fc4 commit e9af9a0
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 5 deletions.
10 changes: 10 additions & 0 deletions packages/volar/index.d.ts
@@ -1,8 +1,18 @@
import { type FilterPattern } from '@rollup/pluginutils'

export interface VolarOptions {
defineModels?: {
unified?: boolean
}
shortVmodel?: {
prefix?: '::' | '$' | '*'
}
exportExpose?: {
include?: FilterPattern
exclude?: FilterPattern
}
exportProps?: {
include?: FilterPattern
exclude?: FilterPattern
}
}
1 change: 1 addition & 0 deletions packages/volar/package.json
Expand Up @@ -48,6 +48,7 @@
}
},
"dependencies": {
"@rollup/pluginutils": "^5.0.2",
"@volar/language-core": "1.6.9",
"@volar/vue-language-core": "1.6.5",
"@vue-macros/common": "workspace:~",
Expand Down
5 changes: 4 additions & 1 deletion packages/volar/src/common.ts
Expand Up @@ -4,6 +4,7 @@ import {
replace,
} from '@volar/vue-language-core'
import { type FileRangeCapabilities } from '@volar/language-core'
import { type VolarOptions } from '..'

export function getVueLibraryName(vueVersion: number) {
return vueVersion < 2.7 ? '@vue/runtime-dom' : 'vue'
Expand Down Expand Up @@ -47,6 +48,8 @@ export function addEmits(
return true
}

export function getVolarOptions(vueCompilerOptions: VueCompilerOptions) {
export function getVolarOptions(
vueCompilerOptions: VueCompilerOptions
): VolarOptions {
return vueCompilerOptions.vueMacros
}
97 changes: 97 additions & 0 deletions packages/volar/src/export-expose.ts
@@ -0,0 +1,97 @@
import { FileKind } from '@volar/language-core'
import {
FileRangeCapabilities,
type Segment,
type Sfc,
type VueEmbeddedFile,
type VueLanguagePlugin,
replace,
replaceSourceRange,
} from '@volar/vue-language-core'
import { createFilter } from '@rollup/pluginutils'
import { type VolarOptions } from '..'
import { getVolarOptions } from './common'

function transform({
fileName,
file,
sfc,
ts,
volarOptions,
}: {
fileName: string
file: VueEmbeddedFile
sfc: Sfc
ts: typeof import('typescript/lib/tsserverlibrary')
volarOptions: NonNullable<VolarOptions['exportExpose']>
}) {
const filter = createFilter(
volarOptions.include || /.*/,
volarOptions.exclude
)
if (!filter(fileName)) return

const exposed: Record<string, Segment<FileRangeCapabilities>> = {}
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.getStart(sfc.scriptSetupAst!)
const end = exportModifier.getEnd()
replaceSourceRange(file.content, 'scriptSetup', start, end)

for (const decl of stmt.declarationList.declarations) {
if (!ts.isIdentifier(decl.name)) continue
const name = decl.name.text
const start = decl.name.getStart(sfc.scriptSetupAst!)

exposed[name] = [name, 'scriptSetup', start, FileRangeCapabilities.full]
}
}

if (Object.keys(exposed).length === 0) return

const exposedStrings = Object.entries(exposed).flatMap(([key, value]) => [
`${key}: `,
value,
',\n',
])

replace(
file.content,
'return {\n',
'return {\n...{ ',
...exposedStrings,
' },\n'
)
}

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

transform({
fileName,
file: embeddedFile,
sfc,
ts,
volarOptions: getVolarOptions(vueCompilerOptions)?.exportExpose || {},
})
},
}
}
export = plugin
16 changes: 15 additions & 1 deletion packages/volar/src/export-props.ts
Expand Up @@ -5,19 +5,31 @@ import {
type VueLanguagePlugin,
replaceSourceRange,
} from '@volar/vue-language-core'
import { addProps, getVueLibraryName } from './common'
import { createFilter } from '@rollup/pluginutils'
import { type VolarOptions } from '..'
import { addProps, getVolarOptions, getVueLibraryName } from './common'

function transform({
file,
sfc,
ts,
vueLibName,
volarOptions,
fileName,
}: {
fileName: string
file: VueEmbeddedFile
sfc: Sfc
ts: typeof import('typescript/lib/tsserverlibrary')
vueLibName: string
volarOptions: NonNullable<VolarOptions['exportProps']>
}) {
const filter = createFilter(
volarOptions.include || /.*/,
volarOptions.exclude
)
if (!filter(fileName)) return

const props: Record<string, boolean> = {}
let changed = false
for (const stmt of sfc.scriptSetupAst!.statements) {
Expand Down Expand Up @@ -75,6 +87,8 @@ const plugin: VueLanguagePlugin = ({
sfc,
vueLibName,
ts,
fileName,
volarOptions: getVolarOptions(vueCompilerOptions)?.exportProps || {},
})
},
}
Expand Down
2 changes: 1 addition & 1 deletion playground/vue3/src/examples/export-expose/child.vue
@@ -1,6 +1,6 @@
<script setup lang="ts">
export const foo = 'foo'
export const bar = 'baz'
export const bar = 'bar'
</script>

<template>exportExpose child.</template>
4 changes: 2 additions & 2 deletions playground/vue3/src/examples/export-expose/index.vue
Expand Up @@ -2,10 +2,10 @@
import { ref } from 'vue'
import Child from './child.vue'
const child = ref()
const child = ref<InstanceType<typeof Child>>()
</script>

<template>
<Child ref="child" />
{{ child }}
{{ child?.bar }}
</template>
8 changes: 8 additions & 0 deletions playground/vue3/tsconfig.json
Expand Up @@ -23,12 +23,20 @@
"@vue-macros/volar/define-props-refs",
"@vue-macros/volar/short-vmodel",
"@vue-macros/volar/define-slots",
"@vue-macros/volar/export-expose",
"@vue-macros/volar/export-props"
],
"experimentalDefinePropProposal": "kevinEdition",

"vueMacros": {
"shortVmodel": {
"prefix": "$"
},
"exportExpose": {
"include": ["**/export-expose/**"]
},
"exportProps": {
"include": ["**/export-props/**"]
}
}
},
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e9af9a0

Please sign in to comment.