diff --git a/README-zh_CN.md b/README-zh_CN.md
index cca7800..9d9dd0b 100644
--- a/README-zh_CN.md
+++ b/README-zh_CN.md
@@ -137,6 +137,28 @@ export default {
}
```
+* 或者使用这种实现
+
+```jsx
+
+```
+
+```jsx
+
+```
+
+会变编译成:
+
+```js
+h(A, {
+ 'foo': val,
+ "fooModifiers": {
+ "bar": true
+ },
+ "onUpdate:foo": $event => val = $event
+})
+```
+
自定义指令
```jsx
@@ -145,17 +167,15 @@ const App = {
setup() {
return () => (
);
},
}
```
+> 注意:如果想要使用 `arg`, 第二个参数需要为字符串
+
### 插槽
```jsx
diff --git a/README.md b/README.md
index a95bc52..b453b47 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,7 @@ const App = {
v-model
-* You should use underscore (`_`) instead of dot (`.`) for modifiers (`vModel_trim={this.test}`)
+* You can use underscore (`_`) instead of dot (`.`) for modifiers (`vModel_trim={this.test}`)
```jsx
export default {
@@ -138,6 +138,28 @@ export default {
}
```
+* Or you can use this proposal.
+
+```jsx
+
+```
+
+```jsx
+
+```
+
+Will compile to:
+
+```js
+h(A, {
+ 'foo': val,
+ "fooModifiers": {
+ "bar": true
+ },
+ "onUpdate:foo": $event => val = $event
+})
+```
+
custom directive
```jsx
@@ -146,17 +168,15 @@ const App = {
setup() {
return () => (
);
},
}
```
+> Note: You should pass the second param as string for using `arg`.
+
### Slot
```jsx
diff --git a/packages/babel-plugin-jsx/src/parseDirectives.ts b/packages/babel-plugin-jsx/src/parseDirectives.ts
new file mode 100644
index 0000000..cdc0c22
--- /dev/null
+++ b/packages/babel-plugin-jsx/src/parseDirectives.ts
@@ -0,0 +1,131 @@
+import * as t from '@babel/types';
+import { NodePath } from '@babel/traverse';
+import { createIdentifier } from './utils';
+import { State, ExcludesBoolean } from './';
+
+/**
+ * Get JSX element type
+ *
+ * @param path Path
+ */
+const getType = (path: NodePath) => {
+ const typePath = path
+ .get('attributes')
+ .find((attribute) => {
+ if (!t.isJSXAttribute(attribute)) {
+ return false;
+ }
+ return t.isJSXIdentifier(attribute.get('name'))
+ && (attribute.get('name') as NodePath).get('name') === 'type'
+ && t.isStringLiteral(attribute.get('value'))
+ },
+ );
+
+ return typePath ? typePath.get('value.value') : '';
+};
+
+const parseModifiers = (value: t.Expression) => {
+ let modifiers: string[] = [];
+ if (t.isArrayExpression(value)) {
+ modifiers = (value as t.ArrayExpression).elements.map(el => t.isStringLiteral(el) ? el.value : '').filter(Boolean)
+ }
+ return modifiers;
+}
+
+const parseDirectives = (args: {
+ name: string,
+ path: NodePath,
+ value: t.StringLiteral | t.Expression | null,
+ state: State,
+ tag: t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression,
+ isComponent: boolean
+}) => {
+ const {
+ name, path, value, state, tag, isComponent,
+ } = args
+ let modifiers: string[] = name.split('_');
+ let arg;
+ let val;
+
+ const directiveName: string = modifiers.shift()
+ ?.replace(/^v/, '')
+ .replace(/^-/, '')
+ .replace(/^\S/, (s: string) => s.toLowerCase()) || '';
+
+ if (directiveName === 'model' && !t.isJSXExpressionContainer(path.get('value'))) {
+ throw new Error('You have to use JSX Expression inside your v-model');
+ }
+
+ const hasDirective = directiveName !== 'model' || (directiveName === 'model' && !isComponent);
+
+ if (t.isArrayExpression(value)) {
+ const { elements } = value as t.ArrayExpression;
+ const [first, second, third] = elements;
+ if (t.isStringLiteral(second)) {
+ arg = second;
+ modifiers = parseModifiers(third as t.Expression);
+ } else if (second) {
+ modifiers = parseModifiers(second as t.Expression);
+ }
+ val = first;
+ }
+
+ const modifiersSet = new Set(modifiers);
+
+ return {
+ directiveName,
+ modifiers: modifiersSet,
+ value: val || value,
+ arg,
+ directive: hasDirective ? [
+ resolveDirective(path, state, tag, directiveName),
+ val as t.Identifier,
+ !!modifiersSet.size && t.unaryExpression('void', t.numericLiteral(0), true),
+ !!modifiersSet.size && t.objectExpression(
+ [...modifiersSet].map(
+ (modifier) => t.objectProperty(
+ t.identifier(modifier as string),
+ t.booleanLiteral(true),
+ ),
+ ),
+ ),
+ ].filter(Boolean as any as ExcludesBoolean) : undefined,
+ };
+};
+
+const resolveDirective = (path: NodePath, state: State, tag: any, directiveName: string) => {
+ if (directiveName === 'show') {
+ return createIdentifier(state, 'vShow');
+ }
+ if (directiveName === 'model') {
+ let modelToUse;
+ const type = getType(path.parentPath as NodePath);
+ switch (tag.value) {
+ case 'select':
+ modelToUse = createIdentifier(state, 'vModelSelect');
+ break;
+ case 'textarea':
+ modelToUse = createIdentifier(state, 'vModelText');
+ break;
+ default:
+ switch (type) {
+ case 'checkbox':
+ modelToUse = createIdentifier(state, 'vModelCheckbox');
+ break;
+ case 'radio':
+ modelToUse = createIdentifier(state, 'vModelRadio');
+ break;
+ default:
+ modelToUse = createIdentifier(state, 'vModelText');
+ }
+ }
+ return modelToUse;
+ }
+ return t.callExpression(
+ createIdentifier(state, 'resolveDirective'), [
+ t.stringLiteral(directiveName),
+ ],
+ );
+};
+
+export default parseDirectives;
diff --git a/packages/babel-plugin-jsx/src/transform-vue-jsx.ts b/packages/babel-plugin-jsx/src/transform-vue-jsx.ts
index 4f5a721..6e98d8f 100644
--- a/packages/babel-plugin-jsx/src/transform-vue-jsx.ts
+++ b/packages/babel-plugin-jsx/src/transform-vue-jsx.ts
@@ -10,10 +10,10 @@ import {
getJSXAttributeName,
transformJSXText,
transformJSXExpressionContainer,
- parseDirectives,
isFragment,
walksScope,
} from './utils';
+import parseDirectives from './parseDirectives';
import { PatchFlags, PatchFlagNames } from './patchFlags';
import { State, ExcludesBoolean } from './';
@@ -198,7 +198,7 @@ const buildProps = (path: NodePath, state: State) => {
return;
}
if (isDirective(name)) {
- const { directive, modifiers, directiveName } = parseDirectives({
+ const { directive, modifiers, value, arg, directiveName } = parseDirectives({
tag,
isComponent,
name,
@@ -206,6 +206,8 @@ const buildProps = (path: NodePath, state: State) => {
state,
value: attributeValue,
});
+ const argVal = (arg as t.StringLiteral)?.value;
+ const propName = argVal || 'modelValue';
if (directiveName === 'slots') {
slots = attributeValue;
@@ -215,16 +217,16 @@ const buildProps = (path: NodePath, state: State) => {
} else {
// must be v-model and is a component
properties.push(t.objectProperty(
- t.stringLiteral('modelValue'),
+ arg || t.stringLiteral('modelValue'),
// @ts-ignore
- attributeValue,
+ value,
));
- dynamicPropNames.add('modelValue');
+ dynamicPropNames.add(propName);
if (modifiers.size) {
properties.push(t.objectProperty(
- t.stringLiteral('modelModifiers'),
+ t.stringLiteral(`${argVal || 'model'}Modifiers`),
t.objectExpression(
[...modifiers].map((modifier) => (
t.objectProperty(
@@ -237,17 +239,19 @@ const buildProps = (path: NodePath, state: State) => {
}
}
- if (directiveName === 'model' && attributeValue) {
+ console.log(value)
+
+ if (directiveName === 'model' && value) {
properties.push(t.objectProperty(
- t.stringLiteral('onUpdate:modelValue'),
+ t.stringLiteral(`onUpdate:${propName}`),
t.arrowFunctionExpression(
[t.identifier('$event')],
// @ts-ignore
- t.assignmentExpression('=', attributeValue, t.identifier('$event')),
+ t.assignmentExpression('=', value, t.identifier('$event')),
),
));
- dynamicPropNames.add('onUpdate:modelValue');
+ dynamicPropNames.add(`onUpdate:${propName}`);
}
return;
}
diff --git a/packages/babel-plugin-jsx/src/utils.ts b/packages/babel-plugin-jsx/src/utils.ts
index 2fb562b..75ed603 100644
--- a/packages/babel-plugin-jsx/src/utils.ts
+++ b/packages/babel-plugin-jsx/src/utils.ts
@@ -2,7 +2,7 @@ import * as t from '@babel/types';
import htmlTags from 'html-tags';
import svgTags from 'svg-tags';
import { NodePath } from '@babel/traverse';
-import { State, ExcludesBoolean } from './';
+import { State } from './';
/**
* create Identifier
@@ -169,111 +169,6 @@ const transformJSXSpreadChild = (
path: NodePath
): t.SpreadElement => t.spreadElement(path.get('expression').node);
-/**
- * Get JSX element type
- *
- * @param path Path
- */
-const getType = (path: NodePath) => {
- const typePath = path
- .get('attributes')
- .find((attribute) => {
- if (!t.isJSXAttribute(attribute)) {
- return false;
- }
- return t.isJSXIdentifier(attribute.get('name'))
- && (attribute.get('name') as NodePath).get('name') === 'type'
- && t.isStringLiteral(attribute.get('value'))
- },
- );
-
- return typePath ? typePath.get('value.value') : '';
-};
-
-const resolveDirective = (path: NodePath, state: State, tag: any, directiveName: string) => {
- if (directiveName === 'show') {
- return createIdentifier(state, 'vShow');
- } if (directiveName === 'model') {
- let modelToUse;
- const type = getType(path.parentPath as NodePath);
- switch (tag.value) {
- case 'select':
- modelToUse = createIdentifier(state, 'vModelSelect');
- break;
- case 'textarea':
- modelToUse = createIdentifier(state, 'vModelText');
- break;
- default:
- switch (type) {
- case 'checkbox':
- modelToUse = createIdentifier(state, 'vModelCheckbox');
- break;
- case 'radio':
- modelToUse = createIdentifier(state, 'vModelRadio');
- break;
- default:
- modelToUse = createIdentifier(state, 'vModelText');
- }
- }
- return modelToUse;
- }
- return t.callExpression(
- createIdentifier(state, 'resolveDirective'), [
- t.stringLiteral(directiveName),
- ],
- );
-};
-
-/**
- * Parse directives metadata
- *
- * @param path JSXAttribute
- * @returns null | Object<{ modifiers: Set, valuePath: Path}>
- */
-const parseDirectives = (args: {
- name: string,
- path: NodePath,
- value: t.StringLiteral | t.Expression | null,
- state: State,
- tag: t.Identifier | t.MemberExpression | t.StringLiteral | t.CallExpression,
- isComponent: boolean
-}) => {
- const {
- name, path, value, state, tag, isComponent,
- } = args
- const modifiers: string[] = name.split('_');
- const directiveName: string = modifiers.shift()
- ?.replace(/^v/, '')
- .replace(/^-/, '')
- .replace(/^\S/, (s: string) => s.toLowerCase()) || '';
-
- if (directiveName === 'model' && !t.isJSXExpressionContainer(path.get('value'))) {
- throw new Error('You have to use JSX Expression inside your v-model');
- }
-
- const modifiersSet = new Set(modifiers);
-
- const hasDirective = directiveName !== 'model' || (directiveName === 'model' && !isComponent);
-
- return {
- directiveName,
- modifiers: modifiersSet,
- directive: hasDirective ? [
- resolveDirective(path, state, tag, directiveName),
- value,
- !!modifiersSet.size && t.unaryExpression('void', t.numericLiteral(0), true),
- !!modifiersSet.size && t.objectExpression(
- [...modifiersSet].map(
- (modifier) => t.objectProperty(
- t.identifier(modifier),
- t.booleanLiteral(true),
- ),
- ),
- ),
- ].filter(Boolean as any as ExcludesBoolean) : undefined,
- };
-};
-
const walksScope = (path: NodePath, name: string) => {
if (path.scope.hasBinding(name) && path.parentPath) {
path.parentPath.setData('optimize', false);
@@ -291,7 +186,6 @@ export {
transformJSXText,
transformJSXSpreadChild,
transformJSXExpressionContainer,
- parseDirectives,
isFragment,
walksScope,
};