Skip to content

Commit 5953fd3

Browse files
authored
feat: implement .sync modifiers in v-bind to v-model transformation (vuejs#12)
1 parent 77c3171 commit 5953fd3

File tree

7 files changed

+106
-7
lines changed

7 files changed

+106
-7
lines changed

Diff for: src/operationUtils.ts

+12
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,15 @@ export function removeRange(range: number[]): Operation {
128128
text: ''
129129
}
130130
}
131+
132+
/**
133+
* Get text of Node
134+
* @param {Node} node The node to get text
135+
* @param {string} source The full text of the source code
136+
* @returns {string} The text of the node
137+
*/
138+
export function getText(node: Node, source: string): string {
139+
const start = node.range[0]
140+
const end = node.range[1]
141+
return source.slice(start, end)
142+
}

Diff for: vue-transformations/__test__/v-bind-sync.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { runTest } from '../../src/testUtils'
2+
3+
runTest(
4+
'Convert usage of slot-scope before vue 2.6',
5+
'v-bind-sync',
6+
'v-bind-sync',
7+
'vue',
8+
'vue'
9+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div class="hello">
3+
<ChildComponent :title.sync="pageTitle" />
4+
</div>
5+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div class="hello">
3+
<ChildComponent v-model:title="pageTitle" />
4+
</div>
5+
</template>

Diff for: vue-transformations/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const transformationMap: {
1515
'transition-group-root': require('./transition-group-root'),
1616
'v-bind-order-sensitive': require('./v-bind-order-sensitive'),
1717
'v-for-v-if-precedence-changed': require('./v-for-v-if-precedence-changed'),
18-
'remove-listeners': require('./remove-listeners')
18+
'remove-listeners': require('./remove-listeners'),
19+
'v-bind-sync': require('./v-bind-sync')
1920
}
2021

2122
export const excludedVueTransformations = ['v-bind-order-sensitive']

Diff for: vue-transformations/slot-scope-attribute.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,8 @@ function findNodes(context: any): Node[] {
5151
function fix(node: Node, source: string): Operation[] {
5252
let fixOperations: Operation[] = []
5353
const element: any = node!.parent!.parent
54-
const scopeValue: string = source.slice(
55-
// @ts-ignore
56-
node.value.range[0],
57-
// @ts-ignore
58-
node.value.range[1]
59-
)
54+
// @ts-ignore
55+
const scopeValue: string = OperationUtils.getText(node.value, source)
6056

6157
if (!!element && element.type == 'VElement' && element.name == 'template') {
6258
// template element replace slot-scope="xxx" to v-slot="xxx"

Diff for: vue-transformations/v-bind-sync.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Node } from 'vue-eslint-parser/ast/nodes'
2+
import * as OperationUtils from '../src/operationUtils'
3+
import type { Operation } from '../src/operationUtils'
4+
import type { VueASTTransformation } from '../src/wrapVueTransformation'
5+
import * as parser from 'vue-eslint-parser'
6+
import wrap from '../src/wrapVueTransformation'
7+
8+
export const transformAST: VueASTTransformation = context => {
9+
let fixOperations: Operation[] = []
10+
const { file } = context
11+
const source = file.source
12+
const toFixNodes: Node[] = findNodes(context)
13+
toFixNodes.forEach(node => {
14+
fixOperations = fixOperations.concat(fix(node, source))
15+
})
16+
return fixOperations
17+
}
18+
19+
export default wrap(transformAST)
20+
/**
21+
* search slot attribute nodes
22+
*
23+
* @param context
24+
* @returns slot attribute nodes
25+
*/
26+
function findNodes(context: any): Node[] {
27+
const { file } = context
28+
const source = file.source
29+
const options = { sourceType: 'module' }
30+
const ast = parser.parse(source, options)
31+
let toFixNodes: Node[] = []
32+
let root: Node = <Node>ast.templateBody
33+
parser.AST.traverseNodes(root, {
34+
enterNode(node: Node) {
35+
if (
36+
node.type === 'VAttribute' &&
37+
node.directive &&
38+
node.key.name.name === 'bind'
39+
) {
40+
toFixNodes.push(node)
41+
}
42+
},
43+
leaveNode(node: Node) {}
44+
})
45+
return toFixNodes
46+
}
47+
/**
48+
* fix logic
49+
* @param node
50+
*/
51+
function fix(node: Node, source: string): Operation[] {
52+
let fixOperations: Operation[] = []
53+
// @ts-ignore
54+
const keyNode = node.key
55+
const argument = keyNode.argument
56+
const modifiers = keyNode.modifiers
57+
const bindArgument = OperationUtils.getText(argument, source)
58+
59+
if (
60+
argument !== null &&
61+
modifiers.length === 1 &&
62+
modifiers[0].name === 'sync'
63+
) {
64+
// .sync modifiers in v-bind should be replaced with v-model
65+
fixOperations.push(
66+
OperationUtils.replaceText(keyNode, `v-model:${bindArgument}`)
67+
)
68+
}
69+
70+
return fixOperations
71+
}

0 commit comments

Comments
 (0)