11import { inspect } from '../jsutils/inspect' ;
2- import type { Maybe } from '../jsutils/Maybe' ;
32
43import type { ASTNode , ASTKindToNode } from './ast' ;
54import { isNode } from './ast' ;
5+ import { Kind } from './kinds' ;
66
77/**
88 * A visitor is provided to visit, it contains the collection of
@@ -256,6 +256,15 @@ export function visit(
256256 visitor : ASTVisitor | ASTReducer < any > ,
257257 visitorKeys : ASTVisitorKeyMap = QueryDocumentKeys ,
258258) : any {
259+ const enterLeaveMap = new Map <
260+ keyof ASTKindToNode ,
261+ EnterLeaveVisitor < ASTNode >
262+ > ( ) ;
263+
264+ for ( const kind of Object . values ( Kind ) ) {
265+ enterLeaveMap . set ( kind , getEnterLeaveForKind ( visitor , kind ) ) ;
266+ }
267+
259268 /* eslint-disable no-undef-init */
260269 let stack : any = undefined ;
261270 let inArray = Array . isArray ( root ) ;
@@ -318,29 +327,30 @@ export function visit(
318327 if ( ! isNode ( node ) ) {
319328 throw new Error ( `Invalid AST Node: ${ inspect ( node ) } .` ) ;
320329 }
321- const visitFn = getVisitFn ( visitor , node . kind , isLeaving ) ;
322- if ( visitFn ) {
323- result = visitFn . call ( visitor , node , key , parent , path , ancestors ) ;
330+ const visitFn = isLeaving
331+ ? enterLeaveMap . get ( node . kind ) ?. leave
332+ : enterLeaveMap . get ( node . kind ) ?. enter ;
324333
325- if ( result === BREAK ) {
326- break ;
327- }
334+ result = visitFn ?. call ( visitor , node , key , parent , path , ancestors ) ;
335+
336+ if ( result === BREAK ) {
337+ break ;
338+ }
328339
329- if ( result === false ) {
330- if ( ! isLeaving ) {
340+ if ( result === false ) {
341+ if ( ! isLeaving ) {
342+ path . pop ( ) ;
343+ continue ;
344+ }
345+ } else if ( result !== undefined ) {
346+ edits . push ( [ key , result ] ) ;
347+ if ( ! isLeaving ) {
348+ if ( isNode ( result ) ) {
349+ node = result ;
350+ } else {
331351 path . pop ( ) ;
332352 continue ;
333353 }
334- } else if ( result !== undefined ) {
335- edits . push ( [ key , result ] ) ;
336- if ( ! isLeaving ) {
337- if ( isNode ( result ) ) {
338- node = result ;
339- } else {
340- path . pop ( ) ;
341- continue ;
342- }
343- }
344354 }
345355 }
346356 }
@@ -380,16 +390,31 @@ export function visit(
380390export function visitInParallel (
381391 visitors : ReadonlyArray < ASTVisitor > ,
382392) : ASTVisitor {
383- const skipping = new Array ( visitors . length ) ;
384-
385- return {
386- enter ( ...args ) {
387- const node = args [ 0 ] ;
388- for ( let i = 0 ; i < visitors . length ; i ++ ) {
389- if ( skipping [ i ] == null ) {
390- const fn = getVisitFn ( visitors [ i ] , node . kind , /* isLeaving */ false ) ;
391- if ( fn ) {
392- const result = fn . apply ( visitors [ i ] , args ) ;
393+ const skipping = new Array ( visitors . length ) . fill ( null ) ;
394+ const mergedVisitor = Object . create ( null ) ;
395+
396+ for ( const kind of Object . values ( Kind ) ) {
397+ let hasVisitor = false ;
398+ const enterList = new Array ( visitors . length ) . fill ( undefined ) ;
399+ const leaveList = new Array ( visitors . length ) . fill ( undefined ) ;
400+
401+ for ( let i = 0 ; i < visitors . length ; ++ i ) {
402+ const { enter, leave } = getEnterLeaveForKind ( visitors [ i ] , kind ) ;
403+ hasVisitor ||= enter != null || leave != null ;
404+ enterList [ i ] = enter ;
405+ leaveList [ i ] = leave ;
406+ }
407+
408+ if ( ! hasVisitor ) {
409+ continue ;
410+ }
411+
412+ const mergedEnterLeave : EnterLeaveVisitor < ASTNode > = {
413+ enter ( ...args ) {
414+ const node = args [ 0 ] ;
415+ for ( let i = 0 ; i < visitors . length ; i ++ ) {
416+ if ( skipping [ i ] === null ) {
417+ const result = enterList [ i ] ?. apply ( visitors [ i ] , args ) ;
393418 if ( result === false ) {
394419 skipping [ i ] = node ;
395420 } else if ( result === BREAK ) {
@@ -399,50 +424,66 @@ export function visitInParallel(
399424 }
400425 }
401426 }
402- }
403- } ,
404- leave ( ...args ) {
405- const node = args [ 0 ] ;
406- for ( let i = 0 ; i < visitors . length ; i ++ ) {
407- if ( skipping [ i ] == null ) {
408- const fn = getVisitFn ( visitors [ i ] , node . kind , /* isLeaving */ true ) ;
409- if ( fn ) {
410- const result = fn . apply ( visitors [ i ] , args ) ;
427+ } ,
428+ leave ( ...args ) {
429+ const node = args [ 0 ] ;
430+ for ( let i = 0 ; i < visitors . length ; i ++ ) {
431+ if ( skipping [ i ] === null ) {
432+ const result = leaveList [ i ] ?. apply ( visitors [ i ] , args ) ;
411433 if ( result === BREAK ) {
412434 skipping [ i ] = BREAK ;
413435 } else if ( result !== undefined && result !== false ) {
414436 return result ;
415437 }
438+ } else if ( skipping [ i ] === node ) {
439+ skipping [ i ] = null ;
416440 }
417- } else if ( skipping [ i ] === node ) {
418- skipping [ i ] = null ;
419441 }
420- }
421- } ,
422- } ;
442+ } ,
443+ } ;
444+
445+ mergedVisitor [ kind ] = mergedEnterLeave ;
446+ }
447+
448+ return mergedVisitor ;
423449}
424450
425451/**
426- * Given a visitor instance, if it is leaving or not, and a node kind, return
427- * the function the visitor runtime should call.
452+ * Given a visitor instance and a node kind, return EnterLeaveVisitor for that kind.
428453 */
429- export function getVisitFn (
454+ export function getEnterLeaveForKind (
430455 visitor : ASTVisitor ,
431456 kind : keyof ASTKindToNode ,
432- isLeaving : boolean ,
433- ) : Maybe < ASTVisitFn < ASTNode > > {
457+ ) : EnterLeaveVisitor < ASTNode > {
434458 const kindVisitor :
435459 | ASTVisitFn < ASTNode >
436460 | EnterLeaveVisitor < ASTNode >
437461 | undefined = ( visitor as any ) [ kind ] ;
438- if ( kindVisitor ) {
439- if ( typeof kindVisitor === 'function' ) {
440- // { Kind() {} }
441- return isLeaving ? undefined : kindVisitor ;
442- }
462+
463+ if ( typeof kindVisitor === 'object' ) {
443464 // { Kind: { enter() {}, leave() {} } }
444- return isLeaving ? kindVisitor . leave : kindVisitor . enter ;
465+ return kindVisitor ;
466+ } else if ( typeof kindVisitor === 'function' ) {
467+ // { Kind() {} }
468+ return { enter : kindVisitor , leave : undefined } ;
445469 }
470+
446471 // { enter() {}, leave() {} }
447- return isLeaving ? ( visitor as any ) . leave : ( visitor as any ) . enter ;
472+ return { enter : ( visitor as any ) . enter , leave : ( visitor as any ) . leave } ;
473+ }
474+
475+ /**
476+ * Given a visitor instance, if it is leaving or not, and a node kind, return
477+ * the function the visitor runtime should call.
478+ *
479+ * @deprecated Please use `getEnterLeaveForKind` instead. Will be removed in v17
480+ */
481+ // istanbul ignore next (Deprecated code)
482+ export function getVisitFn (
483+ visitor : ASTVisitor ,
484+ kind : keyof ASTKindToNode ,
485+ isLeaving : boolean ,
486+ ) : ASTVisitFn < ASTNode > | undefined {
487+ const { enter, leave } = getEnterLeaveForKind ( visitor , kind ) ;
488+ return isLeaving ? leave : enter ;
448489}
0 commit comments