mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2024-11-10 09:39:14 +08:00
feat: support optional enableObjectSlots
(#259)
* feat: user can toggle off the object slot syntax parser (#257) * chore(deps-dev): bump vue from 3.0.0 to 3.0.5 (#246) Bumps [vue](https://github.com/vuejs/vue) from 3.0.0 to 3.0.5. - [Release notes](https://github.com/vuejs/vue/releases) - [Commits](https://github.com/vuejs/vue/commits) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> * chore(deps-dev): bump @typescript-eslint/eslint-plugin (#244) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.9.1 to 4.11.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.11.1/packages/eslint-plugin) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> * chore(deps-dev): bump @typescript-eslint/parser from 4.9.1 to 4.11.1 (#245) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.9.1 to 4.11.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.11.1/packages/parser) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> * feat: user can toggle off the object slot syntax parser Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> * feat: default value of enableObjectSlots should be true Co-authored-by: 逆寒 <869732751@qq.com> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
This commit is contained in:
parent
67d16001e6
commit
9238fab697
@ -18,6 +18,7 @@ export interface Opts {
|
|||||||
optimize?: boolean;
|
optimize?: boolean;
|
||||||
mergeProps?: boolean;
|
mergeProps?: boolean;
|
||||||
isCustomElement?: (tag: string) => boolean;
|
isCustomElement?: (tag: string) => boolean;
|
||||||
|
enableObjectSlots?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
|
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
|
||||||
@ -70,7 +71,7 @@ export default ({ types }: typeof BabelCore) => ({
|
|||||||
importNames.forEach((name) => {
|
importNames.forEach((name) => {
|
||||||
state.set(name, () => {
|
state.set(name, () => {
|
||||||
if (importMap[name]) {
|
if (importMap[name]) {
|
||||||
return types.cloneDeep(importMap[name]);
|
return types.cloneNode(importMap[name]);
|
||||||
}
|
}
|
||||||
const identifier = addNamed(
|
const identifier = addNamed(
|
||||||
path,
|
path,
|
||||||
@ -84,24 +85,27 @@ export default ({ types }: typeof BabelCore) => ({
|
|||||||
return identifier;
|
return identifier;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
|
const { enableObjectSlots = true } = state.opts;
|
||||||
if (importMap.runtimeIsSlot) {
|
if (enableObjectSlots) {
|
||||||
return importMap.runtimeIsSlot;
|
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
|
||||||
}
|
if (importMap.runtimeIsSlot) {
|
||||||
const { name: isVNodeName } = state.get('isVNode')();
|
return importMap.runtimeIsSlot;
|
||||||
const isSlot = path.scope.generateUidIdentifier('isSlot');
|
|
||||||
const ast = template.ast`
|
|
||||||
function ${isSlot.name}(s) {
|
|
||||||
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s));
|
|
||||||
}
|
}
|
||||||
`;
|
const { name: isVNodeName } = state.get('isVNode')();
|
||||||
const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
|
const isSlot = path.scope.generateUidIdentifier('isSlot');
|
||||||
if (lastImport) {
|
const ast = template.ast`
|
||||||
lastImport.insertAfter(ast);
|
function ${isSlot.name}(s) {
|
||||||
}
|
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s));
|
||||||
importMap.runtimeIsSlot = isSlot;
|
}
|
||||||
return isSlot;
|
`;
|
||||||
});
|
const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
|
||||||
|
if (lastImport) {
|
||||||
|
lastImport.insertAfter(ast);
|
||||||
|
}
|
||||||
|
importMap.runtimeIsSlot = isSlot;
|
||||||
|
return isSlot;
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// var _vue = require('vue');
|
// var _vue = require('vue');
|
||||||
let sourceName = '';
|
let sourceName = '';
|
||||||
|
@ -82,10 +82,14 @@ const transformJSXElement = (
|
|||||||
const { optimize = false } = state.opts;
|
const { optimize = false } = state.opts;
|
||||||
|
|
||||||
const slotFlag = path.getData('slotFlag') || SlotFlags.STABLE;
|
const slotFlag = path.getData('slotFlag') || SlotFlags.STABLE;
|
||||||
|
|
||||||
let VNodeChild;
|
let VNodeChild;
|
||||||
|
|
||||||
if (children.length > 1 || slots) {
|
if (children.length > 1 || slots) {
|
||||||
|
/*
|
||||||
|
<A v-slots={slots}>{a}{b}</A>
|
||||||
|
---> {{ default: () => [a, b], ...slots }}
|
||||||
|
---> {[a, b]}
|
||||||
|
*/
|
||||||
VNodeChild = isComponent ? t.objectExpression([
|
VNodeChild = isComponent ? t.objectExpression([
|
||||||
!!children.length && t.objectProperty(
|
!!children.length && t.objectProperty(
|
||||||
t.identifier('default'),
|
t.identifier('default'),
|
||||||
@ -102,51 +106,61 @@ const transformJSXElement = (
|
|||||||
),
|
),
|
||||||
].filter(Boolean as any)) : t.arrayExpression(children);
|
].filter(Boolean as any)) : t.arrayExpression(children);
|
||||||
} else if (children.length === 1) {
|
} else if (children.length === 1) {
|
||||||
|
/*
|
||||||
|
<A>{a}</A> or <A>{() => a}</A>
|
||||||
|
*/
|
||||||
|
const { enableObjectSlots = true } = state.opts;
|
||||||
const child = children[0];
|
const child = children[0];
|
||||||
|
const objectExpression = t.objectExpression([
|
||||||
|
t.objectProperty(
|
||||||
|
t.identifier('default'),
|
||||||
|
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [child]))),
|
||||||
|
),
|
||||||
|
optimize && t.objectProperty(
|
||||||
|
t.identifier('_'),
|
||||||
|
t.numericLiteral(slotFlag),
|
||||||
|
) as any,
|
||||||
|
].filter(Boolean));
|
||||||
if (t.isIdentifier(child)) {
|
if (t.isIdentifier(child)) {
|
||||||
VNodeChild = t.conditionalExpression(
|
VNodeChild = enableObjectSlots ? t.conditionalExpression(
|
||||||
t.callExpression(state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(), [child]),
|
t.callExpression(state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(), [child]),
|
||||||
child,
|
child,
|
||||||
t.objectExpression([
|
objectExpression,
|
||||||
t.objectProperty(
|
) : objectExpression;
|
||||||
t.identifier('default'),
|
|
||||||
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [child]))),
|
|
||||||
),
|
|
||||||
optimize && t.objectProperty(
|
|
||||||
t.identifier('_'),
|
|
||||||
t.numericLiteral(slotFlag),
|
|
||||||
) as any,
|
|
||||||
].filter(Boolean)),
|
|
||||||
);
|
|
||||||
} else if (
|
} else if (
|
||||||
t.isCallExpression(child) && child.loc && isComponent
|
t.isCallExpression(child) && child.loc && isComponent
|
||||||
) { // the element was generated and doesn't have location information
|
) { // the element was generated and doesn't have location information
|
||||||
const { scope } = path;
|
if (enableObjectSlots) {
|
||||||
const slotId = scope.generateUidIdentifier('slot');
|
const { scope } = path;
|
||||||
if (scope) {
|
const slotId = scope.generateUidIdentifier('slot');
|
||||||
scope.push({
|
if (scope) {
|
||||||
id: slotId,
|
scope.push({
|
||||||
kind: 'let',
|
id: slotId,
|
||||||
});
|
kind: 'let',
|
||||||
}
|
});
|
||||||
|
}
|
||||||
VNodeChild = t.conditionalExpression(
|
const alternate = t.objectExpression([
|
||||||
t.callExpression(
|
|
||||||
state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(),
|
|
||||||
[t.assignmentExpression('=', slotId, child)],
|
|
||||||
),
|
|
||||||
slotId,
|
|
||||||
t.objectExpression([
|
|
||||||
t.objectProperty(
|
t.objectProperty(
|
||||||
t.identifier('default'),
|
t.identifier('default'),
|
||||||
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [slotId]))),
|
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [slotId]))),
|
||||||
),
|
), optimize && t.objectProperty(
|
||||||
optimize && t.objectProperty(
|
|
||||||
t.identifier('_'),
|
t.identifier('_'),
|
||||||
t.numericLiteral(slotFlag),
|
t.numericLiteral(slotFlag),
|
||||||
) as any,
|
) as any,
|
||||||
].filter(Boolean)),
|
].filter(Boolean));
|
||||||
);
|
const assignment = t.assignmentExpression('=', slotId, child);
|
||||||
|
const condition = t.callExpression(
|
||||||
|
state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(),
|
||||||
|
[assignment],
|
||||||
|
);
|
||||||
|
VNodeChild = t.conditionalExpression(
|
||||||
|
condition,
|
||||||
|
slotId,
|
||||||
|
alternate,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
VNodeChild = objectExpression;
|
||||||
|
}
|
||||||
} else if (t.isFunctionExpression(child) || t.isArrowFunctionExpression(child)) {
|
} else if (t.isFunctionExpression(child) || t.isArrowFunctionExpression(child)) {
|
||||||
VNodeChild = t.objectExpression([
|
VNodeChild = t.objectExpression([
|
||||||
t.objectProperty(
|
t.objectProperty(
|
||||||
|
@ -42,6 +42,16 @@ import { resolveComponent as _resolveComponent } from \\"vue\\";
|
|||||||
_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);"
|
_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`disable object slot syntax with defaultSlot: defaultSlot 1`] = `
|
||||||
|
"import { createVNode as _createVNode } from \\"vue\\";
|
||||||
|
import { resolveComponent as _resolveComponent } from \\"vue\\";
|
||||||
|
|
||||||
|
_createVNode(_resolveComponent(\\"Badge\\"), null, {
|
||||||
|
default: () => [slots.default()],
|
||||||
|
_: 1
|
||||||
|
});"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`dynamic type in input: dynamic type in input 1`] = `
|
exports[`dynamic type in input: dynamic type in input 1`] = `
|
||||||
"import { withDirectives as _withDirectives } from \\"vue\\";
|
"import { withDirectives as _withDirectives } from \\"vue\\";
|
||||||
import { createVNode as _createVNode } from \\"vue\\";
|
import { createVNode as _createVNode } from \\"vue\\";
|
||||||
|
@ -155,7 +155,7 @@ tests.forEach((
|
|||||||
test(
|
test(
|
||||||
name,
|
name,
|
||||||
async () => {
|
async () => {
|
||||||
expect(await transpile(from, { optimize: true })).toMatchSnapshot(name);
|
expect(await transpile(from, { optimize: true, enableObjectSlots: true })).toMatchSnapshot(name);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -205,7 +205,26 @@ slotsTests.forEach(({
|
|||||||
test(
|
test(
|
||||||
`passing object slots via JSX children ${name}`,
|
`passing object slots via JSX children ${name}`,
|
||||||
async () => {
|
async () => {
|
||||||
expect(await transpile(from, { optimize: true })).toMatchSnapshot(name);
|
expect(await transpile(from, { optimize: true, enableObjectSlots: true })).toMatchSnapshot(name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const objectSlotsTests = [
|
||||||
|
{
|
||||||
|
name: 'defaultSlot',
|
||||||
|
from: '<Badge>{slots.default()}</Badge>',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
objectSlotsTests.forEach(({
|
||||||
|
name, from,
|
||||||
|
}) => {
|
||||||
|
test(
|
||||||
|
`disable object slot syntax with ${name}`,
|
||||||
|
async () => {
|
||||||
|
expect(await transpile(from, { optimize: true, enableObjectSlots: false }))
|
||||||
|
.toMatchSnapshot(name);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user