mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2025-01-11 00:39:10 +08:00
refactor: Dedupe props in an object literal
This commit is contained in:
parent
b8aa96eb20
commit
dd787c0256
@ -1,13 +1,11 @@
|
|||||||
import syntaxJsx from '@babel/plugin-syntax-jsx';
|
import syntaxJsx from '@babel/plugin-syntax-jsx';
|
||||||
import tranformVueJSX from './transform-vue-jsx';
|
import tranformVueJSX from './transform-vue-jsx';
|
||||||
import sugarVModel from './sugar-v-model';
|
|
||||||
import sugarFragment from './sugar-fragment';
|
import sugarFragment from './sugar-fragment';
|
||||||
|
|
||||||
export default ({ types: t }) => ({
|
export default ({ types: t }) => ({
|
||||||
name: 'babel-plugin-jsx',
|
name: 'babel-plugin-jsx',
|
||||||
inherits: syntaxJsx,
|
inherits: syntaxJsx,
|
||||||
visitor: {
|
visitor: {
|
||||||
...sugarVModel(t),
|
|
||||||
...tranformVueJSX(t),
|
...tranformVueJSX(t),
|
||||||
...sugarFragment(t),
|
...sugarFragment(t),
|
||||||
},
|
},
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
import camelCase from 'camelcase';
|
|
||||||
import { addNamespace } from '@babel/helper-module-imports';
|
|
||||||
import { createIdentifier, checkIsComponent } from './utils';
|
|
||||||
|
|
||||||
const cachedCamelCase = (() => {
|
|
||||||
const cache = Object.create(null);
|
|
||||||
return (string) => {
|
|
||||||
if (!cache[string]) {
|
|
||||||
cache[string] = camelCase(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache[string];
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
const startsWithCamel = (string, match) => string.startsWith(match)
|
|
||||||
|| string.startsWith(cachedCamelCase(match));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add property to a JSX element
|
|
||||||
*
|
|
||||||
* @param t
|
|
||||||
* @param path JSXOpeningElement
|
|
||||||
* @param value string
|
|
||||||
*/
|
|
||||||
const addProp = (path, value) => {
|
|
||||||
path.node.attributes.push(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get JSX element tag name
|
|
||||||
*
|
|
||||||
* @param path Path<JSXOpeningElement>
|
|
||||||
*/
|
|
||||||
const getTagName = (path) => path.get('name.name').node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get JSX element type
|
|
||||||
*
|
|
||||||
* @param t
|
|
||||||
* @param path Path<JSXOpeningElement>
|
|
||||||
*/
|
|
||||||
const getType = (t, path) => {
|
|
||||||
const typePath = path
|
|
||||||
.get('attributes')
|
|
||||||
.find(
|
|
||||||
(attributePath) => t.isJSXAttribute(attributePath)
|
|
||||||
&& t.isJSXIdentifier(attributePath.get('name'))
|
|
||||||
&& attributePath.get('name.name').node === 'type'
|
|
||||||
&& t.isStringLiteral(attributePath.get('value')),
|
|
||||||
);
|
|
||||||
|
|
||||||
return typePath ? typePath.get('value.value').node : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param t
|
|
||||||
* Transform vModel
|
|
||||||
*/
|
|
||||||
const getModelDirective = (t, path, state, value) => {
|
|
||||||
const tag = getTagName(path);
|
|
||||||
const type = getType(t, path);
|
|
||||||
|
|
||||||
addProp(path, t.jsxSpreadAttribute(
|
|
||||||
t.objectExpression([
|
|
||||||
t.objectProperty(
|
|
||||||
t.stringLiteral('onUpdate:modelValue'),
|
|
||||||
t.arrowFunctionExpression(
|
|
||||||
[t.identifier('$event')],
|
|
||||||
t.assignmentExpression('=', value, t.identifier('$event')),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
));
|
|
||||||
|
|
||||||
if (checkIsComponent(t, path)) {
|
|
||||||
addProp(path, t.jsxAttribute(t.jsxIdentifier('modelValue'), t.jsxExpressionContainer(value)));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modelToUse;
|
|
||||||
switch (tag) {
|
|
||||||
case 'select':
|
|
||||||
modelToUse = createIdentifier(t, state, 'vModelSelect');
|
|
||||||
break;
|
|
||||||
case 'textarea':
|
|
||||||
modelToUse = createIdentifier(t, state, 'vModelText');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
switch (type) {
|
|
||||||
case 'checkbox':
|
|
||||||
modelToUse = createIdentifier(t, state, 'vModelCheckbox');
|
|
||||||
break;
|
|
||||||
case 'radio':
|
|
||||||
modelToUse = createIdentifier(t, state, 'vModelRadio');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
modelToUse = createIdentifier(t, state, 'vModelText');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return modelToUse;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse vModel metadata
|
|
||||||
*
|
|
||||||
* @param t
|
|
||||||
* @param path JSXAttribute
|
|
||||||
* @returns null | Object<{ modifiers: Set<string>, valuePath: Path<Expression>}>
|
|
||||||
*/
|
|
||||||
const parseVModel = (t, path) => {
|
|
||||||
if (t.isJSXNamespacedName(path.get('name')) || !startsWithCamel(path.get('name.name').node, 'v-model')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!t.isJSXExpressionContainer(path.get('value'))) {
|
|
||||||
throw new Error('You have to use JSX Expression inside your v-model');
|
|
||||||
}
|
|
||||||
|
|
||||||
const modifiers = path.get('name.name').node.split('_');
|
|
||||||
modifiers.shift();
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiers: new Set(modifiers),
|
|
||||||
value: path.get('value.expression').node,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default (t) => ({
|
|
||||||
JSXAttribute: {
|
|
||||||
exit(path, state) {
|
|
||||||
const parsed = parseVModel(t, path);
|
|
||||||
if (!parsed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.get('vue')) {
|
|
||||||
state.set('vue', addNamespace(path, 'vue'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { modifiers, value } = parsed;
|
|
||||||
|
|
||||||
const parent = path.parentPath;
|
|
||||||
// v-model={xx} --> v-_model={[directive, xx, void 0, { a: true, b: true }]}
|
|
||||||
const directive = getModelDirective(t, parent, state, value);
|
|
||||||
if (directive) {
|
|
||||||
path.replaceWith(
|
|
||||||
t.jsxAttribute(
|
|
||||||
t.jsxIdentifier('_model'), // TODO
|
|
||||||
t.jsxExpressionContainer(
|
|
||||||
t.arrayExpression([
|
|
||||||
directive,
|
|
||||||
value,
|
|
||||||
modifiers.size && t.unaryExpression('void', t.numericLiteral(0), true),
|
|
||||||
modifiers.size && t.objectExpression(
|
|
||||||
[...modifiers].map(
|
|
||||||
(modifier) => t.objectProperty(
|
|
||||||
t.identifier(modifier),
|
|
||||||
t.booleanLiteral(true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
].filter(Boolean)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
path.remove();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
@ -10,11 +10,12 @@ import {
|
|||||||
transformJSXText,
|
transformJSXText,
|
||||||
transformJSXExpressionContainer,
|
transformJSXExpressionContainer,
|
||||||
transformJSXSpreadChild,
|
transformJSXSpreadChild,
|
||||||
|
parseDirectives,
|
||||||
|
isFragment,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
const xlinkRE = /^xlink([A-Z])/;
|
const xlinkRE = /^xlink([A-Z])/;
|
||||||
const onRE = /^on[A-Z][a-z]+$/;
|
const onRE = /^on[^a-z]/;
|
||||||
const rootAttributes = ['class', 'style'];
|
|
||||||
|
|
||||||
const isOn = (key) => onRE.test(key);
|
const isOn = (key) => onRE.test(key);
|
||||||
|
|
||||||
@ -22,28 +23,13 @@ const transformJSXSpreadAttribute = (t, path, mergeArgs) => {
|
|||||||
const argument = path.get('argument').node;
|
const argument = path.get('argument').node;
|
||||||
const { properties } = argument;
|
const { properties } = argument;
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
return t.spreadElement(argument);
|
// argument is an Identifier
|
||||||
|
mergeArgs.push(argument);
|
||||||
|
} else {
|
||||||
|
mergeArgs.push(t.objectExpression(properties));
|
||||||
}
|
}
|
||||||
return t.spreadElement(t.objectExpression(properties.filter((property) => {
|
|
||||||
const { key, value } = property;
|
|
||||||
const name = key.value;
|
|
||||||
if (rootAttributes.includes(name)) {
|
|
||||||
mergeArgs.push(
|
|
||||||
t.objectExpression([
|
|
||||||
t.objectProperty(
|
|
||||||
t.stringLiteral(name),
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const needToMerge = (name) => rootAttributes.includes(name) || isOn(name);
|
|
||||||
|
|
||||||
const getJSXAttributeValue = (t, path) => {
|
const getJSXAttributeValue = (t, path) => {
|
||||||
const valuePath = path.get('value');
|
const valuePath = path.get('value');
|
||||||
if (valuePath.isJSXElement()) {
|
if (valuePath.isJSXElement()) {
|
||||||
@ -81,28 +67,68 @@ const isConstant = (t, path) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mergeAsArray = (t, existing, incoming) => {
|
||||||
|
if (t.isArrayExpression(existing.value)) {
|
||||||
|
existing.value.elements.push(incoming.value);
|
||||||
|
} else {
|
||||||
|
existing.value = t.arrayExpression([
|
||||||
|
existing.value,
|
||||||
|
incoming.value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dedupeProperties = (t, properties = []) => {
|
||||||
|
const knownProps = new Map();
|
||||||
|
const deduped = [];
|
||||||
|
properties.forEach((prop) => {
|
||||||
|
const { key: { value: name } = {} } = prop;
|
||||||
|
const existing = knownProps.get(name);
|
||||||
|
if (existing) {
|
||||||
|
if (name === 'style' || name === 'class' || name.startsWith('on')) {
|
||||||
|
mergeAsArray(t, existing, prop);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
knownProps.set(name, prop);
|
||||||
|
deduped.push(prop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deduped;
|
||||||
|
};
|
||||||
|
|
||||||
const buildProps = (t, path, state) => {
|
const buildProps = (t, path, state) => {
|
||||||
|
const tag = getTag(t, path);
|
||||||
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
||||||
const props = path.get('openingElement').get('attributes');
|
const props = path.get('openingElement').get('attributes');
|
||||||
const directives = [];
|
const directives = [];
|
||||||
|
const dynamicPropNames = new Set();
|
||||||
|
|
||||||
|
let patchFlag = 0;
|
||||||
|
|
||||||
|
if (isFragment(t, path.get('openingElement.name'))) {
|
||||||
|
patchFlag |= PatchFlags.STABLE_FRAGMENT;
|
||||||
|
}
|
||||||
|
|
||||||
if (props.length === 0) {
|
if (props.length === 0) {
|
||||||
return {
|
return {
|
||||||
|
tag,
|
||||||
props: t.nullLiteral(),
|
props: t.nullLiteral(),
|
||||||
directives,
|
directives,
|
||||||
|
patchFlag,
|
||||||
|
dynamicPropNames,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const propsExpression = [];
|
const properties = [];
|
||||||
|
|
||||||
// patchFlag analysis
|
// patchFlag analysis
|
||||||
let patchFlag = 0;
|
|
||||||
let hasRef = false;
|
let hasRef = false;
|
||||||
let hasClassBinding = false;
|
let hasClassBinding = false;
|
||||||
let hasStyleBinding = false;
|
let hasStyleBinding = false;
|
||||||
let hasHydrationEventBinding = false;
|
let hasHydrationEventBinding = false;
|
||||||
let hasDynamicKeys = false;
|
let hasDynamicKeys = false;
|
||||||
|
|
||||||
const dynamicPropNames = [];
|
|
||||||
const mergeArgs = [];
|
const mergeArgs = [];
|
||||||
|
|
||||||
props
|
props
|
||||||
@ -110,10 +136,6 @@ const buildProps = (t, path, state) => {
|
|||||||
if (prop.isJSXAttribute()) {
|
if (prop.isJSXAttribute()) {
|
||||||
let name = getJSXAttributeName(t, prop);
|
let name = getJSXAttributeName(t, prop);
|
||||||
|
|
||||||
if (name === '_model') {
|
|
||||||
name = 'onUpdate:modelValue';
|
|
||||||
}
|
|
||||||
|
|
||||||
const attributeValue = getJSXAttributeValue(t, prop);
|
const attributeValue = getJSXAttributeValue(t, prop);
|
||||||
|
|
||||||
if (!isConstant(t, attributeValue) || name === 'ref') {
|
if (!isConstant(t, attributeValue) || name === 'ref') {
|
||||||
@ -139,69 +161,85 @@ const buildProps = (t, path, state) => {
|
|||||||
name !== 'key'
|
name !== 'key'
|
||||||
&& !isDirective(name)
|
&& !isDirective(name)
|
||||||
&& name !== 'on'
|
&& name !== 'on'
|
||||||
&& !dynamicPropNames.includes(name)
|
|
||||||
) {
|
) {
|
||||||
dynamicPropNames.push(name);
|
dynamicPropNames.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
|
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
|
||||||
const transformOn = addDefault(
|
if (!state.get('transformOn')) {
|
||||||
path,
|
state.set('transformOn', addDefault(
|
||||||
'@ant-design-vue/babel-helper-vue-transform-on',
|
path,
|
||||||
{ nameHint: '_transformOn' },
|
'@ant-design-vue/babel-helper-vue-transform-on',
|
||||||
);
|
{ nameHint: '_transformOn' },
|
||||||
|
));
|
||||||
|
}
|
||||||
mergeArgs.push(t.callExpression(
|
mergeArgs.push(t.callExpression(
|
||||||
transformOn,
|
state.get('transformOn'),
|
||||||
[attributeValue || t.booleanLiteral(true)],
|
[attributeValue || t.booleanLiteral(true)],
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDirective(name) || name === 'onUpdate:modelValue') {
|
if (isDirective(name)) {
|
||||||
if (name === 'onUpdate:modelValue') {
|
const { directive, modifiers, directiveName } = parseDirectives(
|
||||||
directives.push(attributeValue);
|
t, {
|
||||||
|
tag,
|
||||||
|
isComponent,
|
||||||
|
name,
|
||||||
|
path: prop,
|
||||||
|
state,
|
||||||
|
value: attributeValue,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (directive) {
|
||||||
|
directives.push(t.arrayExpression(directive));
|
||||||
} else {
|
} else {
|
||||||
const directiveName = name.startsWith('v-')
|
// must be v-model and is a component
|
||||||
? name.replace('v-', '')
|
properties.push(t.objectProperty(
|
||||||
: name.replace(`v${name[1]}`, name[1].toLowerCase());
|
t.stringLiteral('modelValue'),
|
||||||
if (directiveName === 'show') {
|
attributeValue,
|
||||||
directives.push(t.arrayExpression([
|
));
|
||||||
createIdentifier(t, state, 'vShow'),
|
|
||||||
attributeValue,
|
dynamicPropNames.add('modelValue');
|
||||||
]));
|
|
||||||
} else {
|
if (modifiers.size) {
|
||||||
directives.push(t.arrayExpression([
|
properties.push(t.objectProperty(
|
||||||
t.callExpression(createIdentifier(t, state, 'resolveDirective'), [
|
t.stringLiteral('modelModifiers'),
|
||||||
t.stringLiteral(directiveName),
|
t.objectExpression(
|
||||||
]),
|
[...modifiers].map((modifier) => (
|
||||||
attributeValue,
|
t.objectProperty(
|
||||||
]));
|
t.stringLiteral(modifier),
|
||||||
|
t.booleanLiteral(true),
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
if (directiveName === 'model') {
|
||||||
if (needToMerge(name)) {
|
properties.push(t.objectProperty(
|
||||||
mergeArgs.push(
|
t.stringLiteral('onUpdate:modelValue'),
|
||||||
t.objectExpression([
|
t.arrowFunctionExpression(
|
||||||
t.objectProperty(
|
[t.identifier('$event')],
|
||||||
t.stringLiteral(
|
t.assignmentExpression('=', attributeValue, t.identifier('$event')),
|
||||||
name,
|
|
||||||
),
|
|
||||||
attributeValue,
|
|
||||||
),
|
),
|
||||||
]),
|
));
|
||||||
);
|
|
||||||
|
dynamicPropNames.add('onUpdate:modelValue');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (name.match(xlinkRE)) {
|
if (name.match(xlinkRE)) {
|
||||||
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
|
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
|
||||||
}
|
}
|
||||||
propsExpression.push(t.objectProperty(
|
properties.push(t.objectProperty(
|
||||||
t.stringLiteral(name),
|
t.stringLiteral(name),
|
||||||
attributeValue || t.booleanLiteral(true),
|
attributeValue || t.booleanLiteral(true),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
hasDynamicKeys = true;
|
hasDynamicKeys = true;
|
||||||
propsExpression.push(transformJSXSpreadAttribute(t, prop, mergeArgs));
|
transformJSXSpreadAttribute(t, prop, mergeArgs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -215,7 +253,7 @@ const buildProps = (t, path, state) => {
|
|||||||
if (hasStyleBinding) {
|
if (hasStyleBinding) {
|
||||||
patchFlag |= PatchFlags.STYLE;
|
patchFlag |= PatchFlags.STYLE;
|
||||||
}
|
}
|
||||||
if (dynamicPropNames.length) {
|
if (dynamicPropNames.size) {
|
||||||
patchFlag |= PatchFlags.PROPS;
|
patchFlag |= PatchFlags.PROPS;
|
||||||
}
|
}
|
||||||
if (hasHydrationEventBinding) {
|
if (hasHydrationEventBinding) {
|
||||||
@ -230,14 +268,42 @@ const buildProps = (t, path, state) => {
|
|||||||
patchFlag |= PatchFlags.NEED_PATCH;
|
patchFlag |= PatchFlags.NEED_PATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let propsExpression;
|
||||||
|
|
||||||
|
if (mergeArgs.length) {
|
||||||
|
if (properties.length) {
|
||||||
|
mergeArgs.push(...dedupeProperties(t, properties));
|
||||||
|
}
|
||||||
|
if (mergeArgs.length > 1) {
|
||||||
|
const exps = [];
|
||||||
|
const objectProperties = [];
|
||||||
|
mergeArgs.forEach((arg) => {
|
||||||
|
if (t.isIdentifier(arg) || t.isExpression(arg)) {
|
||||||
|
exps.push(arg);
|
||||||
|
} else {
|
||||||
|
objectProperties.push(arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
propsExpression = t.callExpression(
|
||||||
|
createIdentifier(t, state, 'mergeProps'),
|
||||||
|
[
|
||||||
|
...exps,
|
||||||
|
objectProperties.length
|
||||||
|
&& t.objectExpression(objectProperties),
|
||||||
|
].filter(Boolean),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// single no need for a mergeProps call
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
propsExpression = mergeArgs[0];
|
||||||
|
}
|
||||||
|
} else if (properties.length) {
|
||||||
|
propsExpression = t.objectExpression(dedupeProperties(t, properties));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: mergeArgs.length ? t.callExpression(
|
tag,
|
||||||
createIdentifier(t, state, 'mergeProps'),
|
props: propsExpression,
|
||||||
[
|
|
||||||
...mergeArgs,
|
|
||||||
propsExpression.length && t.objectExpression(propsExpression),
|
|
||||||
].filter(Boolean),
|
|
||||||
) : t.objectExpression(propsExpression),
|
|
||||||
directives,
|
directives,
|
||||||
patchFlag,
|
patchFlag,
|
||||||
dynamicPropNames,
|
dynamicPropNames,
|
||||||
@ -270,19 +336,19 @@ const getChildren = (t, paths) => paths
|
|||||||
throw new Error(`getChildren: ${path.type} is not supported`);
|
throw new Error(`getChildren: ${path.type} is not supported`);
|
||||||
}).filter((value) => (
|
}).filter((value) => (
|
||||||
value !== undefined
|
value !== undefined
|
||||||
&& value !== null
|
&& value !== null
|
||||||
&& !t.isJSXEmptyExpression(value)
|
&& !t.isJSXEmptyExpression(value)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
const transformJSXElement = (t, path, state) => {
|
const transformJSXElement = (t, path, state) => {
|
||||||
const tag = getTag(t, path);
|
|
||||||
const children = t.arrayExpression(getChildren(t, path.get('children')));
|
const children = t.arrayExpression(getChildren(t, path.get('children')));
|
||||||
const {
|
const {
|
||||||
|
tag,
|
||||||
props,
|
props,
|
||||||
directives,
|
directives,
|
||||||
patchFlag,
|
patchFlag,
|
||||||
dynamicPropNames = [],
|
dynamicPropNames,
|
||||||
} = buildProps(t, path, state);
|
} = buildProps(t, path, state);
|
||||||
|
|
||||||
const flagNames = Object.keys(PatchFlagNames)
|
const flagNames = Object.keys(PatchFlagNames)
|
||||||
@ -292,12 +358,17 @@ const transformJSXElement = (t, path, state) => {
|
|||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
||||||
|
const child = children.elements.length === 1 ? children.elements[0] : children;
|
||||||
|
if (state.opts.compatibleProps && !state.get('compatibleProps')) {
|
||||||
|
state.set('compatibleProps', addDefault(
|
||||||
|
path, '@ant-design-vue/babel-helper-vue-compatible-props', { nameHint: '_compatibleProps' },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
const createVNode = t.callExpression(createIdentifier(t, state, 'createVNode'), [
|
const createVNode = t.callExpression(createIdentifier(t, state, 'createVNode'), [
|
||||||
tag,
|
tag,
|
||||||
state.opts.compatibleProps ? t.callExpression(addDefault(
|
state.opts.compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props,
|
||||||
path, '@ant-design-vue/babel-helper-vue-compatible-props', { nameHint: '_compatibleProps' },
|
children.elements[0]
|
||||||
), [props]) : props,
|
|
||||||
children.elements.length
|
|
||||||
? (
|
? (
|
||||||
isComponent
|
isComponent
|
||||||
? t.objectExpression([
|
? t.objectExpression([
|
||||||
@ -306,16 +377,25 @@ const transformJSXElement = (t, path, state) => {
|
|||||||
t.callExpression(createIdentifier(t, state, 'withCtx'), [
|
t.callExpression(createIdentifier(t, state, 'withCtx'), [
|
||||||
t.arrowFunctionExpression(
|
t.arrowFunctionExpression(
|
||||||
[],
|
[],
|
||||||
children,
|
t.isStringLiteral(child)
|
||||||
|
? t.callExpression(
|
||||||
|
createIdentifier(t, state, 'createTextVNode'),
|
||||||
|
[child],
|
||||||
|
)
|
||||||
|
: child,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
t.objectProperty(
|
||||||
|
t.identifier('_'),
|
||||||
|
t.numericLiteral(1),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
: children
|
: child
|
||||||
) : t.nullLiteral(),
|
) : t.nullLiteral(),
|
||||||
patchFlag && t.addComment(t.numericLiteral(patchFlag), 'leading', ` ${flagNames} `),
|
patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false),
|
||||||
dynamicPropNames.length
|
dynamicPropNames.size
|
||||||
&& t.arrayExpression(dynamicPropNames.map((name) => t.stringLiteral(name))),
|
&& t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name))),
|
||||||
].filter(Boolean));
|
].filter(Boolean));
|
||||||
|
|
||||||
if (!directives.length) {
|
if (!directives.length) {
|
||||||
|
@ -43,6 +43,15 @@ const createIdentifier = (t, state, id) => t.memberExpression(state.get('vue'),
|
|||||||
const isDirective = (src) => src.startsWith('v-')
|
const isDirective = (src) => src.startsWith('v-')
|
||||||
|| (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z');
|
|| (src.startsWith('v') && src.length >= 2 && src[1] >= 'A' && src[1] <= 'Z');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a JSXOpeningElement is fragment
|
||||||
|
* @param {*} t
|
||||||
|
* @param {*} path
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
const isFragment = (t, path) => t.isJSXMemberExpression(path)
|
||||||
|
&& path.node.property.name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a JSXOpeningElement is a component
|
* Check if a JSXOpeningElement is a component
|
||||||
*
|
*
|
||||||
@ -54,7 +63,7 @@ const checkIsComponent = (t, path) => {
|
|||||||
const namePath = path.get('name');
|
const namePath = path.get('name');
|
||||||
|
|
||||||
if (t.isJSXMemberExpression(namePath)) {
|
if (t.isJSXMemberExpression(namePath)) {
|
||||||
return namePath.node.property.name !== 'Fragment'; // For withCtx
|
return !isFragment(t, namePath); // For withCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag = namePath.get('name').node;
|
const tag = namePath.get('name').node;
|
||||||
@ -180,6 +189,102 @@ const transformJSXExpressionContainer = (path) => path.get('expression').node;
|
|||||||
*/
|
*/
|
||||||
const transformJSXSpreadChild = (t, path) => t.spreadElement(path.get('expression').node);
|
const transformJSXSpreadChild = (t, path) => t.spreadElement(path.get('expression').node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get JSX element type
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* @param path Path<JSXOpeningElement>
|
||||||
|
*/
|
||||||
|
const getType = (t, path) => {
|
||||||
|
const typePath = path
|
||||||
|
.get('attributes')
|
||||||
|
.find(
|
||||||
|
(attributePath) => t.isJSXAttribute(attributePath)
|
||||||
|
&& t.isJSXIdentifier(attributePath.get('name'))
|
||||||
|
&& attributePath.get('name.name').node === 'type'
|
||||||
|
&& t.isStringLiteral(attributePath.get('value')),
|
||||||
|
);
|
||||||
|
|
||||||
|
return typePath ? typePath.get('value.value').node : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveDirective = (t, path, state, tag, directiveName) => {
|
||||||
|
if (directiveName === 'show') {
|
||||||
|
return createIdentifier(t, state, 'vShow');
|
||||||
|
} if (directiveName === 'model') {
|
||||||
|
let modelToUse;
|
||||||
|
const type = getType(t, path.parentPath);
|
||||||
|
switch (tag.value) {
|
||||||
|
case 'select':
|
||||||
|
modelToUse = createIdentifier(t, state, 'vModelSelect');
|
||||||
|
break;
|
||||||
|
case 'textarea':
|
||||||
|
modelToUse = createIdentifier(t, state, 'vModelText');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
switch (type) {
|
||||||
|
case 'checkbox':
|
||||||
|
modelToUse = createIdentifier(t, state, 'vModelCheckbox');
|
||||||
|
break;
|
||||||
|
case 'radio':
|
||||||
|
modelToUse = createIdentifier(t, state, 'vModelRadio');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modelToUse = createIdentifier(t, state, 'vModelText');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modelToUse;
|
||||||
|
}
|
||||||
|
return t.callExpression(
|
||||||
|
createIdentifier(t, state, 'resolveDirective'), [
|
||||||
|
t.stringLiteral(directiveName),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse directives metadata
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* @param path JSXAttribute
|
||||||
|
* @returns null | Object<{ modifiers: Set<string>, valuePath: Path<Expression>}>
|
||||||
|
*/
|
||||||
|
const parseDirectives = (t, {
|
||||||
|
name, path, value, state, tag, isComponent,
|
||||||
|
}) => {
|
||||||
|
const modifiers = name.split('_');
|
||||||
|
const directiveName = modifiers.shift()
|
||||||
|
.replace(/^v/, '')
|
||||||
|
.replace(/^-/, '')
|
||||||
|
.replace(/^\S/, (s) => s.toLowerCase());
|
||||||
|
|
||||||
|
if (directiveName === 'model' && !t.isJSXExpressionContainer(path.get('value'))) {
|
||||||
|
throw new Error('You have to use JSX Expression inside your v-model');
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiersSet = new Set(modifiers);
|
||||||
|
|
||||||
|
const hasDirective = directiveName !== 'model' || (directiveName === 'model' && !isComponent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
directiveName,
|
||||||
|
modifiers: new Set(modifiers),
|
||||||
|
directive: hasDirective ? [
|
||||||
|
resolveDirective(t, path, state, tag, directiveName),
|
||||||
|
value,
|
||||||
|
modifiersSet.size && t.unaryExpression('void', t.numericLiteral(0), true),
|
||||||
|
modifiersSet.size && t.objectExpression(
|
||||||
|
[...modifiersSet].map(
|
||||||
|
(modifier) => t.objectProperty(
|
||||||
|
t.identifier(modifier),
|
||||||
|
t.booleanLiteral(true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
].filter(Boolean) : undefined,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
@ -193,4 +298,6 @@ export {
|
|||||||
transformJSXExpressionContainer,
|
transformJSXExpressionContainer,
|
||||||
PatchFlags,
|
PatchFlags,
|
||||||
PatchFlagNames,
|
PatchFlagNames,
|
||||||
|
parseDirectives,
|
||||||
|
isFragment,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user