Skip to content

Commit be91c60

Browse files
fix: add support for removing assertion calls in SequenceExpression
1 parent b88e5e0 commit be91c60

File tree

3 files changed

+120
-70
lines changed

3 files changed

+120
-70
lines changed

src/index.mjs

Lines changed: 118 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ function createVisitor (options) {
6969
const targetModules = new Set(config.modules);
7070
const targetVariables = new Set(config.variables);
7171

72+
const nodeUpdates = new WeakMap();
73+
7274
function isAssertionModuleName (lit) {
7375
return isLiteral(lit) && targetModules.has(lit.value);
7476
}
@@ -167,105 +169,153 @@ function createVisitor (options) {
167169
return isAssertionFunction(callee) || isAssertionMethod(callee) || isConsoleAssert(callee);
168170
}
169171

170-
const nodeToRemove = new WeakSet();
172+
function removeNode (node) {
173+
nodeUpdates.set(node, null);
174+
}
175+
176+
function replaceNode (node, replacement) {
177+
nodeUpdates.set(node, replacement);
178+
}
179+
180+
function createNoopExpression () {
181+
return {
182+
type: 'UnaryExpression',
183+
operator: 'void',
184+
prefix: true,
185+
argument: {
186+
type: 'Literal',
187+
value: 0,
188+
raw: '0'
189+
}
190+
};
191+
}
192+
193+
function createNoopBlock () {
194+
return {
195+
type: 'BlockStatement',
196+
body: []
197+
};
198+
}
199+
200+
function unassertImportDeclaration (currentNode, parentNode) {
201+
const source = currentNode.source;
202+
if (!(isAssertionModuleName(source))) {
203+
return;
204+
}
205+
// remove current ImportDeclaration
206+
removeNode(currentNode);
207+
this.skip();
208+
// register local identifier(s) as assertion variable
209+
registerAssertionVariables(currentNode);
210+
}
211+
212+
function unassertVariableDeclarator (currentNode, parentNode) {
213+
if (isRemovalTargetRequire(currentNode.id, currentNode.init)) {
214+
if (parentNode.declarations.length === 1) {
215+
// remove parent VariableDeclaration
216+
removeNode(parentNode);
217+
} else {
218+
// single var pattern
219+
// remove current VariableDeclarator
220+
removeNode(currentNode);
221+
}
222+
this.skip();
223+
// register local identifier(s) as assertion variable
224+
registerAssertionVariables(currentNode.id);
225+
}
226+
}
227+
228+
function unassertAssignmentExpression (currentNode, parentNode) {
229+
if (currentNode.operator !== '=') {
230+
return;
231+
}
232+
if (!isExpressionStatement(parentNode)) {
233+
return;
234+
}
235+
if (isRemovalTargetRequire(currentNode.left, currentNode.right)) {
236+
// remove parent ExpressionStatement
237+
removeNode(parentNode);
238+
this.skip();
239+
// register local identifier(s) as assertion variable
240+
registerAssertionVariables(currentNode.left);
241+
}
242+
}
243+
244+
function unassertCallExpression (currentNode, parentNode) {
245+
const callee = currentNode.callee;
246+
if (!isRemovalTargetAssertion(callee)) {
247+
return;
248+
}
249+
250+
switch (parentNode.type) {
251+
case 'ExpressionStatement': {
252+
// remove parent ExpressionStatement
253+
removeNode(parentNode);
254+
this.skip();
255+
break;
256+
}
257+
case 'SequenceExpression': {
258+
// replace the asserstion with essentially nothing
259+
replaceNode(currentNode, createNoopExpression());
260+
break;
261+
}
262+
}
263+
}
264+
265+
function unassertAwaitExpression (currentNode, parentNode) {
266+
const childNode = currentNode.argument;
267+
if (isExpressionStatement(parentNode) && isCallExpression(childNode)) {
268+
const callee = childNode.callee;
269+
if (isRemovalTargetAssertion(callee)) {
270+
// remove parent ExpressionStatement
271+
removeNode(parentNode);
272+
this.skip();
273+
}
274+
}
275+
}
171276

172277
return {
173278
enter: function (currentNode, parentNode) {
174279
switch (currentNode.type) {
175280
case 'ImportDeclaration': {
176-
const source = currentNode.source;
177-
if (!(isAssertionModuleName(source))) {
178-
return;
179-
}
180-
// remove current ImportDeclaration
181-
nodeToRemove.add(currentNode);
182-
this.skip();
183-
// register local identifier(s) as assertion variable
184-
registerAssertionVariables(currentNode);
281+
unassertImportDeclaration.bind(this)(currentNode, parentNode);
185282
break;
186283
}
187284
case 'VariableDeclarator': {
188-
if (isRemovalTargetRequire(currentNode.id, currentNode.init)) {
189-
if (parentNode.declarations.length === 1) {
190-
// remove parent VariableDeclaration
191-
nodeToRemove.add(parentNode);
192-
} else {
193-
// single var pattern
194-
// remove current VariableDeclarator
195-
nodeToRemove.add(currentNode);
196-
}
197-
this.skip();
198-
// register local identifier(s) as assertion variable
199-
registerAssertionVariables(currentNode.id);
200-
}
285+
unassertVariableDeclarator.bind(this)(currentNode, parentNode);
201286
break;
202287
}
203288
case 'AssignmentExpression': {
204-
if (currentNode.operator !== '=') {
205-
return;
206-
}
207-
if (!isExpressionStatement(parentNode)) {
208-
return;
209-
}
210-
if (isRemovalTargetRequire(currentNode.left, currentNode.right)) {
211-
// remove parent ExpressionStatement
212-
nodeToRemove.add(parentNode);
213-
this.skip();
214-
// register local identifier(s) as assertion variable
215-
registerAssertionVariables(currentNode.left);
216-
}
289+
unassertAssignmentExpression.bind(this)(currentNode, parentNode);
217290
break;
218291
}
219292
case 'CallExpression': {
220-
if (!isExpressionStatement(parentNode)) {
221-
return;
222-
}
223-
const callee = currentNode.callee;
224-
if (isRemovalTargetAssertion(callee)) {
225-
// remove parent ExpressionStatement
226-
nodeToRemove.add(parentNode);
227-
this.skip();
228-
}
293+
unassertCallExpression.bind(this)(currentNode, parentNode);
229294
break;
230295
}
231296
case 'AwaitExpression': {
232-
const childNode = currentNode.argument;
233-
if (isExpressionStatement(parentNode) && isCallExpression(childNode)) {
234-
const callee = childNode.callee;
235-
if (isRemovalTargetAssertion(callee)) {
236-
// remove parent ExpressionStatement
237-
nodeToRemove.add(parentNode);
238-
this.skip();
239-
}
240-
}
297+
unassertAwaitExpression.bind(this)(currentNode, parentNode);
241298
break;
242299
}
243300
}
244301
},
245302
leave: function (currentNode, parentNode) {
246-
switch (currentNode.type) {
247-
case 'ImportDeclaration':
248-
case 'VariableDeclarator':
249-
case 'VariableDeclaration':
250-
case 'ExpressionStatement':
251-
break;
252-
default:
253-
return undefined;
303+
const update = nodeUpdates.get(currentNode);
304+
if (update === undefined) {
305+
return undefined;
254306
}
255-
if (nodeToRemove.has(currentNode)) {
307+
if (update === null) {
256308
if (isExpressionStatement(currentNode)) {
257309
const path = this.path();
258310
const key = path[path.length - 1];
259311
if (isNonBlockChildOfParentNode(currentNode, parentNode, key)) {
260-
return {
261-
type: 'BlockStatement',
262-
body: []
263-
};
312+
return createNoopBlock();
264313
}
265314
}
266315
this.remove();
316+
return undefined;
267317
}
268-
return undefined;
318+
return update;
269319
}
270320
};
271321
}

test/fixtures/non_block_statement/expected.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ function add(a, b) {
1515
b
1616
]) {
1717
}
18-
return a + b;
18+
return a + (void 0, b);
1919
}

test/fixtures/non_block_statement/fixture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ function add (a, b) {
2121
for (const i of [a, b])
2222
assert (0 < i);
2323

24-
return a + b;
24+
return a + (assert(a > b), b);
2525
}

0 commit comments

Comments
 (0)