diff --git a/Gruntfile.js b/Gruntfile.js index 71f3ffd258f5..0e74fa01bac5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -94,6 +94,10 @@ module.exports = function(grunt) { grunt.registerTask('npm-react-test:release', npmReactTestRendererTasks.buildRelease); grunt.registerTask('npm-react-test:pack', npmReactTestRendererTasks.packRelease); + var npmReactNoopRendererTasks = require('./grunt/tasks/npm-react-noop'); + grunt.registerTask('npm-react-noop:release', npmReactNoopRendererTasks.buildRelease); + grunt.registerTask('npm-react-noop:pack', npmReactNoopRendererTasks.packRelease); + grunt.registerTask('version-check', function() { // Use gulp here. spawnGulp(['version-check'], null, this.async()); @@ -186,6 +190,8 @@ module.exports = function(grunt) { 'npm-react-addons:pack', 'npm-react-test:release', 'npm-react-test:pack', + 'npm-react-noop:release', + 'npm-react-noop:pack', 'compare_size', ]); diff --git a/examples/fiber/debugger/.gitignore b/examples/fiber/debugger/.gitignore new file mode 100644 index 000000000000..6c96c5cff124 --- /dev/null +++ b/examples/fiber/debugger/.gitignore @@ -0,0 +1,15 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules + +# testing +coverage + +# production +build + +# misc +.DS_Store +.env +npm-debug.log diff --git a/examples/fiber/debugger/README.md b/examples/fiber/debugger/README.md new file mode 100644 index 000000000000..f52646d9c654 --- /dev/null +++ b/examples/fiber/debugger/README.md @@ -0,0 +1,25 @@ +# Fiber Debugger + +This is a debugger handy for visualizing how [Fiber](https://github.com/facebook/react/issues/6170) works internally. + +**It is only meant to be used by React contributors, and not by React users.** + +It is likely that it might get broken at some point. If it's broken, ping [Dan](https://twitter.com/dan_abramov). + +### Running + +First, `npm run build` in React root repo folder. + +Then `npm install` and `npm start` in this folder. + +Open `http://localhost:3000` in Chrome. + +### Features + +* Edit code that uses `ReactNoop` renderer +* Visualize how relationships between fibers change over time +* Current tree is displayed in green + +![fiber debugger](https://d17oy1vhnax1f7.cloudfront.net/items/3R2W1H2M3a0h3p1l133r/Screen%20Recording%202016-10-21%20at%2020.41.gif?v=e4323e51) + + diff --git a/examples/fiber/debugger/package.json b/examples/fiber/debugger/package.json new file mode 100644 index 000000000000..eba0df6fcdc8 --- /dev/null +++ b/examples/fiber/debugger/package.json @@ -0,0 +1,20 @@ +{ + "name": "react-fiber-debugger", + "version": "0.0.1", + "private": true, + "devDependencies": { + "react-scripts": "0.6.1" + }, + "dependencies": { + "dagre": "^0.7.4", + "pretty-format": "^4.2.1", + "react": "^15.3.2", + "react-dom": "^15.3.2", + "react-draggable": "^2.2.2", + "react-motion": "^0.4.5" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build" + } +} diff --git a/examples/fiber/debugger/public/favicon.ico b/examples/fiber/debugger/public/favicon.ico new file mode 100644 index 000000000000..5c125de5d897 Binary files /dev/null and b/examples/fiber/debugger/public/favicon.ico differ diff --git a/examples/fiber/debugger/public/index.html b/examples/fiber/debugger/public/index.html new file mode 100644 index 000000000000..418da90bcc78 --- /dev/null +++ b/examples/fiber/debugger/public/index.html @@ -0,0 +1,13 @@ + + + + + + + + React App + + +
+ + diff --git a/examples/fiber/debugger/src/App.js b/examples/fiber/debugger/src/App.js new file mode 100644 index 000000000000..dd224428869c --- /dev/null +++ b/examples/fiber/debugger/src/App.js @@ -0,0 +1,215 @@ +import React, { Component } from 'react'; +import Draggable from 'react-draggable'; +import ReactNoop from '../../../../build/packages/react-noop-renderer'; +import ReactFiberInstrumentation from '../../../../build/packages/react-noop-renderer/lib/ReactFiberInstrumentation'; +import Editor from './Editor'; +import Fibers from './Fibers'; +import describeFibers from './describeFibers'; + +function getFiberState(root, workInProgress) { + if (!root) { + return null; + } + return describeFibers(root.current, workInProgress); +} + +const defaultCode = ` +log('Render
Hello
'); +ReactNoop.render(
Hello
); +ReactNoop.flush(); + +log('Render

Goodbye

'); +ReactNoop.render(

Goodbye

); +ReactNoop.flush(); +`; + +class App extends Component { + constructor(props) { + super(props); + this.state = { + code: defaultCode, + isEditing: false, + history: [], + currentStep: 0, + show: { + alt: false, + child: true, + sibling: true, + return: false, + fx: false, + progressedChild: false, + progressedDel: false + } + }; + } + + componentDidMount() { + this.runCode(this.state.code); + } + + runCode(code) { + let currentStage; + let currentRoot; + + ReactFiberInstrumentation.debugTool = { + onMountContainer: (root) => { + currentRoot = root; + }, + onUpdateContainer: (root) => { + currentRoot = root; + }, + onWillBeginWork: (fiber) => { + const fibers = getFiberState(currentRoot, fiber); + const stage = currentStage; + this.setState(({ history }) => ({ + history: [ + ...history, { + action: 'willBeginWork', + fibers, + stage + } + ] + })); + }, + onDidBeginWork: (fiber) => { + const fibers = getFiberState(currentRoot, fiber); + const stage = currentStage; + this.setState(({ history }) => ({ + history: [ + ...history, { + action: 'didBeginWork', + fibers, + stage + } + ] + })); + }, + onWillCompleteWork: (fiber) => { + const fibers = getFiberState(currentRoot, fiber); + const stage = currentStage; + this.setState(({ history }) => ({ + history: [ + ...history, { + action: 'willCompleteWork', + fibers, + stage + } + ] + })); + }, + onDidCompleteWork: (fiber) => { + const fibers = getFiberState(currentRoot, fiber); + const stage = currentStage; + this.setState(({ history }) => ({ + history: [ + ...history, { + action: 'didCompleteWork', + fibers, + stage + } + ] + })); + }, + }; + window.React = React; + window.ReactNoop = ReactNoop; + window.log = s => currentStage = s; + // eslint-disable-next-line + eval(window.Babel.transform(code, { + presets: ['react', 'es2015'] + }).code); + } + + handleEdit = (e) => { + e.preventDefault(); + this.setState({ + isEditing: true + }); + } + + handleCloseEdit = (nextCode) => { + this.setState({ + isEditing: false, + history: [], + currentStep: 0, + code: nextCode + }); + this.runCode(nextCode); + } + + render() { + const { history, currentStep, isEditing, code } = this.state; + if (isEditing) { + return ; + } + + const { fibers, action, stage } = history[currentStep] || {}; + let friendlyAction; + + if (fibers) { + let wipFiber = fibers.descriptions[fibers.workInProgressID]; + let friendlyFiber = wipFiber.type || wipFiber.tag + ' #' + wipFiber.id; + switch (action) { + case 'willBeginWork': + friendlyAction = 'Before BEGIN phase on ' + friendlyFiber; + break; + case 'didBeginWork': + friendlyAction = 'After BEGIN phase on ' + friendlyFiber; + break; + case 'willCompleteWork': + friendlyAction = 'Before COMPLETE phase on ' + friendlyFiber; + break; + case 'didCompleteWork': + friendlyAction = 'After COMPLETE phase on ' + friendlyFiber; + break; + default: + throw new Error('Unknown action'); + } + } + + return ( +
+ {fibers && + + + + } +
+ this.setState({ currentStep: Number(e.target.value) })} + /> +

Step {currentStep}: {friendlyAction} (Edit)

+ {stage &&

Stage: {stage}

} + {Object.keys(this.state.show).map(key => + + )} +
+
+ ); + } +} + +export default App; diff --git a/examples/fiber/debugger/src/Editor.js b/examples/fiber/debugger/src/Editor.js new file mode 100644 index 000000000000..644d04f6d490 --- /dev/null +++ b/examples/fiber/debugger/src/Editor.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; + +class Editor extends Component { + constructor(props) { + super(props); + this.state = { + code: props.code + }; + } + + render() { + return ( +
+