( root: ASTNode, visitor: ASTVisitor | ASTReducer<any>, visitorKeys: ASTVisitorKeyMap = QueryDocumentKeys, )
| 259 | * @internal |
| 260 | */ |
| 261 | export function visit( |
| 262 | root: ASTNode, |
| 263 | visitor: ASTVisitor | ASTReducer<any>, |
| 264 | visitorKeys: ASTVisitorKeyMap = QueryDocumentKeys, |
| 265 | ): any { |
| 266 | const enterLeaveMap = new Map<Kind, EnterLeaveVisitor<ASTNode>>(); |
| 267 | for (const kind of Object.values(Kind)) { |
| 268 | enterLeaveMap.set(kind, getEnterLeaveForKind(visitor, kind)); |
| 269 | } |
| 270 | |
| 271 | /* eslint-disable no-undef-init */ |
| 272 | let stack: any = undefined; |
| 273 | let inArray = Array.isArray(root); |
| 274 | let keys: any = [root]; |
| 275 | let index = -1; |
| 276 | let edits = []; |
| 277 | let node: any = root; |
| 278 | let key: any = undefined; |
| 279 | let parent: any = undefined; |
| 280 | const path: any = []; |
| 281 | const ancestors = []; |
| 282 | /* eslint-enable no-undef-init */ |
| 283 | |
| 284 | do { |
| 285 | index++; |
| 286 | const isLeaving = index === keys.length; |
| 287 | const isEdited = isLeaving && edits.length !== 0; |
| 288 | if (isLeaving) { |
| 289 | key = ancestors.length === 0 ? undefined : path[path.length - 1]; |
| 290 | node = parent; |
| 291 | parent = ancestors.pop(); |
| 292 | if (isEdited) { |
| 293 | if (inArray) { |
| 294 | node = node.slice(); |
| 295 | |
| 296 | let editOffset = 0; |
| 297 | for (const [editKey, editValue] of edits) { |
| 298 | const arrayKey = editKey - editOffset; |
| 299 | if (editValue === null) { |
| 300 | node.splice(arrayKey, 1); |
| 301 | editOffset++; |
| 302 | } else { |
| 303 | node[arrayKey] = editValue; |
| 304 | } |
| 305 | } |
| 306 | } else { |
| 307 | node = { ...node }; |
| 308 | for (const [editKey, editValue] of edits) { |
| 309 | node[editKey] = editValue; |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | index = stack.index; |
| 314 | keys = stack.keys; |
| 315 | edits = stack.edits; |
| 316 | inArray = stack.inArray; |
| 317 | stack = stack.prev; |
| 318 | } else if (parent != null) { |
no test coverage detected