This repository has been archived by the owner on May 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
idx.macro.js
116 lines (111 loc) · 3.17 KB
/
idx.macro.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
114
115
116
const t = require('@babel/types');
const { createMacro } = require('babel-plugin-macros');
function checkIdxArguments(file, node) {
const args = node.arguments;
if (args.length !== 2) {
throw file.buildCodeFrameError(
node,
'The `idx` function takes exactly two arguments.'
);
}
const arrowFunction = args[1];
if (!t.isArrowFunctionExpression(arrowFunction)) {
throw file.buildCodeFrameError(
arrowFunction,
'The second argument supplied to `idx` must be an arrow function.'
);
}
if (!t.isExpression(arrowFunction.body)) {
throw file.buildCodeFrameError(
arrowFunction.body,
'The body of the arrow function supplied to `idx` must be a single ' +
'expression (without curly braces).'
);
}
if (arrowFunction.params.length !== 1) {
throw file.buildCodeFrameError(
arrowFunction.params[2] || arrowFunction,
'The arrow function supplied to `idx` must take exactly one parameter.'
);
}
const input = arrowFunction.params[0];
if (!t.isIdentifier(input)) {
throw file.buildCodeFrameError(
arrowFunction.params[0],
'The parameter supplied to `idx` must be an identifier.'
);
}
}
function makeCondition(node, state, inside) {
if (inside) {
return t.ConditionalExpression(
t.BinaryExpression(
'!=',
t.AssignmentExpression('=', state.temp, node),
t.NullLiteral()
),
inside,
state.temp
);
} else {
return node;
}
}
function makeChain(node, state, inside) {
if (t.isCallExpression(node)) {
return makeChain(
node.callee,
state,
makeCondition(t.CallExpression(state.temp, node.arguments), state, inside)
);
} else if (t.isMemberExpression(node)) {
return makeChain(
node.object,
state,
makeCondition(
t.MemberExpression(state.temp, node.property, node.computed),
state,
inside
)
);
} else if (t.isIdentifier(node)) {
if (node.name !== state.base.name) {
throw state.file.buildCodeFrameError(
node,
'The parameter of the arrow function supplied to `idx` must match ' +
'the base of the body expression.'
);
}
return makeCondition(state.input, state, inside);
} else {
throw state.file.buildCodeFrameError(
node,
'The `idx` body can only be composed of properties and methods.'
);
}
}
const idx_transform = (path, state) => {
// const args = path.get('arguments');
const node = path.node;
checkIdxArguments(state.file, node);
const temp = path.scope.generateUidIdentifier('ref');
const replacement = makeChain(node.arguments[1].body, {
file: state.file,
input: node.arguments[0],
base: node.arguments[1].params[0],
temp,
});
path.replaceWith(replacement);
path.scope.push({ id: temp });
};
module.exports = createMacro(({ state, references }) => {
references.default.forEach(referencePath => {
if (referencePath.parentPath.type === 'CallExpression') {
idx_transform(referencePath.parentPath, state);
} else {
throw Error(
`idx.macro can only be used a function, and can not be passed around as an argument.`
);
}
});
});