fix: wrong compilation result of withDirectives (#404)

This commit is contained in:
Amour1688 2021-04-30 01:06:23 +08:00
parent cbad0e5c5f
commit 224196d3ec
5 changed files with 66 additions and 58 deletions

View File

@ -242,8 +242,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
values.forEach((value, index) => { values.forEach((value, index) => {
const propName = args[index]; const propName = args[index];
// v-model target with variable // v-model target with variable
// const isIdentifierProp = t.isIdentifier(propName); const isDynamic = propName && !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
const isDynamic = !t.isStringLiteral(propName) && !t.isNullLiteral(propName);
// must be v-model or v-models and is a component // must be v-model or v-models and is a component
if (!directive) { if (!directive) {

View File

@ -1,7 +1,7 @@
import * as t from '@babel/types'; import * as t from '@babel/types';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import { createIdentifier } from './utils'; import { createIdentifier } from './utils';
import { State, ExcludesBoolean } from '.'; import { State } from '.';
export type Tag = t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression; export type Tag = t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression;
@ -24,7 +24,7 @@ const getType = (path: NodePath<t.JSXOpeningElement>) => {
return typePath ? typePath.get('value').node : null; return typePath ? typePath.get('value').node : null;
}; };
const parseModifiers = (value: t.ArrayExpression): string[] => ( const parseModifiers = (value: any): string[] => (
t.isArrayExpression(value) t.isArrayExpression(value)
? value.elements ? value.elements
.map((el) => (t.isStringLiteral(el) ? el.value : '')) .map((el) => (t.isStringLiteral(el) ? el.value : ''))
@ -64,37 +64,38 @@ const parseDirectives = (params: {
const shouldResolve = !['html', 'text', 'model', 'models'].includes(directiveName) const shouldResolve = !['html', 'text', 'model', 'models'].includes(directiveName)
|| (isVModel && !isComponent); || (isVModel && !isComponent);
if (['models', 'model'].includes(directiveName)) { let modifiers = underscoreModifiers;
if (t.isArrayExpression(value)) {
const elementsList = isVModels ? value.elements! : [value];
elementsList.forEach((element) => { if (t.isArrayExpression(value)) {
if (isVModels && !t.isArrayExpression(element)) { const elementsList = isVModels ? value.elements! : [value];
throw new Error('You should pass a Two-dimensional Arrays to v-models');
}
const { elements } = element as t.ArrayExpression; elementsList.forEach((element) => {
const [first, second, third] = elements; if (isVModels && !t.isArrayExpression(element)) {
let modifiers = underscoreModifiers; throw new Error('You should pass a Two-dimensional Arrays to v-models');
}
if (second && !t.isArrayExpression(second) && !t.isSpreadElement(second)) { const { elements } = element as t.ArrayExpression;
args.push(second); const [first, second, third] = elements;
modifiers = parseModifiers(third as t.ArrayExpression);
} else if (t.isArrayExpression(second)) { if (second && !t.isArrayExpression(second) && !t.isSpreadElement(second)) {
args.push(t.nullLiteral()); args.push(second);
modifiers = parseModifiers(second); modifiers = parseModifiers(third as t.ArrayExpression);
} else { } else if (t.isArrayExpression(second)) {
// work as v-model={[value]} or v-models={[[value]]} if (!shouldResolve) {
args.push(t.nullLiteral()); args.push(t.nullLiteral());
} }
modifiersSet.push(new Set(modifiers)); modifiers = parseModifiers(second);
vals.push(first as t.Expression); } else if (!shouldResolve) {
}); // work as v-model={[value]} or v-models={[[value]]}
} else if (isVModel) { args.push(t.nullLiteral());
// work as v-model={value} }
args.push(t.nullLiteral()); modifiersSet.push(new Set(modifiers));
modifiersSet.push(new Set(underscoreModifiers)); vals.push(first as t.Expression);
} });
} else if (isVModel && !shouldResolve) {
// work as v-model={value}
args.push(t.nullLiteral());
modifiersSet.push(new Set(underscoreModifiers));
} else { } else {
modifiersSet.push(new Set(underscoreModifiers)); modifiersSet.push(new Set(underscoreModifiers));
} }
@ -107,7 +108,9 @@ const parseDirectives = (params: {
directive: shouldResolve ? [ directive: shouldResolve ? [
resolveDirective(path, state, tag, directiveName), resolveDirective(path, state, tag, directiveName),
vals[0] || value, vals[0] || value,
!!modifiersSet[0]?.size && t.unaryExpression('void', t.numericLiteral(0), true), modifiersSet[0]?.size
? args[0] || t.unaryExpression('void', t.numericLiteral(0), true)
: args[0],
!!modifiersSet[0]?.size && t.objectExpression( !!modifiersSet[0]?.size && t.objectExpression(
[...modifiersSet[0]].map( [...modifiersSet[0]].map(
(modifier) => t.objectProperty( (modifier) => t.objectProperty(
@ -116,7 +119,7 @@ const parseDirectives = (params: {
), ),
), ),
), ),
].filter(Boolean as any as ExcludesBoolean) : undefined, ].filter(Boolean) as t.Expression[] : undefined,
}; };
}; };

View File

@ -36,6 +36,24 @@ exports[`custom directive: custom directive 1`] = `
_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);" _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);"
`; `;
exports[`custom directive: custom directive 2`] = `
"import { 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', {
a: true,
b: true
}]]), _withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"xxx\\"), x, void 0, {
a: true,
b: true
}]]), _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, y, {
a: true,
b: true
}]])]);"
`;
exports[`disable object slot syntax with defaultSlot: defaultSlot 1`] = ` exports[`disable object slot syntax with defaultSlot: defaultSlot 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent } from \\"vue\\"; "import { createVNode as _createVNode, resolveComponent as _resolveComponent } from \\"vue\\";

View File

@ -257,32 +257,6 @@ describe('Transform JSX', () => {
}); });
describe('directive', () => { describe('directive', () => {
test('custom', () => {
const calls: number[] = [];
const customDirective = {
mounted() {
calls.push(1);
},
};
const wrapper = shallowMount({
directives: { custom: customDirective },
setup() {
return () => (
<a
v-custom={{
value: 123,
modifiers: { modifier: true },
arg: 'arg',
}}
/>
);
},
});
const node = wrapper.vm.$.subTree;
expect(calls).toEqual(expect.arrayContaining([1]));
expect(node.dirs).toHaveLength(1);
});
test('vHtml', () => { test('vHtml', () => {
const wrapper = shallowMount({ const wrapper = shallowMount({
setup() { setup() {

View File

@ -139,6 +139,20 @@ const transpile = (
a = <A>{a}</A>; a = <A>{a}</A>;
`, `,
}, },
{
name: 'custom directive',
from: `
<>
<A v-xxx={x} />
<A v-xxx={[x]} />
<A v-xxx={[x, 'y']} />
<A v-xxx={[x, 'y', ['a', 'b']]} />
<A v-xxx={[x, ['a', 'b']]} />
<A v-xxx={[x, y, ['a', 'b']]} />
<A v-xxx={[x, y, ['a', 'b']]} />
</>
`,
},
{ {
name: 'vModels', name: 'vModels',
from: '<C v-models={[[foo, ["modifier"]], [bar, "bar", ["modifier1", "modifier2"]]]} />', from: '<C v-models={[[foo, ["modifier"]], [bar, "bar", ["modifier1", "modifier2"]]]} />',