react/scripts/bench/extract-component.js

208 lines
6.5 KiB
JavaScript

/*global copy */
/*eslint-disable no-debugger */
// Copy and paste this file into your (Chrome) browser console after changing
// the React root ID. Works on facebook.com as of 7/6/16 (use a test user).
// Then run this to convert the JSX:
//
// ../../node_modules/.bin/babel \
// --presets ../../node_modules/babel-preset-react \
// --no-babelrc --compact=false \
// bench-foo.js -o bench-foo-es5.js
'use strict';
var rootID = 5;
var outputStatelessFunctional = false;
var React = require('React');
var ReactMount = require('ReactMount');
copy(print(ReactMount._instancesByReactRootID[rootID]._renderedComponent));
function elementMeta(element) {
var meta = '';
var key = element.key;
if (key) {
meta += ' key={' + JSON.stringify(key) + '}';
}
var ref = element.ref;
if (typeof ref === 'string') {
meta += ' ref={' + JSON.stringify(ref) + '}';
} else if (typeof ref === 'function') {
meta += ' ref={function() {}}';
}
return meta;
}
function print(outerComponent) {
var typeCounter = 0;
var elementCounter = 0;
var composites = new Map();
function addComposite(type, child) {
var info = composites.get(type);
if (!info) {
var name = (type.displayName || type.name || 'Component').replace(/(?:^[^a-z]|\W)+/gi, '_') + typeCounter++;
if (!/^[A-Z]/.test(name)) {
name = '_' + name;
}
info = {name: name, values: new Map()};
composites.set(type, info);
}
var c = elementCounter++;
info.values.set(c, child);
return '<' + info.name + ' x={' + c + '} />';
}
function printComposite(info) {
if (outputStatelessFunctional) {
output += 'var ' + info.name + ' = function(props) {\n';
} else {
output += 'var ' + info.name + ' = React.createClass({\n';
output += ' render: function() {\n';
output += ' var props = this.props;\n';
}
for (var [c, child] of info.values) {
output += ' if (props.x === ' + c + ') {\n';
if (child.indexOf('\n') !== -1) {
output += ' return (\n';
output += child.replace(/^|\n/g, '$& ') + '\n';
output += ' );\n';
} else {
output += ' return ' + child + ';\n';
}
output += ' }\n';
}
if (outputStatelessFunctional) {
output += '};\n';
} else {
output += ' },\n';
output += '});\n';
}
output += '\n';
}
function printImpl(component) {
var element = component._currentElement;
// Empty component
if (element === null || element === false) {
return '' + element;
}
// Text component
if (typeof element === 'string' || typeof element === 'number') {
return JSON.stringify(element);
}
// Composite component
if (typeof element.type === 'function') {
var rendered = printImpl(component._renderedComponent);
return addComposite(component._currentElement.type, rendered)
.replace(/(?= \/>$)/, elementMeta(component._currentElement));
}
// Native component
if (typeof element.type === 'string') {
var markup = '<' + element.type;
markup += elementMeta(component._currentElement);
for (var propKey in element.props) {
var value = element.props[propKey];
var valueString = null;
if (propKey === 'style' || propKey === 'dangerouslySetInnerHTML') {
valueString = JSON.stringify(value);
} else if (propKey === 'children') {
} else {
if (typeof value === 'function') {
valueString = 'function() {}';
} else if (typeof value === 'string' || typeof value === 'number') {
valueString = JSON.stringify(value);
} else if (value == null || typeof value === 'boolean') {
valueString = '' + value;
} else if (typeof value === 'object') {
valueString = '{}';
console.log('smooshing', element.type, propKey, value);
} else {
debugger;
throw new Error('huh? ' + typeof value + ' ' + value);
}
}
if (valueString) {
markup += ' ' + propKey + '={' + valueString + '}';
}
}
markup += '>';
if (
typeof element.props.children === 'string' ||
typeof element.props.children === 'number'
) {
markup += '{' + JSON.stringify(element.props.children) + '}';
} else if (component._renderedChildren) {
var renderedChildren = component._renderedChildren;
var keys = Object.keys(renderedChildren);
var values = keys.map((childKey) => renderedChildren[childKey]);
if (keys.length) {
var dump = function(children) {
if (typeof children === 'boolean' || children == null) {
return '' + children;
}
if (typeof children === 'object' && !Array.isArray(children) && children[Symbol.iterator]) {
// TODO: Not quite right.
children = Array.from(children);
}
if (Array.isArray(children)) {
return children.length ? (
'[\n' +
children.map(function(ch) {
return ' ' + dump(ch).replace(/\n/g, '$& ') + ',\n';
}).join('') +
']'
) : '[]';
} else if (React.isValidElement(children) || typeof children === 'string' || typeof children === 'number') {
return printImpl(values.shift());
} else {
debugger;
throw new Error('hmm');
}
};
markup += '\n';
var children = element.props.children;
children = Array.isArray(children) ? children : [children];
children.forEach(function(child) {
var dumped = dump(child).replace(/\n/g, '$& ');
if (dumped.charAt(0) === '<') {
markup += ' ' + dumped + '\n';
} else {
markup += ' {' + dumped + '}\n';
}
});
if (values.length !== 0) {
debugger;
throw new Error('not all children processed');
}
}
}
markup += '</' + element.type + '>';
return markup;
}
debugger;
throw new Error('hmm');
}
var output = '(function() {\n\n';
var tail = printImpl(outerComponent);
for (var info of composites.values()) {
printComposite(info);
}
printComposite({name: 'Benchmark', values: new Map([[undefined, tail]])});
output += 'this.Benchmark = Benchmark;\n';
output += '\n})(this);\n';
return output;
}