fix: throw state.get(...) when using object slots (#494)

This commit is contained in:
Amour1688 2021-09-03 23:52:48 +08:00
parent e0d0162d6b
commit 55035e3f1d
5 changed files with 73 additions and 30 deletions

View File

@ -13,7 +13,8 @@ export { VueJSXPluginOptions };
const hasJSX = (parentPath: NodePath<t.Program>) => {
let fileHasJSX = false;
parentPath.traverse({
JSXElement(path) { // skip ts error
JSXElement(path) {
// skip ts error
fileHasJSX = true;
path.stop();
},
@ -62,14 +63,9 @@ export default ({ types }: typeof BabelCore) => ({
if (importMap[name]) {
return types.cloneNode(importMap[name]);
}
const identifier = addNamed(
path,
name,
'vue',
{
ensureLiveReference: true,
},
);
const identifier = addNamed(path, name, 'vue', {
ensureLiveReference: true,
});
importMap[name] = identifier;
return identifier;
});
@ -80,14 +76,18 @@ export default ({ types }: typeof BabelCore) => ({
if (importMap.runtimeIsSlot) {
return importMap.runtimeIsSlot;
}
const { name: isVNodeName } = state.get('isVNode')();
const { name: isVNodeName } = state.get(
'isVNode',
)() as t.Identifier;
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 lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop();
const lastImport = (path.get('body') as NodePath[])
.filter((p) => p.isImportDeclaration())
.pop();
if (lastImport) {
lastImport.insertAfter(ast);
}
@ -97,24 +97,57 @@ export default ({ types }: typeof BabelCore) => ({
}
} else {
// var _vue = require('vue');
let sourceName = '';
let sourceName: t.Identifier;
importNames.forEach((name) => {
state.set(name, () => {
if (!sourceName) {
sourceName = addNamespace(
path,
'vue',
{
ensureLiveReference: true,
},
).name;
sourceName = addNamespace(path, 'vue', {
ensureLiveReference: true,
});
}
return t.memberExpression(t.identifier(sourceName), t.identifier(name));
return t.memberExpression(sourceName, t.identifier(name));
});
});
const helpers: Record<string, t.Identifier> = {};
const { enableObjectSlots = true } = state.opts;
if (enableObjectSlots) {
state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => {
if (helpers.runtimeIsSlot) {
return helpers.runtimeIsSlot;
}
const isSlot = path.scope.generateUidIdentifier('isSlot');
const { object: objectName } = state.get(
'isVNode',
)() as t.MemberExpression;
const ast = template.ast`
function ${isSlot.name}(s) {
return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${(objectName as t.Identifier).name}.isVNode(s));
}
`;
const nodePaths = path.get('body') as NodePath[];
const lastImport = nodePaths
.filter(
(p) => p.isVariableDeclaration()
&& p.node.declarations.some(
(d) => (d.id as t.Identifier)?.name === sourceName.name,
),
)
.pop();
if (lastImport) {
lastImport.insertAfter(ast);
}
return isSlot;
});
}
}
const { opts: { pragma = '' }, file } = state;
const {
opts: { pragma = '' },
file,
} = state;
if (pragma) {
state.set('createVNode', () => t.identifier(pragma));
@ -134,13 +167,20 @@ export default ({ types }: typeof BabelCore) => ({
const body = path.get('body') as NodePath[];
const specifiersMap = new Map<string, t.ImportSpecifier>();
body.filter((nodePath) => t.isImportDeclaration(nodePath.node)
&& nodePath.node.source.value === 'vue')
body
.filter(
(nodePath) => t.isImportDeclaration(nodePath.node)
&& nodePath.node.source.value === 'vue',
)
.forEach((nodePath) => {
const { specifiers } = nodePath.node as t.ImportDeclaration;
let shouldRemove = false;
specifiers.forEach((specifier) => {
if (!specifier.loc && t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) {
if (
!specifier.loc
&& t.isImportSpecifier(specifier)
&& t.isIdentifier(specifier.imported)
) {
specifiersMap.set(specifier.imported.name, specifier);
shouldRemove = true;
}
@ -154,7 +194,10 @@ export default ({ types }: typeof BabelCore) => ({
(imported) => specifiersMap.get(imported)!,
);
if (specifiers.length) {
path.unshiftContainer('body', t.importDeclaration(specifiers, t.stringLiteral('vue')));
path.unshiftContainer(
'body',
t.importDeclaration(specifiers, t.stringLiteral('vue')),
);
}
},
},

View File

@ -24,5 +24,3 @@ export interface VueJSXPluginOptions {
/** Replace the function used when compiling JSX expressions */
pragma?: string;
}
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;

View File

@ -20,10 +20,12 @@ import {
import SlotFlags from './slotFlags';
import { PatchFlags } from './patchFlags';
import parseDirectives from './parseDirectives';
import type { State, ExcludesBoolean, Slots } from './interface';
import type { State, Slots } from './interface';
const xlinkRE = /^xlink([A-Z])/;
type ExcludesBoolean = <T>(x: T | false | true) => x is T;
const getJSXAttributeValue = (
path: NodePath<t.JSXAttribute>,
state: State,

View File

@ -4,7 +4,8 @@
"rootDirs": ["./src"],
"outDir": "dist",
"downlevelIteration": true,
"declaration": true
"declaration": true,
"jsx": "preserve",
},
"include": ["src/**/*", "global.d.ts"],
"exclude": ["node_modules"]

View File

@ -11,7 +11,6 @@
"resolveJsonModule": true,
"esModuleInterop": true,
"removeComments": false,
"jsx": "preserve",
"lib": [
"esnext",
"dom"