Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions generator/codemods/global-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module.exports = function(fileInfo, api) {
require('./remove-production-tip')(context)
require('./remove-vue-use')(context)
require('./remove-contextual-h')(context)
require('./next-tick')(context)
require('./observable')(context)
require('./version')(context)

// remove extraneous imports
const removeExtraneousImport = require('../utils/remove-extraneous-import')
Expand Down
30 changes: 30 additions & 0 deletions generator/codemods/global-api/next-tick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @param {Object} context
* @param {import('jscodeshift').JSCodeshift} context.j
* @param {ReturnType<import('jscodeshift').Core>} context.root
*/
module.exports = function createAppMount(context) {
const { j, root } = context

// Vue.nextTick(() => {})
const nextTickCalls = root.find(j.CallExpression, n => {
return (
n.callee.type === 'MemberExpression' &&
n.callee.property.name === 'nextTick' &&
n.callee.object.name === 'Vue'
)
})

if (!nextTickCalls.length) {
return
}

const addImport = require('../utils/add-import')
addImport(context, { imported: 'nextTick' }, 'vue')

nextTickCalls.replaceWith(({ node }) => {
const el = node.arguments[0]

return j.callExpression(j.identifier('nextTick'), [el])
})
}
30 changes: 30 additions & 0 deletions generator/codemods/global-api/observable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @param {Object} context
* @param {import('jscodeshift').JSCodeshift} context.j
* @param {ReturnType<import('jscodeshift').Core>} context.root
*/
module.exports = function createAppMount(context) {
const { j, root } = context

// Vue.observable(state)
const observableCalls = root.find(j.CallExpression, n => {
return (
n.callee.type === 'MemberExpression' &&
n.callee.property.name === 'observable' &&
n.callee.object.name === 'Vue'
)
})

if (!observableCalls.length) {
return
}

const addImport = require('../utils/add-import')
addImport(context, { imported: 'reactive' }, 'vue')

observableCalls.replaceWith(({ node }) => {
const el = node.arguments[0]

return j.callExpression(j.identifier('reactive'), [el])
})
}
29 changes: 29 additions & 0 deletions generator/codemods/global-api/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @param {Object} context
* @param {import('jscodeshift').JSCodeshift} context.j
* @param {ReturnType<import('jscodeshift').Core>} context.root
*/
module.exports = function createAppMount(context) {
const { j, root } = context

// Vue.version
const versionCalls = root.find(j.MemberExpression, n => {
return (
n.property.name === 'version' &&
n.object.name === 'Vue'
)
})

if (!versionCalls.length) {
return
}

const addImport = require('../utils/add-import')
addImport(context, { imported: 'version' }, 'vue')

versionCalls.replaceWith(({ node }) => {
const property = node.property.name

return j.identifier(property)
})
}
28 changes: 28 additions & 0 deletions generator/codemods/vue-addition/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = function(files, filename) {
let content = files[filename]
content = removeEventNative(content)
content = addTransitionFrom(content)
files[filename] = content
}

// template
// v-on:event.native => v-on:event
// @event.native => @event
function removeEventNative(content) {
const reg = new RegExp(
'(?<=<template>[\\s\\S]*?\\s(?:v-on:|@)\\w+).native(?==[\\s\\S]*?</template>)',
'g'
)
return content.replace(reg, '')
}

// style
// .xxx-enter => .xxx-enter-from
// .xxx-leave => .xxx-leave-from
function addTransitionFrom(content) {
const reg = new RegExp(
'(?<=<style[\\s>][\\s\\S]*?\\s\\.[A-Za-z0-9_-]+-)(enter|leave)(?=[,{\\s][\\s\\S]*?</style>)',
'g'
)
return content.replace(reg, '$1-from')
}
48 changes: 48 additions & 0 deletions generator/codemods/vue/add-emit-declaration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @param {Object} context
* @param {import('jscodeshift').JSCodeshift} context.j
* @param {ReturnType<import('jscodeshift').Core>} context.root
*/
module.exports = function addEmitDeclaration(context) {
const { j, root } = context

// this.$emit('xxx') => emits: ['xxx']
const this$emits = root.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: { type: 'ThisExpression' },
property: {
type: 'Identifier',
name: '$emit'
}
}
})

const emits = []
for (let i = 0; i < this$emits.length; i++) {
const arg = this$emits.at(i).get().node.arguments[0]
if (arg.type === 'StringLiteral') {
emits.push(arg.value)
}
}

if (emits.length === 0) {
return
}

const properties = root
.find(j.ExportDefaultDeclaration)
.at(0)
.find(j.ObjectExpression)
.at(0)

properties.replaceWith(nodePath => {
nodePath.node.properties.unshift(
j.objectProperty(
j.identifier('emits'),
j.arrayExpression(emits.map(el => j.stringLiteral(el)))
)
)
return nodePath.node
})
}
124 changes: 124 additions & 0 deletions generator/codemods/vue/add-watch-deep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* @param {Object} context
* @param {import('jscodeshift').JSCodeshift} context.j
* @param {ReturnType<import('jscodeshift').Core>} context.root
*/
module.exports = function addEmitDeclaration(context) {
const { j, root } = context

// this.$watch(...) add deep option
const this$watches = root.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: { type: 'ThisExpression' },
property: {
type: 'Identifier',
name: '$watch'
}
}
})

for (let i = 0; i < this$watches.length; i++) {
const watchFunc = this$watches.at(i)
const deepProperty = watchFunc.find(j.ObjectProperty, {
key: {
type: 'Identifier',
name: 'deep'
}
})
if (deepProperty.length > 0) {
continue
}
const arguments = watchFunc.get().node.arguments
if (arguments.length < 2) {
continue
}
if (arguments[1].type != 'ObjectExpression') {
if (arguments.length < 3) {
watchFunc.replaceWith(nodePath => {
nodePath.node.arguments.push(j.objectExpression([]))
return nodePath.node
})
}
const target = watchFunc.find(j.ObjectExpression).at(0)
target.replaceWith(nodePath => {
nodePath.node.properties.push(
j.objectProperty(j.identifier('deep'), j.booleanLiteral(true))
)
return nodePath.node
})
}
}

// watch: {...} add deep option
const watchFuncs = root
.find(j.ExportDefaultDeclaration)
.at(0)
.find(j.ObjectExpression)
.at(0)
.find(j.ObjectProperty, {
key: {
type: 'Identifier',
name: 'watch'
}
})
.at(0)
.find(j.ObjectExpression)
.at(0)
.find(j.ObjectProperty)

for (let i = 0; i < watchFuncs.length; i++) {
const watchProperty = watchFuncs.at(i)
if (!inExportDefaultLevel(watchProperty, 2)) {
continue
}
const deepProperty = watchProperty.find(j.ObjectProperty, {
key: {
type: 'Identifier',
name: 'deep'
}
})
if (deepProperty.length > 0) {
continue
}

if (watchProperty.get().node.value.type === 'ObjectExpression') {
const target = watchProperty.find(j.ObjectExpression).at(0)
target.replaceWith(nodePath => {
nodePath.node.properties.push(
j.objectProperty(j.identifier('deep'), j.booleanLiteral(true))
)
return nodePath.node
})
} else {
watchProperty.replaceWith(nodePath => {
nodePath.node.value = j.objectExpression([
j.objectProperty(j.identifier('handler'), nodePath.node.value),
j.objectProperty(j.identifier('deep'), j.booleanLiteral(true))
])
return nodePath.node
})
}
}
}

function getExportDefaultLevel(collection) {
let path = collection.get()
let level = 0
while (path) {
if (path.node.type === 'ExportDefaultDeclaration') {
return level
}
path = path.parentPath
level++
}
return -1
}

function inExportDefaultLevel(collection, level) {
const lvl = getExportDefaultLevel(collection)
if (level * 3 === lvl) {
return true
}
return false
}
15 changes: 15 additions & 0 deletions generator/codemods/vue/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @type {import('jscodeshift').Transform} */
module.exports = function(fileInfo, api) {
const j = api.jscodeshift
const root = j(fileInfo.source)
const context = { j, root }

require('./add-emit-declaration')(context)
require('./add-watch-deep')(context)
require('./rename-lifecycle')(context)
require('./vModel')(context)

return root.toSource({ lineTerminator: '\n' })
}

module.exports.parser = 'babylon'
28 changes: 28 additions & 0 deletions generator/codemods/vue/rename-lifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/** @type {import('jscodeshift').Transform} */

const DEPRECATED_LIFECYCLE = Object.create(null)
DEPRECATED_LIFECYCLE.destroyed = 'unmounted'
DEPRECATED_LIFECYCLE.beforeDestroy = 'beforeUnmount'

module.exports = function renameLifecycle(context) {
const { j, root } = context

const renameDeprecatedLifecycle = path => {
const name = path.node.key.name

if (
DEPRECATED_LIFECYCLE[name] &&
path.parent &&
path.parent.parent &&
path.parent.parent.value.type === 'ExportDefaultDeclaration'
) {
path.value.key.name = DEPRECATED_LIFECYCLE[name]
}
}

root.find(j.ObjectProperty).forEach(renameDeprecatedLifecycle)
root.find(j.ObjectMethod).forEach(renameDeprecatedLifecycle)
root.find(j.ClassProperty).forEach(renameDeprecatedLifecycle)

return root.toSource({ lineTerminator: '\n' })
}
Loading