-
Notifications
You must be signed in to change notification settings - Fork 109
/
arrow-function.js
113 lines (102 loc) · 3.73 KB
/
arrow-function.js
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
module.exports = (file, api, options) => {
const j = api.jscodeshift;
const printOptions = options.printOptions || {quote: 'single'};
const root = j(file.source);
const getBodyStatement = fn => {
// 79 characters fit on a line of length 80
const maxWidth = options['max-width'] ? options['max-width'] - 1 : undefined;
if (
fn.body.type == 'BlockStatement' &&
fn.body.body.length == 1
) {
const inner = fn.body.body[0];
const comments = (fn.body.comments || []).concat(inner.comments || []);
if (
options['inline-single-expressions'] &&
inner.type == 'ExpressionStatement'
) {
inner.expression.comments = (inner.expression.comments || []).concat(comments);
return inner.expression;
} else if (inner.type == 'ReturnStatement') {
if (inner.argument === null) {
// The rare case of a function with a lone return statement.
fn.body.body = [];
return fn.body;
}
const lineStart = fn.loc.start.line;
const originalLineLength = fn.loc.lines.getLineLength(lineStart);
const approachDifference = 'function(a, b) {'.length - '(a, b) => );'.length;
const argumentLength = inner.argument.end - inner.argument.start;
const newLength = originalLineLength + argumentLength - approachDifference;
const tooLong = maxWidth && newLength > maxWidth;
if (!tooLong) {
inner.argument.comments = (inner.argument.comments || []).concat(comments);
return inner.argument;
}
}
}
return fn.body;
};
const createArrowFunctionExpression = fn => {
const arrowFunction = j.arrowFunctionExpression(
fn.params,
getBodyStatement(fn),
false
);
arrowFunction.comments = fn.comments;
arrowFunction.async = fn.async;
return arrowFunction;
};
const replacedBoundFunctions = root
.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: {
type: 'FunctionExpression',
generator: false,
},
property: {
type: 'Identifier',
name: 'bind',
},
},
})
.filter(path => (
!path.value.callee.object.id &&
path.value.arguments &&
path.value.arguments.length == 1 &&
path.value.arguments[0].type == 'ThisExpression'
))
.forEach(path => {
const comments = path.value.comments || [];
for (const node of [path.value.callee, path.value.callee.property, path.value.arguments[0]]) {
for (const comment of node.comments || []) {
comment.leading = false;
comment.trailing = true;
comments.push(comment);
}
}
const arrowFunction = createArrowFunctionExpression(path.value.callee.object);
arrowFunction.comments = (arrowFunction.comments || []).concat(comments);
j(path).replaceWith(arrowFunction);
})
.size() > 0;
const replacedCallbacks = root
.find(j.FunctionExpression, {
generator: false,
})
.filter(path => {
const isArgument = path.parentPath.name === 'arguments' && path.parentPath.value.indexOf(path.value) > -1;
const noThis = j(path).find(j.ThisExpression).size() == 0;
const notNamed = !path.value.id || !path.value.id.name;
const noArgumentsRef = j(path).find(j.Identifier).filter(idPath => idPath.node.name === 'arguments' && idPath.scope.depth === path.get('body').scope.depth).size() === 0;
return isArgument && noThis && notNamed && noArgumentsRef;
})
.forEach(path =>
j(path).replaceWith(
createArrowFunctionExpression(path.value)
)
)
.size() > 0;
return replacedBoundFunctions || replacedCallbacks ? root.toSource(printOptions) : null;
};