refactor: support transformOn

This commit is contained in:
Amour1688 2020-05-26 21:04:56 +08:00
parent 402568dc52
commit 7a1a39a4cb
19 changed files with 161 additions and 95 deletions

View File

@ -1,8 +0,0 @@
{
"presets": [
"@babel/env"
],
"plugins": [
"./src/index.js"
]
}

6
lerna.json Normal file
View File

@ -0,0 +1,6 @@
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}

View File

@ -1,49 +1,17 @@
{ {
"name": "@ant-design-vue/babel-plugin-jsx",
"version": "1.0.0-alpha.2",
"description": "Babel plugin for Vue 3.0 JSX",
"main": "src/index.js",
"scripts": { "scripts": {
"dev": "webpack-dev-server", "publish": "lerna publish"
"lint": "eslint --ext .js src",
"test": "jest --config .jest.js"
}, },
"repository": {
"type": "git",
"url": "git+https://github.com/vueComponent/jsx.git"
},
"author": "",
"license": "MIT", "license": "MIT",
"bugs": {
"url": "https://github.com/vueComponent/jsx/issues"
},
"homepage": "https://github.com/vueComponent/jsx#readme",
"keywords": [ "keywords": [
"vue", "vue",
"jsx" "jsx"
], ],
"dependencies": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/plugin-syntax-jsx": "^7.0.0",
"camelcase": "^6.0.0",
"html-tags": "^3.1.0",
"svg-tags": "^1.0.0"
},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@vue/test-utils": "^2.0.0-alpha.6",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.0.1",
"babel-loader": "^8.1.0",
"eslint": "^7.0.0", "eslint": "^7.0.0",
"eslint-config-airbnb": "^18.1.0", "eslint-config-airbnb": "^18.1.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"jest": "^26.0.1", "lerna": "^3.19.0"
"regenerator-runtime": "^0.13.5",
"vue": "^3.0.0-beta.14",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
} }
} }

View File

@ -0,0 +1,11 @@
# @ant-design-vue/babel-helper-vue-transform-on
A package used internally by vue jsx transformer to merge props spread. It is required to merge some prop trees like this:
## Usage
```
const babelHelperVueTransformOn = require('babel-helper-vue-transform-on');
// TODO: DEMONSTRATE API
```

View File

@ -0,0 +1,9 @@
const transformOn = (obj) => {
const result = {};
Object.keys(obj).forEach((evt) => {
result[`on${evt[0].toUpperCase()}${evt.substr(1)}`] = obj[evt];
});
return result;
};
module.exports = transformOn;

View File

@ -0,0 +1,9 @@
{
"name": "@ant-design-vue/babel-helper-vue-transform-on",
"version": "1.0.0",
"description": "> TODO: description",
"author": "Amour1688 <lcz_1996@foxmail.com>",
"license": "MIT",
"main": "index.js",
"private": true
}

View File

@ -0,0 +1,13 @@
{
"presets": [
[
"@babel/env",
{
// "modules": "cjs"
}
]
],
"plugins": [
["./src/index.js", { "transformOn": true }]
]
}

View File

@ -0,0 +1,11 @@
# `babel-plugin-jsx`
> TODO: description
## Usage
```
const babelPluginJsx = require('babel-plugin-jsx');
// TODO: DEMONSTRATE API
```

View File

@ -1,19 +1,26 @@
import { createApp, ref, defineComponent } from 'vue'; import { createApp, ref, defineComponent } from 'vue';
const SuperButton = (props, context) => ( const SuperButton = (props, context) => {
<div class={props.class}> const obj = {
Super mouseover: () => {
<button context.emit('mouseover');
on={{ },
click: () => { click: () => {
context.emit('click'); context.emit('click');
}, },
}} };
return (
<div class={props.class}>
Super
<button
on={obj}
> >
{ props.buttonText } { props.buttonText }
{context.slots.default()}
</button> </button>
</div> </div>
); );
};
SuperButton.inheritAttrs = false; SuperButton.inheritAttrs = false;
@ -23,17 +30,23 @@ const App = defineComponent(() => {
count.value++; count.value++;
}; };
const obj = {
click: inc,
mouseover: inc,
};
return () => ( return () => (
<> <div>
Foo {count.value} Foo {count.value}
<SuperButton <SuperButton
buttonText="VueComponent" buttonText="VueComponent"
class="xxx" class="xxx"
on={{ vShow={true}
click: inc, on={obj}
}} >
/> <button>1234</button>
</> </SuperButton>
</div>
); );
}); });

View File

@ -0,0 +1,42 @@
{
"name": "@ant-design-vue/babel-plugin-jsx",
"version": "1.0.0",
"description": "Babel plugin for Vue 3.0 JSX",
"author": "Amour1688 <lcz_1996@foxmail.com>",
"homepage": "https://github.com/vueComponent/jsx/tree/master/packages/babel-plugin-jsx#readme",
"license": "MIT",
"main": "src/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/vueComponent/jsx.git"
},
"scripts": {
"dev": "webpack-dev-server",
"lint": "eslint --ext .js src",
"test": "jest --config .jest.js"
},
"bugs": {
"url": "https://github.com/vueComponent/jsx/issues"
},
"dependencies": {
"@ant-design-vue/babel-helper-vue-transform-on": "^1.0.0",
"@babel/helper-module-imports": "^7.0.0",
"@babel/plugin-syntax-jsx": "^7.0.0",
"camelcase": "^6.0.0",
"html-tags": "^3.1.0",
"svg-tags": "^1.0.0"
},
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@vue/test-utils": "^2.0.0-alpha.6",
"babel-jest": "^26.0.1",
"babel-loader": "^8.1.0",
"jest": "^26.0.1",
"regenerator-runtime": "^0.13.5",
"vue": "^3.0.0-beta.14",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
}

View File

@ -3,12 +3,12 @@ const tranformVueJSX = require('./transform-vue-jsx');
const sugarVModel = require('./sugar-v-model'); const sugarVModel = require('./sugar-v-model');
const sugarFragment = require('./sugar-fragment'); const sugarFragment = require('./sugar-fragment');
module.exports = ({ types: t }) => ({ module.exports = ({ types: t }, opts = {}) => ({
name: 'babel-plugin-jsx', name: 'babel-plugin-jsx',
inherits: syntaxJsx, inherits: syntaxJsx,
visitor: { visitor: {
...sugarVModel(t), ...sugarVModel(t),
...tranformVueJSX(t), ...tranformVueJSX(t, opts),
...sugarFragment(t), ...sugarFragment(t),
}, },
}); });

View File

@ -1,16 +1,11 @@
const htmlTags = require('html-tags'); const htmlTags = require('html-tags');
const svgTags = require('svg-tags'); const svgTags = require('svg-tags');
const { addNamed } = require('@babel/helper-module-imports'); const { addNamed, addDefault } = require('@babel/helper-module-imports');
const xlinkRE = /^xlink([A-Z])/; const xlinkRE = /^xlink([A-Z])/;
const eventRE = /^on[A-Z][a-z]+$/; const eventRE = /^on[A-Z][a-z]+$/;
const rootAttributes = ['class', 'style']; const rootAttributes = ['class', 'style'];
/**
* click --> onClick
*/
const transformOn = (event = '') => `on${event[0].toUpperCase()}${event.substr(1)}`;
/** /**
* Checks if string is describing a directive * Checks if string is describing a directive
@ -85,18 +80,15 @@ const getJSXAttributeValue = (t, path) => {
return null; return null;
}; };
const transformJSXAttribute = (t, path, attributesToMerge, directives) => { const transformJSXAttribute = (t, path, state, attributesToMerge, directives) => {
let name = getJSXAttributeName(t, path); let name = getJSXAttributeName(t, path);
if (name === 'on') { const attributeValue = getJSXAttributeValue(t, path);
const { properties = [] } = getJSXAttributeValue(t, path); if (state.opts.transformOn && (name === 'on' || name === 'nativeOn')) {
properties.forEach((property) => { const transformOn = addDefault(path, '@ant-design-vue/babel-helper-vue-transform-on', { nameHint: '_transformOn' });
attributesToMerge.push(t.objectExpression([ attributesToMerge.push(t.callExpression(
t.objectProperty( transformOn,
t.identifier(transformOn(property.key.name)), [attributeValue || t.booleanLiteral(true)],
property.value, ));
),
]));
});
return null; return null;
} }
if (isDirective(name)) { if (isDirective(name)) {
@ -104,18 +96,18 @@ const transformJSXAttribute = (t, path, attributesToMerge, directives) => {
? name.replace('v-', '') ? name.replace('v-', '')
: name.replace(`v${name[1]}`, name[1].toLowerCase()); : name.replace(`v${name[1]}`, name[1].toLowerCase());
if (directiveName === '_model') { if (directiveName === '_model') {
directives.push(getJSXAttributeValue(t, path)); directives.push(attributeValue);
} else if (directiveName === 'show') { } else if (directiveName === 'show') {
directives.push(t.arrayExpression([ directives.push(t.arrayExpression([
path.vShow, state.vShow,
getJSXAttributeValue(t, path), attributeValue,
])); ]));
} else { } else {
directives.push(t.arrayExpression([ directives.push(t.arrayExpression([
t.callExpression(path.resolveDirective, [ t.callExpression(state.resolveDirective, [
t.stringLiteral(directiveName), t.stringLiteral(directiveName),
]), ]),
getJSXAttributeValue(t, path), attributeValue,
])); ]));
} }
return null; return null;
@ -127,7 +119,7 @@ const transformJSXAttribute = (t, path, attributesToMerge, directives) => {
t.stringLiteral( t.stringLiteral(
name, name,
), ),
getJSXAttributeValue(t, path), attributeValue,
), ),
]), ]),
); );
@ -141,7 +133,7 @@ const transformJSXAttribute = (t, path, attributesToMerge, directives) => {
t.stringLiteral( t.stringLiteral(
name, name,
), ),
getJSXAttributeValue(t, path) || t.booleanLiteral(true), attributeValue || t.booleanLiteral(true),
); );
}; };
@ -169,12 +161,12 @@ const transformJSXSpreadAttribute = (t, path, attributesToMerge) => {
}))); })));
}; };
const transformAttribute = (t, path, attributesToMerge, directives) => ( const transformAttribute = (t, path, state, attributesToMerge, directives) => (
path.isJSXAttribute() path.isJSXAttribute()
? transformJSXAttribute(t, path, attributesToMerge, directives) ? transformJSXAttribute(t, path, state, attributesToMerge, directives)
: transformJSXSpreadAttribute(t, path, attributesToMerge)); : transformJSXSpreadAttribute(t, path, attributesToMerge));
const getAttributes = (t, path, directives) => { const getAttributes = (t, path, state, directives) => {
const attributes = path.get('openingElement').get('attributes'); const attributes = path.get('openingElement').get('attributes');
if (attributes.length === 0) { if (attributes.length === 0) {
return t.nullLiteral(); return t.nullLiteral();
@ -184,13 +176,13 @@ const getAttributes = (t, path, directives) => {
const attributeArray = []; const attributeArray = [];
attributes attributes
.forEach((attribute) => { .forEach((attribute) => {
const attr = transformAttribute(t, attribute, attributesToMerge, directives); const attr = transformAttribute(t, attribute, state, attributesToMerge, directives);
if (attr) { if (attr) {
attributeArray.push(attr); attributeArray.push(attr);
} }
}); });
return t.callExpression( return t.callExpression(
path.mergeProps, state.mergeProps,
[ [
...attributesToMerge, ...attributesToMerge,
t.objectExpression(attributeArray), t.objectExpression(attributeArray),
@ -295,18 +287,18 @@ const getChildren = (t, paths) => paths
&& !t.isJSXEmptyExpression(value) && !t.isJSXEmptyExpression(value)
)); ));
const transformJSXElement = (t, path) => { const transformJSXElement = (t, path, state) => {
const directives = []; const directives = [];
const tag = getTag(t, path); const tag = getTag(t, path);
const children = t.arrayExpression(getChildren(t, path.get('children'))); const children = t.arrayExpression(getChildren(t, path.get('children')));
const h = t.callExpression(path.h, [ const h = t.callExpression(state.h, [
tag, tag,
getAttributes(t, path, directives), getAttributes(t, path, state, directives),
!t.isStringLiteral(tag) !t.isStringLiteral(tag)
? t.objectExpression([ ? t.objectExpression([
t.objectProperty( t.objectProperty(
t.identifier('default'), t.identifier('default'),
t.callExpression(path.withCtx, [ t.callExpression(state.withCtx, [
t.arrowFunctionExpression( t.arrowFunctionExpression(
[], [],
children, children,
@ -319,7 +311,7 @@ const transformJSXElement = (t, path) => {
if (!directives.length) { if (!directives.length) {
return h; return h;
} }
return t.callExpression(path.withDirectives, [ return t.callExpression(state.withDirectives, [
h, h,
t.arrayExpression(directives), t.arrayExpression(directives),
]); ]);
@ -332,14 +324,14 @@ const imports = [
module.exports = (t) => ({ module.exports = (t) => ({
JSXElement: { JSXElement: {
exit(path) { exit(path, state) {
imports.forEach((m) => { imports.forEach((m) => {
if (!path[m]) { if (!state[m]) {
path[m] = addNamed(path, m, 'vue'); state[m] = addNamed(path, m, 'vue');
} }
}); });
path.replaceWith( path.replaceWith(
transformJSXElement(t, path), transformJSXElement(t, path, state),
); );
}, },
}, },