mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2024-11-10 09:39:14 +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 tranformVueJSX from './transform-vue-jsx';
|
||||
import sugarVModel from './sugar-v-model';
|
||||
import sugarFragment from './sugar-fragment';
|
||||
|
||||
export default ({ types: t }) => ({
|
||||
name: 'babel-plugin-jsx',
|
||||
inherits: syntaxJsx,
|
||||
visitor: {
|
||||
...sugarVModel(t),
|
||||
...tranformVueJSX(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,
|
||||
transformJSXExpressionContainer,
|
||||
transformJSXSpreadChild,
|
||||
parseDirectives,
|
||||
isFragment,
|
||||
} from './utils';
|
||||
|
||||
const xlinkRE = /^xlink([A-Z])/;
|
||||
const onRE = /^on[A-Z][a-z]+$/;
|
||||
const rootAttributes = ['class', 'style'];
|
||||
const onRE = /^on[^a-z]/;
|
||||
|
||||
const isOn = (key) => onRE.test(key);
|
||||
|
||||
@ -22,28 +23,13 @@ const transformJSXSpreadAttribute = (t, path, mergeArgs) => {
|
||||
const argument = path.get('argument').node;
|
||||
const { properties } = argument;
|
||||
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 valuePath = path.get('value');
|
||||
if (valuePath.isJSXElement()) {
|
||||
@ -81,28 +67,68 @@ const isConstant = (t, path) => {
|
||||
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 tag = getTag(t, path);
|
||||
const isComponent = checkIsComponent(t, path.get('openingElement'));
|
||||
const props = path.get('openingElement').get('attributes');
|
||||
const directives = [];
|
||||
const dynamicPropNames = new Set();
|
||||
|
||||
let patchFlag = 0;
|
||||
|
||||
if (isFragment(t, path.get('openingElement.name'))) {
|
||||
patchFlag |= PatchFlags.STABLE_FRAGMENT;
|
||||
}
|
||||
|
||||
if (props.length === 0) {
|
||||
return {
|
||||
tag,
|
||||
props: t.nullLiteral(),
|
||||
directives,
|
||||
patchFlag,
|
||||
dynamicPropNames,
|
||||
};
|
||||
}
|
||||
|
||||
const propsExpression = [];
|
||||
const properties = [];
|
||||
|
||||
// patchFlag analysis
|
||||
let patchFlag = 0;
|
||||
let hasRef = false;
|
||||
let hasClassBinding = false;
|
||||
let hasStyleBinding = false;
|
||||
let hasHydrationEventBinding = false;
|
||||
let hasDynamicKeys = false;
|
||||
|
||||
const dynamicPropNames = [];
|
||||
const mergeArgs = [];
|
||||
|
||||
props
|
||||
@ -110,10 +136,6 @@ const buildProps = (t, path, state) => {
|
||||
if (prop.isJSXAttribute()) {
|
||||
let name = getJSXAttributeName(t, prop);
|
||||
|
||||
if (name === '_model') {
|
||||
name = 'onUpdate:modelValue';
|
||||
}
|
||||
|
||||
const attributeValue = getJSXAttributeValue(t, prop);
|
||||
|
||||
if (!isConstant(t, attributeValue) || name === 'ref') {
|
||||
@ -139,69 +161,85 @@ const buildProps = (t, path, state) => {
|
||||
name !== 'key'
|
||||
&& !isDirective(name)
|
||||
&& name !== 'on'
|
||||
&& !dynamicPropNames.includes(name)
|
||||
) {
|
||||
dynamicPropNames.push(name);
|
||||
dynamicPropNames.add(name);
|
||||
}
|
||||
}
|
||||
if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
|
||||
const transformOn = addDefault(
|
||||
path,
|
||||
'@ant-design-vue/babel-helper-vue-transform-on',
|
||||
{ nameHint: '_transformOn' },
|
||||
);
|
||||
if (!state.get('transformOn')) {
|
||||
state.set('transformOn', addDefault(
|
||||
path,
|
||||
'@ant-design-vue/babel-helper-vue-transform-on',
|
||||
{ nameHint: '_transformOn' },
|
||||
));
|
||||
}
|
||||
mergeArgs.push(t.callExpression(
|
||||
transformOn,
|
||||
state.get('transformOn'),
|
||||
[attributeValue || t.booleanLiteral(true)],
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (isDirective(name) || name === 'onUpdate:modelValue') {
|
||||
if (name === 'onUpdate:modelValue') {
|
||||
directives.push(attributeValue);
|
||||
if (isDirective(name)) {
|
||||
const { directive, modifiers, directiveName } = parseDirectives(
|
||||
t, {
|
||||
tag,
|
||||
isComponent,
|
||||
name,
|
||||
path: prop,
|
||||
state,
|
||||
value: attributeValue,
|
||||
},
|
||||
);
|
||||
|
||||
if (directive) {
|
||||
directives.push(t.arrayExpression(directive));
|
||||
} else {
|
||||
const directiveName = name.startsWith('v-')
|
||||
? name.replace('v-', '')
|
||||
: name.replace(`v${name[1]}`, name[1].toLowerCase());
|
||||
if (directiveName === 'show') {
|
||||
directives.push(t.arrayExpression([
|
||||
createIdentifier(t, state, 'vShow'),
|
||||
attributeValue,
|
||||
]));
|
||||
} else {
|
||||
directives.push(t.arrayExpression([
|
||||
t.callExpression(createIdentifier(t, state, 'resolveDirective'), [
|
||||
t.stringLiteral(directiveName),
|
||||
]),
|
||||
attributeValue,
|
||||
]));
|
||||
// must be v-model and is a component
|
||||
properties.push(t.objectProperty(
|
||||
t.stringLiteral('modelValue'),
|
||||
attributeValue,
|
||||
));
|
||||
|
||||
dynamicPropNames.add('modelValue');
|
||||
|
||||
if (modifiers.size) {
|
||||
properties.push(t.objectProperty(
|
||||
t.stringLiteral('modelModifiers'),
|
||||
t.objectExpression(
|
||||
[...modifiers].map((modifier) => (
|
||||
t.objectProperty(
|
||||
t.stringLiteral(modifier),
|
||||
t.booleanLiteral(true),
|
||||
)
|
||||
)),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (needToMerge(name)) {
|
||||
mergeArgs.push(
|
||||
t.objectExpression([
|
||||
t.objectProperty(
|
||||
t.stringLiteral(
|
||||
name,
|
||||
),
|
||||
attributeValue,
|
||||
|
||||
if (directiveName === 'model') {
|
||||
properties.push(t.objectProperty(
|
||||
t.stringLiteral('onUpdate:modelValue'),
|
||||
t.arrowFunctionExpression(
|
||||
[t.identifier('$event')],
|
||||
t.assignmentExpression('=', attributeValue, t.identifier('$event')),
|
||||
),
|
||||
]),
|
||||
);
|
||||
));
|
||||
|
||||
dynamicPropNames.add('onUpdate:modelValue');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (name.match(xlinkRE)) {
|
||||
name = name.replace(xlinkRE, (_, firstCharacter) => `xlink:${firstCharacter.toLowerCase()}`);
|
||||
}
|
||||
propsExpression.push(t.objectProperty(
|
||||
properties.push(t.objectProperty(
|
||||
t.stringLiteral(name),
|
||||
attributeValue || t.booleanLiteral(true),
|
||||
));
|
||||
} else {
|
||||
hasDynamicKeys = true;
|
||||
propsExpression.push(transformJSXSpreadAttribute(t, prop, mergeArgs));
|
||||
transformJSXSpreadAttribute(t, prop, mergeArgs);
|
||||
}
|
||||
});
|
||||
|
||||
@ -215,7 +253,7 @@ const buildProps = (t, path, state) => {
|
||||
if (hasStyleBinding) {
|
||||
patchFlag |= PatchFlags.STYLE;
|
||||
}
|
||||
if (dynamicPropNames.length) {
|
||||
if (dynamicPropNames.size) {
|
||||
patchFlag |= PatchFlags.PROPS;
|
||||
}
|
||||
if (hasHydrationEventBinding) {
|
||||
@ -230,14 +268,42 @@ const buildProps = (t, path, state) => {
|
||||
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 {
|
||||
props: mergeArgs.length ? t.callExpression(
|
||||
createIdentifier(t, state, 'mergeProps'),
|
||||
[
|
||||
...mergeArgs,
|
||||
propsExpression.length && t.objectExpression(propsExpression),
|
||||
].filter(Boolean),
|
||||
) : t.objectExpression(propsExpression),
|
||||
tag,
|
||||
props: propsExpression,
|
||||
directives,
|
||||
patchFlag,
|
||||
dynamicPropNames,
|
||||
@ -270,19 +336,19 @@ const getChildren = (t, paths) => paths
|
||||
throw new Error(`getChildren: ${path.type} is not supported`);
|
||||
}).filter((value) => (
|
||||
value !== undefined
|
||||
&& value !== null
|
||||
&& !t.isJSXEmptyExpression(value)
|
||||
&& value !== null
|
||||
&& !t.isJSXEmptyExpression(value)
|
||||
));
|
||||
|
||||
|
||||
const transformJSXElement = (t, path, state) => {
|
||||
const tag = getTag(t, path);
|
||||
const children = t.arrayExpression(getChildren(t, path.get('children')));
|
||||
const {
|
||||
tag,
|
||||
props,
|
||||
directives,
|
||||
patchFlag,
|
||||
dynamicPropNames = [],
|
||||
dynamicPropNames,
|
||||
} = buildProps(t, path, state);
|
||||
|
||||
const flagNames = Object.keys(PatchFlagNames)
|
||||
@ -292,12 +358,17 @@ const transformJSXElement = (t, path, state) => {
|
||||
.join(', ');
|
||||
|
||||
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'), [
|
||||
tag,
|
||||
state.opts.compatibleProps ? t.callExpression(addDefault(
|
||||
path, '@ant-design-vue/babel-helper-vue-compatible-props', { nameHint: '_compatibleProps' },
|
||||
), [props]) : props,
|
||||
children.elements.length
|
||||
state.opts.compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props,
|
||||
children.elements[0]
|
||||
? (
|
||||
isComponent
|
||||
? t.objectExpression([
|
||||
@ -306,16 +377,25 @@ const transformJSXElement = (t, path, state) => {
|
||||
t.callExpression(createIdentifier(t, state, 'withCtx'), [
|
||||
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(),
|
||||
patchFlag && t.addComment(t.numericLiteral(patchFlag), 'leading', ` ${flagNames} `),
|
||||
dynamicPropNames.length
|
||||
&& t.arrayExpression(dynamicPropNames.map((name) => t.stringLiteral(name))),
|
||||
patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false),
|
||||
dynamicPropNames.size
|
||||
&& t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name))),
|
||||
].filter(Boolean));
|
||||
|
||||
if (!directives.length) {
|
||||
|
@ -43,6 +43,15 @@ const createIdentifier = (t, state, id) => t.memberExpression(state.get('vue'),
|
||||
const isDirective = (src) => src.startsWith('v-')
|
||||
|| (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
|
||||
*
|
||||
@ -54,7 +63,7 @@ const checkIsComponent = (t, path) => {
|
||||
const namePath = path.get('name');
|
||||
|
||||
if (t.isJSXMemberExpression(namePath)) {
|
||||
return namePath.node.property.name !== 'Fragment'; // For withCtx
|
||||
return !isFragment(t, namePath); // For withCtx
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
createIdentifier,
|
||||
@ -193,4 +298,6 @@ export {
|
||||
transformJSXExpressionContainer,
|
||||
PatchFlags,
|
||||
PatchFlagNames,
|
||||
parseDirectives,
|
||||
isFragment,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user