Skip to content

Commit cd20b45

Browse files
committed
Initial setup
1 parent 56dd7d5 commit cd20b45

12 files changed

+4978
-1
lines changed

.editorconfig

Whitespace-only changes.

.prettierrc.js

Whitespace-only changes.

README.md

+60-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,61 @@
11
# vue-codemod
2-
Vue codemod scripts
2+
3+
**Current status: pre-MVP, not usable**
4+
5+
This repository contains a collection of codemod scripts for use with [JSCodeshift](https://github.com/facebook/jscodeshift) that help update Vue.js APIs.
6+
7+
Inspired by [react-codemod](https://github.com/reactjs/react-codemod).
8+
9+
## Usage
10+
11+
`npx vue-codemod -t <transformation> -f <path> [...options]
12+
13+
- `transformation` - name of transformation, see available transformations below. Or you can provide a path to a custom transformation module.
14+
- `path` - files or directory to transform
15+
- use the `--dry` options for a dry-run
16+
17+
## TODOs
18+
19+
(Sort by priority)
20+
21+
- [ ] Basic testing setup and an MVP
22+
- [ ] Define an interface for transformation of template blocks
23+
- [ ] Automatically apply transformations to `.vue` file without explicitly depending on `vue-jscodeshift-adapter` (should open source this repo after finishing this one)
24+
- [ ] A playground for writing transformations
25+
- [ ] Implement more transformations for [active RFCs](https://github.com/vuejs/rfcs/tree/master/active-rfcs)
26+
- [ ] Support `.ts` and `<script lang="ts">`
27+
28+
## Included Transformations
29+
30+
### rfc-0009
31+
32+
Alias of [`new-vue-to-create-app`](#new-vue-to-create-app)
33+
34+
### rfc-0013
35+
36+
**WIP**
37+
Runs [`object-to-composition-api`](#object-to-composition-api)
38+
39+
### new-vue-to-create-app
40+
41+
Implements [RFC-0009 Global API Change](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md).
42+
Converts `new Vue(...).$mount('#app')` calls to `createApp(...).mount(App, '#app')`.
43+
44+
### object-to-composition-api
45+
46+
**WIP**
47+
Implements [RFC-0013 Composition API](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md).
48+
Migrated from https://github.com/vuejs/function-api-converter
49+
50+
## Custom Transformation
51+
52+
TODO
53+
54+
## Post Transformation
55+
56+
- Running transformations will generally ruin the formatting of your files. A recommended way to solve that problem is by using [Prettier](https://prettier.io/) or `eslint --fix`.
57+
- Even after running prettier its possible to have unnecessary new lines added/removed. This can be solved by ignoring white spaces while staging the changes in git.
58+
59+
```sh
60+
git diff --ignore-blank-lines | git apply --cached
61+
```

bin/vue-codemod.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env node
2+
3+
import program from 'commander'
4+
import execa from 'execa'
5+
6+
import * as fs from 'fs'
7+
import * as path from 'path'
8+
9+
import { version } from '../package.json'
10+
11+
program
12+
.name('vue-codemod')
13+
.version(version)
14+
15+
.requiredOption('-t, --transformation <name>', 'Name or path of a transfromation module')
16+
.requiredOption('-f, --file <glob>', 'Files or directory to run codemod on')
17+
.option('--dry', 'Dry run (no changes are made to files)')
18+
19+
.parse(process.argv)
20+
21+
function resolveTransformation (name: string) {
22+
const builtInPath = require.resolve(`../transforms/${name}`)
23+
if (fs.existsSync(builtInPath)) {
24+
return builtInPath
25+
}
26+
27+
const customModulePath = path.resolve(process.cwd(), name)
28+
if (fs.existsSync(customModulePath)) {
29+
return customModulePath
30+
}
31+
}
32+
33+
const transformationPath = resolveTransformation(program.transformation)
34+
if (!transformationPath) {
35+
console.error(`Cannot find transformation module ${program.transformation}`)
36+
process.exit(1)
37+
}
38+
39+
// TODO:
40+
// don't depend on the jscodeshift **CLI** interface
41+
// so that we can apply the adapter to all code transforms directly
42+
const jscodeshiftExecutable = require.resolve('.bin/jscodeshift')
43+
execa.sync(jscodeshiftExecutable, ['-t', transformationPath, program.file, '--extensions', 'vue,js'], {
44+
stdio: 'inherit',
45+
stripFinalNewline: false
46+
})

package.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "vue-codemod",
3+
"version": "0.0.1",
4+
"description": "Vue codemod scripts",
5+
"main": "index.js",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/vuejs/vue-codemod.git"
9+
},
10+
"author": "Haoqun Jiang",
11+
"license": "MIT",
12+
"dependencies": {
13+
"commander": "^4.1.0",
14+
"execa": "^4.0.0",
15+
"globby": "^11.0.0",
16+
"inquirer": "^7.0.3",
17+
"jscodeshift": "^0.7.0",
18+
"vue-jscodeshift-adapter": "^2.0.3"
19+
},
20+
"devDependencies": {
21+
"@types/jest": "^24.0.25",
22+
"@types/jscodeshift": "^0.6.3",
23+
"@types/node": "^13.1.6",
24+
"jest": "^24.9.0",
25+
"typescript": "^3.7.4"
26+
}
27+
}

transforms/new-vue-to-create-app.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './rfc-0009'

transforms/rfc-0009/index.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Transform } from "jscodeshift"
2+
import adapt from '../../utils/adapter'
3+
4+
import removeProductionTip from './remove-production-tip'
5+
import transformMount from './transformMount'
6+
7+
const transform: Transform = (file, api) => {
8+
const j = api.jscodeshift
9+
const root = j(file.source)
10+
11+
// add a `createApp` import
12+
const vueImportExpr = root.find(j.ImportDeclaration, {
13+
source: {
14+
value: 'vue'
15+
}
16+
})
17+
vueImportExpr.forEach(({ node }) => {
18+
node.specifiers.push(j.importSpecifier(j.identifier('createApp')))
19+
})
20+
21+
removeProductionTip(j, root)
22+
transformMount(j, root)
23+
24+
// remove extraneous Vue import
25+
const localVueUsages = root.find(j.Identifier, { name: 'Vue' })
26+
if (localVueUsages.length === 1) {
27+
localVueUsages.closest(j.ImportDefaultSpecifier).remove()
28+
}
29+
30+
return root.toSource({ lineTerminator: '\n' })
31+
}
32+
33+
export default adapt(transform)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { JSCodeshift } from 'jscodeshift'
2+
3+
export default function removeProductionTip(j: JSCodeshift, root: ReturnType<JSCodeshift>) {
4+
const productionTipAssignment = root.find(
5+
j.AssignmentExpression,
6+
n =>
7+
n.left.type === 'MemberExpression' &&
8+
n.left.property.name === 'productionTip' &&
9+
n.left.object.property.name === 'config' &&
10+
n.left.object.object.name === 'Vue'
11+
)
12+
productionTipAssignment.remove()
13+
14+
return root
15+
}

transforms/rfc-0009/transformMount.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { JSCodeshift } from 'jscodeshift'
2+
3+
export default function transformMount(j: JSCodeshift, root: ReturnType<JSCodeshift>) {
4+
// new Vue(...).$mount()
5+
const mountCalls = root.find(j.CallExpression, n => {
6+
return (
7+
n.callee.type === 'MemberExpression' &&
8+
n.callee.property.name === '$mount' &&
9+
n.callee.object &&
10+
n.callee.object.type === 'NewExpression' &&
11+
n.callee.object.callee.name === 'Vue'
12+
)
13+
})
14+
15+
mountCalls.replaceWith(({ node }) => {
16+
// FIXME:
17+
// @ts-ignore
18+
let options = node.callee.object.arguments[0]
19+
const el = node.arguments[0]
20+
21+
// if it's a simple option, like `{ render: h => h(App) }`,
22+
// then just use the App variable
23+
if (
24+
options.properties.length === 1 &&
25+
options.properties[0].key.name === 'render' &&
26+
options.properties[0].value.type === 'ArrowFunctionExpression' &&
27+
options.properties[0].value.body.type === 'CallExpression'
28+
) {
29+
options = options.properties[0].value.body.arguments[0]
30+
}
31+
32+
return j.callExpression(
33+
j.memberExpression(
34+
j.callExpression(j.identifier('createApp'), []),
35+
j.identifier('mount')
36+
),
37+
[options, el]
38+
)
39+
})
40+
41+
return root
42+
}

tsconfig.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": ".",
4+
"outDir": "dist",
5+
"sourceMap": false,
6+
"target": "esnext",
7+
"module": "commonjs",
8+
"moduleResolution": "node",
9+
"allowJs": false,
10+
"noUnusedLocals": true,
11+
"strictNullChecks": true,
12+
"noImplicitAny": true,
13+
"noImplicitThis": true,
14+
"resolveJsonModule": true,
15+
"esModuleInterop": true,
16+
"removeComments": false,
17+
"jsx": "preserve",
18+
"lib": ["esnext"],
19+
"types": [
20+
"jest",
21+
"jscodeshift",
22+
"node"
23+
],
24+
"rootDir": ".",
25+
}
26+
}

utils/adapter.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @ts-ignore
2+
import adapt from 'vue-jscodeshift-adapter'
3+
4+
// TODO:
5+
// 1. support lang="ts" block in .vue files
6+
// 2. automatically apply the adapter function to all transform modules
7+
export default adapt

0 commit comments

Comments
 (0)