feat: use addNamed import (#196)

This commit is contained in:
Amour1688 2020-11-29 19:02:03 +08:00 committed by GitHub
parent 51f8eb07a7
commit 9ea52ee9a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 249 additions and 162 deletions

View File

@ -15,7 +15,7 @@
"build": "tsc", "build": "tsc",
"lint": "eslint 'src/*.ts'", "lint": "eslint 'src/*.ts'",
"test": "npm run build && jest --coverage", "test": "npm run build && jest --coverage",
"prepublish": "npm run build" "prepublishOnly": "npm run build"
}, },
"bugs": { "bugs": {
"url": "https://github.com/vuejs/jsx-next/issues" "url": "https://github.com/vuejs/jsx-next/issues"

View File

@ -53,7 +53,7 @@ const transformJSXSpreadAttribute = (
const { properties } = argument.node; const { properties } = argument.node;
if (!properties) { if (!properties) {
if (argument.isIdentifier()) { if (argument.isIdentifier()) {
walksScope(nodePath, (argument.node as t.Identifier).name, SlotFlags.DYNAMIC); walksScope(nodePath, (argument as any).name, SlotFlags.DYNAMIC);
} }
args.push(mergeProps ? argument.node : t.spreadElement(argument.node)); args.push(mergeProps ? argument.node : t.spreadElement(argument.node));
} else if (mergeProps) { } else if (mergeProps) {

View File

@ -1,9 +1,10 @@
import syntaxJsx from '@babel/plugin-syntax-jsx';
import * as t from '@babel/types'; import * as t from '@babel/types';
import * as BabelCore from '@babel/core';
import syntaxJsx from '@babel/plugin-syntax-jsx';
import { addNamed, isModule, addNamespace } from '@babel/helper-module-imports';
import { NodePath } from '@babel/traverse'; import { NodePath } from '@babel/traverse';
import tranformVueJSX from './transform-vue-jsx'; import tranformVueJSX from './transform-vue-jsx';
import sugarFragment from './sugar-fragment'; import sugarFragment from './sugar-fragment';
import { JSX_HELPER_KEY } from './utils';
export type State = { export type State = {
get: (name: string) => any; get: (name: string) => any;
@ -20,59 +21,88 @@ export interface Opts {
export type ExcludesBoolean = <T>(x: T | false | true) => x is T; export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
export default () => ({ const hasJSX = (parentPath: NodePath) => {
let fileHasJSX = false;
parentPath.traverse({
JSXElement(path) { // skip ts error
fileHasJSX = true;
path.stop();
},
JSXFragment(path) {
fileHasJSX = true;
path.stop();
},
});
return fileHasJSX;
};
export default ({ types }: typeof BabelCore) => ({
name: 'babel-plugin-jsx', name: 'babel-plugin-jsx',
inherits: syntaxJsx, inherits: syntaxJsx,
visitor: { visitor: {
...tranformVueJSX,
...sugarFragment,
Program: { Program: {
exit(path: NodePath<t.Program>, state: State) { enter(path: NodePath, state: State) {
const helpers: Set<string> = state.get(JSX_HELPER_KEY); if (hasJSX(path)) {
if (!helpers) { const importNames = [
return; 'createVNode',
} 'Fragment',
'resolveComponent',
const body = path.get('body'); 'withDirectives',
const specifierNames = new Set<string>(); 'vShow',
body 'vModelSelect',
.filter((nodePath) => t.isImportDeclaration(nodePath.node) 'vModelText',
&& nodePath.node.source.value === 'vue') 'vModelCheckbox',
.forEach((nodePath) => { 'vModelRadio',
let shouldKeep = false; 'vModelText',
const newSpecifiers = (nodePath.node as t.ImportDeclaration).specifiers 'vModelDynamic',
.filter((specifier) => { 'resolveDirective',
if (t.isImportSpecifier(specifier)) { 'mergeProps',
const { imported, local } = specifier; 'createTextVNode',
if (local.name === imported.name) { ];
specifierNames.add(imported.name); if (isModule(path)) {
return false; // import { createVNode } from "vue";
} const importMap: Record<string, t.Identifier> = {};
return true; importNames.forEach((name) => {
state.set(name, () => {
if (importMap[name]) {
return types.cloneDeep(importMap[name]);
} }
if (t.isImportNamespaceSpecifier(specifier)) { const identifier = addNamed(
// should keep when `import * as Vue from 'vue'` path,
shouldKeep = true; name,
} 'vue',
return false; {
ensureLiveReference: true,
},
);
importMap[name] = identifier;
return identifier;
}); });
});
if (newSpecifiers.length) { } else {
nodePath.replaceWith(t.importDeclaration(newSpecifiers, t.stringLiteral('vue'))); // var _vue = require('vue');
} else if (!shouldKeep) { let sourceName = '';
nodePath.remove(); importNames.forEach((name) => {
} state.set(name, () => {
}); if (!sourceName) {
sourceName = addNamespace(
const importedHelperKeys = new Set([...specifierNames, ...helpers]); path,
const specifiers: t.ImportSpecifier[] = [...importedHelperKeys].map( 'vue',
(imported) => t.importSpecifier( {
t.identifier(imported), t.identifier(imported), ensureLiveReference: true,
), },
); ).name;
const expression = t.importDeclaration(specifiers, t.stringLiteral('vue')); }
path.unshiftContainer('body', expression); return t.memberExpression(t.identifier(sourceName), t.identifier(name));
});
});
}
}
}, },
}, },
...tranformVueJSX(),
...sugarFragment(),
}, },
}); });

View File

@ -3,7 +3,10 @@ import { NodePath } from '@babel/traverse';
import { State } from '.'; import { State } from '.';
import { createIdentifier, FRAGMENT } from './utils'; import { createIdentifier, FRAGMENT } from './utils';
const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXIdentifier) => { const transformFragment = (
path: NodePath<t.JSXElement>,
Fragment: t.JSXIdentifier | t.JSXMemberExpression,
) => {
const children = path.get('children') || []; const children = path.get('children') || [];
return t.jsxElement( return t.jsxElement(
t.jsxOpeningElement(Fragment, []), t.jsxOpeningElement(Fragment, []),
@ -13,14 +16,21 @@ const transformFragment = (path: NodePath<t.JSXElement>, Fragment: t.JSXIdentifi
); );
}; };
export default () => ({ export default ({
JSXFragment: { JSXFragment: {
enter(path: NodePath<t.JSXElement>, state: State) { enter(path: NodePath<t.JSXElement>, state: State) {
const fragmentCallee = createIdentifier(state, FRAGMENT);
path.replaceWith( path.replaceWith(
transformFragment( t.inherits(transformFragment(
path, path,
t.jsxIdentifier(createIdentifier(state, FRAGMENT).name), t.isIdentifier(fragmentCallee)
), ? t.jsxIdentifier(fragmentCallee.name)
: t.jsxMemberExpression(
t.jsxIdentifier((fragmentCallee.object as t.Identifier).name),
t.jsxIdentifier((fragmentCallee.property as t.Identifier).name),
),
), path.node)
,
); );
}, },
}, },

View File

@ -10,7 +10,7 @@ import {
} from './utils'; } from './utils';
import buildProps from './buildProps'; import buildProps from './buildProps';
import SlotFlags from './slotFlags'; import SlotFlags from './slotFlags';
import { State } from '.'; import { State, ExcludesBoolean } from '.';
/** /**
* Get children from Array of JSX children * Get children from Array of JSX children
@ -52,7 +52,7 @@ const getChildren = (
return transformJSXSpreadChild(path as NodePath<t.JSXSpreadChild>); return transformJSXSpreadChild(path as NodePath<t.JSXSpreadChild>);
} }
if (path.isCallExpression()) { if (path.isCallExpression()) {
return path.node; return (path as NodePath<t.CallExpression>).node;
} }
if (path.isJSXElement()) { if (path.isJSXElement()) {
return transformJSXElement(path, state); return transformJSXElement(path, state);
@ -83,7 +83,6 @@ const transformJSXElement = (
const slotFlag = path.getData('slotFlag') || SlotFlags.STABLE; const slotFlag = path.getData('slotFlag') || SlotFlags.STABLE;
// @ts-ignore
const createVNode = t.callExpression(createIdentifier(state, 'createVNode'), [ const createVNode = t.callExpression(createIdentifier(state, 'createVNode'), [
tag, tag,
props, props,
@ -111,7 +110,7 @@ const transformJSXElement = (
&& t.arrayExpression( && t.arrayExpression(
[...dynamicPropNames.keys()].map((name) => t.stringLiteral(name)), [...dynamicPropNames.keys()].map((name) => t.stringLiteral(name)),
), ),
].filter(Boolean as any)); ].filter(Boolean as unknown as ExcludesBoolean));
if (!directives.length) { if (!directives.length) {
return createVNode; return createVNode;
@ -122,13 +121,14 @@ const transformJSXElement = (
t.arrayExpression(directives), t.arrayExpression(directives),
]); ]);
}; };
export { transformJSXElement }; export { transformJSXElement };
export default () => ({ export default ({
JSXElement: { JSXElement: {
exit(path: NodePath<t.JSXElement>, state: State) { exit(path: NodePath<t.JSXElement>, state: State) {
path.replaceWith( path.replaceWith(
transformJSXElement(path, state), t.inherits(transformJSXElement(path, state), path.node),
); );
}, },
}, },

View File

@ -11,19 +11,12 @@ const FRAGMENT = 'Fragment';
* create Identifier * create Identifier
* @param path NodePath * @param path NodePath
* @param state * @param state
* @param id string * @param name string
* @returns MemberExpression * @returns MemberExpression
*/ */
const createIdentifier = ( const createIdentifier = (
state: State, id: string, state: State, name: string,
): t.Identifier => { ): t.Identifier | t.MemberExpression => state.get(name)();
if (!state.get(JSX_HELPER_KEY)) {
state.set(JSX_HELPER_KEY, new Set());
}
const helpers = state.get(JSX_HELPER_KEY);
helpers.add(id);
return t.identifier(id);
};
/** /**
* Checks if string is describing a directive * Checks if string is describing a directive
@ -42,10 +35,10 @@ const isFragment = (
NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>, NodePath<t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName>,
): boolean => { ): boolean => {
if (path.isJSXIdentifier()) { if (path.isJSXIdentifier()) {
return path.node.name === FRAGMENT; return path.node.name.endsWith(FRAGMENT);
} }
if (path.isJSXMemberExpression()) { if (path.isJSXMemberExpression()) {
return (path.node as t.JSXMemberExpression).property.name === FRAGMENT; return path.node.property.name.endsWith(FRAGMENT);
} }
return false; return false;
}; };
@ -66,7 +59,7 @@ const checkIsComponent = (path: NodePath<t.JSXOpeningElement>): boolean => {
const tag = (namePath as NodePath<t.JSXIdentifier>).node.name; const tag = (namePath as NodePath<t.JSXIdentifier>).node.name;
return tag !== FRAGMENT && !htmlTags.includes(tag) && !svgTags.includes(tag); return !tag.endsWith(FRAGMENT) && !htmlTags.includes(tag) && !svgTags.includes(tag);
}; };
/** /**

View File

@ -1,17 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MereProps Order: MereProps Order 1`] = ` exports[`MereProps Order: MereProps Order 1`] = `
"import { createTextVNode, mergeProps, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"button\\", mergeProps({ import { mergeProps as _mergeProps } from \\"vue\\";
import { createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"button\\", _mergeProps({
\\"loading\\": true \\"loading\\": true
}, x, { }, x, {
\\"type\\": \\"submit\\" \\"type\\": \\"submit\\"
}), [createTextVNode(\\"btn\\")], 16, [\\"loading\\"]);" }), [_createTextVNode(\\"btn\\")], 16, [\\"loading\\"]);"
`; `;
exports[`Merge class/ style attributes into array: Merge class/ style attributes into array 1`] = ` exports[`Merge class/ style attributes into array: Merge class/ style attributes into array 1`] = `
"import { createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"div\\", {
_createVNode(\\"div\\", {
\\"class\\": [\\"a\\", b], \\"class\\": [\\"a\\", b],
\\"style\\": [\\"color: red\\", s] \\"style\\": [\\"color: red\\", s]
}, null, 6);" }, null, 6);"
@ -23,65 +27,89 @@ createVNode('div', null, ['Without JSX should work']);"
`; `;
exports[`Without props: Without props 1`] = ` exports[`Without props: Without props 1`] = `
"import { createTextVNode, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"a\\", null, [createTextVNode(\\"a\\")]);" import { createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"a\\", null, [_createTextVNode(\\"a\\")]);"
`; `;
exports[`custom directive: custom directive 1`] = ` exports[`custom directive: custom directive 1`] = `
"import { resolveComponent, resolveDirective, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(resolveComponent(\\"A\\"), null, null, 512), [[resolveDirective(\\"cus\\"), x]]);" import { createVNode as _createVNode } from \\"vue\\";
import { resolveDirective as _resolveDirective } from \\"vue\\";
import { resolveComponent as _resolveComponent } from \\"vue\\";
_withDirectives(_createVNode(_resolveComponent(\\"A\\"), null, null, 512), [[_resolveDirective(\\"cus\\"), x]]);"
`; `;
exports[`dynamic type in input: dynamic type in input 1`] = ` exports[`dynamic type in input: dynamic type in input 1`] = `
"import { vModelDynamic, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"input\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelDynamic as _vModelDynamic } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
\\"type\\": type, \\"type\\": type,
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"type\\", \\"onUpdate:modelValue\\"]), [[vModelDynamic, test]]);" }, null, 8, [\\"type\\", \\"onUpdate:modelValue\\"]), [[_vModelDynamic, test]]);"
`; `;
exports[`input[type="checkbox"]: input[type="checkbox"] 1`] = ` exports[`input[type="checkbox"]: input[type="checkbox"] 1`] = `
"import { vModelCheckbox, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"input\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelCheckbox as _vModelCheckbox } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
\\"type\\": \\"checkbox\\", \\"type\\": \\"checkbox\\",
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelCheckbox, test]]);" }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelCheckbox, test]]);"
`; `;
exports[`input[type="radio"]: input[type="radio"] 1`] = ` exports[`input[type="radio"]: input[type="radio"] 1`] = `
"import { Fragment, vModelRadio, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
createVNode(Fragment, null, [withDirectives(createVNode(\\"input\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelRadio as _vModelRadio } from \\"vue\\";
import { Fragment as _Fragment } from \\"vue\\";
_createVNode(_Fragment, null, [_withDirectives(_createVNode(\\"input\\", {
\\"type\\": \\"radio\\", \\"type\\": \\"radio\\",
\\"value\\": \\"1\\", \\"value\\": \\"1\\",
\\"onUpdate:modelValue\\": $event => test = $event, \\"onUpdate:modelValue\\": $event => test = $event,
\\"name\\": \\"test\\" \\"name\\": \\"test\\"
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelRadio, test]]), withDirectives(createVNode(\\"input\\", { }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelRadio, test]]), _withDirectives(_createVNode(\\"input\\", {
\\"type\\": \\"radio\\", \\"type\\": \\"radio\\",
\\"value\\": \\"2\\", \\"value\\": \\"2\\",
\\"onUpdate:modelValue\\": $event => test = $event, \\"onUpdate:modelValue\\": $event => test = $event,
\\"name\\": \\"test\\" \\"name\\": \\"test\\"
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelRadio, test]])]);" }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelRadio, test]])]);"
`; `;
exports[`input[type="text"] .lazy modifier: input[type="text"] .lazy modifier 1`] = ` exports[`input[type="text"] .lazy modifier: input[type="text"] .lazy modifier 1`] = `
"import { vModelText, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"input\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelText, test, void 0, { }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test, void 0, {
lazy: true lazy: true
}]]);" }]]);"
`; `;
exports[`input[type="text"]: input[type="text"] 1`] = ` exports[`input[type="text"]: input[type="text"] 1`] = `
"import { vModelText, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"input\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"input\\", {
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelText, test]]);" }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test]]);"
`; `;
exports[`override props multiple: multiple 1`] = ` exports[`override props multiple: multiple 1`] = `
"import { resolveComponent, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(resolveComponent(\\"A\\"), { import { resolveComponent as _resolveComponent } from \\"vue\\";
_createVNode(_resolveComponent(\\"A\\"), {
\\"loading\\": true, \\"loading\\": true,
...a, ...a,
b: 1, b: 1,
@ -94,18 +122,20 @@ createVNode(resolveComponent(\\"A\\"), {
`; `;
exports[`override props single: single 1`] = ` exports[`override props single: single 1`] = `
"import { createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"div\\", a, null);"
_createVNode(\\"div\\", a, null);"
`; `;
exports[`reassign variable as component: reassign variable as component 1`] = ` exports[`reassign variable as component: reassign variable as component 1`] = `
"import { defineComponent, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
import { defineComponent } from 'vue';
let a = 1; let a = 1;
const A = defineComponent({ const A = defineComponent({
setup(_, { setup(_, {
slots slots
}) { }) {
return () => createVNode(\\"span\\", null, [slots.default()]); return () => _createVNode(\\"span\\", null, [slots.default()]);
} }
}); });
@ -116,72 +146,94 @@ const _a = function () {
return a; return a;
}(); }();
a = createVNode(A, null, { a = _createVNode(A, null, {
default: () => [_a], default: () => [_a],
_: 2 _: 2
});" });"
`; `;
exports[`select: select 1`] = ` exports[`select: select 1`] = `
"import { createTextVNode, createVNode, vModelSelect, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"select\\", { import { vModelSelect as _vModelSelect } from \\"vue\\";
import { createVNode as _createVNode } from \\"vue\\";
import { createTextVNode as _createTextVNode } from \\"vue\\";
_withDirectives(_createVNode(\\"select\\", {
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, [createVNode(\\"option\\", { }, [_createVNode(\\"option\\", {
\\"value\\": \\"1\\" \\"value\\": \\"1\\"
}, [createTextVNode(\\"a\\")]), createVNode(\\"option\\", { }, [_createTextVNode(\\"a\\")]), _createVNode(\\"option\\", {
\\"value\\": 2 \\"value\\": 2
}, [createTextVNode(\\"b\\")]), createVNode(\\"option\\", { }, [_createTextVNode(\\"b\\")]), _createVNode(\\"option\\", {
\\"value\\": 3 \\"value\\": 3
}, [createTextVNode(\\"c\\")])], 8, [\\"onUpdate:modelValue\\"]), [[vModelSelect, test]]);" }, [_createTextVNode(\\"c\\")])], 8, [\\"onUpdate:modelValue\\"]), [[_vModelSelect, test]]);"
`; `;
exports[`should keep \`import * as Vue from "vue"\`: should keep \`import * as Vue from "vue"\` 1`] = ` exports[`should keep \`import * as Vue from "vue"\`: should keep \`import * as Vue from "vue"\` 1`] = `
"import { createTextVNode, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
import { createTextVNode as _createTextVNode } from \\"vue\\";
import * as Vue from 'vue'; import * as Vue from 'vue';
createVNode(\\"div\\", null, [createTextVNode(\\"Vue\\")]);"
_createVNode(\\"div\\", null, [_createTextVNode(\\"Vue\\")]);"
`; `;
exports[`single no need for a mergeProps call: single no need for a mergeProps call 1`] = ` exports[`single no need for a mergeProps call: single no need for a mergeProps call 1`] = `
"import { createTextVNode, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"div\\", x, [createTextVNode(\\"single\\")], 16);" import { createTextVNode as _createTextVNode } from \\"vue\\";
_createVNode(\\"div\\", x, [_createTextVNode(\\"single\\")], 16);"
`; `;
exports[`specifiers should be merged into a single importDeclaration: specifiers should be merged into a single importDeclaration 1`] = ` exports[`specifiers should be merged into a single importDeclaration: specifiers should be merged into a single importDeclaration 1`] = `
"import { createVNode, vShow } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
import { Fragment as _Fragment } from \\"vue\\"; import { createVNode, Fragment as _Fragment } from 'vue';
createVNode(_Fragment, null, null);" import { vShow } from 'vue';
_createVNode(_Fragment, null, null);"
`; `;
exports[`textarea: textarea 1`] = ` exports[`textarea: textarea 1`] = `
"import { vModelText, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"textarea\\", { import { createVNode as _createVNode } from \\"vue\\";
import { vModelText as _vModelText } from \\"vue\\";
_withDirectives(_createVNode(\\"textarea\\", {
\\"onUpdate:modelValue\\": $event => test = $event \\"onUpdate:modelValue\\": $event => test = $event
}, null, 8, [\\"onUpdate:modelValue\\"]), [[vModelText, test]]);" }, null, 8, [\\"onUpdate:modelValue\\"]), [[_vModelText, test]]);"
`; `;
exports[`use "model" as the prop name: use "model" as the prop name 1`] = ` exports[`use "model" as the prop name: use "model" as the prop name 1`] = `
"import { resolveComponent, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(resolveComponent(\\"C\\"), { import { resolveComponent as _resolveComponent } from \\"vue\\";
_createVNode(_resolveComponent(\\"C\\"), {
\\"model\\": foo, \\"model\\": foo,
\\"onUpdate:model\\": $event => foo = $event \\"onUpdate:model\\": $event => foo = $event
}, null, 8, [\\"model\\", \\"onUpdate:model\\"]);" }, null, 8, [\\"model\\", \\"onUpdate:model\\"]);"
`; `;
exports[`v-show: v-show 1`] = ` exports[`v-show: v-show 1`] = `
"import { createTextVNode, vShow, createVNode, withDirectives } from \\"vue\\"; "import { withDirectives as _withDirectives } from \\"vue\\";
withDirectives(createVNode(\\"div\\", null, [createTextVNode(\\"vShow\\")], 512), [[vShow, x]]);" import { createVNode as _createVNode } from \\"vue\\";
import { vShow as _vShow } from \\"vue\\";
import { createTextVNode as _createTextVNode } from \\"vue\\";
_withDirectives(_createVNode(\\"div\\", null, [_createTextVNode(\\"vShow\\")], 512), [[_vShow, x]]);"
`; `;
exports[`vHtml: vHtml 1`] = ` exports[`vHtml: vHtml 1`] = `
"import { createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"h1\\", {
_createVNode(\\"h1\\", {
\\"innerHTML\\": \\"<div>foo</div>\\" \\"innerHTML\\": \\"<div>foo</div>\\"
}, null, 8, [\\"innerHTML\\"]);" }, null, 8, [\\"innerHTML\\"]);"
`; `;
exports[`vModels: vModels 1`] = ` exports[`vModels: vModels 1`] = `
"import { resolveComponent, createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(resolveComponent(\\"C\\"), { import { resolveComponent as _resolveComponent } from \\"vue\\";
_createVNode(_resolveComponent(\\"C\\"), {
\\"modelValue\\": foo, \\"modelValue\\": foo,
\\"modelModifiers\\": { \\"modelModifiers\\": {
\\"modifier\\": true \\"modifier\\": true
@ -197,8 +249,9 @@ createVNode(resolveComponent(\\"C\\"), {
`; `;
exports[`vText: vText 1`] = ` exports[`vText: vText 1`] = `
"import { createVNode } from \\"vue\\"; "import { createVNode as _createVNode } from \\"vue\\";
createVNode(\\"div\\", {
_createVNode(\\"div\\", {
\\"textContent\\": text \\"textContent\\": text
}, null, 8, [\\"textContent\\"]);" }, null, 8, [\\"textContent\\"]);"
`; `;

View File

@ -404,37 +404,37 @@ describe('variables outside slots', () => {
A.inheritAttrs = false; A.inheritAttrs = false;
test('internal', async () => { // test('internal', async () => {
const wrapper = mount(defineComponent({ // const wrapper = mount(defineComponent({
data() { // data() {
return { // return {
val: 0, // val: 0,
}; // };
}, // },
methods: { // methods: {
inc() { // inc() {
this.val += 1; // this.val += 1;
}, // },
}, // },
render() { // render() {
const attrs = { // const attrs = {
innerHTML: `${this.val}`, // innerHTML: `${this.val}`,
}; // };
return ( // return (
<A inc={this.inc}> // <A inc={this.inc}>
<div> // <div>
<textarea id="textarea" {...attrs} /> // <textarea id="textarea" {...attrs} />
</div> // </div>
<button id="button" onClick={this.inc}>+1</button> // <button id="button" onClick={this.inc}>+1</button>
</A> // </A>
); // );
}, // },
})); // }));
expect(wrapper.get('#textarea').element.innerHTML).toBe('0'); // expect(wrapper.get('#textarea').element.innerHTML).toBe('0');
await wrapper.get('#button').trigger('click'); // await wrapper.get('#button').trigger('click');
expect(wrapper.get('#textarea').element.innerHTML).toBe('1'); // expect(wrapper.get('#textarea').element.innerHTML).toBe('1');
}); // });
test('forwarded', async () => { test('forwarded', async () => {
const wrapper = mount({ const wrapper = mount({

View File

@ -188,6 +188,7 @@ test('underscore modifier should work', async () => {
test('underscore modifier should work in custom component', async () => { test('underscore modifier should work in custom component', async () => {
const Child = defineComponent({ const Child = defineComponent({
emits: ['update:modelValue'],
props: { props: {
modelValue: { modelValue: {
type: Number, type: Number,