1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 | 1x
1x
1x
1x
1x
21x
36x
13x
13x
13x
13x
13x
13x
10x
3x
3x
13x
13x
24x
24x
24x
24x
24x
24x
16x
8x
8x
24x
24x
24x
24x
24x
24x
24x
24x
25x
25x
25x
36x
36x
36x
13x
23x
36x
25x
25x
72x
36x
36x
25x
25x
25x
11x
11x
11x
11x
11x
11x
11x
11x
11x
25x
25x
25x
25x
25x
36x
18x
18x
81x
25x
25x
21x
21x
20x
18x
| import {
Walker,
builders,
} from 'glimmer-engine/dist/node_modules/glimmer-syntax';
import _ from 'underscore';
import {
locDiff,
addOffsets,
} from '../utils/location';
import {
nodeIndex,
offsetNode,
removeNode,
sizeOfNodes,
} from '../utils/node';
import classStringParser from '../class-string-parser';
function isBindAttr(modifier) {
return modifier.path.original === 'bind-attr';
}
function isClassBinding(pair) {
return pair.key === 'class';
}
function classBindingToAttribute(pair) {
const classString = pair.value.value;
const nodes = classStringParser(classString, { spaces: true });
const size = sizeOfNodes(nodes);
let node;
let quoteSize = 0;
if (nodes.length === 1) {
node = nodes[0];
} else {
node = builders.concat(nodes);
quoteSize = 2;
}
const loc = builders.loc(
1,
0,
1 + size.line,
size.column + 6 + quoteSize + 1, // "class=" 6
);
return builders.attr('class', node, loc);
}
function attributeBindingToAttribute(pair) {
const attrKey = pair.key;
const startAttr = 0;
const equalCol = startAttr + attrKey.length;
const startMust = equalCol + 1; // "=" 1
let value;
if (pair.value.type === 'PathExpression') {
value = pair.value.original;
} else Eif (pair.value.type === 'StringLiteral') {
value = pair.value.value;
}
const valueLength = value.length;
const endMust = startMust + 2 + valueLength + 2;
const node = builders.mustache(value);
node.loc = builders.loc(1, startMust, 1, endMust);
node.path.loc = builders.loc(1, startMust + 2, 1, endMust - 2);
const newAttr = builders.attr(pair.key, node);
newAttr.loc = builders.loc(1, startAttr, 1, endMust);
return newAttr;
}
function removeBindAttr(modifier, node, ast) {
const pairs = modifier.hash.pairs;
const attrs = [];
for (let j = 0; j < pairs.length; j += 1) {
const pair = pairs[j];
let attr;
if (isClassBinding(pair)) {
attr = classBindingToAttribute(pair);
} else {
attr = attributeBindingToAttribute(pair);
}
attrs.push({ pair, attr });
}
let firstAttrOffset;
let prevAttrsOffset = { line: 0, column: 0 };
// set locations of new attributes
for (let i = 0; i < attrs.length; i += 1) {
const { attr, pair } = attrs[i];
if (i === 0) {
firstAttrOffset = locDiff(attr.loc, modifier.loc).start;
offsetNode(attr, firstAttrOffset, { recursive: true, both: true });
prevAttrsOffset = addOffsets(prevAttrsOffset, sizeOfNodes(attr));
} else {
// find old offset to previous value
const {
pair: prevPair,
} = attrs[i - 1];
offsetNode(attr, firstAttrOffset, { recursive: true, both: true });
let offset = { line: 0, column: 0 };
const prevPairDiff = {
line: pair.loc.start.line - prevPair.loc.end.line,
column: pair.loc.start.column - prevPair.loc.end.column,
};
offset = addOffsets(offset, prevPairDiff);
offset = addOffsets(offset, prevAttrsOffset);
offsetNode(attr, offset, { recursive: true, both: false });
prevAttrsOffset = addOffsets(prevAttrsOffset, sizeOfNodes(attr));
prevAttrsOffset = addOffsets(prevAttrsOffset, prevPairDiff);
}
}
removeNode(modifier, ast);
const startingAt = modifier.loc.start;
const offset = prevAttrsOffset;
offsetNode(ast, offset, { recursive: true, startingAt });
const bindAttrIndex = nodeIndex(modifier, node.attributes);
attrs.forEach((a, i) => node.attributes.splice(bindAttrIndex + i, 0, a.attr));
}
export default function convertBindAttr(ast) {
const walker = new Walker(ast);
walker.visit(ast, (node) => {
if (node.type === 'ElementNode' && node.modifiers) {
const modifiers = _.clone(node.modifiers);
for (let i = 0; i < modifiers.length; i += 1) {
const modifier = modifiers[i];
if (isBindAttr(modifier)) {
removeBindAttr(modifier, node, ast);
}
}
}
});
return ast;
}
export {
attributeBindingToAttribute,
removeBindAttr,
};
|