Skip to content

Commit

Permalink
feat: introduce exportRender feature (#483)
Browse files Browse the repository at this point in the history
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
  • Loading branch information
zhiyuanzmj and sxzz committed Aug 24, 2023
1 parent 8067a81 commit 0803973
Show file tree
Hide file tree
Showing 29 changed files with 413 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .changeset/pink-items-compare.md
@@ -0,0 +1,7 @@
---
'@vue-macros/export-render': minor
'@vue-macros/volar': minor
'unplugin-vue-macros': patch
---

Introduce `exportRender`
4 changes: 4 additions & 0 deletions docs/.vitepress/locales/common.ts
Expand Up @@ -179,6 +179,10 @@ export const sidebar = (lang: string): DefaultTheme.SidebarItem[] => {
text: 'exportExpose',
link: `${urlPrefix}/features/export-expose`,
},
{
text: 'exportRender',
link: `${urlPrefix}/features/export-render`,
},
{
text: 'jsxDirective',
link: `${urlPrefix}/features/jsx-directive`,
Expand Down
30 changes: 30 additions & 0 deletions docs/features/export-render.md
@@ -0,0 +1,30 @@
# exportRender

<StabilityLevel level="experimental" />

Transform the default export statement, in `<script setup>` of Vue SFC, as a component render function.

| Features | Supported |
| :----------: | :----------------: |
| Vue 3 | :white_check_mark: |
| Nuxt 3 | :question: |
| Vue 2 | :question: |
| Volar Plugin | :white_check_mark: |

::: tip

This feature depends on `defineRender`, and make sure `defineRender` is not disabled.

:::

## Usage

```vue
<script setup lang="tsx">
// JSX passed directly
export default <div>ok</div>
// Or using render function
export default () => <div>ok</div>
</script>
```
1 change: 1 addition & 0 deletions docs/guide/configurations.md
Expand Up @@ -10,6 +10,7 @@ All features are enabled by default except the following.
- `shortEmits` (Vue >= 3.3)
- `exportExpose`
- `exportProps`
- `exportRender`
- `setupSFC`

You can disable them by setting the option to `false`.
Expand Down
30 changes: 30 additions & 0 deletions docs/zh-CN/features/export-render.md
@@ -0,0 +1,30 @@
# exportRender

<StabilityLevel level="experimental" />

在 Vue SFC 的 `<script setup>` 中,把 export default 语句转换为组件的渲染函数。

| Features | Supported |
| :----------: | :----------------: |
| Vue 3 | :white_check_mark: |
| Nuxt 3 | :question: |
| Vue 2 | :question: |
| Volar Plugin | :white_check_mark: |

::: tip

这个特性依赖于 `defineRender`,并确保 `defineRender` 没有被禁用。

:::

## 用法

```vue
<script setup lang="tsx">
// 可以直接传递 JSX
export default <div>ok</div>
// 或使用渲染函数
export default () => <div>ok</div>
</script>
```
3 changes: 2 additions & 1 deletion docs/zh-CN/guide/configurations.md
Expand Up @@ -2,14 +2,15 @@

## 插件选项

默认情况下启用所有功能,但以下功能除外
以下功能除外,默认情况下将启用所有功能

- `defineOptions` (Vue >= 3.3)
- `defineSlots` (Vue >= 3.3)
- `hoistStatic` (Vue >= 3.3)
- `shortEmits` (Vue >= 3.3)
- `exportExpose`
- `exportProps`
- `exportRender`
- `setupSFC`

您可以通过将选项设置为 `false` 来禁用它们。
Expand Down
3 changes: 3 additions & 0 deletions packages/export-render/README.md
@@ -0,0 +1,3 @@
# @vue-macros/export-render [![npm](https://img.shields.io/npm/v/@vue-macros/export-render.svg)](https://npmjs.com/package/@vue-macros/export-render)

Please refer to [README.md](https://github.com/vue-macros/vue-macros#readme)
95 changes: 95 additions & 0 deletions packages/export-render/package.json
@@ -0,0 +1,95 @@
{
"name": "@vue-macros/export-render",
"version": "0.0.0",
"packageManager": "pnpm@8.6.12",
"description": "export-render feature from Vue Macros.",
"keywords": [
"vue-macros",
"macros",
"vue",
"sfc",
"setup",
"script-setup",
"export-render",
"unplugin"
],
"license": "MIT",
"homepage": "https://github.com/vue-macros/vue-macros#readme",
"bugs": {
"url": "https://github.com/vue-macros/vue-macros/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vue-macros/vue-macros.git",
"directory": "packages/export-render"
},
"author": "三咲智子 <sxzz@sxzz.moe>",
"files": [
"dist"
],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"dev": "./src/index.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./api": {
"dev": "./src/api.ts",
"require": "./dist/api.js",
"import": "./dist/api.mjs"
},
"./esbuild": {
"dev": "./src/esbuild.ts",
"require": "./dist/esbuild.js",
"import": "./dist/esbuild.mjs"
},
"./rollup": {
"dev": "./src/rollup.ts",
"require": "./dist/rollup.js",
"import": "./dist/rollup.mjs"
},
"./vite": {
"dev": "./src/vite.ts",
"require": "./dist/vite.js",
"import": "./dist/vite.mjs"
},
"./webpack": {
"dev": "./src/webpack.ts",
"require": "./dist/webpack.js",
"import": "./dist/webpack.mjs"
},
"./*": [
"./*",
"./*.d.ts"
]
},
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./*"
]
}
},
"scripts": {
"build": "tsup",
"dev": "DEV=true tsup"
},
"peerDependencies": {
"vue": "^2.7.0 || ^3.2.25"
},
"dependencies": {
"@vue-macros/common": "workspace:*",
"@vue/compiler-sfc": "^3.3.4",
"unplugin": "^1.4.0"
},
"devDependencies": {
"vue": "^3.3.4"
},
"engines": {
"node": ">=16.14.0"
}
}
1 change: 1 addition & 0 deletions packages/export-render/src/api.ts
@@ -0,0 +1 @@
export * from './core'
29 changes: 29 additions & 0 deletions packages/export-render/src/core/index.ts
@@ -0,0 +1,29 @@
import { MagicString, generateTransform, parseSFC } from '@vue-macros/common'

export function transformExportRender(code: string, id: string) {
const { scriptSetup, getSetupAst } = parseSFC(code, id)
if (!scriptSetup) return

const s = new MagicString(code)
const nodes = getSetupAst()!.body
const offset = scriptSetup.loc.start.offset

let codegen = ''
for (const stmt of nodes) {
if (
stmt.type === 'ExportDefaultDeclaration' &&
stmt.exportKind === 'value'
) {
codegen = s.sliceNode(stmt.declaration, { offset })
s.removeNode(stmt, { offset })
}
}

if (codegen.length === 0) return

codegen = `defineRender(${codegen})`

s.prependLeft(scriptSetup.loc.end.offset, `${codegen}\n`)

return generateTransform(s, id)
}
3 changes: 3 additions & 0 deletions packages/export-render/src/esbuild.ts
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.esbuild
47 changes: 47 additions & 0 deletions packages/export-render/src/index.ts
@@ -0,0 +1,47 @@
import { createUnplugin } from 'unplugin'
import {
type BaseOptions,
type MarkRequired,
REGEX_SETUP_SFC,
REGEX_VUE_SFC,
REGEX_VUE_SUB,
createFilter,
detectVueVersion,
} from '@vue-macros/common'
import { transformExportRender } from './core'

export { transformExportRender } from './core'

export type Options = BaseOptions
export type OptionsResolved = MarkRequired<Options, 'include' | 'version'>

function resolveOption(options: Options): OptionsResolved {
const version = options.version || detectVueVersion()
return {
include: [REGEX_VUE_SFC, REGEX_SETUP_SFC, REGEX_VUE_SUB],
...options,
version,
}
}

const name = 'unplugin-vue-export-render'

export default createUnplugin<Options | undefined, false>(
(userOptions = {}) => {
const options = resolveOption(userOptions)
const filter = createFilter(options)

return {
name,
enforce: 'pre',

transformInclude(id) {
return filter(id)
},

transform(code, id) {
return transformExportRender(code, id)
},
}
}
)
3 changes: 3 additions & 0 deletions packages/export-render/src/rollup.ts
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.rollup
3 changes: 3 additions & 0 deletions packages/export-render/src/vite.ts
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.vite
3 changes: 3 additions & 0 deletions packages/export-render/src/webpack.ts
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.webpack
@@ -0,0 +1,9 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`fixtures > ./fixtures/basic.vue 1`] = `
"<script setup lang=\\"tsx\\">
defineRender(() => <div>123</div>)
</script>
"
`;
13 changes: 13 additions & 0 deletions packages/export-render/tests/fixtures.test.ts
@@ -0,0 +1,13 @@
import { testFixtures } from '@vue-macros/test-utils'
import { describe } from 'vitest'
import { transformExportRender } from '../src/core'

describe('fixtures', async () => {
await testFixtures(
import.meta.glob('./fixtures/**/*.vue', {
eager: true,
as: 'raw',
}),
(args, id, code) => transformExportRender(code, id)?.code
)
})
3 changes: 3 additions & 0 deletions packages/export-render/tests/fixtures/basic.vue
@@ -0,0 +1,3 @@
<script setup lang="tsx">
export default () => <div>123</div>
</script>
1 change: 1 addition & 0 deletions packages/export-render/tsup.config.ts
@@ -0,0 +1 @@
export { default } from '../../tsup.config.js'
1 change: 1 addition & 0 deletions packages/macros/package.json
Expand Up @@ -91,6 +91,7 @@
"@vue-macros/devtools": "workspace:^",
"@vue-macros/export-expose": "workspace:*",
"@vue-macros/export-props": "workspace:*",
"@vue-macros/export-render": "workspace:*",
"@vue-macros/hoist-static": "workspace:*",
"@vue-macros/jsx-directive": "workspace:*",
"@vue-macros/named-template": "workspace:*",
Expand Down
11 changes: 11 additions & 0 deletions packages/macros/src/index.ts
Expand Up @@ -45,6 +45,9 @@ import VueExportExpose, {
import VueExportProps, {
type Options as OptionsExportProps,
} from '@vue-macros/export-props'
import VueExportRender, {
type Options as OptionsExportRender,
} from '@vue-macros/export-render'
import VueHoistStatic, {
type Options as OptionsHoistStatic,
} from '@vue-macros/hoist-static'
Expand Down Expand Up @@ -82,6 +85,7 @@ export interface FeatureOptionsMap {
definePropsRefs: OptionsDefinePropsRefs
defineRender: OptionsDefineRender
defineSlots: OptionsDefineSlots
exportRender: OptionsExportRender
exportExpose: OptionsExportExpose
exportProps: OptionsExportProps
hoistStatic: OptionsHoistStatic
Expand Down Expand Up @@ -137,6 +141,7 @@ export function resolveOptions({
definePropsRefs,
defineRender,
defineSlots,
exportRender,
exportExpose,
exportProps,
hoistStatic,
Expand Down Expand Up @@ -200,6 +205,11 @@ export function resolveOptions({
{ version },
version < 3.3
),
exportRender: resolveSubOptions<'exportRender'>(
exportRender,
{ version },
false
),
exportExpose: resolveSubOptions<'exportExpose'>(
exportExpose,
{ version },
Expand Down Expand Up @@ -300,6 +310,7 @@ export default createCombinePlugin<Options | undefined>(
resolvePlugin(VueDefineProp, framework, options.defineProp),

resolvePlugin(VueDefineSlots, framework, options.defineSlots),
resolvePlugin(VueExportRender, framework, options.exportRender),
resolvePlugin(VueExportExpose, framework, options.exportExpose),
resolvePlugin(
VueReactivityTransform,
Expand Down
4 changes: 4 additions & 0 deletions packages/volar/index.d.ts
Expand Up @@ -15,4 +15,8 @@ export interface VolarOptions {
include?: FilterPattern
exclude?: FilterPattern
}
exportRender?: {
include?: FilterPattern
exclude?: FilterPattern
}
}

0 comments on commit 0803973

Please sign in to comment.