Full Code of ctrlplusb/react-tree-walker for AI

master c96fe5702db5 cached
16 files
39.9 KB
9.7k tokens
41 symbols
1 requests
Download .txt
Repository: ctrlplusb/react-tree-walker
Branch: master
Commit: c96fe5702db5
Files: 16
Total size: 39.9 KB

Directory structure:
gitextract_ozle0tzi/

├── .babelrc
├── .gitignore
├── .nvmrc
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── rollup-min.config.js
├── rollup.config.js
├── src/
│   ├── __tests__/
│   │   └── index.test.js
│   └── index.js
├── todo.tasks
├── tools/
│   ├── .eslintrc
│   ├── scripts/
│   │   └── build.js
│   └── utils.js
└── wallaby.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [["env", { "targets": { "node": true } }], "stage-3", "react"]
}


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Dependencies
node_modules

# Debug log from npm
npm-debug.log

# Jest
coverage

# Flow
flow-coverage
flow-typed

# Build output
dist

================================================
FILE: .nvmrc
================================================
8


================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
cache:
  yarn: true
  directories:
    - node_modules
node_js:
- '8'
script:
- npm run precommit
after_success:
# Deploy code coverage report to codecov.io
- npm run test:coverage:deploy


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2017 Sean Matheson, Ben Newman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
### Update

I haven't been focusing on this library as I tend to reach for Next.js for my SSR needs. If anyone is interested in maintaining the library further I am happy to add you as a collaborator to the project. 

As an alternative I can highly recommend [react-ssr-prepass](https://github.com/FormidableLabs/react-ssr-prepass), developed by @kitten of Formidable Labs, which provides the same functionality with built in support for suspense.

### Disclaimer

This library does not operate in an idiomatic manner against React. It makes some assumptions about the internals of React and makes calls against Components directly. This is a risk as it likely to break with future releases of React, i.e. the upcoming Suspense release.

Personally, I've found this library helpful in providing me with a solution for my server side rendering data fetching needs. That being said I very much look forward to being able to move over to Suspense as soon as it is stable and avoid having to use hacks/workarounds such as this library.

Please consider carefully before adopting this library. If you are happy to take on the risk I would recommend you write an abstraction over it that will allow you to easily remove/replace it from your codebase with Suspense or another more idiomatic solution.

----


# react-tree-walker 🌲

Walk a React (or Preact) element tree, executing a "visitor" function against each element.

[![npm](https://img.shields.io/npm/v/react-tree-walker.svg?style=flat-square)](http://npm.im/react-tree-walker)
[![MIT License](https://img.shields.io/npm/l/react-tree-walker.svg?style=flat-square)](http://opensource.org/licenses/MIT)
[![Travis](https://img.shields.io/travis/ctrlplusb/react-tree-walker.svg?style=flat-square)](https://travis-ci.org/ctrlplusb/react-tree-walker)
[![Codecov](https://img.shields.io/codecov/c/github/ctrlplusb/react-tree-walker.svg?style=flat-square)](https://codecov.io/github/ctrlplusb/react-tree-walker)

## TOCs

* [Introduction](#introduction)
* [Illustrative Example](#illustrative-example)
* [Order of Execution](#order-of-execution)
* [API](#api)

## Introduction

Inspired/lifted from the awesome [`react-apollo`](https://github.com/apollostack/react-apollo) project. 😗

This modified version expands upon the design, making it `Promise` based, allowing the visitor to return a `Promise`, which would subsequently delay the tree walking until the `Promise` is resolved. The tree is still walked in a depth-first fashion.

With this you could, for example, perform pre-rendering parses on your React element tree to do things like data prefetching. Which can be especially helpful when dealing with declarative APIs such as the one provided by React Router 4.

# Illustrative Example

In the below example we will create a visitor that will walk a React application, looking for any "class" component that has a `getData` method on it. We will then execute the `getData` function, storing the results into an array.

```jsx
import reactTreeWalker from 'react-tree-walker'

class DataFetcher extends React.Component {
  constructor(props) {
    super(props)
    this.getData = this.getData.bind(this)
  }

  getData() {
    // Supports promises! You could call an API for example to fetch some
    // data, or do whatever "bootstrapping" you desire.
    return Promise.resolve(this.props.id)
  }

  render() {
    return <div>{this.props.children}</div>
  }
}

const app = (
  <div>
    <h1>Hello World!</h1>
    <DataFetcher id={1} />
    <DataFetcher id={2}>
      <DataFetcher id={3}>
        <DataFetcher id={4} />
      </DataFetcher>
    </DataFetcher>
    <DataFetcher id={5} />
  </div>
)

const values = []

// You provide this! See the API docs below for full details.
function visitor(element, instance) {
  if (instance && typeof instance.getData) {
    return instance.getData().then(value => {
      values.push(value)
      // Return "false" to indicate that we do not want to visit "3"'s children,
      // therefore we do not expect "4" to make it into our values array.
      return value !== 3
    })
  }
}

reactTreeWalker(app, visitor)
  .then(() => {
    console.log(values) // [1, 2, 3, 5];
    // Now is a good time to call React's renderToString whilst exposing
    // whatever values you built up to your app.
  })
  // since v3.0.0 you need to do your own error handling!
  .catch(err => console.error(err))
```

Not a particularly useful piece of code, but hopefully it is illustrative enough as to indicate the posibilities. One could use this to warm a cache or a `redux` state, subsequently performing a `renderToString` execution with all the required data in place.

## Order of Execution

`react-tree-walker` walks your React application in a depth-first fashion, i.e. from the top down, visiting each child until their are no more children available before moving on to the next element. We can illustrate this behaviour using the below example:

```jsx
<div>
  <h1>Foo</h1>
  <section>
    <p>One</p>
    <p>Two</p>
  </section>
  <Footer />
</div>
```

In this example the order of elements being visited would be:

    div -> h1 -> "Foo" -> section -> p -> "One" -> p -> "Two" -> Footer

Whilst your application is being walked its behaviour will be much the same as if it were being rendered on the server - i.e. the `componentWillMount` lifecycle will be executed for any "class" components, and context provided by any components will be passed down and become available to child components.

Despite emulating a server side render, the tree walking process is far cheaper as it doesn't actually perform any rendering of the element tree to a string. It simply interogates your app building up an object/element tree. The really expensive cycles will likely be the API calls that you make. 😀

That being said you do have a bail-out ability allowing you to suspend the traversal down a branch of the tree. To do so you simply need to return `false` from your visitor function, or if returning a `Promise` ensure that the `Promise` resolves a `false` for the same behaviour.

## API

The API is very simple at the moment, only exposing a single function. We will describe the API of the `reactTreeWalker` function below as well as the API for the `visitor` function that `reactTreeWalker` expects as a parameter.

---

### **reactTreeWalker**

The default export of the library. The function that performs the magic.

```javascript
const reactTreeWalker = require('react-tree-walker')
```

_or_

```javascript
import reactTreeWalker from 'react-tree-walker'
```

**Parameters**

* **tree** (React/Preact element, _required_)

  The react application you wish to walk.

  e.g. `<div>Hello world</div>`

* **visitor** (`Function`, _required_)

  The function you wish to execute against _each_ element that is walked on the `tree`.

  See its [API docs](#visitor) below.

* **context** (`Object`, _optional_)

  Any root context you wish to provide to your application.

  e.g. `{ myContextItem: 'foo' }`

* **options** (`Object`, _optional_)

  Additional options/configuration. It currently supports the following values:

  * _componentWillUnmount_: Enable this to have the `componentWillUnmount` lifecycle event be executed whilst walking your tree. Defaults to `false`. This was added as an experimental additional flag to help with applications where they have critical disposal logic being executed within the `componentWillUnmount` lifecycle event.

**Returns**

A `Promise` that resolves when the tree walking is completed.

---

### **visitor**

The function that you create and provide to `reactTreeWalker`.

It should encapsulates the logic you wish to execute against each element.

**Parameters**

* **element** (React/Preact element, _required_)

  The current element being walked.

* **instance** (Component Instance, _optional_)

  If the current element being walked is a "class" Component then this will contain the instance of the Component - allowing you to interface with its methods etc.

* **context** (`Object`, _required_)

  The React context that is available to the current element. `react-tree-walker` emulates React in exposing context down the tree.

* **childContext** (`Object`, _optional_)

  If the current element being walked is a "class" Component and it exposes additional "child" context (via the `getChildContext` method) then this will contain the context that is being provided by the component instance.

**Returns**

If you return `false` then the children of the current element will not be visited.

e.g.

```javascript
function visitor(element) {
  if (element.type === 'menu') {
    // We will not traverse the children for any <menu /> nodes
    return 'false'
  }
}
```

You can also return a `Promise` which will cause the tree walking to wait for the `Promise` to be resolved before attempting to visit the children for the current element.

```javascript
function visitor(element, instance) {
  // This will make every visit take 1 second to execution.
  return new Promise(resolve => setTimeout(resolve, 1000))
}
```

You can make the Promise resolve a `false` to indicate that you do not want the children of the current element to be visited.

```javascript
function visitor(element, instance) {
  // Only the first element will be executed, and it will take 1 second to complete.
  return (
    new Promise(resolve => setTimeout(resolve, 1000))
      // This prevents any walking down the current elements children
      .then(() => false)
  )
}
```


================================================
FILE: package.json
================================================
{
  "name": "react-tree-walker",
  "version": "4.3.0",
  "description": "Walk a React element tree, executing a provided function against each node.",
  "license": "MIT",
  "main": "dist/react-tree-walker.js",
  "files": [
    "*.js",
    "*.md",
    "dist"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/ctrlplusb/react-tree-walker.git"
  },
  "homepage": "https://github.com/ctrlplusb/react-tree-walker#readme",
  "author": "Sean Matheson <sean@ctrlplusb.com>",
  "keywords": [
    "react",
    "react-element",
    "util",
    "tree",
    "visitor"
  ],
  "scripts": {
    "build": "node ./tools/scripts/build.js",
    "clean": "rimraf ./dist && rimraf ./coverage",
    "lint": "eslint src",
    "precommit": "lint-staged && npm run test",
    "prepublish": "npm run build",
    "test": "jest",
    "test:coverage": "npm run test -- --coverage",
    "test:coverage:deploy": "npm run test:coverage && codecov"
  },
  "peerDependencies": {
    "react": "^0.14.0 || ^15.0.0 || ^16.0.0"
  },
  "devDependencies": {
    "app-root-dir": "1.0.2",
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.3",
    "babel-eslint": "^8.2.6",
    "babel-jest": "^23.4.2",
    "babel-plugin-external-helpers": "^6.22.0",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "6.24.1",
    "babel-preset-stage-3": "6.24.1",
    "babel-register": "^6.26.0",
    "change-case": "^3.0.2",
    "codecov": "^3.0.4",
    "cross-env": "^5.2.0",
    "enzyme": "^3.4.1",
    "enzyme-to-json": "^3.3.4",
    "eslint": "^4.19.1",
    "eslint-config-airbnb": "^17.0.0",
    "eslint-config-prettier": "^2.6.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.1",
    "eslint-plugin-react": "^7.10.0",
    "gzip-size": "^5.0.0",
    "husky": "^0.14.3",
    "in-publish": "2.0.0",
    "jest": "^23.5.0",
    "lint-staged": "^7.2.2",
    "preact": "^8.3.0",
    "prettier": "^1.14.2",
    "pretty-bytes": "5.1.0",
    "prop-types": "^15.6.2",
    "ramda": "^0.25.0",
    "react": "^16.4.2",
    "react-addons-test-utils": "^15.6.2",
    "react-dom": "^16.4.2",
    "readline-sync": "1.4.9",
    "rimraf": "^2.6.2",
    "rollup": "^0.64.1",
    "rollup-plugin-babel": "^3.0.7",
    "rollup-plugin-uglify": "^4.0.0"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx}"
    ],
    "snapshotSerializers": [
      "<rootDir>/node_modules/enzyme-to-json/serializer"
    ],
    "testPathIgnorePatterns": [
      "<rootDir>/(coverage|dist|node_modules|tools)/"
    ]
  },
  "eslintConfig": {
    "root": true,
    "parser": "babel-eslint",
    "env": {
      "browser": true,
      "es6": true,
      "node": true,
      "jest": true
    },
    "extends": [
      "airbnb",
      "prettier",
      "prettier/react",
      "prettier/standard"
    ],
    "rules": {
      "camelcase": 0,
      "import/prefer-default-export": 0,
      "import/no-extraneous-dependencies": 0,
      "no-nested-ternary": 0,
      "no-underscore-dangle": 0,
      "react/destructuring-assignment": 0,
      "react/no-array-index-key": 0,
      "react/react-in-jsx-scope": 0,
      "semi": [
        2,
        "never"
      ],
      "react/forbid-prop-types": 0,
      "react/jsx-filename-extension": 0,
      "react/sort-comp": 0
    }
  },
  "eslintIgnore": [
    "node_modules/",
    "dist/",
    "coverage/"
  ],
  "prettier": {
    "semi": false,
    "singleQuote": true,
    "trailingComma": "all"
  },
  "lint-staged": {
    "*.js": [
      "prettier --write \"src/**/*.js\"",
      "git add"
    ]
  }
}


================================================
FILE: rollup-min.config.js
================================================
const { uglify } = require('rollup-plugin-uglify')
const packageJson = require('./package.json')

const baseConfig = require('./rollup.config.js')

baseConfig.plugins.push(uglify())
baseConfig.output.file = `dist/${packageJson.name}.min.js`

module.exports = baseConfig


================================================
FILE: rollup.config.js
================================================
const babel = require('rollup-plugin-babel')
const changeCase = require('change-case')
const packageJson = require('./package.json')

process.env.BABEL_ENV = 'production'

module.exports = {
  external: ['react'],
  input: 'src/index.js',
  output: {
    file: `dist/${packageJson.name}.js`,
    format: 'cjs',
    sourcemap: true,
    name: changeCase
      .titleCase(packageJson.name.replace(/-/g, ' '))
      .replace(/ /g, ''),
  },
  plugins: [
    babel({
      babelrc: false,
      exclude: 'node_modules/**',
      presets: [['env', { modules: false }], 'stage-3', 'react'],
      plugins: ['external-helpers'],
    }),
  ],
}


================================================
FILE: src/__tests__/index.test.js
================================================
/* eslint-disable react/sort-comp */
/* eslint-disable react/no-multi-comp */
/* eslint-disable react/prop-types */
/* eslint-disable react/prefer-stateless-function */
/* eslint-disable react/require-default-props */
/* eslint-disable class-methods-use-this */

import React, {
  createElement as reactCreateElement,
  Component as ReactComponent,
} from 'react'
import ReactDOM from 'react-dom'
import {
  createElement as preactCreateElement,
  Component as PreactComponent,
} from 'preact'
import PropTypes from 'prop-types'
import reactTreeWalker from '../index'

const resolveLater = result =>
  new Promise(resolve =>
    setTimeout(() => {
      resolve(result)
    }, 10),
  )

describe('reactTreeWalker', () => {
  describe('react + preact', () => {
    ;[
      { Component: ReactComponent, h: reactCreateElement },
      { Component: PreactComponent, h: preactCreateElement },
    ].forEach(({ Component, h }) => {
      const Stateless = jest.fn(({ children }) => <div>{children}</div>)
      Stateless.contextTypes = { theContext: PropTypes.string.isRequired }

      class Stateful extends Component {
        getData() {
          return typeof this.props.data === 'function'
            ? this.props.data()
            : this.props.data
        }

        render() {
          return h('div', null, this.props.children)
        }
      }

      const createTree = ({ async } = { async: false }) => {
        const Foo = Stateful
        const Bob = Stateless
        return h('div', null, [
          h('h1', null, 'Hello World!'),
          h(Foo, { data: async ? () => resolveLater(1) : 1 }),
          h(
            Foo,
            {
              data: async ? () => resolveLater(2) : 2,
            },
            h('div', null, [
              h(
                Bob,
                null,
                h(Foo, {
                  children: [
                    h(Foo, { data: async ? () => resolveLater(5) : 5 }),
                    h(Foo, { data: async ? () => resolveLater(6) : 6 }),
                  ],
                  data: async ? () => resolveLater(4) : 4,
                }),
              ),
              h('div', null, 'hi!'),
            ]),
          ),
          h(Foo, { data: async ? () => resolveLater(3) : 3 }),
        ])
      }

      it('simple sync visitor', () => {
        const actual = []
        const visitor = (element, instance) => {
          if (instance && typeof instance.getData === 'function') {
            const data = instance.getData()
            actual.push(data)
          }
        }
        return reactTreeWalker(createTree(), visitor).then(() => {
          const expected = [1, 2, 4, 5, 6, 3]
          expect(actual).toEqual(expected)
        })
      })

      it('promise based visitor', () => {
        const actual = []
        const visitor = (element, instance) => {
          if (instance && typeof instance.getData === 'function') {
            return instance.getData().then(data => {
              actual.push(data)
              return true
            })
          }
          return true
        }
        return reactTreeWalker(createTree({ async: true }), visitor).then(
          () => {
            const expected = [1, 2, 4, 5, 6, 3]
            expect(actual).toEqual(expected)
          },
        )
      })

      it('promise based visitor stops resolving', () => {
        const actual = []
        const visitor = (element, instance) => {
          if (instance && typeof instance.getData === 'function') {
            return instance.getData().then(data => {
              actual.push(data)
              return data !== 4
            })
          }
          return true
        }
        return reactTreeWalker(createTree({ async: true }), visitor).then(
          () => {
            const expected = [1, 2, 4, 3]
            expect(actual).toEqual(expected)
          },
        )
      })

      it('componentWillMount & setState', () => {
        let actual = {}

        class Foo extends Component {
          constructor(props) {
            super(props)
            this.state = { foo: 'foo' }
          }

          componentWillMount() {
            this.setState({ foo: 'bar' })
            this.setState((state, props) => ({
              other: `I am ${props.value} ${state.foo}`,
            }))
          }

          render() {
            actual = this.state
            return h('div', null, this.state.foo)
          }
        }

        return reactTreeWalker(h(Foo, { value: 'foo' }), () => true).then(
          () => {
            const expected = { foo: 'bar', other: 'I am foo bar' }
            expect(actual).toMatchObject(expected)
          },
        )
      })

      it('UNSAFE_componentWillMount', () => {
        let actual = {}

        class Foo extends Component {
          constructor(props) {
            super(props)
            this.state = { foo: 'foo' }
          }

          UNSAFE_componentWillMount() {
            this.setState({ foo: 'bar' })
          }

          render() {
            actual = this.state
            return h('div', null, this.state.foo)
          }
        }

        return reactTreeWalker(h(Foo, { value: 'foo' }), () => true).then(
          () => {
            const expected = { foo: 'bar' }
            expect(actual).toMatchObject(expected)
          },
        )
      })

      it('getDerivedStateFromProps', () => {
        let actual = {}

        class Foo extends Component {
          constructor(props) {
            super(props)
            this.state = { foo: 'foo' }
          }

          static getDerivedStateFromProps(props, state) {
            return { foo: `${state.foo}bar` }
          }

          render() {
            actual = this.state
            return h('div', null, this.state.foo)
          }
        }

        return reactTreeWalker(h(Foo, { value: 'foo' }), () => true).then(
          () => {
            const expected = { foo: 'foobar' }
            expect(actual).toMatchObject(expected)
          },
        )
      })

      it('calls componentWillUnmount', () => {
        let called = true

        class Foo extends Component {
          componentWillUnmount() {
            called = true
          }

          render() {
            return 'foo'
          }
        }

        return reactTreeWalker(h(Foo), () => true, null, {
          componentWillUnmount: true,
        }).then(() => {
          expect(called).toBeTruthy()
        })
      })

      it('getChildContext', () => {
        class Foo extends Component {
          getChildContext() {
            return { foo: 'val' }
          }

          render() {
            return h('div', null, this.props.children)
          }
        }

        let actual
        function Bar(props, context) {
          actual = context
          return 'bar'
        }
        Bar.contextTypes = { foo: PropTypes.string.isRequired }

        return reactTreeWalker(h(Foo, null, h(Bar)), () => true).then(() => {
          const expected = { foo: 'val' }
          expect(actual).toMatchObject(expected)
        })
      })

      it('works with instance-as-result component', () => {
        class Foo extends Component {
          render() {
            return h('div', null, [
              h(Stateful, { data: 1 }),
              h(Stateful, { data: 2 }),
            ])
          }
        }
        const Bar = props => new Foo(props)
        const actual = []
        const visitor = (element, instance) => {
          if (instance && typeof instance.getData === 'function') {
            const data = instance.getData()
            actual.push(data)
          }
        }
        return reactTreeWalker(h(Bar), visitor).then(() => {
          const expected = [1, 2]
          expect(actual).toEqual(expected)
        })
      })

      describe('error handling', () => {
        it('throws async visitor errors', () => {
          const tree = createTree({ async: true })
          const actual = []
          const visitor = (element, instance) => {
            if (instance && typeof instance.getData === 'function') {
              return instance.getData().then(data => {
                actual.push(data)
                if (data === 4) {
                  return Promise.reject(new Error('Visitor made 💩'))
                }
                return true
              })
            }
            return true
          }
          return reactTreeWalker(tree, visitor).then(
            () => {
              throw new Error('Expected error was not thrown')
            },
            err => {
              expect(err).toMatchObject(new Error('Visitor made 💩'))
              expect(actual).toEqual([1, 2, 4])
            },
          )
        })

        it('throws sync visitor errors', () => {
          const tree = createTree()
          const actual = []
          const visitor = (element, instance) => {
            if (instance && typeof instance.getData === 'function') {
              const data = instance.getData()
              actual.push(data)
              if (data === 4) {
                throw new Error('Visitor made 💩')
              }
            }
            return true
          }
          return reactTreeWalker(tree, visitor).then(
            () => {
              throw new Error('Expected error was not thrown')
            },
            err => {
              expect(err).toMatchObject(new Error('Visitor made 💩'))
              expect(actual).toEqual([1, 2, 4])
            },
          )
        })
      })

      it('complex context configuration', () => {
        class Wrapper extends Component {
          getChildContext() {
            this.id = 0

            return {
              foo: {
                getNextId: () => {
                  this.id += 1
                  return this.id
                },
              },
            }
          }

          render() {
            return this.props.children
          }
        }

        const ids = []
        class Baz extends Component {
          getData() {
            if (!this.context.foo) {
              return undefined
            }
            return new Promise(resolve => setTimeout(resolve, 1000)).then(
              () => {
                this.resolved = true
                ids.push(this.context.foo.getNextId())
              },
            )
          }

          render() {
            return this.resolved ? this.props.children : null
          }
        }

        const visitor = (element, instance) => {
          if (instance && typeof instance.getData === 'function') {
            return instance.getData()
          }
          return undefined
        }

        const app = h(
          Wrapper,
          null,
          h(
            'div',
            null,
            h(Baz, null, h('div', null, [h(Baz), h(Baz), h(Baz)])),
          ),
        )

        return reactTreeWalker(app, visitor).then(() => {
          expect(ids).toEqual([1, 2, 3, 4])
        })
      })
    })
  })

  describe('react', () => {
    it('supports new context API', () => {
      const { Provider, Consumer } = React.createContext()

      class Foo extends React.Component {
        render() {
          return this.props.children
        }
      }

      const tree = (
        <Provider
          value={{
            message: 'Message',
            identity: x => x,
          }}
        >
          <Consumer>
            {({ message, identity }) => (
              <strong>
                <i>{`${message}: ${identity('Hello world')}`}</i>
              </strong>
            )}
          </Consumer>
          bar
          <Foo>foo</Foo>
        </Provider>
      )

      const elements = []
      return reactTreeWalker(tree, element => {
        elements.push(element)
      }).then(() => {
        expect(elements.pop()).toBe('foo')
        expect(elements.pop().type).toBe(Foo)
        expect(elements.pop()).toBe('bar')
        expect(elements.pop()).toBe('Message: Hello world')
        expect(elements.pop().type).toBe('i')
        expect(elements.pop().type).toBe('strong')
      })
    })

    it('supports portals', () => {
      class Foo extends ReactComponent {
        getData() {
          return this.props.data
        }

        render() {
          return 'foo'
        }
      }

      function Baz() {
        return ReactDOM.createPortal(
          <div>
            <Foo data={1} />
            <Foo data={2} />
          </div>,
          document.createElement('div'),
        )
      }

      const actual = []
      const visitor = (element, instance) => {
        if (instance && typeof instance.getData === 'function') {
          const data = instance.getData()
          actual.push(data)
        }
      }
      return reactTreeWalker(<Baz />, visitor).then(() => {
        const expected = [1, 2]
        expect(actual).toEqual(expected)
      })
    })

    it('supports forwardRef', () => {
      class Foo extends ReactComponent {
        render() {
          return this.props.children
        }
      }

      const Bar = React.forwardRef((props, ref) => <Foo ref={ref} {...props} />)
      const ref = React.createRef()

      const tree = <Bar ref={ref}>foo</Bar>

      const elements = []
      return reactTreeWalker(tree, element => {
        elements.push(element)
      }).then(() => {
        expect(elements.pop()).toBe('foo')
        expect(elements.pop().type).toBe(Foo)
        expect(elements.pop().type).toBe(Bar)
      })
    })
  })
})


================================================
FILE: src/index.js
================================================
/* eslint-disable no-console */

// Inspired by the awesome work by the Apollo team: 😘
// https://github.com/apollographql/react-apollo/blob/master/src/getDataFromTree.ts
//
// This version has been adapted to be Promise based and support native Preact.

const defaultOptions = {
  componentWillUnmount: false,
}

const forwardRefSymbol = Symbol.for('react.forward_ref')

// Lifted from https://github.com/sindresorhus/p-reduce
// Thanks @sindresorhus! 🙏
const pReduce = (iterable, reducer, initVal) =>
  new Promise((resolve, reject) => {
    const iterator = iterable[Symbol.iterator]()
    let i = 0

    const next = total => {
      const el = iterator.next()

      if (el.done) {
        resolve(total)
        return
      }

      Promise.all([total, el.value])
        .then(value => {
          // eslint-disable-next-line no-plusplus
          next(reducer(value[0], value[1], i++))
        })
        .catch(reject)
    }

    next(initVal)
  })

// Lifted from https://github.com/sindresorhus/p-map-series
// Thanks @sindresorhus! 🙏
const pMapSeries = (iterable, iterator) => {
  const ret = []

  return pReduce(iterable, (a, b, i) =>
    Promise.resolve(iterator(b, i)).then(val => {
      ret.push(val)
    }),
  ).then(() => ret)
}

const ensureChild = child =>
  child && typeof child.render === 'function'
    ? ensureChild(child.render())
    : child

// Preact puts children directly on element, and React via props
const getChildren = element =>
  element.props && element.props.children
    ? element.props.children
    : element.children
      ? element.children
      : undefined

// Preact uses "nodeName", React uses "type"
const getType = element => element.type || element.nodeName

// Preact uses "attributes", React uses "props"
const getProps = element => element.props || element.attributes

const isReactElement = element => !!getType(element)

const isClassComponent = Comp =>
  Comp.prototype &&
  (Comp.prototype.render ||
    Comp.prototype.isReactComponent ||
    Comp.prototype.isPureReactComponent)

const isForwardRef = Comp =>
  Comp.type && Comp.type.$$typeof === forwardRefSymbol

const providesChildContext = instance => !!instance.getChildContext

// Recurse a React Element tree, running the provided visitor against each element.
// If a visitor call returns `false` then we will not recurse into the respective
// elements children.
export default function reactTreeWalker(
  tree,
  visitor,
  context,
  options = defaultOptions,
) {
  return new Promise((resolve, reject) => {
    const safeVisitor = (...args) => {
      try {
        return visitor(...args)
      } catch (err) {
        reject(err)
      }
      return undefined
    }

    const recursive = (currentElement, currentContext) => {
      if (Array.isArray(currentElement)) {
        return Promise.all(
          currentElement.map(item => recursive(item, currentContext)),
        )
      }

      if (!currentElement) {
        return Promise.resolve()
      }

      if (
        typeof currentElement === 'string' ||
        typeof currentElement === 'number'
      ) {
        // Just visit these, they are leaves so we don't keep traversing.
        safeVisitor(currentElement, null, currentContext)
        return Promise.resolve()
      }

      if (currentElement.type) {
        const _context =
          currentElement.type._context ||
          (currentElement.type.Provider &&
            currentElement.type.Provider._context)

        if (_context) {
          if ('value' in currentElement.props) {
            // <Provider>
            // eslint-disable-next-line no-param-reassign
            currentElement.type._context._currentValue =
              currentElement.props.value
          }

          if (typeof currentElement.props.children === 'function') {
            // <Consumer>
            const el = currentElement.props.children(_context._currentValue)
            return recursive(el, currentContext)
          }
        }
      }

      if (isReactElement(currentElement)) {
        return new Promise(innerResolve => {
          const visitCurrentElement = (
            render,
            compInstance,
            elContext,
            childContext,
          ) =>
            Promise.resolve(
              safeVisitor(
                currentElement,
                compInstance,
                elContext,
                childContext,
              ),
            )
              .then(result => {
                if (result !== false) {
                  // A false wasn't returned so we will attempt to visit the children
                  // for the current element.
                  const tempChildren = render()
                  const children = ensureChild(tempChildren)
                  if (children) {
                    if (Array.isArray(children)) {
                      // If its a react Children collection we need to breadth-first
                      // traverse each of them, and pMapSeries allows us to do a
                      // depth-first traversal that respects Promises. Thanks @sindresorhus!
                      return pMapSeries(
                        children,
                        child =>
                          child
                            ? recursive(child, childContext)
                            : Promise.resolve(),
                      )
                        .then(innerResolve, reject)
                        .catch(reject)
                    }
                    // Otherwise we pass the individual child to the next recursion.
                    return recursive(children, childContext)
                      .then(innerResolve, reject)
                      .catch(reject)
                  }
                }
                return undefined
              })
              .catch(reject)

          if (
            typeof getType(currentElement) === 'function' ||
            isForwardRef(currentElement)
          ) {
            const Component = getType(currentElement)
            const props = Object.assign(
              {},
              Component.defaultProps,
              getProps(currentElement),
              // For Preact support so that the props get passed into render
              // function.
              {
                children: getChildren(currentElement),
              },
            )
            if (isForwardRef(currentElement)) {
              visitCurrentElement(
                () => currentElement.type.render(props),
                null,
                currentContext,
                currentContext,
              ).then(innerResolve)
            } else if (isClassComponent(Component)) {
              // Class component
              const instance = new Component(props, currentContext)

              // In case the user doesn't pass these to super in the constructor
              Object.defineProperty(instance, 'props', {
                value: instance.props || props,
              })
              instance.context = instance.context || currentContext
              // set the instance state to null (not undefined) if not set, to match React behaviour
              instance.state = instance.state || null

              // Make the setState synchronous.
              instance.setState = newState => {
                if (typeof newState === 'function') {
                  // eslint-disable-next-line no-param-reassign
                  newState = newState(
                    instance.state,
                    instance.props,
                    instance.context,
                  )
                }
                instance.state = Object.assign({}, instance.state, newState)
              }

              if (Component.getDerivedStateFromProps) {
                const result = Component.getDerivedStateFromProps(
                  instance.props,
                  instance.state,
                )
                if (result !== null) {
                  instance.state = Object.assign({}, instance.state, result)
                }
              } else if (instance.UNSAFE_componentWillMount) {
                instance.UNSAFE_componentWillMount()
              } else if (instance.componentWillMount) {
                instance.componentWillMount()
              }

              const childContext = providesChildContext(instance)
                ? Object.assign({}, currentContext, instance.getChildContext())
                : currentContext

              visitCurrentElement(
                // Note: preact API also allows props and state to be referenced
                // as arguments to the render func, so we pass them through
                // here
                () => instance.render(instance.props, instance.state),
                instance,
                currentContext,
                childContext,
              )
                .then(() => {
                  if (
                    options.componentWillUnmount &&
                    instance.componentWillUnmount
                  ) {
                    instance.componentWillUnmount()
                  }
                })
                .then(innerResolve)
            } else {
              // Stateless Functional Component
              visitCurrentElement(
                () => Component(props, currentContext),
                null,
                currentContext,
                currentContext,
              ).then(innerResolve)
            }
          } else {
            // A basic element, such as a dom node, string, number etc.
            visitCurrentElement(
              () => getChildren(currentElement),
              null,
              currentContext,
              currentContext,
            ).then(innerResolve)
          }
        })
      }

      // Portals
      if (
        currentElement.containerInfo &&
        currentElement.children &&
        currentElement.children.props &&
        Array.isArray(currentElement.children.props.children)
      ) {
        return Promise.all(
          currentElement.children.props.children.map(child =>
            recursive(child, currentContext),
          ),
        )
      }

      return Promise.resolve()
    }

    recursive(tree, context).then(resolve, reject)
  })
}


================================================
FILE: todo.tasks
================================================
☐ support array types
  if (Array.isArray(element)) {
    element.forEach(item => walkTree(item, context, visitor));
    return;
  }

================================================
FILE: tools/.eslintrc
================================================
{
  "rules": {
    "no-console": 0,
    "import/no-extraneous-dependencies": 0
  }
}


================================================
FILE: tools/scripts/build.js
================================================
const { readFileSync } = require('fs')
const { inInstall } = require('in-publish')
const prettyBytes = require('pretty-bytes')
const gzipSize = require('gzip-size')
const { pipe } = require('ramda')
const { exec } = require('../utils')
const packageJson = require('../../package.json')

if (inInstall()) {
  process.exit(0)
}

const nodeEnv = Object.assign({}, process.env, {
  NODE_ENV: 'production',
})

exec('npx rollup -c rollup-min.config.js', nodeEnv)
exec('npx rollup -c rollup.config.js', nodeEnv)

function fileGZipSize(path) {
  return pipe(readFileSync, gzipSize.sync, prettyBytes)(path)
}

console.log(
  `\ngzipped, the build is ${fileGZipSize(`dist/${packageJson.name}.min.js`)}`,
)


================================================
FILE: tools/utils.js
================================================
const { execSync } = require('child_process')
const appRootDir = require('app-root-dir')

function exec(command) {
  execSync(command, { stdio: 'inherit', cwd: appRootDir.get() })
}

module.exports = {
  exec,
}


================================================
FILE: wallaby.js
================================================
const fs = require('fs')
const path = require('path')

process.env.NODE_ENV = 'test'

const babelConfigContents = fs.readFileSync(path.join(__dirname, '.babelrc'))
const babelConfig = JSON.parse(babelConfigContents)

module.exports = wallaby => ({
  files: ['src/**/*.js', { pattern: 'src/**/*.test.js', ignore: true }],
  tests: ['src/**/*.test.js'],
  testFramework: 'jest',
  env: {
    type: 'node',
    runner: 'node',
  },
  compilers: {
    'src/**/*.js': wallaby.compilers.babel(babelConfig),
  },
})
Download .txt
gitextract_ozle0tzi/

├── .babelrc
├── .gitignore
├── .nvmrc
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
├── rollup-min.config.js
├── rollup.config.js
├── src/
│   ├── __tests__/
│   │   └── index.test.js
│   └── index.js
├── todo.tasks
├── tools/
│   ├── .eslintrc
│   ├── scripts/
│   │   └── build.js
│   └── utils.js
└── wallaby.js
Download .txt
SYMBOL INDEX (41 symbols across 4 files)

FILE: src/__tests__/index.test.js
  class Stateful (line 36) | class Stateful extends Component {
    method getData (line 37) | getData() {
    method render (line 43) | render() {
  class Foo (line 133) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Foo (line 163) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Foo (line 190) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Foo (line 217) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Foo (line 235) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  function Bar (line 246) | function Bar(props, context) {
  class Foo (line 259) | class Foo extends Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Wrapper (line 334) | class Wrapper extends Component {
    method getChildContext (line 335) | getChildContext() {
    method render (line 348) | render() {
  class Baz (line 354) | class Baz extends Component {
    method getData (line 355) | getData() {
    method render (line 367) | render() {
  class Foo (line 400) | class Foo extends React.Component {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  class Foo (line 439) | class Foo extends ReactComponent {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {
  function Baz (line 449) | function Baz() {
    method getData (line 355) | getData() {
    method render (line 367) | render() {
  class Foo (line 473) | class Foo extends ReactComponent {
    method constructor (line 134) | constructor(props) {
    method componentWillMount (line 139) | componentWillMount() {
    method render (line 146) | render() {
    method constructor (line 164) | constructor(props) {
    method UNSAFE_componentWillMount (line 169) | UNSAFE_componentWillMount() {
    method render (line 173) | render() {
    method constructor (line 191) | constructor(props) {
    method getDerivedStateFromProps (line 196) | static getDerivedStateFromProps(props, state) {
    method render (line 200) | render() {
    method componentWillUnmount (line 218) | componentWillUnmount() {
    method render (line 222) | render() {
    method getChildContext (line 236) | getChildContext() {
    method render (line 240) | render() {
    method render (line 260) | render() {
    method render (line 401) | render() {
    method getData (line 440) | getData() {
    method render (line 444) | render() {
    method render (line 474) | render() {

FILE: src/index.js
  function reactTreeWalker (line 87) | function reactTreeWalker(

FILE: tools/scripts/build.js
  function fileGZipSize (line 20) | function fileGZipSize(path) {

FILE: tools/utils.js
  function exec (line 4) | function exec(command) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (43K chars).
[
  {
    "path": ".babelrc",
    "chars": 80,
    "preview": "{\n  \"presets\": [[\"env\", { \"targets\": { \"node\": true } }], \"stage-3\", \"react\"]\n}\n"
  },
  {
    "path": ".gitignore",
    "chars": 153,
    "preview": "# Logs\nlogs\n*.log\n\n# Dependencies\nnode_modules\n\n# Debug log from npm\nnpm-debug.log\n\n# Jest\ncoverage\n\n# Flow\nflow-coverag"
  },
  {
    "path": ".nvmrc",
    "chars": 2,
    "preview": "8\n"
  },
  {
    "path": ".travis.yml",
    "chars": 217,
    "preview": "sudo: false\nlanguage: node_js\ncache:\n  yarn: true\n  directories:\n    - node_modules\nnode_js:\n- '8'\nscript:\n- npm run pre"
  },
  {
    "path": "LICENSE",
    "chars": 1092,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017 Sean Matheson, Ben Newman\n\nPermission is hereby granted, free of charge, to an"
  },
  {
    "path": "README.md",
    "chars": 9503,
    "preview": "### Update\n\nI haven't been focusing on this library as I tend to reach for Next.js for my SSR needs. If anyone is intere"
  },
  {
    "path": "package.json",
    "chars": 3562,
    "preview": "{\n  \"name\": \"react-tree-walker\",\n  \"version\": \"4.3.0\",\n  \"description\": \"Walk a React element tree, executing a provided"
  },
  {
    "path": "rollup-min.config.js",
    "chars": 270,
    "preview": "const { uglify } = require('rollup-plugin-uglify')\nconst packageJson = require('./package.json')\n\nconst baseConfig = req"
  },
  {
    "path": "rollup.config.js",
    "chars": 637,
    "preview": "const babel = require('rollup-plugin-babel')\nconst changeCase = require('change-case')\nconst packageJson = require('./pa"
  },
  {
    "path": "src/__tests__/index.test.js",
    "chars": 13516,
    "preview": "/* eslint-disable react/sort-comp */\n/* eslint-disable react/no-multi-comp */\n/* eslint-disable react/prop-types */\n/* e"
  },
  {
    "path": "src/index.js",
    "chars": 10238,
    "preview": "/* eslint-disable no-console */\n\n// Inspired by the awesome work by the Apollo team: 😘\n// https://github.com/apollograph"
  },
  {
    "path": "todo.tasks",
    "chars": 132,
    "preview": "☐ support array types\n  if (Array.isArray(element)) {\n    element.forEach(item => walkTree(item, context, visitor));\n   "
  },
  {
    "path": "tools/.eslintrc",
    "chars": 85,
    "preview": "{\n  \"rules\": {\n    \"no-console\": 0,\n    \"import/no-extraneous-dependencies\": 0\n  }\n}\n"
  },
  {
    "path": "tools/scripts/build.js",
    "chars": 697,
    "preview": "const { readFileSync } = require('fs')\nconst { inInstall } = require('in-publish')\nconst prettyBytes = require('pretty-b"
  },
  {
    "path": "tools/utils.js",
    "chars": 212,
    "preview": "const { execSync } = require('child_process')\nconst appRootDir = require('app-root-dir')\n\nfunction exec(command) {\n  exe"
  },
  {
    "path": "wallaby.js",
    "chars": 509,
    "preview": "const fs = require('fs')\nconst path = require('path')\n\nprocess.env.NODE_ENV = 'test'\n\nconst babelConfigContents = fs.rea"
  }
]

About this extraction

This page contains the full source code of the ctrlplusb/react-tree-walker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (39.9 KB), approximately 9.7k tokens, and a symbol index with 41 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!