Skip to content

Commit

Permalink
perf: use compiler hints
Browse files Browse the repository at this point in the history
  • Loading branch information
KaelWD committed Sep 5, 2021
1 parent 65ebf6f commit cb21ed9
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 68 deletions.
25 changes: 15 additions & 10 deletions packages/babel-plugin-jsx/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,26 @@ export default ({ types }: typeof BabelCore) => ({
enter(path: NodePath<t.Program>, state: State) {
if (hasJSX(path)) {
const importNames = [
'createVNode',
'Fragment',
'createElementVNode',
'createTextVNode',
'createVNode',
'guardReactiveProps',
'isVNode',
'mergeProps',
'normalizeClass',
'normalizeProps',
'normalizeStyle',
'resolveComponent',
'withDirectives',
'vShow',
'vModelSelect',
'vModelText',
'resolveDirective',
'vModelCheckbox',
'vModelDynamic',
'vModelRadio',
'vModelSelect',
'vModelText',
'vModelDynamic',
'resolveDirective',
'mergeProps',
'createTextVNode',
'isVNode',
'vModelText',
'vShow',
'withDirectives',
];
if (isModule(path)) {
// import { createVNode } from "vue";
Expand Down
37 changes: 29 additions & 8 deletions packages/babel-plugin-jsx/src/transform-vue-jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
let hasDynamicKeys = false;

const mergeArgs: (t.CallExpression | t.ObjectExpression | t.Identifier)[] = [];
const { mergeProps = true } = state.opts;
const { mergeProps = true, optimize = false } = state.opts;
props
.forEach((prop) => {
if (prop.isJSXAttribute()) {
Expand Down Expand Up @@ -286,6 +286,22 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
propsExpression = (properties[0] as unknown as t.SpreadElement).argument;
} else {
propsExpression = t.objectExpression(dedupeProperties(properties, mergeProps));
if (optimize) {
if (hasClassBinding) {
const klass = (propsExpression.properties as t.ObjectProperty[])
.find((prop) => 'value' in prop.key && prop.key.value === 'class');
if (klass?.type === 'ObjectProperty') {
klass.value = t.callExpression(createIdentifier(state, 'normalizeClass'), [klass.value as any]);
}
}
if (hasStyleBinding) {
const style = (propsExpression.properties as t.ObjectProperty[])
.find((prop) => 'value' in prop.key && prop.key.value === 'style');
if (style?.type === 'ObjectProperty') {
style.value = t.callExpression(createIdentifier(state, 'normalizeStyle'), [style.value as any]);
}
}
}
}
}

Expand Down Expand Up @@ -478,16 +494,21 @@ const transformJSXElement = (
}
}

const createVNode = t.callExpression(createIdentifier(state, 'createVNode'), [
tag,
props,
VNodeChild || t.nullLiteral(),
!!patchFlag && optimize && t.numericLiteral(patchFlag),
!!dynamicPropNames.size && optimize
const createVNode = t.callExpression(
optimize
? createIdentifier(state, isComponent ? 'createVNode' : 'createElementVNode')
: createIdentifier(state, 'createVNode'),
[
tag,
props,
VNodeChild || t.nullLiteral(),
!!patchFlag && optimize && t.numericLiteral(patchFlag),
!!dynamicPropNames.size && optimize
&& t.arrayExpression(
[...dynamicPropNames.keys()].map((name) => t.stringLiteral(name)),
),
].filter(Boolean as unknown as ExcludesBoolean));
].filter(Boolean as unknown as ExcludesBoolean),
);

if (!directives.length) {
return createVNode;
Expand Down
100 changes: 50 additions & 50 deletions packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MereProps Order: MereProps Order 1`] = `
"import { createVNode as _createVNode, mergeProps as _mergeProps, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, mergeProps as _mergeProps, createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"button\\", _mergeProps({
_createElementVNode(\\"button\\", _mergeProps({
\\"loading\\": true
}, x, {
\\"type\\": \\"submit\\"
}), [_createTextVNode(\\"btn\\")], 16, [\\"loading\\"]);"
`;

exports[`Merge class/ style attributes into array: Merge class/ style attributes into array 1`] = `
"import { createVNode as _createVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, normalizeStyle as _normalizeStyle, normalizeClass as _normalizeClass } from \\"vue\\";
_createVNode(\\"div\\", {
\\"class\\": [\\"a\\", b],
\\"style\\": [\\"color: red\\", s]
_createElementVNode(\\"div\\", {
\\"class\\": _normalizeClass([\\"a\\", b]),
\\"style\\": _normalizeStyle([\\"color: red\\", s])
}, null, 6);"
`;

Expand All @@ -25,9 +25,9 @@ createVNode('div', null, ['Without JSX should work']);"
`;

exports[`Without props: Without props 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"a\\", null, [_createTextVNode(\\"a\\")]);"
_createElementVNode(\\"a\\", null, [_createTextVNode(\\"a\\")]);"
`;

exports[`custom directive: custom directive 1`] = `
Expand All @@ -37,9 +37,9 @@ _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_re
`;

exports[`custom directive: custom directive 2`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, resolveDirective as _resolveDirective, resolveComponent as _resolveComponent, Fragment as _Fragment } from \\"vue\\";
"import { createElementVNode as _createElementVNode, withDirectives as _withDirectives, createVNode as _createVNode, resolveDirective as _resolveDirective, resolveComponent as _resolveComponent, Fragment as _Fragment } from \\"vue\\";
_createVNode(_Fragment, null, [_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, 'y']]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, 'y', {
_createElementVNode(_Fragment, null, [_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, 'y']]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, 'y', {
a: true,
b: true
}]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, void 0, {
Expand All @@ -64,32 +64,32 @@ _createVNode(_resolveComponent(\\"Badge\\"), null, {
`;

exports[`dynamic type in input: dynamic type in input 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelDynamic as _vModelDynamic } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelDynamic as _vModelDynamic } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
_withDirectives(_createElementVNode(\\"input\\", {
\\"type\\": type,
\\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"type\\", \\"onUpdate:modelValue\\"]), [[_vModelDynamic, test]]);"
`;

exports[`input[type="checkbox"]: input[type="checkbox"] 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelCheckbox as _vModelCheckbox } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelCheckbox as _vModelCheckbox } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
_withDirectives(_createElementVNode(\\"input\\", {
\\"type\\": \\"checkbox\\",
\\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelCheckbox, test]]);"
`;

exports[`input[type="radio"]: input[type="radio"] 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelRadio as _vModelRadio, Fragment as _Fragment } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelRadio as _vModelRadio, Fragment as _Fragment } from \\"vue\\";
_createVNode(_Fragment, null, [_withDirectives(_createVNode(\\"input\\", {
_createElementVNode(_Fragment, null, [_withDirectives(_createElementVNode(\\"input\\", {
\\"type\\": \\"radio\\",
\\"value\\": \\"1\\",
\\"onUpdate:modelValue\\": $event => test = $event,
\\"name\\": \\"test\\"
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelRadio, test]]), _withDirectives(_createVNode(\\"input\\", {
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelRadio, test]]), _withDirectives(_createElementVNode(\\"input\\", {
\\"type\\": \\"radio\\",
\\"value\\": \\"2\\",
\\"onUpdate:modelValue\\": $event => test = $event,
Expand All @@ -98,19 +98,19 @@ _createVNode(_Fragment, null, [_withDirectives(_createVNode(\\"input\\", {
`;

exports[`input[type="text"] .lazy modifier: input[type="text"] .lazy modifier 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelText as _vModelText } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
_withDirectives(_createElementVNode(\\"input\\", {
\\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test, void 0, {
lazy: true
}]]);"
`;

exports[`input[type="text"]: input[type="text"] 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelText as _vModelText } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
_withDirectives(_createElementVNode(\\"input\\", {
\\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test]]);"
`;
Expand All @@ -122,17 +122,17 @@ _createVNode(\\"foo\\", null, [_createVNode(\\"span\\", null, [_createTextVNode(
`;

exports[`named import specifier \`Keep Alive\`: named import specifier \`Keep Alive\` 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
import { KeepAlive } from 'vue';
_createVNode(KeepAlive, null, [_createTextVNode(\\"123\\")]);"
_createElementVNode(KeepAlive, null, [_createTextVNode(\\"123\\")]);"
`;

exports[`namespace specifier \`Keep Alive\`: namespace specifier \`Keep Alive\` 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
import * as Vue from 'vue';
_createVNode(Vue.KeepAlive, null, [_createTextVNode(\\"123\\")]);"
_createElementVNode(Vue.KeepAlive, null, [_createTextVNode(\\"123\\")]);"
`;

exports[`override props multiple: multiple 1`] = `
Expand Down Expand Up @@ -191,7 +191,7 @@ _createVNode(_resolveComponent(\\"A\\"), null, _isSlot(_slot = foo()) ? _slot :
`;

exports[`reassign variable as component: reassign variable as component 1`] = `
"import { isVNode as _isVNode, createVNode as _createVNode } from \\"vue\\";
"import { createVNode as _createVNode, isVNode as _isVNode, createElementVNode as _createElementVNode } from \\"vue\\";
import { defineComponent } from 'vue';
function _isSlot(s) {
Expand All @@ -203,7 +203,7 @@ const A = defineComponent({
setup(_, {
slots
}) {
return () => _createVNode(\\"span\\", null, [slots.default()]);
return () => _createElementVNode(\\"span\\", null, [slots.default()]);
}
});
Expand All @@ -221,15 +221,15 @@ a = _createVNode(A, null, _isSlot(a) ? a : {
`;

exports[`select: select 1`] = `
"import { withDirectives as _withDirectives, vModelSelect as _vModelSelect, createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { withDirectives as _withDirectives, vModelSelect as _vModelSelect, createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
_withDirectives(_createVNode(\\"select\\", {
_withDirectives(_createElementVNode(\\"select\\", {
\\"onUpdate:modelValue\\": $event => test = $event
}, [_createVNode(\\"option\\", {
}, [_createElementVNode(\\"option\\", {
\\"value\\": \\"1\\"
}, [_createTextVNode(\\"a\\")]), _createVNode(\\"option\\", {
}, [_createTextVNode(\\"a\\")]), _createElementVNode(\\"option\\", {
\\"value\\": 2
}, [_createTextVNode(\\"b\\")]), _createVNode(\\"option\\", {
}, [_createTextVNode(\\"b\\")]), _createElementVNode(\\"option\\", {
\\"value\\": 3
}, [_createTextVNode(\\"c\\")])], 8, [\\"onUpdate:modelValue\\"]), [[_vModelSelect, test]]);"
`;
Expand All @@ -240,39 +240,39 @@ custom(\\"div\\", null, [_createTextVNode(\\"pragma\\")]);"
`;

exports[`should keep \`import * as Vue from "vue"\`: should keep \`import * as Vue from "vue"\` 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
import * as Vue from 'vue';
_createVNode(\\"div\\", null, [_createTextVNode(\\"Vue\\")]);"
_createElementVNode(\\"div\\", null, [_createTextVNode(\\"Vue\\")]);"
`;

exports[`single no need for a mergeProps call: single no need for a mergeProps call 1`] = `
"import { createVNode as _createVNode, createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"div\\", x, [_createTextVNode(\\"single\\")], 16);"
_createElementVNode(\\"div\\", x, [_createTextVNode(\\"single\\")], 16);"
`;

exports[`specifiers should be merged into a single importDeclaration: specifiers should be merged into a single importDeclaration 1`] = `
"import { createVNode as _createVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode } from \\"vue\\";
import { createVNode, Fragment as _Fragment } from 'vue';
import { vShow } from 'vue';
_createVNode(_Fragment, null, null);"
_createElementVNode(_Fragment, null, null);"
`;

exports[`textarea: textarea 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vModelText as _vModelText } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"textarea\\", {
_withDirectives(_createElementVNode(\\"textarea\\", {
\\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test]]);"
`;

exports[`use "@jsx" comment specify pragma: use "@jsx" comment specify pragma 1`] = `
"import { createTextVNode as _createTextVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createTextVNode as _createTextVNode } from \\"vue\\";
/* @jsx custom */
custom(\\"div\\", {
_createElementVNode(\\"div\\", {
\\"id\\": \\"custom\\"
}, [_createTextVNode(\\"Hello\\")]);"
`;
Expand All @@ -293,7 +293,7 @@ _createVNode(_resolveComponent(\\"A\\"), null, slots);"
`;

exports[`v-model target value support variable: v-model target value support variable 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, Fragment as _Fragment } from \\"vue\\";
"import { createElementVNode as _createElementVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, Fragment as _Fragment } from \\"vue\\";
const foo = 'foo';
const a = () => 'a';
Expand All @@ -302,7 +302,7 @@ const b = {
c: 'c'
};
_createVNode(_Fragment, null, [_createVNode(_resolveComponent(\\"A\\"), {
_createElementVNode(_Fragment, null, [_createVNode(_resolveComponent(\\"A\\"), {
[foo]: xx,
[\\"onUpdate\\" + foo]: $event => xx = $event
}, null, 16), _createVNode(_resolveComponent(\\"B\\"), {
Expand Down Expand Up @@ -339,15 +339,15 @@ _createVNode(_Fragment, null, [_createVNode(_resolveComponent(\\"A\\"), {
`;

exports[`v-show: v-show 1`] = `
"import { withDirectives as _withDirectives, createVNode as _createVNode, vShow as _vShow, createTextVNode as _createTextVNode } from \\"vue\\";
"import { withDirectives as _withDirectives, createElementVNode as _createElementVNode, vShow as _vShow, createTextVNode as _createTextVNode } from \\"vue\\";
_withDirectives(_createVNode(\\"div\\", null, [_createTextVNode(\\"vShow\\")], 512), [[_vShow, x]]);"
_withDirectives(_createElementVNode(\\"div\\", null, [_createTextVNode(\\"vShow\\")], 512), [[_vShow, x]]);"
`;

exports[`vHtml: vHtml 1`] = `
"import { createVNode as _createVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode } from \\"vue\\";
_createVNode(\\"h1\\", {
_createElementVNode(\\"h1\\", {
\\"innerHTML\\": \\"<div>foo</div>\\"
}, null, 8, [\\"innerHTML\\"]);"
`;
Expand All @@ -371,9 +371,9 @@ _createVNode(_resolveComponent(\\"C\\"), {
`;

exports[`vText: vText 1`] = `
"import { createVNode as _createVNode } from \\"vue\\";
"import { createElementVNode as _createElementVNode } from \\"vue\\";
_createVNode(\\"div\\", {
_createElementVNode(\\"div\\", {
\\"textContent\\": text
}, null, 8, [\\"textContent\\"]);"
`;

0 comments on commit cb21ed9

Please sign in to comment.