Skip to content

Commit eda1741

Browse files
antfuluwanquan
andcommitted
feat: add @vue/babel-sugar-composition-api-render-instance
Co-authored-by: luwanquan <[email protected]>
1 parent 356870a commit eda1741

File tree

9 files changed

+5187
-0
lines changed

9 files changed

+5187
-0
lines changed

Diff for: packages/babel-preset-jsx/src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import babelPluginTransformVueJsx from '@vue/babel-plugin-transform-vue-jsx'
22
import babelSugarFunctionalVue from '@vue/babel-sugar-functional-vue'
33
import babelSugarInjectH from '@vue/babel-sugar-inject-h'
44
import babelSugarCompositionApiInjectH from '@vue/babel-sugar-composition-api-inject-h'
5+
import babelSugarCompositionApiRenderInstance from '@vue/babel-sugar-composition-api-render-instance'
56
import babelSugarVModel from '@vue/babel-sugar-v-model'
67
import babelSugarVOn from '@vue/babel-sugar-v-on'
78

@@ -10,6 +11,7 @@ export default (_, { functional = true, injectH = true, vModel = true, vOn = tru
1011
plugins: [
1112
functional && babelSugarFunctionalVue,
1213
injectH && (compositionAPI ? babelSugarCompositionApiInjectH : babelSugarInjectH),
14+
compositionAPI && babelSugarCompositionApiRenderInstance,
1315
vModel && babelSugarVModel,
1416
vOn && babelSugarVOn,
1517
babelPluginTransformVueJsx,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/dist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## @vue/babel-sugar-composition-api-render-instance
2+
3+
> Ported from [luwanquan/babel-preset-vca-jsx](https://github.com/luwanquan/babel-preset-vca-jsx) by [@luwanquan](https://github.com/luwanquan)
4+
5+
Babel syntactic sugar for replaceing `this` with `getCurrentInstance()` in Vue JSX with @vue/composition-api
6+
7+
### Babel Compatibility Notes
8+
9+
- This repo is only compatible with Babel 7.x
10+
11+
### Usage
12+
13+
Install the dependencies:
14+
15+
```sh
16+
# for yarn:
17+
yarn add @vue/babel-sugar-composition-api-render-instance
18+
# for npm:
19+
npm install @vue/babel-sugar-composition-api-render-instance --save
20+
```
21+
22+
In your `.babelrc`:
23+
24+
```json
25+
{
26+
"plugins": ["@vue/babel-sugar-composition-api-render-instance"]
27+
}
28+
```
29+
30+
However it is recommended to use the [configurable preset](../babel-preset-jsx/README.md) instead.
31+
32+
### Details
33+
34+
This plugin automatically injects `h` in every method that has JSX. By using this plugin you don't have to always specifically declare `h` as first parameter in your `render()` function.
35+
36+
```js
37+
// Without @vue/babel-sugar-inject-h
38+
import { h } from '@vue/composition-api'
39+
40+
export default {
41+
setup() {
42+
return () => <button />
43+
},
44+
}
45+
46+
// With @vue/babel-sugar-inject-h
47+
export default {
48+
setup() {
49+
return () => <button />
50+
},
51+
}
52+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "@vue/babel-sugar-composition-api-render-instance",
3+
"version": "1.1.2",
4+
"description": "Babel syntactic sugar for replaceing `this` with `getCurrentInstance()` in Vue JSX with @vue/composition-api",
5+
"main": "dist/plugin.js",
6+
"repository": "https://github.com/vuejs/jsx/tree/master/packages/babel-sugar-composition-api-render-instance",
7+
"author": "luwanquan <[email protected]>",
8+
"license": "MIT",
9+
"private": false,
10+
"files": [],
11+
"scripts": {
12+
"prepublish": "yarn build",
13+
"build": "rollup -c",
14+
"build:test": "rollup -c rollup.config.testing.js",
15+
"pretest": "yarn build:test",
16+
"test": "nyc --reporter=html --reporter=text-summary ava -v test/test.js"
17+
},
18+
"devDependencies": {
19+
"@babel/cli": "^7.2.0",
20+
"@babel/core": "^7.2.0",
21+
"@babel/preset-env": "^7.2.0",
22+
"ava": "^0.25.0",
23+
"nyc": "^13.1.0",
24+
"rollup": "^0.67.4",
25+
"rollup-plugin-babel": "4.0.3",
26+
"rollup-plugin-istanbul": "^2.0.1",
27+
"rollup-plugin-uglify-es": "^0.0.1",
28+
"vue": "^2.5.17"
29+
},
30+
"dependencies": {
31+
"@babel/plugin-syntax-jsx": "^7.2.0"
32+
},
33+
"peerDependencies": {
34+
"@babel/core": "^7.0.0-0"
35+
},
36+
"nyc": {
37+
"exclude": [
38+
"dist",
39+
"test"
40+
]
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { rollup } from 'rollup'
2+
import babel from 'rollup-plugin-babel'
3+
import uglify from 'rollup-plugin-uglify-es'
4+
5+
export default {
6+
input: 'src/index.js',
7+
plugins: [
8+
babel({
9+
presets: [
10+
[
11+
'@babel/preset-env',
12+
{
13+
targets: {
14+
node: '8',
15+
},
16+
modules: false,
17+
loose: true,
18+
},
19+
],
20+
],
21+
}),
22+
uglify(),
23+
],
24+
output: [
25+
{
26+
file: 'dist/plugin.js',
27+
format: 'cjs',
28+
},
29+
],
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import istanbul from 'rollup-plugin-istanbul'
2+
3+
export default {
4+
input: 'src/index.js',
5+
plugins: [istanbul()],
6+
output: [
7+
{
8+
file: 'dist/plugin.testing.js',
9+
format: 'cjs',
10+
},
11+
],
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import syntaxJsx from '@babel/plugin-syntax-jsx'
2+
3+
const autoImportGetCurrentInstance = (t, path) => {
4+
const importSource = '@vue/composition-api'
5+
const importNodes = path
6+
.get('body')
7+
.filter(p => p.isImportDeclaration())
8+
.map(p => p.node)
9+
const vcaImportNodes = importNodes.filter(p => p.source.value === importSource)
10+
const hasH = vcaImportNodes.some(p =>
11+
p.specifiers.some(s => t.isImportSpecifier(s) && s.local.name === 'getCurrentInstance'),
12+
)
13+
if (!hasH) {
14+
const vcaImportSpecifier = t.importSpecifier(t.identifier('getCurrentInstance'), t.identifier('getCurrentInstance'))
15+
if (vcaImportNodes.length > 0) {
16+
vcaImportNodes[0].specifiers.push(vcaImportSpecifier)
17+
} else {
18+
path.unshiftContainer('body', t.importDeclaration([vcaImportSpecifier], t.stringLiteral(importSource)))
19+
}
20+
}
21+
}
22+
23+
export default ({ types: t }) => {
24+
return {
25+
inherits: syntaxJsx,
26+
visitor: {
27+
Program(p) {
28+
p.traverse({
29+
'ObjectMethod|ObjectProperty'(path) {
30+
if (path.node.key.name !== 'setup') return
31+
path.traverse({
32+
JSXAttribute(path) {
33+
const n = path.get('name')
34+
const isInputOrModel = ['v-on', 'on-input', 'on-change', 'model'].includes(n.node.name)
35+
if (!isInputOrModel) return
36+
path.traverse({
37+
MemberExpression(path) {
38+
const obj = path.get('object')
39+
const prop = path.get('property')
40+
if (t.isThisExpression(obj) && t.isIdentifier(prop) && ['$', '_'].includes(prop.node.name[0])) {
41+
autoImportGetCurrentInstance(t, p)
42+
obj.replaceWith(t.callExpression(t.identifier('getCurrentInstance'), []))
43+
}
44+
},
45+
})
46+
},
47+
})
48+
},
49+
})
50+
},
51+
},
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import test from 'ava'
2+
import { transform } from '@babel/core'
3+
import plugin from '../dist/plugin.testing'
4+
import vModelPlugin from '../../babel-sugar-v-model/dist/plugin.testing'
5+
6+
const transpile = src =>
7+
new Promise((resolve, reject) => {
8+
transform(
9+
src,
10+
{
11+
plugins: [vModelPlugin],
12+
},
13+
(err, result) => {
14+
if (err) {
15+
return reject(err)
16+
}
17+
transform(
18+
result.code,
19+
{
20+
plugins: [plugin],
21+
},
22+
(err, result) => {
23+
if (err) {
24+
return reject(err)
25+
}
26+
resolve(result.code)
27+
},
28+
)
29+
},
30+
)
31+
})
32+
33+
const tests = [
34+
{
35+
name: 'Ignores non vModel arguments',
36+
from: `const a = { setup: () => { return () => <A x="y" a:b={c} /> } }`,
37+
to: `const a = {
38+
setup: () => {
39+
return () => <A x="y" a:b={c} />;
40+
}
41+
};`,
42+
},
43+
{
44+
name: 'Generic component vModel',
45+
from: `const a = { setup: () => { return () => <MyComponent vModel={a.b} /> } }`,
46+
to: `import { getCurrentInstance } from "@vue/composition-api";
47+
const a = {
48+
setup: () => {
49+
return () => <MyComponent model={{
50+
value: a.b,
51+
callback: $$v => {
52+
getCurrentInstance().$set(a, "b", $$v);
53+
}
54+
}} />;
55+
}
56+
};`,
57+
},
58+
{
59+
name: 'Component vModel_number',
60+
from: `const a = { setup: () => { return () => <MyComponent vModel_number={a.b} /> } }`,
61+
to: `import { getCurrentInstance } from "@vue/composition-api";
62+
const a = {
63+
setup: () => {
64+
return () => <MyComponent model={{
65+
value: a.b,
66+
callback: $$v => {
67+
getCurrentInstance().$set(a, "b", getCurrentInstance()._n($$v));
68+
}
69+
}} />;
70+
}
71+
};`,
72+
},
73+
{
74+
name: 'Ignore outside of setup()',
75+
from: `const A = <MyComponent vModel={a[b]} />`,
76+
to: `const A = <MyComponent model={{
77+
value: a[b],
78+
callback: $$v => {
79+
this.$set(a, b, $$v);
80+
}
81+
}} />;`,
82+
},
83+
]
84+
85+
tests.map(({ name, from, to, NODE_ENV }) => {
86+
test.serial(name, async t => {
87+
const nodeEnvCopy = process.env.NODE_ENV
88+
if (NODE_ENV) {
89+
process.env.NODE_ENV = NODE_ENV
90+
}
91+
t.is(await transpile(from), to)
92+
if (NODE_ENV) {
93+
process.env.NODE_ENV = nodeEnvCopy
94+
}
95+
})
96+
})

0 commit comments

Comments
 (0)