Skip to content

Commit 9238fab

Browse files
Amour1688逆寒dependabot-preview[bot]
authored
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] <[email protected]> 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] <[email protected]> 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] <[email protected]> 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: 逆寒 <[email protected]> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
1 parent 67d1600 commit 9238fab

File tree

4 files changed

+100
-53
lines changed

4 files changed

+100
-53
lines changed

packages/babel-plugin-jsx/src/index.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface Opts {
1818
optimize?: boolean;
1919
mergeProps?: boolean;
2020
isCustomElement?: (tag: string) => boolean;
21+
enableObjectSlots?: boolean;
2122
}
2223

2324
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
@@ -70,7 +71,7 @@ export default ({ types }: typeof BabelCore) => ({
7071
importNames.forEach((name) => {
7172
state.set(name, () => {
7273
if (importMap[name]) {
73-
return types.cloneDeep(importMap[name]);
74+
return types.cloneNode(importMap[name]);
7475
}
7576
const identifier = addNamed(
7677
path,
@@ -84,24 +85,27 @@ export default ({ types }: typeof BabelCore) => ({
8485
return identifier;
8586
});
8687
});
87-
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
88-
if (importMap.runtimeIsSlot) {
89-
return importMap.runtimeIsSlot;
90-
}
91-
const { name: isVNodeName } = state.get('isVNode')();
92-
const isSlot = path.scope.generateUidIdentifier('isSlot');
93-
const ast = template.ast`
94-
function ${isSlot.name}(s) {
95-
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s));
88+
const { enableObjectSlots = true } = state.opts;
89+
if (enableObjectSlots) {
90+
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
91+
if (importMap.runtimeIsSlot) {
92+
return importMap.runtimeIsSlot;
9693
}
97-
`;
98-
const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
99-
if (lastImport) {
100-
lastImport.insertAfter(ast);
101-
}
102-
importMap.runtimeIsSlot = isSlot;
103-
return isSlot;
104-
});
94+
const { name: isVNodeName } = state.get('isVNode')();
95+
const isSlot = path.scope.generateUidIdentifier('isSlot');
96+
const ast = template.ast`
97+
function ${isSlot.name}(s) {
98+
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s));
99+
}
100+
`;
101+
const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
102+
if (lastImport) {
103+
lastImport.insertAfter(ast);
104+
}
105+
importMap.runtimeIsSlot = isSlot;
106+
return isSlot;
107+
});
108+
}
105109
} else {
106110
// var _vue = require('vue');
107111
let sourceName = '';

packages/babel-plugin-jsx/src/transform-vue-jsx.ts

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ const transformJSXElement = (
8282
const { optimize = false } = state.opts;
8383

8484
const slotFlag = path.getData('slotFlag') || SlotFlags.STABLE;
85-
8685
let VNodeChild;
8786

8887
if (children.length > 1 || slots) {
88+
/*
89+
<A v-slots={slots}>{a}{b}</A>
90+
---> {{ default: () => [a, b], ...slots }}
91+
---> {[a, b]}
92+
*/
8993
VNodeChild = isComponent ? t.objectExpression([
9094
!!children.length && t.objectProperty(
9195
t.identifier('default'),
@@ -102,51 +106,61 @@ const transformJSXElement = (
102106
),
103107
].filter(Boolean as any)) : t.arrayExpression(children);
104108
} else if (children.length === 1) {
109+
/*
110+
<A>{a}</A> or <A>{() => a}</A>
111+
*/
112+
const { enableObjectSlots = true } = state.opts;
105113
const child = children[0];
114+
const objectExpression = t.objectExpression([
115+
t.objectProperty(
116+
t.identifier('default'),
117+
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [child]))),
118+
),
119+
optimize && t.objectProperty(
120+
t.identifier('_'),
121+
t.numericLiteral(slotFlag),
122+
) as any,
123+
].filter(Boolean));
106124
if (t.isIdentifier(child)) {
107-
VNodeChild = t.conditionalExpression(
125+
VNodeChild = enableObjectSlots ? t.conditionalExpression(
108126
t.callExpression(state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(), [child]),
109127
child,
110-
t.objectExpression([
111-
t.objectProperty(
112-
t.identifier('default'),
113-
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [child]))),
114-
),
115-
optimize && t.objectProperty(
116-
t.identifier('_'),
117-
t.numericLiteral(slotFlag),
118-
) as any,
119-
].filter(Boolean)),
120-
);
128+
objectExpression,
129+
) : objectExpression;
121130
} else if (
122131
t.isCallExpression(child) && child.loc && isComponent
123132
) { // the element was generated and doesn't have location information
124-
const { scope } = path;
125-
const slotId = scope.generateUidIdentifier('slot');
126-
if (scope) {
127-
scope.push({
128-
id: slotId,
129-
kind: 'let',
130-
});
131-
}
132-
133-
VNodeChild = t.conditionalExpression(
134-
t.callExpression(
135-
state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(),
136-
[t.assignmentExpression('=', slotId, child)],
137-
),
138-
slotId,
139-
t.objectExpression([
133+
if (enableObjectSlots) {
134+
const { scope } = path;
135+
const slotId = scope.generateUidIdentifier('slot');
136+
if (scope) {
137+
scope.push({
138+
id: slotId,
139+
kind: 'let',
140+
});
141+
}
142+
const alternate = t.objectExpression([
140143
t.objectProperty(
141144
t.identifier('default'),
142145
t.arrowFunctionExpression([], t.arrayExpression(buildIIFE(path, [slotId]))),
143-
),
144-
optimize && t.objectProperty(
146+
), optimize && t.objectProperty(
145147
t.identifier('_'),
146148
t.numericLiteral(slotFlag),
147149
) as any,
148-
].filter(Boolean)),
149-
);
150+
].filter(Boolean));
151+
const assignment = t.assignmentExpression('=', slotId, child);
152+
const condition = t.callExpression(
153+
state.get('@vue/babel-plugin-jsx/runtimeIsSlot')(),
154+
[assignment],
155+
);
156+
VNodeChild = t.conditionalExpression(
157+
condition,
158+
slotId,
159+
alternate,
160+
);
161+
} else {
162+
VNodeChild = objectExpression;
163+
}
150164
} else if (t.isFunctionExpression(child) || t.isArrowFunctionExpression(child)) {
151165
VNodeChild = t.objectExpression([
152166
t.objectProperty(

packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ import { resolveComponent as _resolveComponent } from \\"vue\\";
4242
_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);"
4343
`;
4444

45+
exports[`disable object slot syntax with defaultSlot: defaultSlot 1`] = `
46+
"import { createVNode as _createVNode } from \\"vue\\";
47+
import { resolveComponent as _resolveComponent } from \\"vue\\";
48+
49+
_createVNode(_resolveComponent(\\"Badge\\"), null, {
50+
default: () => [slots.default()],
51+
_: 1
52+
});"
53+
`;
54+
4555
exports[`dynamic type in input: dynamic type in input 1`] = `
4656
"import { withDirectives as _withDirectives } from \\"vue\\";
4757
import { createVNode as _createVNode } from \\"vue\\";

packages/babel-plugin-jsx/test/snapshot.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ tests.forEach((
155155
test(
156156
name,
157157
async () => {
158-
expect(await transpile(from, { optimize: true })).toMatchSnapshot(name);
158+
expect(await transpile(from, { optimize: true, enableObjectSlots: true })).toMatchSnapshot(name);
159159
},
160160
);
161161
});
@@ -205,7 +205,26 @@ slotsTests.forEach(({
205205
test(
206206
`passing object slots via JSX children ${name}`,
207207
async () => {
208-
expect(await transpile(from, { optimize: true })).toMatchSnapshot(name);
208+
expect(await transpile(from, { optimize: true, enableObjectSlots: true })).toMatchSnapshot(name);
209+
},
210+
);
211+
});
212+
213+
const objectSlotsTests = [
214+
{
215+
name: 'defaultSlot',
216+
from: '<Badge>{slots.default()}</Badge>',
217+
},
218+
];
219+
220+
objectSlotsTests.forEach(({
221+
name, from,
222+
}) => {
223+
test(
224+
`disable object slot syntax with ${name}`,
225+
async () => {
226+
expect(await transpile(from, { optimize: true, enableObjectSlots: false }))
227+
.toMatchSnapshot(name);
209228
},
210229
);
211230
});

0 commit comments

Comments
 (0)