Skip to content

Commit 40ea50a

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

File tree

3 files changed

+119
-70
lines changed

3 files changed

+119
-70
lines changed

src/index.mjs

Lines changed: 117 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,152 @@ 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+
function unassertAwaitExpression (currentNode, parentNode) {
265+
const childNode = currentNode.argument;
266+
if (isExpressionStatement(parentNode) && isCallExpression(childNode)) {
267+
const callee = childNode.callee;
268+
if (isRemovalTargetAssertion(callee)) {
269+
// remove parent ExpressionStatement
270+
removeNode(parentNode);
271+
this.skip();
272+
}
273+
}
274+
}
171275

172276
return {
173277
enter: function (currentNode, parentNode) {
174278
switch (currentNode.type) {
175279
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);
280+
unassertImportDeclaration.bind(this)(currentNode, parentNode);
185281
break;
186282
}
187283
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-
}
284+
unassertVariableDeclarator.bind(this)(currentNode, parentNode);
201285
break;
202286
}
203287
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-
}
288+
unassertAssignmentExpression.bind(this)(currentNode, parentNode);
217289
break;
218290
}
219291
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-
}
292+
unassertCallExpression.bind(this)(currentNode, parentNode);
229293
break;
230294
}
231295
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-
}
296+
unassertAwaitExpression.bind(this)(currentNode, parentNode);
241297
break;
242298
}
243299
}
244300
},
245301
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;
302+
const update = nodeUpdates.get(currentNode);
303+
if (update === undefined) {
304+
return undefined;
254305
}
255-
if (nodeToRemove.has(currentNode)) {
306+
if (update === null) {
256307
if (isExpressionStatement(currentNode)) {
257308
const path = this.path();
258309
const key = path[path.length - 1];
259310
if (isNonBlockChildOfParentNode(currentNode, parentNode, key)) {
260-
return {
261-
type: 'BlockStatement',
262-
body: []
263-
};
311+
return createNoopBlock();
264312
}
265313
}
266314
this.remove();
315+
return undefined;
267316
}
268-
return undefined;
317+
return update;
269318
}
270319
};
271320
}

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)