1 | import { blank } from '../utils/object';
|
2 |
|
3 | let shouldSkip;
|
4 | let shouldAbort;
|
5 |
|
6 | export default function walk ( ast, { enter, leave }) {
|
7 | shouldAbort = false;
|
8 | visit( ast, null, enter, leave );
|
9 | }
|
10 |
|
11 | let context = {
|
12 | skip: () => shouldSkip = true,
|
13 | abort: () => shouldAbort = true
|
14 | };
|
15 |
|
16 | let childKeys = blank();
|
17 |
|
18 | let toString = Object.prototype.toString;
|
19 |
|
20 | function isArray ( thing ) {
|
21 | return toString.call( thing ) === '[object Array]';
|
22 | }
|
23 |
|
24 | function visit ( node, parent, enter, leave ) {
|
25 | if ( !node || shouldAbort ) return;
|
26 |
|
27 | if ( enter ) {
|
28 | shouldSkip = false;
|
29 | enter.call( context, node, parent );
|
30 | if ( shouldSkip || shouldAbort ) return;
|
31 | }
|
32 |
|
33 | let keys = childKeys[ node.type ] || (
|
34 | childKeys[ node.type ] = Object.keys( node ).filter( key => typeof node[ key ] === 'object' )
|
35 | );
|
36 |
|
37 | let key, value, i, j;
|
38 |
|
39 | i = keys.length;
|
40 | while ( i-- ) {
|
41 | key = keys[i];
|
42 | value = node[ key ];
|
43 |
|
44 | if ( isArray( value ) ) {
|
45 | j = value.length;
|
46 | while ( j-- ) {
|
47 | visit( value[j], node, enter, leave );
|
48 | }
|
49 | }
|
50 |
|
51 | else if ( value && value.type ) {
|
52 | visit( value, node, enter, leave );
|
53 | }
|
54 | }
|
55 |
|
56 | if ( leave && !shouldAbort ) {
|
57 | leave( node, parent );
|
58 | }
|
59 | }
|