From a5190004e8c7b7476b1dcb2d2d34ffe6dc33809c Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Fri, 3 Jul 2020 12:30:35 +0800 Subject: [PATCH] feat: support fragment --- README.md | 11 +++++++++++ src/buildCreateVNode.ts | 5 ++++- src/buildFragment.ts | 17 +++++++++++++++++ src/main.ts | 7 +++++++ tests/__snapshots__/fragment.spec.ts.snap | 6 ++++++ tests/fragment.spec.ts | 14 ++++++++++++++ 6 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/buildFragment.ts create mode 100644 tests/__snapshots__/fragment.spec.ts.snap create mode 100644 tests/fragment.spec.ts diff --git a/README.md b/README.md index 6aa477f..1115a0b 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,17 @@ const App = { In Vue3, the children of KeepAlive and Teleport components will not be built as `slots`, so you can use them as in the template: +## Fragment + +Since Vue3 supports multiple root elements, so we need to support `Fragment`: + +```html +<> +

foo

+
bar
+ +``` + ## Optimization mode `Vue3` makes full use of compile-time information to generate `PatchFlags` for runtime update performance improvement, `vue-next-jsx` behaves as `Vue3 Compiler` after enabling **optimization mode**. diff --git a/src/buildCreateVNode.ts b/src/buildCreateVNode.ts index 80af6df..1686552 100644 --- a/src/buildCreateVNode.ts +++ b/src/buildCreateVNode.ts @@ -321,7 +321,10 @@ function processJSXAttrValue(value: bt.JSXAttribute['value']) { } } -function buildChildren(children: bt.JSXElement['children'], state: State) { +export function buildChildren( + children: bt.JSXElement['children'], + state: State +) { return bt.arrayExpression( children .map((child) => { diff --git a/src/buildFragment.ts b/src/buildFragment.ts new file mode 100644 index 0000000..ae1d2bb --- /dev/null +++ b/src/buildFragment.ts @@ -0,0 +1,17 @@ +import * as bt from '@babel/types' +import { NodePath } from '@babel/traverse' +import { State } from './main' +import { buildChildren } from './buildCreateVNode' + +export function buildFragment( + jsxElementPath: NodePath, + state: State +) { + const createVNode = state.visitorContext.addHelper('createVNode') + const Fragment = state.visitorContext.addHelper('Fragment') + return bt.callExpression(createVNode, [ + Fragment, + bt.nullLiteral(), + buildChildren(jsxElementPath.node.children, state) + ]) +} diff --git a/src/main.ts b/src/main.ts index 8071f69..782b030 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ import jsx from '@babel/plugin-syntax-jsx' import * as bt from '@babel/types' import { Visitor, NodePath } from '@babel/traverse' import { buildCreateVNodeCall } from './buildCreateVNode' +import { buildFragment } from './buildFragment' export interface Options { optimizate?: boolean @@ -94,6 +95,12 @@ export default function VueNextJSX() { state.opts = { ...defaultOptions, ...state.opts } path.replaceWith(buildCreateVNodeCall(path, state)) } + }, + JSXFragment: { + exit(path: NodePath, state: State) { + state.opts = { ...defaultOptions, ...state.opts } + path.replaceWith(buildFragment(path, state)) + } } } as Visitor } diff --git a/tests/__snapshots__/fragment.spec.ts.snap b/tests/__snapshots__/fragment.spec.ts.snap new file mode 100644 index 0000000..b8de664 --- /dev/null +++ b/tests/__snapshots__/fragment.spec.ts.snap @@ -0,0 +1,6 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Fragment <> 1`] = ` +"import { createVNode, Fragment } from \\"vue\\"; +const el = createVNode(Fragment, null, [createVNode(\\"p\\")]);" +`; diff --git a/tests/fragment.spec.ts b/tests/fragment.spec.ts new file mode 100644 index 0000000..842a24f --- /dev/null +++ b/tests/fragment.spec.ts @@ -0,0 +1,14 @@ +import { transformWithPlugin } from './utils' + +describe('Fragment', () => { + test('<>', () => { + const code = ` + const el = ( + <> +

+ + ) + ` + transformWithPlugin(code) + }) +})