Skip to content

Commit 55035e3

Browse files
committed
fix: throw state.get(...) when using object slots (#494)
1 parent e0d0162 commit 55035e3

File tree

5 files changed

+73
-30
lines changed

5 files changed

+73
-30
lines changed

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

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export { VueJSXPluginOptions };
1313
const hasJSX = (parentPath: NodePath<t.Program>) => {
1414
let fileHasJSX = false;
1515
parentPath.traverse({
16-
JSXElement(path) { // skip ts error
16+
JSXElement(path) {
17+
// skip ts error
1718
fileHasJSX = true;
1819
path.stop();
1920
},
@@ -62,14 +63,9 @@ export default ({ types }: typeof BabelCore) => ({
6263
if (importMap[name]) {
6364
return types.cloneNode(importMap[name]);
6465
}
65-
const identifier = addNamed(
66-
path,
67-
name,
68-
'vue',
69-
{
70-
ensureLiveReference: true,
71-
},
72-
);
66+
const identifier = addNamed(path, name, 'vue', {
67+
ensureLiveReference: true,
68+
});
7369
importMap[name] = identifier;
7470
return identifier;
7571
});
@@ -80,14 +76,18 @@ export default ({ types }: typeof BabelCore) => ({
8076
if (importMap.runtimeIsSlot) {
8177
return importMap.runtimeIsSlot;
8278
}
83-
const { name: isVNodeName } = state.get('isVNode')();
79+
const { name: isVNodeName } = state.get(
80+
'isVNode',
81+
)() as t.Identifier;
8482
const isSlot = path.scope.generateUidIdentifier('isSlot');
8583
const ast = template.ast`
8684
function ${isSlot.name}(s) {
8785
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s));
8886
}
8987
`;
90-
const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
88+
const lastImport = (path.get('body') as NodePath[])
89+
.filter((p) => p.isImportDeclaration())
90+
.pop();
9191
if (lastImport) {
9292
lastImport.insertAfter(ast);
9393
}
@@ -97,24 +97,57 @@ export default ({ types }: typeof BabelCore) => ({
9797
}
9898
} else {
9999
// var _vue = require('vue');
100-
let sourceName = '';
100+
let sourceName: t.Identifier;
101101
importNames.forEach((name) => {
102102
state.set(name, () => {
103103
if (!sourceName) {
104-
sourceName = addNamespace(
105-
path,
106-
'vue',
107-
{
108-
ensureLiveReference: true,
109-
},
110-
).name;
104+
sourceName = addNamespace(path, 'vue', {
105+
ensureLiveReference: true,
106+
});
111107
}
112-
return t.memberExpression(t.identifier(sourceName), t.identifier(name));
108+
return t.memberExpression(sourceName, t.identifier(name));
113109
});
114110
});
111+
112+
const helpers: Record<string, t.Identifier> = {};
113+
114+
const { enableObjectSlots = true } = state.opts;
115+
if (enableObjectSlots) {
116+
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
117+
if (helpers.runtimeIsSlot) {
118+
return helpers.runtimeIsSlot;
119+
}
120+
const isSlot = path.scope.generateUidIdentifier('isSlot');
121+
const { object: objectName } = state.get(
122+
'isVNode',
123+
)() as t.MemberExpression;
124+
const ast = template.ast`
125+
function ${isSlot.name}(s) {
126+
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${(objectName as t.Identifier).name}.isVNode(s));
127+
}
128+
`;
129+
130+
const nodePaths = path.get('body') as NodePath[];
131+
const lastImport = nodePaths
132+
.filter(
133+
(p) => p.isVariableDeclaration()
134+
&& p.node.declarations.some(
135+
(d) => (d.id as t.Identifier)?.name === sourceName.name,
136+
),
137+
)
138+
.pop();
139+
if (lastImport) {
140+
lastImport.insertAfter(ast);
141+
}
142+
return isSlot;
143+
});
144+
}
115145
}
116146

117-
const { opts: { pragma = '' }, file } = state;
147+
const {
148+
opts: { pragma = '' },
149+
file,
150+
} = state;
118151

119152
if (pragma) {
120153
state.set('createVNode', () => t.identifier(pragma));
@@ -134,13 +167,20 @@ export default ({ types }: typeof BabelCore) => ({
134167
const body = path.get('body') as NodePath[];
135168
const specifiersMap = new Map<string, t.ImportSpecifier>();
136169

137-
body.filter((nodePath) => t.isImportDeclaration(nodePath.node)
138-
&& nodePath.node.source.value === 'vue')
170+
body
171+
.filter(
172+
(nodePath) => t.isImportDeclaration(nodePath.node)
173+
&& nodePath.node.source.value === 'vue',
174+
)
139175
.forEach((nodePath) => {
140176
const { specifiers } = nodePath.node as t.ImportDeclaration;
141177
let shouldRemove = false;
142178
specifiers.forEach((specifier) => {
143-
if (!specifier.loc && t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) {
179+
if (
180+
!specifier.loc
181+
&& t.isImportSpecifier(specifier)
182+
&& t.isIdentifier(specifier.imported)
183+
) {
144184
specifiersMap.set(specifier.imported.name, specifier);
145185
shouldRemove = true;
146186
}
@@ -154,7 +194,10 @@ export default ({ types }: typeof BabelCore) => ({
154194
(imported) => specifiersMap.get(imported)!,
155195
);
156196
if (specifiers.length) {
157-
path.unshiftContainer('body', t.importDeclaration(specifiers, t.stringLiteral('vue')));
197+
path.unshiftContainer(
198+
'body',
199+
t.importDeclaration(specifiers, t.stringLiteral('vue')),
200+
);
158201
}
159202
},
160203
},

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,3 @@ export interface VueJSXPluginOptions {
2424
/** Replace the function used when compiling JSX expressions */
2525
pragma?: string;
2626
}
27-
28-
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import {
2020
import SlotFlags from './slotFlags';
2121
import { PatchFlags } from './patchFlags';
2222
import parseDirectives from './parseDirectives';
23-
import type { State, ExcludesBoolean, Slots } from './interface';
23+
import type { State, Slots } from './interface';
2424

2525
const xlinkRE = /^xlink([A-Z])/;
2626

27+
type ExcludesBoolean = <T>(x: T | false | true) => x is T;
28+
2729
const getJSXAttributeValue = (
2830
path: NodePath<t.JSXAttribute>,
2931
state: State,

packages/babel-plugin-jsx/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"rootDirs": ["./src"],
55
"outDir": "dist",
66
"downlevelIteration": true,
7-
"declaration": true
7+
"declaration": true,
8+
"jsx": "preserve",
89
},
910
"include": ["src/**/*", "global.d.ts"],
1011
"exclude": ["node_modules"]

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"resolveJsonModule": true,
1212
"esModuleInterop": true,
1313
"removeComments": false,
14-
"jsx": "preserve",
1514
"lib": [
1615
"esnext",
1716
"dom"

0 commit comments

Comments
 (0)