Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: use compiler hints #497

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
.find((prop) => t.isObjectProperty(prop) && t.isStringLiteral(prop.key) && prop.key.value === 'class');
if (t.isObjectProperty(klass)) {
klass.value = t.callExpression(createIdentifier(state, 'normalizeClass'), [klass.value as any]);
}
}
if (hasStyleBinding) {
const style = propsExpression.properties
.find((prop) => t.isObjectProperty(prop) && t.isStringLiteral(prop.key) && prop.key.value === 'style');
if (t.isObjectProperty(style)) {
style.value = t.callExpression(createIdentifier(state, 'normalizeStyle'), [style.value as any]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lmk if there's a better way to do this.

}
}
}
}
}

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\\"]);"
`;