Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

babel-plugin-transform-react-to-vue

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

babel-plugin-transform-react-to-vue - npm Package Compare versions

Comparing version 0.0.0 to 0.1.0

311

index.js

@@ -1,78 +0,275 @@

module.exports = function ({ types: t }) {
const mapKey = key => {
const map = {
componentDidMount: t.identifier('mounted'),
componentWillMount: t.identifier('beforeMount'),
componentWillUnmount: t.identifier('beforeDestroy'),
state: t.identifier('data')
const isSpecialMethod = (() => {
const specialMethods = new Set(['render', 'componentDidMount', 'componentWillMount', 'componentWillUnmount'])
return methodName => specialMethods.has(methodName)
})()
const mapMethodName = (() => {
let map = null
return (t, key) => {
if (!map) {
map = {
componentDidMount: t.identifier('mounted'),
componentWillMount: t.identifier('beforeMount'),
componentWillUnmount: t.identifier('beforeDestroy')
}
}
return map[key.name] || key
}
})()
return {
inherits: require('babel-plugin-syntax-class-properties'),
visitor: {
ClassDeclaration(path) {
if (!path.node.superClass || path.node.superClass.name !== 'Component') {
const removeReactImport = (t, path) => {
path.traverse({
ImportDeclaration(path) {
const specifiers = path.get('specifiers')
const source = path.get('source')
if (!t.isStringLiteral(source) || source.node.value !== 'react') {
return
}
specifiers.forEach(specifier => {
if (t.isImportDefaultSpecifier(specifier)) {
specifier.remove()
if (specifiers.length === 1) {
path.remove()
}
}
})
}
})
}
const getReactComponentIdentifier = (t, path) => {
let result = null
path.traverse({
ImportDeclaration(path) {
const specifiers = path.get('specifiers')
const source = path.get('source')
if (
specifiers.length === 1 &&
t.isStringLiteral(source) &&
source.node.value === 'react-dom' &&
t.isImportDefaultSpecifier(specifiers[0]) &&
specifiers[0].node.local.name === 'ReactDOM'
) {
path.replaceWith(t.importDeclaration([t.importDefaultSpecifier(t.identifier('Vue'))], t.stringLiteral('vue')))
return
}
if (!t.isStringLiteral(source) || source.node.value !== 'react') {
return
}
specifiers.forEach(specifier => {
if (!t.isImportSpecifier(specifier)) {
return
}
const body = []
const imported = specifier.get('imported')
const local = specifier.get('local')
path.node.body.body.forEach(exp => {
if (exp.type === 'ClassMethod') {
if (exp.kind === 'method') {
exp.type = 'ObjectMethod'
exp.key = mapKey(exp.key)
body.push(exp)
}
} else if (exp.type === 'ClassProperty') {
if (exp.key.name === 'state') {
exp.key = mapKey(exp.key)
body.push(t.objectMethod(
'method',
t.identifier('data'),
[],
t.blockStatement([
t.returnStatement(exp.value)
])
))
}
if (t.isIdentifier(imported) && imported.node.name === 'Component' && t.isIdentifier(local)) {
result = local.node.name
specifier.remove()
if (specifiers.length === 1) {
path.remove()
}
}
})
}
})
return result
}
const convertReactBody = (t, path) => {
path.traverse({
// this.state.* => this.$data.* and this.props.* => this.$attrs.*
MemberExpression(path) {
const object = path.get('object')
const property = path.get('property')
if (t.isThisExpression(object) && t.isIdentifier(property) && property.node.name === 'state') {
property.replaceWith(t.identifier('$data'))
} else if (t.isThisExpression(object) && t.isIdentifier(property) && property.node.name === 'props') {
property.replaceWith(t.identifier('$attrs'))
}
},
// className => class
JSXAttribute(path) {
const name = path.get('name')
if (t.isJSXIdentifier(name) && name.node.name === 'className') {
path.replaceWith(t.jSXAttribute(t.jSXIdentifier('class'), path.get('value').node))
}
},
// this.setState({...this.state, newProps: newVals})
CallExpression(path) {
const callee = path.get('callee')
const args = path.get('arguments')
if (
t.isMemberExpression(callee) &&
t.isThisExpression(callee.get('object')) &&
t.isIdentifier(callee.get('property')) &&
callee.get('property').node.name === 'setState' &&
args.length === 1 &&
t.isObjectExpression(args[0])
) {
const statePatch = args[0].get('properties')
const toPatch = []
statePatch.forEach(property => {
if (t.isSpreadElement(property)) {
return
}
const key = property.get('key')
toPatch.push({
key,
value: property.get('value')
})
})
path.replaceWith(
t.variableDeclaration(
'var',
[
t.variableDeclarator(
path.node.id,
t.objectExpression(body)
)
]
const assignments = toPatch.map(({ key, value }) =>
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(t.thisExpression(), key.node, !t.isIdentifier(key.node)),
value.node
)
)
)
path.replaceWith(t.blockStatement(assignments))
}
}
}
})
}
function looksLike(a, b) {
return (
a &&
b &&
Object.keys(b).every(bKey => {
const bVal = b[bKey]
const aVal = a[bKey]
if (typeof bVal === 'function') {
return bVal(aVal)
const convertReactComponent = (t, path, isDefaultExport) => {
const id = path.get('id')
const vueBody = []
const reactBody = path.get('body')
const methods = []
reactBody.get('body').forEach(reactProperty => {
const key = reactProperty.get('key')
if (
// normal methods
t.isClassMethod(reactProperty) &&
reactProperty.node.kind === 'method' &&
t.isIdentifier(key)
) {
const body = reactProperty.get('body')
const params = reactProperty.node.params
convertReactBody(t, reactProperty.get('body'))
const newMethod = t.objectMethod('method', mapMethodName(t, key.node), params, body.node)
newMethod.async = reactProperty.node.async
newMethod.generator = reactProperty.node.generator
if (isSpecialMethod(key.node.name)) {
vueBody.push(newMethod)
} else {
methods.push(newMethod)
}
return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal)
})
)
} else if (
// bound-to-class methods
t.isClassProperty(reactProperty) &&
!reactProperty.node.static &&
!reactProperty.node.computed &&
t.isArrowFunctionExpression(reactProperty.get('value'))
) {
const arrowFn = reactProperty.get('value')
let body = arrowFn.get('body').node
if (!t.isBlockStatement(body)) {
arrowFn.get('body').replaceWith(t.blockStatement([t.returnStatement(body)]))
body = arrowFn.get('body').node
}
const params = arrowFn.node.params
convertReactBody(t, arrowFn.get('body'))
const newMethod = t.objectMethod('method', mapMethodName(t, key.node), params, body)
newMethod.async = arrowFn.node.async
newMethod.generator = arrowFn.node.generator
if (isSpecialMethod(key.node.name)) {
vueBody.push(newMethod)
} else {
methods.push(newMethod)
}
} else if (
// state
t.isClassProperty(reactProperty) &&
!reactProperty.node.static &&
!reactProperty.node.computed &&
t.isIdentifier(key) &&
key.node.name === 'state'
) {
vueBody.push(
t.objectProperty(t.identifier('data'), t.arrowFunctionExpression([], reactProperty.get('value').node))
)
}
})
if (methods.length > 0) {
vueBody.push(t.objectProperty(t.identifier('methods'), t.objectExpression(methods)))
}
if (isDefaultExport) {
path.replaceWith(t.objectExpression(vueBody))
} else {
path.replaceWith(t.variableDeclaration('const', [t.variableDeclarator(id.node, t.objectExpression(vueBody))]))
}
}
function isPrimitive(val) {
// eslint-disable-next-line no-eq-null,eqeqeq
return val == null || /^[sbn]/.test(typeof val)
module.exports = ({ types: t }) => {
return {
visitor: {
Program(path) {
removeReactImport(t, path)
const componentIdentifier = getReactComponentIdentifier(t, path)
let defaultExport = null
path.traverse({
ExportDefaultDeclaration(path) {
defaultExport = path.get('declaration')
},
ClassDeclaration(path) {
const superClass = path.get('superClass')
if (superClass && t.isIdentifier(superClass) && superClass.node.name === componentIdentifier) {
convertReactComponent(t, path, path === defaultExport)
}
},
CallExpression(path) {
const callee = path.get('callee')
const object = callee.get('object')
const property = callee.get('property')
const computed = callee.node.computed
if (
!computed &&
t.isIdentifier(object) &&
t.isIdentifier(property) &&
object.node.name === 'ReactDOM' &&
property.node.name === 'render'
) {
const [jsx, el] = path.get('arguments')
path.replaceWith(
t.newExpression(t.identifier('Vue'), [
t.objectExpression([
t.objectProperty(t.identifier('el'), el.node),
t.objectMethod(
'method',
t.identifier('render'),
[],
t.blockStatement([t.returnStatement(jsx.node)])
)
])
])
)
}
}
})
}
}
}
}

29

package.json
{
"name": "babel-plugin-transform-react-to-vue",
"version": "0.0.0",
"version": "0.1.0",
"description": "my impeccable project",
"repository": {
"url": "egoist/babel-plugin-transform-react-to-vue",
"url": "vueact/babel-plugin-transform-react-to-vue",
"type": "git"

@@ -14,25 +14,22 @@ },

"scripts": {
"test": "jest && npm run lint",
"lint": "xo"
"test": "ava && npm run lint",
"lint": "xo",
"cov": "nyc --reporter=lcov ava"
},
"author": "egoist <0x142857@gmail.com>",
"license": "MIT",
"jest": {
"testEnvironment": "node"
},
"dependencies": {
"babel-plugin-syntax-class-properties": "^6.13.0"
},
"dependencies": {},
"devDependencies": {
"babel-core": "^6.25.0",
"ava": "^0.21.0",
"babel-core": "next",
"babel-plugin-syntax-class-properties": "next",
"babel-plugin-syntax-jsx": "next",
"babel-plugin-syntax-object-rest-spread": "next",
"eslint-config-rem": "^3.0.0",
"jest-cli": "^19.0.0",
"nyc": "^11.0.3",
"xo": "^0.18.0"
},
"xo": {
"extends": "rem",
"envs": [
"jest"
]
"extends": "rem"
}
}
# babel-plugin-transform-react-to-vue
[![NPM version](https://img.shields.io/npm/v/babel-plugin-transform-react-to-vue.svg?style=flat)](https://npmjs.com/package/babel-plugin-transform-react-to-vue) [![NPM downloads](https://img.shields.io/npm/dm/babel-plugin-transform-react-to-vue.svg?style=flat)](https://npmjs.com/package/babel-plugin-transform-react-to-vue) [![CircleCI](https://circleci.com/gh/egoist/babel-plugin-transform-react-to-vue/tree/master.svg?style=shield)](https://circleci.com/gh/egoist/babel-plugin-transform-react-to-vue/tree/master) [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/egoist/donate)
[![NPM version](https://img.shields.io/npm/v/babel-plugin-transform-react-to-vue.svg?style=flat)](https://npmjs.com/package/babel-plugin-transform-react-to-vue) [![NPM downloads](https://img.shields.io/npm/dm/babel-plugin-transform-react-to-vue.svg?style=flat)](https://npmjs.com/package/babel-plugin-transform-react-to-vue) [![CircleCI](https://circleci.com/gh/vueact/babel-plugin-transform-react-to-vue/tree/master.svg?style=shield)](https://circleci.com/gh/vueact/babel-plugin-transform-react-to-vue/tree/master) [![donate](https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat)](https://github.com/egoist/donate)
🚧 **In development...**
## Install

@@ -24,13 +22,30 @@

```js
class Counter extends Component {
state = { count: 0 }
import ReactDOM from 'react-dom'
import React, { Component } from 'react'
inc = () => this.setState({count: this.state.count + 1})
class App extends Component {
state = {
hello: 'world'
}
myMethod = () => {
this.setState({ hello: 'not world ;)' })
}
render() {
return <button onClick={this.inc}>
{this.state.count}
</button>
return (
<div className="App">
<div className="App-header" onClick={this.myMethod}>
<h2>
Hello {this.state.hello}
</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
)
}
componentDidMount = () => console.log(this.state)
}
ReactDOM.render(<App />, document.getElementById('root'))
```

@@ -41,19 +56,42 @@

```js
var Counter = {
data() {
return {
count: 0
}
import Vue from 'vue'
const App = {
data: () => ({
hello: 'world'
}),
render() {
return (
<div class="App">
<div class="App-header" onClick={this.myMethod}>
<h2>
Hello {this.$data.hello}
</h2>
</div>
<p class="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
)
},
mounted() {
return console.log(this.$data)
},
methods: {
inc() {
this.count = this.count + 1
myMethod() {
this.hello = 'not world ;)'
}
},
}
}
new Vue({
el: document.getElementById('root'),
render() {
return <button onClick={this.inc}>
{this.count}
</button>
return <App />
}
}
})
```

@@ -70,7 +108,6 @@

## Author
## Team
**babel-plugin-transform-react-to-vue** © [egoist](https://github.com/egoist), Released under the [MIT](./LICENSE) License.<br>
Authored and maintained by egoist with help from contributors ([list](https://github.com/egoist/babel-plugin-transform-react-to-vue/contributors)).
> [egoistian.com](https://egoistian.com) · GitHub [@egoist](https://github.com/egoist) · Twitter [@rem_rin_rin](https://twitter.com/rem_rin_rin)
[![EGOIST](https://github.com/egoist.png?size=100)](https://github.com/egoist) | [![Nick Messing](https://github.com/nickmessing.png?size=100)](https://github.com/nickmessing)
---|---
[EGOIST](http://github.com/egoist) | [Nick Messing](https://github.com/nickmessing)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc