@@ -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}
0 commit comments