mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add React benchmarking infrastructure (#9465)
* Initial commit for WIP benchmarking infrastructure * fixed lint issues and ran prettier * added <rootDir>/scripts/bench/ to ignore paths for Jest * tidied up code and fixed a few bugs in the runner.js * fixed eslint * improved the benchmark output from the runner * fixed typo * tided up print output in runner.js * throw error if chrome canary is not installed on mac * added better bench stats output (tables) * added benchmark diff to table results * adds bundle size comparisons to results * tidied up the results * fixed prettier output * attempt to trigger bech for circleci build * fixes flow exlclusion for lighthouse module * added class components benchmark * cleaned up stats.js * stability changes * circleci node version to 7 * added another benchmark * added colours to the different benchmarks to check if being cached * force no-cache headers * added more info messages * refactor chrome launching. * fixed an issue where launcher.kill might fail * Move server to runner. Launch it only once. * tidy up * changes the logic in how the remote repo is checked out * removes bench from circleci build * removed colors from benchmarks (no longer needed) * added CI integration comment * added hacker news benchmark * added skipBuild functionality * relabelled remote * Add confidence intervals * added first meaningful paint * removed some unused code * reverted code.json * updated benchmark runs back to 10 * no longer breaks when results contain missing bundles * adds CPU throttling * renamed build to remote-repo * small fix to build * fixed bad merge * upped runs to 10 from 2 again * properly pulls master * removes old-bench * runs benchmarks in headless mode * adds a --headless option * improved the git build process * added README * updated based feedback from review * adds merge base commit sha * addressing more PR feedback * remove built JS react files * updated .gitignore * added combined bundle load times to the metrics
This commit is contained in:
+1
-1
@@ -13,6 +13,6 @@ fixtures/
|
||||
# Ignore built files.
|
||||
build/
|
||||
coverage/
|
||||
scripts/bench/bench-*.js
|
||||
scripts/bench/benchmarks/**/*.js
|
||||
vendor/*
|
||||
**/node_modules
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<PROJECT_ROOT>/build/.*
|
||||
<PROJECT_ROOT>/scripts/.*
|
||||
<PROJECT_ROOT>/.*/node_modules/y18n/.*
|
||||
<PROJECT_ROOT>/node_modules/chrome-devtools-frontend/.*
|
||||
<PROJECT_ROOT>/node_modules/devtools-timeline-model/.*
|
||||
<PROJECT_ROOT>/.*/__mocks__/.*
|
||||
<PROJECT_ROOT>/.*/__tests__/.*
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ static
|
||||
_SpecRunner.html
|
||||
__benchmarks__
|
||||
build/
|
||||
remote-repo/
|
||||
coverage/
|
||||
.module-cache
|
||||
*.gem
|
||||
@@ -29,3 +30,4 @@ chrome-user-data
|
||||
.vscode
|
||||
*.swp
|
||||
*.swo
|
||||
*react*min*.js
|
||||
+9
-1
@@ -62,14 +62,19 @@
|
||||
"glob-stream": "^6.1.0",
|
||||
"gzip-js": "~0.3.2",
|
||||
"gzip-size": "^3.0.0",
|
||||
"http-server": "^0.9.0",
|
||||
"http2": "^3.3.6",
|
||||
"jest": "^19.0.1",
|
||||
"jest-config": "^19.0.1",
|
||||
"jest-jasmine2": "^19.0.1",
|
||||
"jest-runtime": "^19.0.1",
|
||||
"lighthouse": "^1.6.3",
|
||||
"loose-envify": "^1.1.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"mime": "^1.3.4",
|
||||
"minimist": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"nodegit": "^0.18.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"platform": "^1.1.0",
|
||||
"prettier": "^1.2.2",
|
||||
@@ -84,6 +89,7 @@
|
||||
"rollup-plugin-replace": "^1.1.1",
|
||||
"rollup-plugin-uglify": "^1.0.1",
|
||||
"run-sequence": "^1.1.4",
|
||||
"stats-analysis": "^2.0.0",
|
||||
"through2": "^2.0.0",
|
||||
"tmp": "~0.0.28",
|
||||
"typescript": "~1.8.10",
|
||||
@@ -99,6 +105,7 @@
|
||||
"version": 7
|
||||
},
|
||||
"scripts": {
|
||||
"bench": "npm run version-check && node scripts/bench/runner.js",
|
||||
"build": "npm run version-check && node scripts/rollup/build.js",
|
||||
"linc": "git diff --name-only --diff-filter=ACMRTUB `git merge-base HEAD master` | grep '\\.js$' | xargs eslint --",
|
||||
"lint": "node ./scripts/tasks/eslint.js",
|
||||
@@ -112,7 +119,8 @@
|
||||
"modulePathIgnorePatterns": [
|
||||
"/.module-cache/",
|
||||
"<rootDir>/build/",
|
||||
"<rootDir>/scripts/rollup/shims/"
|
||||
"<rootDir>/scripts/rollup/shims/",
|
||||
"<rootDir>/scripts/bench/"
|
||||
],
|
||||
"rootDir": "",
|
||||
"transform": {
|
||||
|
||||
+27
-26
@@ -1,40 +1,41 @@
|
||||
Work-in-progress benchmarks.
|
||||
# React Benchmarking
|
||||
|
||||
## Running the suite
|
||||
## Commands
|
||||
|
||||
You'll need two folders to compare, each of them containing `react.min.js` and `react-dom-server.min.js`. You can run `npm run build` at the repo root to get a `build` folder with these files.
|
||||
In most cases, the only two commands you might want to use are:
|
||||
|
||||
For example, if you want to compare a stable verion against master, you can create folders called `build-stable` and `build-master` and use the benchmark scripts like this:
|
||||
- `yarn bench`
|
||||
- `yarn build -- --type=UMD_PROD && yarn bench -- --skip-build`
|
||||
|
||||
```
|
||||
$ ./measure.py build-stable stable.txt build-master master.txt
|
||||
$ ./analyze.py stable.txt master.txt
|
||||
```
|
||||
The first command will run benchmarks with all the default settings. A local and remote build will occcur on all bundles, both local and remote repos will be run against all benchmarks.
|
||||
|
||||
The test measurements (second argument to `analyze`, `master.txt` in this example) will be compared to the control measurements (first argument to `analyze`, `stable.txt` in this example).
|
||||
The second command will run all benchmarks but skip the build process. This is useful for when doing local performance tweaking and the remote repo has already had its bundles built. Bboth local and remote repos will be run against all benchmarks with this command too.
|
||||
|
||||
Changes with the `-` sign in the output mean `master` is faster than `stable`.
|
||||
The other commands are as follows:
|
||||
|
||||
You can name folders any way you like, this was just an example.
|
||||
```bash
|
||||
# will compare local repo vs remote merge base repo
|
||||
yarn bench
|
||||
|
||||
## Running one
|
||||
One thing you can do with them is benchmark initial render time for a realistic hierarchy:
|
||||
# will compare local repo vs remote merge base repo
|
||||
# this can significantly improve bench times due to no build
|
||||
yarn bench -- --skip-build
|
||||
|
||||
```
|
||||
$ which jsc
|
||||
/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Resources/jsc
|
||||
$ jsc react-0.14.0.min.js bench-pe-es5.js -e 'var START=Date.now(); React.renderToString(React.createElement(Benchmark)); var END=Date.now(); print(END-START);'
|
||||
45
|
||||
```
|
||||
# will only build and run local repo against benchmarks (no remote values will be shown)
|
||||
yarn bench -- --local
|
||||
|
||||
Substitute `js` or `v8` for `jsc` to use SpiderMonkey or V8, respectively, if you've installed them.
|
||||
# will only build and run remote merge base repo against benchmarks (no local values will be shown)
|
||||
yarn bench -- --remote
|
||||
|
||||
## Creating one
|
||||
# will only build and run remote master repo against benchmarks
|
||||
yarn bench -- --remote=master
|
||||
|
||||
To create one, copy `extract-component.js` to your clipboard and paste it into the Chrome console on facebook.com, perhaps after changing the root ID if you don't want the tree with ID `.0`.
|
||||
# same as "yarn bench"
|
||||
yarn bench -- --remote --local
|
||||
|
||||
Then to convert it to ES5:
|
||||
# runs benchmarks with Chrome in headless mode
|
||||
yarn bench -- --headless
|
||||
|
||||
```
|
||||
babel --whitelist react,react.displayName --compact false bench-pe.js >bench-pe-es5.js
|
||||
```
|
||||
# runs only specific string matching benchmarks
|
||||
yarn bench -- --benchmark=hacker
|
||||
```
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2015-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
import math
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
import numpy.random as npr
|
||||
import scipy.stats
|
||||
|
||||
|
||||
def _bootstrap_mean_sem(samples):
|
||||
"""Return the estimated standard error for a distribution's mean."""
|
||||
samples = np.array(samples)
|
||||
n = len(samples)
|
||||
indices = npr.randint(0, n, (10000, n))
|
||||
samples = samples[indices]
|
||||
means = np.sort(np.mean(samples, axis=1))
|
||||
return np.std(means, ddof=1)
|
||||
|
||||
|
||||
def _read_measurements(f):
|
||||
"""Read measurements from a file.
|
||||
|
||||
Returns {'a': [1.0, 2.0, 3.0], 'b': [5.0, 5.0, 5.0]} for a file containing
|
||||
the six lines: ['a 1', 'a 2', 'a 3', 'b 5', 'b 5', 'b 5'].
|
||||
"""
|
||||
measurements = {}
|
||||
for line in f:
|
||||
label, value = line.split(None, 1)
|
||||
measurements.setdefault(label, []).append(float(value))
|
||||
return measurements
|
||||
|
||||
|
||||
def _compute_mean_and_sd_of_ratio_from_delta_method(
|
||||
mean_test,
|
||||
sem_test,
|
||||
mean_control,
|
||||
sem_control
|
||||
):
|
||||
mean = (
|
||||
((mean_test - mean_control) / mean_control) -
|
||||
(pow(sem_control, 2) * mean_test / pow(mean_control, 3))
|
||||
)
|
||||
var = (
|
||||
pow(sem_test / mean_control, 2) +
|
||||
(pow(sem_control * mean_test, 2) / pow(mean_control, 4))
|
||||
)
|
||||
return (mean, math.sqrt(var))
|
||||
|
||||
|
||||
def _main():
|
||||
if len(sys.argv) != 3:
|
||||
sys.stderr.write("usage: analyze.py control.txt test.txt\n")
|
||||
return 1
|
||||
|
||||
ci_size = 0.99
|
||||
p_value = scipy.stats.norm.ppf(0.5 * (1 + ci_size))
|
||||
|
||||
control, test = sys.argv[1:]
|
||||
with open(control) as f:
|
||||
control_measurements = _read_measurements(f)
|
||||
with open(test) as f:
|
||||
test_measurements = _read_measurements(f)
|
||||
keys = set()
|
||||
keys.update(control_measurements.iterkeys())
|
||||
keys.update(test_measurements.iterkeys())
|
||||
|
||||
print "Comparing %s (control) vs %s (test)" % (control, test)
|
||||
print "Significant differences marked by ***"
|
||||
print "%% change from control to test, with %g%% CIs:" % (ci_size * 100,)
|
||||
print
|
||||
|
||||
any_sig = False
|
||||
for key in sorted(keys):
|
||||
print "* %s" % (key,)
|
||||
control_nums = control_measurements.get(key, [])
|
||||
test_nums = test_measurements.get(key, [])
|
||||
if not control_nums or not test_nums:
|
||||
print " skipping..."
|
||||
continue
|
||||
|
||||
mean_control = np.mean(control_nums)
|
||||
mean_test = np.mean(test_nums)
|
||||
sem_control = _bootstrap_mean_sem(control_nums)
|
||||
sem_test = _bootstrap_mean_sem(test_nums)
|
||||
|
||||
rat_mean, rat_sem = _compute_mean_and_sd_of_ratio_from_delta_method(
|
||||
mean_test, sem_test, mean_control, sem_control
|
||||
)
|
||||
rat_low = rat_mean - p_value * rat_sem
|
||||
rat_high = rat_mean + p_value * rat_sem
|
||||
|
||||
sig = rat_high < 0 or rat_low > 0
|
||||
any_sig = any_sig or sig
|
||||
|
||||
print " %% change: %+6.2f%% [%+6.2f%%, %+6.2f%%]%s" % (
|
||||
100 * rat_mean,
|
||||
100 * rat_low,
|
||||
100 * rat_high,
|
||||
' ***' if sig else ''
|
||||
)
|
||||
print " means: %g (control), %g (test)" % (mean_control, mean_test)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(_main())
|
||||
@@ -1,243 +0,0 @@
|
||||
React.createClass({
|
||||
displayName: "UnimplementedView",
|
||||
setNativeProps: function() {},
|
||||
render: function() {},
|
||||
});
|
||||
var mixin1 = {
|
||||
measure: function() {},
|
||||
measureLayout: function() {},
|
||||
setNativeProps: function() {},
|
||||
focus: function() {},
|
||||
blur: function() {},
|
||||
componentWillMount: function() {},
|
||||
componentWillReceiveProps: function() {},
|
||||
};
|
||||
React.createClass({
|
||||
displayName: "View",
|
||||
mixins: [mixin1],
|
||||
viewConfig: {uiViewClassName: "RCTView", validAttributes: null},
|
||||
statics: {AccessibilityTraits: null, AccessibilityComponentType: null},
|
||||
propTypes: {accessible: function() {}, accessibilityLabel: function() {}, accessibilityComponentType: function() {}, accessibilityLiveRegion: function() {}, importantForAccessibility: function() {}, accessibilityTraits: function() {}, onAccessibilityTap: function() {}, onMagicTap: function() {}, testID: function() {}, onResponderGrant: function() {}, onResponderMove: function() {}, onResponderReject: function() {}, onResponderRelease: function() {}, onResponderTerminate: function() {}, onResponderTerminationRequest: function() {}, onStartShouldSetResponder: function() {}, onStartShouldSetResponderCapture: function() {}, onMoveShouldSetResponder: function() {}, onMoveShouldSetResponderCapture: function() {}, onLayout: function() {}, pointerEvents: function() {}, style: function() {}, removeClippedSubviews: function() {}, renderToHardwareTextureAndroid: function() {}, shouldRasterizeIOS: function() {}, collapsable: function() {}, needsOffscreenAlphaCompositing: function() {}},
|
||||
render: function() {},
|
||||
});
|
||||
var mixin2 = {
|
||||
componentWillUnmount: function() {},
|
||||
setTimeout: function() {},
|
||||
clearTimeout: function() {},
|
||||
setInterval: function() {},
|
||||
clearInterval: function() {},
|
||||
setImmediate: function() {},
|
||||
clearImmediate: function() {},
|
||||
requestAnimationFrame: function() {},
|
||||
cancelAnimationFrame: function() {},
|
||||
};
|
||||
var mixin3 = {
|
||||
componentWillUnmount: function() {},
|
||||
_interactionMixinHandles: {},
|
||||
createInteractionHandle: function() {},
|
||||
clearInteractionHandle: function() {},
|
||||
runAfterInteractions: function() {},
|
||||
};
|
||||
var mixin4 = {
|
||||
componentWillMount: function() {},
|
||||
componentWillUnmount: function() {},
|
||||
addListenerOn: function() {},
|
||||
};
|
||||
React.createClass({
|
||||
displayName: "Navigator",
|
||||
propTypes: {configureScene: function() {}, renderScene: function() {}, initialRoute: function() {}, initialRouteStack: function() {}, onWillFocus: function() {}, onDidFocus: function() {}, navigationBar: function() {}, navigator: function() {}, sceneStyle: function() {}},
|
||||
statics: {BreadcrumbNavigationBar: null, NavigationBar: null, SceneConfigs: null},
|
||||
mixins: [mixin2, mixin3, mixin4],
|
||||
getDefaultProps: function() {},
|
||||
getInitialState: function() {},
|
||||
componentWillMount: function() {},
|
||||
componentDidMount: function() {},
|
||||
componentWillUnmount: function() {},
|
||||
immediatelyResetRouteStack: function() {},
|
||||
_transitionTo: function() {},
|
||||
_handleSpringUpdate: function() {},
|
||||
_completeTransition: function() {},
|
||||
_emitDidFocus: function() {},
|
||||
_emitWillFocus: function() {},
|
||||
_hideScenes: function() {},
|
||||
_disableScene: function() {},
|
||||
_enableScene: function() {},
|
||||
_onAnimationStart: function() {},
|
||||
_onAnimationEnd: function() {},
|
||||
_setRenderSceneToHardwareTextureAndroid: function() {},
|
||||
_handleTouchStart: function() {},
|
||||
_handleMoveShouldSetPanResponder: function() {},
|
||||
_doesGestureOverswipe: function() {},
|
||||
_deltaForGestureAction: function() {},
|
||||
_handlePanResponderRelease: function() {},
|
||||
_handlePanResponderTerminate: function() {},
|
||||
_attachGesture: function() {},
|
||||
_detachGesture: function() {},
|
||||
_handlePanResponderMove: function() {},
|
||||
_moveAttachedGesture: function() {},
|
||||
_matchGestureAction: function() {},
|
||||
_transitionSceneStyle: function() {},
|
||||
_transitionBetween: function() {},
|
||||
_handleResponderTerminationRequest: function() {},
|
||||
_getDestIndexWithinBounds: function() {},
|
||||
_jumpN: function() {},
|
||||
jumpTo: function() {},
|
||||
jumpForward: function() {},
|
||||
jumpBack: function() {},
|
||||
push: function() {},
|
||||
_popN: function() {},
|
||||
pop: function() {},
|
||||
replaceAtIndex: function() {},
|
||||
replace: function() {},
|
||||
replacePrevious: function() {},
|
||||
popToTop: function() {},
|
||||
popToRoute: function() {},
|
||||
replacePreviousAndPop: function() {},
|
||||
resetTo: function() {},
|
||||
getCurrentRoutes: function() {},
|
||||
_cleanScenesPastIndex: function() {},
|
||||
_renderScene: function() {},
|
||||
_renderNavigationBar: function() {},
|
||||
render: function() {},
|
||||
_getNavigationContext: function() {},
|
||||
});
|
||||
var mixin5 = {
|
||||
mixins: [mixin4],
|
||||
statics: {DecelerationRate: null},
|
||||
scrollResponderMixinGetInitialState: function() {},
|
||||
scrollResponderHandleScrollShouldSetResponder: function() {},
|
||||
scrollResponderHandleStartShouldSetResponder: function() {},
|
||||
scrollResponderHandleStartShouldSetResponderCapture: function() {},
|
||||
scrollResponderHandleResponderReject: function() {},
|
||||
scrollResponderHandleTerminationRequest: function() {},
|
||||
scrollResponderHandleTouchEnd: function() {},
|
||||
scrollResponderHandleResponderRelease: function() {},
|
||||
scrollResponderHandleScroll: function() {},
|
||||
scrollResponderHandleResponderGrant: function() {},
|
||||
scrollResponderHandleScrollBeginDrag: function() {},
|
||||
scrollResponderHandleScrollEndDrag: function() {},
|
||||
scrollResponderHandleMomentumScrollBegin: function() {},
|
||||
scrollResponderHandleMomentumScrollEnd: function() {},
|
||||
scrollResponderHandleTouchStart: function() {},
|
||||
scrollResponderHandleTouchMove: function() {},
|
||||
scrollResponderIsAnimating: function() {},
|
||||
scrollResponderScrollTo: function() {},
|
||||
scrollResponderScrollWithouthAnimationTo: function() {},
|
||||
scrollResponderZoomTo: function() {},
|
||||
scrollResponderScrollNativeHandleToKeyboard: function() {},
|
||||
scrollResponderInputMeasureAndScrollToKeyboard: function() {},
|
||||
scrollResponderTextInputFocusError: function() {},
|
||||
componentWillMount: function() {},
|
||||
scrollResponderKeyboardWillShow: function() {},
|
||||
scrollResponderKeyboardWillHide: function() {},
|
||||
scrollResponderKeyboardDidShow: function() {},
|
||||
scrollResponderKeyboardDidHide: function() {},
|
||||
};
|
||||
React.createClass({
|
||||
displayName: "ScrollView",
|
||||
propTypes: {accessible: function() {}, accessibilityLabel: function() {}, accessibilityComponentType: function() {}, accessibilityLiveRegion: function() {}, importantForAccessibility: function() {}, accessibilityTraits: function() {}, onAccessibilityTap: function() {}, onMagicTap: function() {}, testID: function() {}, onResponderGrant: function() {}, onResponderMove: function() {}, onResponderReject: function() {}, onResponderRelease: function() {}, onResponderTerminate: function() {}, onResponderTerminationRequest: function() {}, onStartShouldSetResponder: function() {}, onStartShouldSetResponderCapture: function() {}, onMoveShouldSetResponder: function() {}, onMoveShouldSetResponderCapture: function() {}, onLayout: function() {}, pointerEvents: function() {}, style: function() {}, removeClippedSubviews: function() {}, renderToHardwareTextureAndroid: function() {}, shouldRasterizeIOS: function() {}, collapsable: function() {}, needsOffscreenAlphaCompositing: function() {}, automaticallyAdjustContentInsets: function() {}, contentInset: function() {}, contentOffset: function() {}, bounces: function() {}, bouncesZoom: function() {}, alwaysBounceHorizontal: function() {}, alwaysBounceVertical: function() {}, centerContent: function() {}, contentContainerStyle: function() {}, decelerationRate: function() {}, horizontal: function() {}, directionalLockEnabled: function() {}, canCancelContentTouches: function() {}, keyboardDismissMode: function() {}, keyboardShouldPersistTaps: function() {}, maximumZoomScale: function() {}, minimumZoomScale: function() {}, onScroll: function() {}, onScrollAnimationEnd: function() {}, onContentSizeChange: function() {}, pagingEnabled: function() {}, scrollEnabled: function() {}, scrollEventThrottle: function() {}, scrollIndicatorInsets: function() {}, scrollsToTop: function() {}, showsHorizontalScrollIndicator: function() {}, showsVerticalScrollIndicator: function() {}, stickyHeaderIndices: function() {}, snapToInterval: function() {}, snapToAlignment: function() {}, zoomScale: function() {}, onRefreshStart: function() {}},
|
||||
mixins: [mixin5],
|
||||
getInitialState: function() {},
|
||||
setNativeProps: function() {},
|
||||
endRefreshing: function() {},
|
||||
getScrollResponder: function() {},
|
||||
getInnerViewNode: function() {},
|
||||
scrollTo: function() {},
|
||||
scrollWithoutAnimationTo: function() {},
|
||||
handleScroll: function() {},
|
||||
_handleContentOnLayout: function() {},
|
||||
render: function() {},
|
||||
});
|
||||
var mixin6 = {
|
||||
componentWillUnmount: function() {},
|
||||
setTimeout: function() {},
|
||||
clearTimeout: function() {},
|
||||
setInterval: function() {},
|
||||
clearInterval: function() {},
|
||||
setImmediate: function() {},
|
||||
clearImmediate: function() {},
|
||||
requestAnimationFrame: function() {},
|
||||
cancelAnimationFrame: function() {},
|
||||
};
|
||||
React.createClass({
|
||||
displayName: "AdsManagerTabsModalView",
|
||||
contextTypes: {navigation: function() {}},
|
||||
mixins: [mixin6, mixin4],
|
||||
getDefaultProps: function() {},
|
||||
getInitialState: function() {},
|
||||
componentWillMount: function() {},
|
||||
componentDidMount: function() {},
|
||||
componentWillUnmount: function() {},
|
||||
onTabSelect: function() {},
|
||||
_handleConnectivityChange: function() {},
|
||||
_onTabTap: function() {},
|
||||
startCreateFlow: function() {},
|
||||
render: function() {},
|
||||
_renderTabs: function() {},
|
||||
_onAdCreated: function() {},
|
||||
_onRemoteNotification: function() {},
|
||||
_setAccountFromURL: function() {},
|
||||
_updateBadgeCount: function() {},
|
||||
_onRetry: function() {},
|
||||
_getUnseenNotifsCount: function() {},
|
||||
_pushNotifPermalink: function() {},
|
||||
_onMobileConfigsLoadDone: function() {},
|
||||
_getAccountRoute: function() {},
|
||||
_getNotifsRoute: function() {},
|
||||
_getSettingsRoute: function() {},
|
||||
_getCampaignsRoute: function() {},
|
||||
_getNavStack: function() {},
|
||||
_onAccountChanged: function() {},
|
||||
_onHelpCenterRequested: function() {},
|
||||
_onGlobalError: function() {},
|
||||
_onGlobalErrorToastDidClose: function() {},
|
||||
_onShowNUX: function() {},
|
||||
_closeToastAndPopover: function() {},
|
||||
_onTabLayout: function() {},
|
||||
_onRootNavigationWillChange: function() {},
|
||||
_handleOpenURL: function() {},
|
||||
_updateAccountIDFromInLink: function() {},
|
||||
_pushInLink: function() {},
|
||||
_pushExternalRoute: function() {},
|
||||
_canPushExernalRoute: function() {},
|
||||
_hasAccountTab: function() {},
|
||||
_hasCampaignsTab: function() {},
|
||||
_getTabForURL: function() {},
|
||||
_getTabForExternalRoute: function() {},
|
||||
});
|
||||
React.createClass({
|
||||
displayName: "ActionBarButton",
|
||||
mixins: [mixin1, mixin6],
|
||||
propTypes: {label: function() {}, iconOnly: function() {}, imageSource: function() {}, style: function() {}, onContentMeasured: function() {}, onPress: function() {}},
|
||||
componentDidMount: function() {},
|
||||
render: function() {},
|
||||
});
|
||||
React.createClass({
|
||||
displayName: "ListView",
|
||||
mixins: [mixin5, mixin2],
|
||||
statics: {DataSource: null},
|
||||
propTypes: {accessible: function() {}, accessibilityLabel: function() {}, accessibilityComponentType: function() {}, accessibilityLiveRegion: function() {}, importantForAccessibility: function() {}, accessibilityTraits: function() {}, onAccessibilityTap: function() {}, onMagicTap: function() {}, testID: function() {}, onResponderGrant: function() {}, onResponderMove: function() {}, onResponderReject: function() {}, onResponderRelease: function() {}, onResponderTerminate: function() {}, onResponderTerminationRequest: function() {}, onStartShouldSetResponder: function() {}, onStartShouldSetResponderCapture: function() {}, onMoveShouldSetResponder: function() {}, onMoveShouldSetResponderCapture: function() {}, onLayout: function() {}, pointerEvents: function() {}, style: function() {}, removeClippedSubviews: function() {}, renderToHardwareTextureAndroid: function() {}, shouldRasterizeIOS: function() {}, collapsable: function() {}, needsOffscreenAlphaCompositing: function() {}, automaticallyAdjustContentInsets: function() {}, contentInset: function() {}, contentOffset: function() {}, bounces: function() {}, bouncesZoom: function() {}, alwaysBounceHorizontal: function() {}, alwaysBounceVertical: function() {}, centerContent: function() {}, contentContainerStyle: function() {}, decelerationRate: function() {}, horizontal: function() {}, directionalLockEnabled: function() {}, canCancelContentTouches: function() {}, keyboardDismissMode: function() {}, keyboardShouldPersistTaps: function() {}, maximumZoomScale: function() {}, minimumZoomScale: function() {}, onScroll: function() {}, onScrollAnimationEnd: function() {}, onContentSizeChange: function() {}, pagingEnabled: function() {}, scrollEnabled: function() {}, scrollEventThrottle: function() {}, scrollIndicatorInsets: function() {}, scrollsToTop: function() {}, showsHorizontalScrollIndicator: function() {}, showsVerticalScrollIndicator: function() {}, stickyHeaderIndices: function() {}, snapToInterval: function() {}, snapToAlignment: function() {}, zoomScale: function() {}, onRefreshStart: function() {}, dataSource: function() {}, renderSeparator: function() {}, renderRow: function() {}, initialListSize: function() {}, onEndReached: function() {}, onEndReachedThreshold: function() {}, pageSize: function() {}, renderFooter: function() {}, renderHeader: function() {}, renderSectionHeader: function() {}, renderScrollComponent: function() {}, scrollRenderAheadDistance: function() {}, onChangeVisibleRows: function() {}},
|
||||
getMetrics: function() {},
|
||||
getScrollResponder: function() {},
|
||||
setNativeProps: function() {},
|
||||
getDefaultProps: function() {},
|
||||
getInitialState: function() {},
|
||||
getInnerViewNode: function() {},
|
||||
componentWillMount: function() {},
|
||||
componentDidMount: function() {},
|
||||
componentWillReceiveProps: function() {},
|
||||
componentDidUpdate: function() {},
|
||||
onRowHighlighted: function() {},
|
||||
render: function() {},
|
||||
_measureAndUpdateScrollProps: function() {},
|
||||
_onContentSizeChange: function() {},
|
||||
_onLayout: function() {},
|
||||
_setScrollVisibleLength: function() {},
|
||||
_updateChildFrames: function() {},
|
||||
_maybeCallOnEndReached: function() {},
|
||||
_renderMoreRowsIfNeeded: function() {},
|
||||
_pageInNewRows: function() {},
|
||||
_getDistanceFromEnd: function() {},
|
||||
_updateVisibleRows: function() {},
|
||||
_onScroll: function() {},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
'use strict';
|
||||
|
||||
const Lighthouse = require('lighthouse');
|
||||
const ChromeLauncher = require('lighthouse/lighthouse-cli/chrome-launcher.js').ChromeLauncher;
|
||||
const stats = require('stats-analysis');
|
||||
const config = require('lighthouse/lighthouse-core/config/perf.json');
|
||||
const spawn = require('child_process').spawn;
|
||||
const os = require('os');
|
||||
|
||||
const timesToRun = 10;
|
||||
|
||||
function wait(val) {
|
||||
return new Promise(resolve => setTimeout(resolve, val));
|
||||
}
|
||||
|
||||
async function runScenario(benchmark, launcher) {
|
||||
const results = await Lighthouse(`http://localhost:8080/${benchmark}/`, {
|
||||
output: 'json',
|
||||
disableCpuThrottling: false,
|
||||
disableNetworkThrottling: false,
|
||||
}, config);
|
||||
const perfMarkings = results.audits['user-timings'].extendedInfo.value;
|
||||
const entries = perfMarkings
|
||||
.filter(marker => !marker.isMark)
|
||||
.map(({ duration, name }) => ({
|
||||
entry: name,
|
||||
time: duration,
|
||||
}));
|
||||
entries.push({
|
||||
entry: 'First Meaningful Paint',
|
||||
time: results.audits['first-meaningful-paint'].rawValue,
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
function bootstrap(data) {
|
||||
const len = data.length;
|
||||
const arr = Array(len);
|
||||
for (let j = 0; j < len; j++) {
|
||||
arr[j] = data[(Math.random() * len) | 0];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function calculateStandardErrorOfMean(data) {
|
||||
const means = [];
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
means.push(stats.mean(bootstrap(data)));
|
||||
}
|
||||
return stats.stdev(means);
|
||||
}
|
||||
|
||||
function calculateAverages(runs) {
|
||||
const data = [];
|
||||
const averages = [];
|
||||
|
||||
runs.forEach((entries, x) => {
|
||||
entries.forEach(({ entry, time }, i) => {
|
||||
if (i >= averages.length) {
|
||||
data.push([time]);
|
||||
averages.push({
|
||||
entry,
|
||||
mean: 0,
|
||||
sem: 0,
|
||||
});
|
||||
} else {
|
||||
data[i].push(time);
|
||||
if (x === runs.length - 1) {
|
||||
const dataWithoutOutliers = stats.filterMADoutliers(data[i]);
|
||||
averages[i].mean = stats.mean(dataWithoutOutliers);
|
||||
averages[i].sem = calculateStandardErrorOfMean(data[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return averages;
|
||||
}
|
||||
|
||||
async function initChrome() {
|
||||
const platform = os.platform();
|
||||
|
||||
if (platform === 'linux') {
|
||||
process.env.XVFBARGS = '-screen 0, 1024x768x16';
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = 'chromium-browser';
|
||||
const child = spawn('xvfb start', [{ detached: true, stdio: ['ignore'] }]);
|
||||
child.unref();
|
||||
// wait for chrome to load then continue
|
||||
await wait(3000);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
async function launchChrome(headless) {
|
||||
let launcher;
|
||||
try {
|
||||
launcher = new ChromeLauncher({
|
||||
additionalFlags: [headless ? '--headless' : ''],
|
||||
});
|
||||
await launcher.isDebuggerReady();
|
||||
} catch (e) {
|
||||
return launcher.run();
|
||||
}
|
||||
return launcher;
|
||||
}
|
||||
|
||||
async function runBenchmark(benchmark, headless) {
|
||||
const results = {
|
||||
runs: [],
|
||||
averages: [],
|
||||
};
|
||||
|
||||
await initChrome();
|
||||
|
||||
for (let i = 0; i < timesToRun; i++) {
|
||||
let launcher = await launchChrome(headless);
|
||||
|
||||
results.runs.push(await runScenario(benchmark, launcher));
|
||||
// add a delay or sometimes it confuses lighthouse and it hangs
|
||||
await wait(500);
|
||||
try {
|
||||
await launcher.kill();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
results.averages = calculateAverages(results.runs);
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = runBenchmark;
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const e = React.createElement;
|
||||
|
||||
function timeAge(time) {
|
||||
const now = new Date().getTime() / 1000;
|
||||
const minutes = (now - time) / 60;
|
||||
|
||||
if (minutes < 60) {
|
||||
return Math.round(minutes) + ' minutes ago';
|
||||
}
|
||||
return Math.round(minutes / 60) + ' hours ago';
|
||||
}
|
||||
|
||||
function getHostUrl(url) {
|
||||
return (url + '')
|
||||
.replace('https://', '')
|
||||
.replace('http://', '')
|
||||
.split('/')[0]
|
||||
}
|
||||
|
||||
function HeaderBar() {
|
||||
return e('tr', {
|
||||
style: {
|
||||
backgroundColor: '#222',
|
||||
},
|
||||
}, e('table', {
|
||||
style: {
|
||||
padding: 4,
|
||||
},
|
||||
width: '100%',
|
||||
cellSpacing: 0,
|
||||
cellPadding: 0,
|
||||
}, e('tbody', null,
|
||||
e('tr', null,
|
||||
e('td', {
|
||||
style: {
|
||||
width: 18,
|
||||
paddingRight: 4,
|
||||
},
|
||||
},
|
||||
e('a', {
|
||||
href: '#',
|
||||
},
|
||||
e('img', {
|
||||
src: 'logo.png',
|
||||
width: 16,
|
||||
height: 16,
|
||||
style: {
|
||||
border: '1px solid #00d8ff',
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
e('td', {
|
||||
style: {
|
||||
lineHeight: '12pt',
|
||||
},
|
||||
height: 10,
|
||||
},
|
||||
e('span', {
|
||||
className: 'pagetop',
|
||||
},
|
||||
e('b', { className: 'hnname' }, 'React HN Benchmark'),
|
||||
e('a', { href: '#' }, 'new'),
|
||||
' | ',
|
||||
e('a', { href: '#' }, 'comments'),
|
||||
' | ',
|
||||
e('a', { href: '#' }, 'show'),
|
||||
' | ',
|
||||
e('a', { href: '#' }, 'ask'),
|
||||
' | ',
|
||||
e('a', { href: '#' }, 'jobs'),
|
||||
' | ',
|
||||
e('a', { href: '#' }, 'submit')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
function Story({ story, rank }) {
|
||||
return [
|
||||
e('tr', {
|
||||
className: 'athing',
|
||||
},
|
||||
e('td', {
|
||||
style: {
|
||||
verticalAlign: 'top',
|
||||
textAlign: 'right',
|
||||
},
|
||||
className: 'title',
|
||||
},
|
||||
e('span', {
|
||||
className: 'rank',
|
||||
}, `${rank}.`)
|
||||
),
|
||||
e('td', {
|
||||
className: 'votelinks',
|
||||
style: {
|
||||
verticalAlign: 'top',
|
||||
},
|
||||
},
|
||||
e('center', null,
|
||||
e('a', {
|
||||
href: '#',
|
||||
},
|
||||
e('div', {
|
||||
className: 'votearrow',
|
||||
title: 'upvote',
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
e('td', {
|
||||
className: 'title',
|
||||
},
|
||||
e('a', {
|
||||
href: '#',
|
||||
className: 'storylink',
|
||||
},
|
||||
story.title
|
||||
),
|
||||
story.url ? (
|
||||
e('span', {
|
||||
className: 'sitebit comhead',
|
||||
},
|
||||
' (',
|
||||
e('a', {
|
||||
href: '#',
|
||||
},
|
||||
getHostUrl(story.url)
|
||||
),
|
||||
')'
|
||||
)
|
||||
) : null
|
||||
)
|
||||
),
|
||||
e('tr', null,
|
||||
e('td', {
|
||||
colSpan: 2,
|
||||
}),
|
||||
e('td', {
|
||||
className: 'subtext',
|
||||
},
|
||||
e('span', {
|
||||
className: 'score',
|
||||
}, `${story.score} points`),
|
||||
' by ',
|
||||
e('a', {
|
||||
href: '#',
|
||||
className: 'hnuser',
|
||||
}, story.by),
|
||||
' ',
|
||||
e('span', {
|
||||
className: 'age',
|
||||
},
|
||||
e('a', {
|
||||
href: '#',
|
||||
}, timeAge(story.time))
|
||||
),
|
||||
' | ',
|
||||
e('a', {
|
||||
href: '#',
|
||||
}, 'hide'),
|
||||
' | ',
|
||||
e('a', {
|
||||
href: '#',
|
||||
}, `${ story.descendants || 0 } comments`)
|
||||
)
|
||||
),
|
||||
e('tr', {
|
||||
style: {
|
||||
height: 5,
|
||||
},
|
||||
className: 'spacer',
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
function StoryList({ stories }) {
|
||||
return e('tr', null,
|
||||
e('td', null,
|
||||
e('table', {
|
||||
cellPadding: 0,
|
||||
cellSpacing: 0,
|
||||
classList: 'itemlist',
|
||||
},
|
||||
e('tbody', null,
|
||||
stories.map((story, i) =>
|
||||
e(Story, { story, rank: ++i, key: story.id })
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function App({ stories }) {
|
||||
return e('center', null,
|
||||
e('table', {
|
||||
id: 'hnmain',
|
||||
border: 0,
|
||||
cellPadding: 0,
|
||||
cellSpacing: 0,
|
||||
width: '85%',
|
||||
style: {
|
||||
'background-color': '#f6f6ef',
|
||||
},
|
||||
}, e('tbody', null,
|
||||
e(HeaderBar, null),
|
||||
e('tr', { height: 10 }),
|
||||
e(StoryList, {
|
||||
stories,
|
||||
})
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
const app = document.getElementById('app');
|
||||
|
||||
window.render = function render() {
|
||||
ReactDOM.render(React.createElement(App, {
|
||||
stories: window.stories
|
||||
}), app);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
join,
|
||||
} = require('path');
|
||||
|
||||
async function build(reactPath, asyncCopyTo) {
|
||||
// copy the UMD bundles
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react.production.min.js'),
|
||||
join(__dirname, 'react.production.min.js')
|
||||
);
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react-dom.production.min.js'),
|
||||
join(__dirname, 'react-dom.production.min.js')
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = build;
|
||||
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
const { writeFileSync } = require('fs');
|
||||
const stories = 50;
|
||||
|
||||
async function getStory(id) {
|
||||
const storyRes = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`);
|
||||
return await storyRes.json();
|
||||
}
|
||||
|
||||
async function getTopStories() {
|
||||
const topStoriesRes = await fetch('https://hacker-news.firebaseio.com/v0/topstories.js');
|
||||
const topStoriesIds = await topStoriesRes.json();
|
||||
|
||||
const topStories = [];
|
||||
for (let i = 0; i < stories; i++) {
|
||||
const topStoriesId = topStoriesIds[i];
|
||||
|
||||
topStories.push(await getStory(topStoriesId));
|
||||
}
|
||||
|
||||
writeFileSync('top-stories.json', `window.stories = ${JSON.stringify(topStories)}`);
|
||||
}
|
||||
|
||||
getTopStories();
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 111 B |
@@ -0,0 +1,37 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>React Hacker News Benchmark</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
performance.mark('Load React');
|
||||
performance.mark('Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="react.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load React', 'Load React');
|
||||
</script>
|
||||
<script>
|
||||
performance.mark('Load ReactDOM');
|
||||
</script>
|
||||
<script src="react-dom.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load ReactDOM', 'Load ReactDOM');
|
||||
performance.measure('Load React+ReactDOM', 'Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="top-stories.js"></script>
|
||||
<script src="benchmark.js"></script>
|
||||
<script>
|
||||
performance.mark('Inital Render');
|
||||
render();
|
||||
performance.measure('Inital Render', 'Inital Render');
|
||||
requestAnimationFrame(() => {
|
||||
performance.mark('Update Render');
|
||||
render();
|
||||
performance.measure('Update Render', 'Update Render');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
@@ -0,0 +1,57 @@
|
||||
body {
|
||||
font-family: Verdana, Geneva, sans-serif
|
||||
}
|
||||
|
||||
.pagetop {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #00d8ff;
|
||||
}
|
||||
|
||||
.hnname {
|
||||
margin-right: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pagetop a, .pagetop a:visited {
|
||||
color: #00d8ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #828282;
|
||||
}
|
||||
|
||||
.subtext {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 7pt;
|
||||
color: #828282;
|
||||
}
|
||||
|
||||
.comhead a:link, .subtext a, .subtext a:visited {
|
||||
color: #828282;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.votearrow {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 0px;
|
||||
margin: 3px 2px 6px;
|
||||
background: url(grayarrow.gif) no-repeat;
|
||||
}
|
||||
|
||||
.title, .title a {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.comhead, .comhead a {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 8pt;
|
||||
color: #828282;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+1216
-1760
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
join,
|
||||
} = require('path');
|
||||
|
||||
async function build(reactPath, asyncCopyTo) {
|
||||
// copy the UMD bundles
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react.production.min.js'),
|
||||
join(__dirname, 'react.production.min.js')
|
||||
);
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react-dom.production.min.js'),
|
||||
join(__dirname, 'react-dom.production.min.js')
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = build;
|
||||
@@ -0,0 +1,32 @@
|
||||
<html>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
performance.mark('Load React');
|
||||
performance.mark('Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="react.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load React', 'Load React');
|
||||
</script>
|
||||
<script>
|
||||
performance.mark('Load ReactDOM');
|
||||
</script>
|
||||
<script src="react-dom.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load ReactDOM', 'Load ReactDOM');
|
||||
performance.measure('Load React+ReactDOM', 'Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="benchmark.js"></script>
|
||||
<script>
|
||||
performance.mark('Inital Render');
|
||||
render();
|
||||
performance.measure('Inital Render', 'Inital Render');
|
||||
requestAnimationFrame(() => {
|
||||
performance.mark('Update Render');
|
||||
render();
|
||||
performance.measure('Update Render', 'Update Render');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+95
-90
@@ -1,4 +1,5 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var ReactImage0 = function (props) {
|
||||
if (props.x === 0) {
|
||||
@@ -135,7 +136,7 @@
|
||||
if (props.x === 43) {
|
||||
return React.createElement(
|
||||
"a",
|
||||
{ className: "_585q _50zy _50-0 _50z- _5upp _42ft", size: "medium", shade: "dark", type: null, title: "Remove", "data-hover": undefined, "data-tooltip-alignh": undefined, "data-tooltip-content": undefined, disabled: null, label: null, href: "#", rel: undefined, onClick: function () {} },
|
||||
{ className: "_585q _50zy _50-0 _50z- _5upp _42ft", size: "medium", type: null, title: "Remove", "data-hover": undefined, "data-tooltip-alignh": undefined, "data-tooltip-content": undefined, disabled: null, label: null, href: "#", rel: undefined, onClick: function () {} },
|
||||
undefined,
|
||||
"Remove",
|
||||
undefined
|
||||
@@ -364,7 +365,7 @@
|
||||
if (props.x === 112) {
|
||||
return React.createElement(
|
||||
"button",
|
||||
{ "aria-label": "Campaign Tags", className: " _5uy7 _4jy0 _4jy4 _517h _51sy _42ft", "data-hover": "tooltip", "data-tooltip-content": "Campaign Tags", disabled: false, haschevron: false, onClick: function () {}, label: null, type: "submit", value: "1" },
|
||||
{ "aria-label": "Campaign Tags", className: " _5uy7 _4jy0 _4jy4 _517h _51sy _42ft", "data-hover": "tooltip", "data-tooltip-content": "Campaign Tags", disabled: false, onClick: function () {}, label: null, type: "submit", value: "1" },
|
||||
React.createElement(ReactImage0, { x: 111 }),
|
||||
undefined,
|
||||
undefined
|
||||
@@ -379,7 +380,7 @@
|
||||
if (props.x === 149) {
|
||||
return React.createElement(
|
||||
"button",
|
||||
{ className: "_3yz9 _1t-2 _50z- _50zy _50zz _50z- _5upp _42ft", size: "small", onClick: function () {}, shade: "dark", type: "button", title: "Remove", "data-hover": undefined, "data-tooltip-alignh": undefined, "data-tooltip-content": undefined, label: null },
|
||||
{ className: "_3yz9 _1t-2 _50z- _50zy _50zz _50z- _5upp _42ft", size: "small", onClick: function () {}, type: "button", title: "Remove", "data-hover": undefined, "data-tooltip-alignh": undefined, "data-tooltip-content": undefined, label: null },
|
||||
undefined,
|
||||
"Remove",
|
||||
undefined
|
||||
@@ -581,7 +582,7 @@
|
||||
return React.createElement(
|
||||
"div",
|
||||
null,
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 6, ref: "openMenuButton" }),
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 6 }),
|
||||
null
|
||||
);
|
||||
}
|
||||
@@ -748,7 +749,7 @@
|
||||
React.createElement(FluxContainer_viewFn_16, { x: 19 }),
|
||||
null,
|
||||
React.createElement(XUIButton4, { x: 21, key: "discard" }),
|
||||
React.createElement(XUIButton4, { x: 24, ref: "reviewChangesButton" })
|
||||
React.createElement(XUIButton4, { x: 24 })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -799,19 +800,19 @@
|
||||
|
||||
var ReactPopoverMenu20 = function (props) {
|
||||
if (props.x === 31) {
|
||||
return React.createElement(InlineBlock19, { x: 30, ref: "root" });
|
||||
return React.createElement(InlineBlock19, { x: 30 });
|
||||
}
|
||||
if (props.x === 74) {
|
||||
return React.createElement(InlineBlock19, { x: 73, ref: "root" });
|
||||
return React.createElement(InlineBlock19, { x: 73 });
|
||||
}
|
||||
if (props.x === 83) {
|
||||
return React.createElement(InlineBlock19, { x: 82, ref: "root" });
|
||||
return React.createElement(InlineBlock19, { x: 82 });
|
||||
}
|
||||
if (props.x === 102) {
|
||||
return React.createElement(InlineBlock19, { x: 101, ref: "root" });
|
||||
return React.createElement(InlineBlock19, { x: 101 });
|
||||
}
|
||||
if (props.x === 274) {
|
||||
return React.createElement(InlineBlock19, { x: 273, ref: "root" });
|
||||
return React.createElement(InlineBlock19, { x: 273 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -861,7 +862,7 @@
|
||||
if (props.x === 232) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ flex: "right", direction: "left", className: "clearfix" },
|
||||
{ direction: "left", className: "clearfix" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ key: "left", className: "_ohe lfloat" },
|
||||
@@ -885,7 +886,7 @@
|
||||
if (props.x === 235) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_3-8x clearfix", flex: "right", direction: "left" },
|
||||
{ className: "_3-8x clearfix", direction: "left" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ key: "left", className: "_ohe lfloat" },
|
||||
@@ -909,7 +910,7 @@
|
||||
if (props.x === 245) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_3-8y clearfix", flex: "right", direction: "left" },
|
||||
{ className: "_3-8y clearfix", direction: "left" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ key: "left", className: "_ohe lfloat" },
|
||||
@@ -1058,7 +1059,7 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
null,
|
||||
React.createElement(ReactCSSTransitionGroupChild31, { x: 52, key: ".0", ref: ".0" })
|
||||
React.createElement(ReactCSSTransitionGroupChild31, { x: 52, key: ".0" })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1095,24 +1096,24 @@
|
||||
if (props.x === 62) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ onResize: function () {}, className: "_4u-c" },
|
||||
[React.createElement(AdsErrorBoundary10, { x: 58 }), React.createElement(AdsErrorBoundary10, { x: 61 })],
|
||||
{ className: "_4u-c" },
|
||||
[React.createElement(AdsErrorBoundary10, { x: 58, key: 1 }), React.createElement(AdsErrorBoundary10, { x: 61, key: 2 })],
|
||||
React.createElement(
|
||||
"div",
|
||||
{ key: "sensor", className: "_4u-f" },
|
||||
React.createElement("iframe", { ref: "sensorNode", "aria-hidden": "true", className: "_1_xb", tabIndex: "-1" })
|
||||
React.createElement("iframe", { "aria-hidden": "true", className: "_1_xb", tabIndex: "-1" })
|
||||
)
|
||||
);
|
||||
}
|
||||
if (props.x === 469) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ onResize: function () {}, className: "_4u-c" },
|
||||
{ className: "_4u-c" },
|
||||
React.createElement(AdsPEDataTableContainer158, { x: 468 }),
|
||||
React.createElement(
|
||||
"div",
|
||||
{ key: "sensor", className: "_4u-f" },
|
||||
React.createElement("iframe", { ref: "sensorNode", "aria-hidden": "true", className: "_1_xb", tabIndex: "-1" })
|
||||
React.createElement("iframe", { "aria-hidden": "true", className: "_1_xb", tabIndex: "-1" })
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1172,7 +1173,7 @@
|
||||
"div",
|
||||
{ className: "_5n7z _51xa" },
|
||||
React.createElement(AdsPEExportImportMenuContainer48, { x: 107 }),
|
||||
React.createElement(XUIButton4, { x: 110, key: "createReport", ref: "ads_create_report_button" }),
|
||||
React.createElement(XUIButton4, { x: 110, key: "createReport" }),
|
||||
React.createElement(AdsPECampaignGroupTagContainer51, { x: 116, key: "tags" })
|
||||
);
|
||||
}
|
||||
@@ -1254,8 +1255,8 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
{ className: " _3d6e" },
|
||||
React.createElement(XUIButton4, { x: 113, ref: "button" }),
|
||||
React.createElement(Constructor49, { x: 114, key: "layer", ref: "layer" })
|
||||
React.createElement(XUIButton4, { x: 113 }),
|
||||
React.createElement(Constructor49, { x: 114, key: "layer" })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1333,8 +1334,8 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
{ className: "_5b-l _5bbe" },
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 133, ref: "searchButton" }),
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 141, ref: "filterButton" }),
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 133 }),
|
||||
React.createElement(ReactXUIPopoverButton6, { x: 141 }),
|
||||
[React.createElement(Constructor49, { x: 142, key: "filterMenu/.0" }), React.createElement(Constructor49, { x: 143, key: "searchMenu/.0" })]
|
||||
);
|
||||
}
|
||||
@@ -1390,7 +1391,7 @@
|
||||
null,
|
||||
React.createElement(
|
||||
"span",
|
||||
{ ref: "tipIcon", onMouseEnter: function () {}, onMouseLeave: function () {}, onMouseUp: undefined },
|
||||
{ onMouseEnter: function () {}, onMouseLeave: function () {}, onMouseUp: undefined },
|
||||
React.createElement("span", { className: "_3o_j" }),
|
||||
React.createElement(ReactImage0, { x: 153 })
|
||||
),
|
||||
@@ -1403,7 +1404,7 @@
|
||||
null,
|
||||
React.createElement(
|
||||
"span",
|
||||
{ ref: "tipIcon", onMouseEnter: function () {}, onMouseLeave: function () {}, onMouseUp: undefined },
|
||||
{ onMouseEnter: function () {}, onMouseLeave: function () {}, onMouseUp: undefined },
|
||||
React.createElement("span", { className: "_3o_j" }),
|
||||
React.createElement(ReactImage0, { x: 237 })
|
||||
),
|
||||
@@ -1449,7 +1450,7 @@
|
||||
{ className: "_3yz1 _3yz2 _3dad" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ ref: "filterToken", className: "_3yz4", "aria-hidden": false },
|
||||
{ className: "_3yz4", "aria-hidden": false },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ onClick: function () {}, className: "_3yz5" },
|
||||
@@ -1468,7 +1469,7 @@
|
||||
null,
|
||||
React.createElement(XUICloseButton28, { x: 151 })
|
||||
),
|
||||
React.createElement(BUIFilterTokenInput64, { x: 158, ref: "filterTokenInput" })
|
||||
React.createElement(BUIFilterTokenInput64, { x: 158 })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1527,7 +1528,7 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
null,
|
||||
React.createElement("span", { ref: "mainChild" }),
|
||||
React.createElement("span", { }),
|
||||
React.createElement(XUIAmbientNUX69, { x: 166, key: "nux" })
|
||||
);
|
||||
}
|
||||
@@ -1595,10 +1596,10 @@
|
||||
"div",
|
||||
null,
|
||||
null,
|
||||
React.createElement("textarea", { ref: "input", className: "_2vli _2vlj _1h26 _1h27", dir: "auto", disabled: undefined, id: undefined, maxLength: null, value: "Test Campaign", onBlur: function () {}, onChange: function () {}, onFocus: function () {}, onKeyDown: function () {} }),
|
||||
React.createElement("textarea", { className: "_2vli _2vlj _1h26 _1h27", dir: "auto", disabled: undefined, id: undefined, maxLength: null, value: "Test Campaign", onBlur: function () {}, onChange: function () {}, onFocus: function () {}, onKeyDown: function () {} }),
|
||||
null
|
||||
),
|
||||
React.createElement("div", { ref: "shadowText", "aria-hidden": "true", className: "_2vlk" })
|
||||
React.createElement("div", { "aria-hidden": "true", className: "_2vlk" })
|
||||
)
|
||||
),
|
||||
null
|
||||
@@ -1626,7 +1627,7 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
null,
|
||||
React.createElement(BUIPopoverButton77, { x: 182, ref: function () {} }),
|
||||
React.createElement(BUIPopoverButton77, { x: 182 }),
|
||||
[React.createElement(Constructor49, { x: 183, key: "layer/.0" })]
|
||||
);
|
||||
}
|
||||
@@ -1652,7 +1653,7 @@
|
||||
"span",
|
||||
{ className: "_3c5l" },
|
||||
React.createElement(AdsPEStatsRangePicker79, { x: 185 }),
|
||||
React.createElement(XUIButton4, { x: 188, key: "settings", ref: "PE_TABLE_LIST_SETTING" })
|
||||
React.createElement(XUIButton4, { x: 188, key: "settings" })
|
||||
),
|
||||
[React.createElement(XUIAmbientNUX69, { x: 190, key: "roasNUX/.0" })]
|
||||
);
|
||||
@@ -1700,7 +1701,7 @@
|
||||
return React.createElement(
|
||||
"span",
|
||||
null,
|
||||
React.createElement(AdsPESideTrayTabButton82, { x: 199, ref: "PE_INSIGHTS_TAB_ICON" }),
|
||||
React.createElement(AdsPESideTrayTabButton82, { x: 199 }),
|
||||
React.createElement(XUIAmbientNUX69, { x: 201, key: "roasNUX" })
|
||||
);
|
||||
}
|
||||
@@ -1796,8 +1797,8 @@
|
||||
{ className: "_5528" },
|
||||
React.createElement(
|
||||
"span",
|
||||
{ key: ".0", className: "_40bg", density: "snug", labelPosition: "left", id: "1467872040612:1961945894" },
|
||||
React.createElement(AdsTextInput91, { x: 217, key: "nameEditor98010048849317", ref: "nameTextInput" }),
|
||||
{ key: ".0", className: "_40bg", id: "1467872040612:1961945894" },
|
||||
React.createElement(AdsTextInput91, { x: 217, key: "nameEditor98010048849317" }),
|
||||
null
|
||||
)
|
||||
),
|
||||
@@ -1821,14 +1822,14 @@
|
||||
if (props.x === 220) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_40bc _12k2 _4-u2 _4-u8", xuiErrorPosition: "above", background: "white" },
|
||||
{ className: "_40bc _12k2 _4-u2 _4-u8" },
|
||||
React.createElement(BUIForm93, { x: 219 })
|
||||
);
|
||||
}
|
||||
if (props.x === 249) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ xuiErrorPosition: "above", className: "_12k2 _4-u2 _4-u8", background: "white" },
|
||||
{ className: "_12k2 _4-u2 _4-u8" },
|
||||
React.createElement(AdsCardHeader103, { x: 230 }),
|
||||
React.createElement(AdsCardSection108, { x: 248 })
|
||||
);
|
||||
@@ -1836,7 +1837,7 @@
|
||||
if (props.x === 279) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ xuiErrorPosition: "above", className: "_12k2 _4-u2 _4-u8", background: "white" },
|
||||
{ className: "_12k2 _4-u2 _4-u8" },
|
||||
React.createElement(AdsCardLeftRightHeader122, { x: 278 })
|
||||
);
|
||||
}
|
||||
@@ -1894,7 +1895,7 @@
|
||||
if (props.x === 227) {
|
||||
return React.createElement(
|
||||
"span",
|
||||
{ itemComponent: "span", className: "_38my" },
|
||||
{ className: "_38my" },
|
||||
"Campaign Details",
|
||||
null,
|
||||
React.createElement("span", { className: "_c1c" })
|
||||
@@ -1903,16 +1904,16 @@
|
||||
if (props.x === 265) {
|
||||
return React.createElement(
|
||||
"span",
|
||||
{ itemComponent: "span", className: "_38my" },
|
||||
{ className: "_38my" },
|
||||
[React.createElement(
|
||||
"span",
|
||||
null,
|
||||
{ key: 1 },
|
||||
"Campaign ID",
|
||||
": ",
|
||||
"98010048849317"
|
||||
), React.createElement(
|
||||
"div",
|
||||
{ className: "_5lh9" },
|
||||
{ className: "_5lh9", key: 2 },
|
||||
React.createElement(FluxContainer_AdsCampaignGroupStatusSwitchContainer_119, { x: 264 })
|
||||
)],
|
||||
null,
|
||||
@@ -1925,7 +1926,7 @@
|
||||
if (props.x === 228) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_5dw9 _5dwa _4-u3", background: "transparent" },
|
||||
{ className: "_5dw9 _5dwa _4-u3" },
|
||||
[React.createElement(XUICardHeaderTitle100, { x: 227, key: ".0" })],
|
||||
undefined,
|
||||
undefined,
|
||||
@@ -1935,7 +1936,7 @@
|
||||
if (props.x === 247) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_12jy _4-u3", background: "transparent" },
|
||||
{ className: "_12jy _4-u3" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_3-8j" },
|
||||
@@ -1965,7 +1966,7 @@
|
||||
if (props.x === 231) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_2oc6 _3bvz", label: "Objective", labelSize: "small", optionalText: "(optional)" },
|
||||
{ className: "_2oc6 _3bvz", label: "Objective" },
|
||||
React.createElement(
|
||||
"label",
|
||||
{ className: "_4el4 _3qwj _3hy-", htmlFor: undefined },
|
||||
@@ -1978,7 +1979,7 @@
|
||||
if (props.x === 234) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_2oc6 _3bvz", label: "Buying Type", labelSize: "small", optionalText: "(optional)" },
|
||||
{ className: "_2oc6 _3bvz", label: "Buying Type" },
|
||||
React.createElement(
|
||||
"label",
|
||||
{ className: "_4el4 _3qwj _3hy-", htmlFor: undefined },
|
||||
@@ -1991,7 +1992,7 @@
|
||||
if (props.x === 240) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_2oc6 _3bvz", helpText: "Set an overall spending limit for your ad campaign. This means your ad sets in the campaign will stop once you've reached your spending limit.", label: "Campaign Spending Limit", labelSize: "small", optionalText: "(optional)" },
|
||||
{ className: "_2oc6 _3bvz" },
|
||||
React.createElement(
|
||||
"label",
|
||||
{ className: "_4el4 _3qwj _3hy-", htmlFor: undefined },
|
||||
@@ -2073,17 +2074,17 @@
|
||||
if (props.x === 259) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ inContainingBlock: true },
|
||||
React.createElement("div", { ref: "sticky", onWheel: function () {} })
|
||||
{ },
|
||||
React.createElement("div", { onWheel: function () {} })
|
||||
);
|
||||
}
|
||||
if (props.x === 292) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ inContainingBlock: true },
|
||||
{ },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ ref: "sticky", onWheel: function () {} },
|
||||
{ onWheel: function () {} },
|
||||
[React.createElement(
|
||||
"div",
|
||||
{ key: "campaign_group_errors_section98010048849317" },
|
||||
@@ -2133,7 +2134,7 @@
|
||||
if (props.x === 261) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ "data-hover": "tooltip", "data-tooltip-content": "Currently active. Click this switch to deactivate it.", "data-tooltip-position": "below", disabled: false, value: true, onToggle: function () {}, animate: true, className: "_128j _128k _128n", role: "checkbox", "aria-checked": "true" },
|
||||
{ "data-hover": "tooltip", "data-tooltip-content": "Currently active. Click this switch to deactivate it.", "data-tooltip-position": "below", disabled: false, value: true, onToggle: function () {}, className: "_128j _128k _128n", role: "checkbox", "aria-checked": "true" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_128o", onClick: function () {}, onKeyDown: function () {}, onMouseDown: function () {}, tabIndex: "0" },
|
||||
@@ -2675,7 +2676,7 @@
|
||||
if (props.x === 316) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Campaigns", dataKey: 0, groupHeaderRenderer: function () {}, groupHeaderLabels: {}, groupHeaderData: {}, columnKey: undefined, height: 40, width: 721, rowIndex: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 721 } },
|
||||
{ label: "Campaigns", height: 40, width: 721, className: "_4lgc _4h2u", style: { "height": 40, "width": 721 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2690,7 +2691,7 @@
|
||||
if (props.x === 321) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Performance", dataKey: 1, groupHeaderRenderer: function () {}, groupHeaderLabels: {}, groupHeaderData: {}, columnKey: undefined, height: 40, width: 798, rowIndex: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 798 } },
|
||||
{ label: "Performance", height: 40, width: 798, className: "_4lgc _4h2u", style: { "height": 40, "width": 798 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2705,7 +2706,7 @@
|
||||
if (props.x === 324) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Overview", dataKey: 2, groupHeaderRenderer: function () {}, groupHeaderLabels: {}, groupHeaderData: {}, columnKey: undefined, height: 40, width: 1022, rowIndex: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 1022 } },
|
||||
{ label: "Overview", height: 40, width: 1022, className: "_4lgc _4h2u", style: { "height": 40, "width": 1022 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2720,7 +2721,7 @@
|
||||
if (props.x === 327) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Toplines", dataKey: 3, groupHeaderRenderer: function () {}, groupHeaderLabels: {}, groupHeaderData: {}, columnKey: undefined, height: 40, width: 0, rowIndex: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 0 } },
|
||||
{ label: "Toplines", height: 40, width: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 0 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2735,7 +2736,7 @@
|
||||
if (props.x === 330) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "", dataKey: 4, groupHeaderRenderer: function () {}, groupHeaderLabels: {}, groupHeaderData: {}, columnKey: undefined, height: 40, width: 25, rowIndex: 0, className: "_4lgc _4h2u", style: { "height": 40, "width": 25 } },
|
||||
{ label: "", height: 40, width: 25, className: "_4lgc _4h2u", style: { "height": 40, "width": 25 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2750,7 +2751,7 @@
|
||||
if (props.x === 338) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: undefined, width: 42, dataKey: "common.id", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "common.id", height: 25, style: { "height": 25, "width": 42 } },
|
||||
{ label: undefined, width: 42, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 42 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2765,7 +2766,7 @@
|
||||
if (props.x === 343) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Campaign Name", width: 400, dataKey: "campaignGroup.name", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "campaignGroup.name", height: 25, style: { "height": 25, "width": 400 } },
|
||||
{ label: "Campaign Name", width: 400, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 400 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2780,7 +2781,7 @@
|
||||
if (props.x === 349) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: undefined, width: 33, dataKey: "edit_status", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "edit_status", height: 25, style: { "height": 25, "width": 33 } },
|
||||
{ label: undefined, width: 33, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 33 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2795,7 +2796,7 @@
|
||||
if (props.x === 355) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: undefined, width: 36, dataKey: "errors", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "errors", height: 25, style: { "height": 25, "width": 36 } },
|
||||
{ label: undefined, width: 36, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 36 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2810,7 +2811,7 @@
|
||||
if (props.x === 360) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Status", width: 60, dataKey: "campaignGroup.status", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "campaignGroup.status", height: 25, style: { "height": 25, "width": 60 } },
|
||||
{ label: "Status", width: 60, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 60 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2825,7 +2826,7 @@
|
||||
if (props.x === 365) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Delivery", width: 150, dataKey: "derivedCampaignGroup.activity_status", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "derivedCampaignGroup.activity_status", height: 25, style: { "height": 25, "width": 150 } },
|
||||
{ label: "Delivery", width: 150, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 150 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2840,7 +2841,7 @@
|
||||
if (props.x === 372) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Results", width: 140, dataKey: "stats.actions", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.actions", height: 25, style: { "height": 25, "width": 140 } },
|
||||
{ label: "Results", width: 140, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 140 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2855,7 +2856,7 @@
|
||||
if (props.x === 377) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Cost", width: 140, dataKey: "stats.cpa", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.cpa", height: 25, style: { "height": 25, "width": 140 } },
|
||||
{ label: "Cost", width: 140, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 140 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2870,7 +2871,7 @@
|
||||
if (props.x === 382) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Reach", width: 80, dataKey: "stats.unique_impressions", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.unique_impressions", height: 25, style: { "height": 25, "width": 80 } },
|
||||
{ label: "Reach", width: 80, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 80 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2885,7 +2886,7 @@
|
||||
if (props.x === 387) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Impressions", width: 80, dataKey: "stats.impressions", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.impressions", height: 25, style: { "height": 25, "width": 80 } },
|
||||
{ label: "Impressions", width: 80, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 80 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2900,7 +2901,7 @@
|
||||
if (props.x === 392) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Clicks", width: 60, dataKey: "stats.clicks", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.clicks", height: 25, style: { "height": 25, "width": 60 } },
|
||||
{ label: "Clicks", width: 60, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 60 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2915,7 +2916,7 @@
|
||||
if (props.x === 397) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Avg. CPM", width: 80, dataKey: "stats.avg_cpm", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.avg_cpm", height: 25, style: { "height": 25, "width": 80 } },
|
||||
{ label: "Avg. CPM", width: 80, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 80 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2930,7 +2931,7 @@
|
||||
if (props.x === 402) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Avg. CPC", width: 78, dataKey: "stats.avg_cpc", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.avg_cpc", height: 25, style: { "height": 25, "width": 78 } },
|
||||
{ label: "Avg. CPC", width: 78, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 78 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2945,7 +2946,7 @@
|
||||
if (props.x === 407) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "CTR %", width: 70, dataKey: "stats.ctr", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.ctr", height: 25, style: { "height": 25, "width": 70 } },
|
||||
{ label: "CTR %", width: 70, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 70 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2960,7 +2961,7 @@
|
||||
if (props.x === 412) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Spent", width: 70, dataKey: "stats.spent_100", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "stats.spent_100", height: 25, style: { "height": 25, "width": 70 } },
|
||||
{ label: "Spent", width: 70, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 70 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2975,7 +2976,7 @@
|
||||
if (props.x === 417) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Objective", width: 200, dataKey: "campaignGroup.objective", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "campaignGroup.objective", height: 25, style: { "height": 25, "width": 200 } },
|
||||
{ label: "Objective", width: 200, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 200 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -2990,7 +2991,7 @@
|
||||
if (props.x === 422) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Buying Type", width: 100, dataKey: "campaignGroup.buying_type", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "campaignGroup.buying_type", height: 25, style: { "height": 25, "width": 100 } },
|
||||
{ label: "Buying Type", width: 100, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 100 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3005,7 +3006,7 @@
|
||||
if (props.x === 427) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Campaign ID", width: 120, dataKey: "campaignGroup.id", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "campaignGroup.id", height: 25, style: { "height": 25, "width": 120 } },
|
||||
{ label: "Campaign ID", width: 120, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 120 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3020,7 +3021,7 @@
|
||||
if (props.x === 432) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Start", width: 113, dataKey: "derivedCampaignGroup.startDate", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "derivedCampaignGroup.startDate", height: 25, style: { "height": 25, "width": 113 } },
|
||||
{ label: "Start", width: 113, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 113 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3035,7 +3036,7 @@
|
||||
if (props.x === 437) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "End", width: 113, dataKey: "derivedCampaignGroup.stopDate", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "derivedCampaignGroup.stopDate", height: 25, style: { "height": 25, "width": 113 } },
|
||||
{ label: "End", width: 113, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 113 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3050,7 +3051,7 @@
|
||||
if (props.x === 442) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Date created", width: 113, dataKey: "derivedCampaignGroup.createdDate", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "derivedCampaignGroup.createdDate", height: 25, style: { "height": 25, "width": 113 } },
|
||||
{ label: "Date created", width: 113, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 113 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3065,7 +3066,7 @@
|
||||
if (props.x === 447) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Date last edited", width: 113, dataKey: "derivedCampaignGroup.updatedDate", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "derivedCampaignGroup.updatedDate", height: 25, style: { "height": 25, "width": 113 } },
|
||||
{ label: "Date last edited", width: 113, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 113 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3080,7 +3081,7 @@
|
||||
if (props.x === 450) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "Tags", width: 150, dataKey: "labels", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "labels", height: 25, style: { "height": 25, "width": 150 } },
|
||||
{ label: "Tags", width: 150, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 150 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3095,7 +3096,7 @@
|
||||
if (props.x === 453) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ isHeaderCell: true, label: "", width: 25, dataKey: "scrollbar_spacer", className: "_4lgc _4h2u", columnData: {}, cellRenderer: function () {}, headerDataGetter: function () {}, columnKey: "scrollbar_spacer", height: 25, style: { "height": 25, "width": 25 } },
|
||||
{ label: "", width: 25, className: "_4lgc _4h2u", height: 25, style: { "height": 25, "width": 25 } },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "_4lgd _4h2w" },
|
||||
@@ -3888,7 +3889,7 @@
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ onFocus: function () {}, onBlur: function () {}, onKeyDown: function () {}, onMouseDown: function () {}, onWheel: function () {}, className: "_1t0r _1t0t _4jdr _1t0u", style: { "width": 1209, "zIndex": 99 }, tabIndex: 0 },
|
||||
React.createElement("div", { ref: "face", className: "_1t0w _1t0y _1t0_", style: { "width": 561.6340607950117, "transform": "translate3d(4px,0px,0)", "backfaceVisibility": "hidden" } })
|
||||
React.createElement("div", { className: "_1t0w _1t0y _1t0_", style: { "width": 561.6340607950117, "transform": "translate3d(4px,0px,0)", "backfaceVisibility": "hidden" } })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -3931,7 +3932,7 @@
|
||||
|
||||
var TransitionTable154 = function (props) {
|
||||
if (props.x === 464) {
|
||||
return React.createElement(FixedDataTable153, { x: 463, ref: "fixedDataTable" });
|
||||
return React.createElement(FixedDataTable153, { x: 463 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3949,7 +3950,7 @@
|
||||
if (props.x === 466) {
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ ref: "tableContainer", className: "_5d6f", tabIndex: "0", onKeyDown: function () {} },
|
||||
{ className: "_5d6f", tabIndex: "0", onKeyDown: function () {} },
|
||||
React.createElement(AdsSelectableFixedDataTable155, { x: 465 })
|
||||
);
|
||||
}
|
||||
@@ -3960,7 +3961,7 @@
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ onCopy: function () {} },
|
||||
React.createElement(AdsDataTableKeyboardSupportDecorator156, { x: 466, ref: "decoratedTable" })
|
||||
React.createElement(AdsDataTableKeyboardSupportDecorator156, { x: 466 })
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -4036,8 +4037,8 @@
|
||||
return React.createElement(
|
||||
"div",
|
||||
{ className: "_49wu", style: { "height": 177, "top": 43, "width": 1306 } },
|
||||
React.createElement(ResponsiveBlock37, { x: 62 }),
|
||||
React.createElement(AdsErrorBoundary10, { x: 476 }),
|
||||
React.createElement(ResponsiveBlock37, { x: 62, key: '0' }),
|
||||
React.createElement(AdsErrorBoundary10, { x: 476, key: '1' }),
|
||||
null
|
||||
);
|
||||
}
|
||||
@@ -4194,5 +4195,9 @@
|
||||
}
|
||||
};
|
||||
|
||||
this.Benchmark = Benchmark;
|
||||
})(this);
|
||||
var app = document.getElementById('app');
|
||||
|
||||
window.render = function render() {
|
||||
ReactDOM.render(React.createElement(Benchmark, null), app);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
join,
|
||||
} = require('path');
|
||||
|
||||
async function build(reactPath, asyncCopyTo) {
|
||||
// copy the UMD bundles
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react.production.min.js'),
|
||||
join(__dirname, 'react.production.min.js')
|
||||
);
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react-dom.production.min.js'),
|
||||
join(__dirname, 'react-dom.production.min.js')
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = build;
|
||||
@@ -0,0 +1,32 @@
|
||||
<html>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
performance.mark('Load React');
|
||||
performance.mark('Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="react.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load React', 'Load React');
|
||||
</script>
|
||||
<script>
|
||||
performance.mark('Load ReactDOM');
|
||||
</script>
|
||||
<script src="react-dom.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load ReactDOM', 'Load ReactDOM');
|
||||
performance.measure('Load React+ReactDOM', 'Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="benchmark.js"></script>
|
||||
<script>
|
||||
performance.mark('Inital Render');
|
||||
render();
|
||||
performance.measure('Inital Render', 'Inital Render');
|
||||
requestAnimationFrame(() => {
|
||||
performance.mark('Update Render');
|
||||
render();
|
||||
performance.measure('Update Render', 'Update Render');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
join,
|
||||
} = require('path');
|
||||
|
||||
async function build(reactPath, asyncCopyTo) {
|
||||
// copy the UMD bundles
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react.production.min.js'),
|
||||
join(__dirname, 'react.production.min.js')
|
||||
);
|
||||
await asyncCopyTo(
|
||||
join(reactPath, 'build', 'dist', 'react-dom.production.min.js'),
|
||||
join(__dirname, 'react-dom.production.min.js')
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = build;
|
||||
@@ -0,0 +1,32 @@
|
||||
<html>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
performance.mark('Load React');
|
||||
performance.mark('Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="react.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load React', 'Load React');
|
||||
</script>
|
||||
<script>
|
||||
performance.mark('Load ReactDOM');
|
||||
</script>
|
||||
<script src="react-dom.production.min.js"></script>
|
||||
<script>
|
||||
performance.measure('Load ReactDOM', 'Load ReactDOM');
|
||||
performance.measure('Load React+ReactDOM', 'Load React+ReactDOM');
|
||||
</script>
|
||||
<script src="benchmark.js"></script>
|
||||
<script>
|
||||
performance.mark('Inital Render');
|
||||
render();
|
||||
performance.measure('Inital Render', 'Inital Render');
|
||||
requestAnimationFrame(() => {
|
||||
performance.mark('Update Render');
|
||||
render();
|
||||
performance.measure('Update Render', 'Update Render');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
const Git = require('nodegit');
|
||||
const rimraf = require('rimraf');
|
||||
const ncp = require('ncp').ncp;
|
||||
const {
|
||||
existsSync,
|
||||
} = require('fs');
|
||||
const exec = require('child_process').exec;
|
||||
const {
|
||||
join,
|
||||
} = require('path');
|
||||
|
||||
const reactUrl = 'https://github.com/facebook/react.git';
|
||||
|
||||
function cleanDir() {
|
||||
return new Promise(_resolve => rimraf('remote-repo', _resolve));
|
||||
}
|
||||
|
||||
function executeCommand(command) {
|
||||
return new Promise(_resolve => exec(command, (error) => {
|
||||
if (!error) {
|
||||
_resolve();
|
||||
} else {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function asyncCopyTo(from, to) {
|
||||
return new Promise(_resolve => {
|
||||
ncp(from, to, error => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
_resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getDefaultReactPath() {
|
||||
return join(__dirname, 'remote-repo');
|
||||
}
|
||||
|
||||
async function buldAllBundles(reactPath = getDefaultReactPath()) {
|
||||
// build the react FB bundles in the build
|
||||
await executeCommand(`cd ${reactPath} && yarn && yarn build`);
|
||||
}
|
||||
|
||||
async function buildBenchmark(reactPath = getDefaultReactPath(), benchmark) {
|
||||
// get the build.js from the benchmark directory and execute it
|
||||
await require(
|
||||
join(__dirname, 'benchmarks', benchmark, 'build.js')
|
||||
)(reactPath, asyncCopyTo);
|
||||
}
|
||||
|
||||
function getBundleResults(reactPath = getDefaultReactPath()) {
|
||||
return require(join(reactPath, 'scripts', 'rollup', 'results.json'));
|
||||
}
|
||||
|
||||
async function getMergeBaseFromLocalGitRepo(localRepo) {
|
||||
const repo = await Git.Repository.open(localRepo);
|
||||
return await Git.Merge.base(
|
||||
repo,
|
||||
await repo.getHeadCommit(),
|
||||
await repo.getBranchCommit('master')
|
||||
);
|
||||
}
|
||||
|
||||
async function buildBenchmarkBundlesFromGitRepo(commitId, skipBuild, url = reactUrl, clean) {
|
||||
let repo;
|
||||
const remoteRepoDir = getDefaultReactPath();
|
||||
|
||||
if (!skipBuild) {
|
||||
if (clean) {
|
||||
//clear remote-repo folder
|
||||
await cleanDir(remoteRepoDir);
|
||||
}
|
||||
// check if remote-repo diretory already exists
|
||||
if (existsSync(join(__dirname, 'remote-repo'))) {
|
||||
repo = await Git.Repository.open(remoteRepoDir);
|
||||
// fetch all the latest remote changes
|
||||
await repo.fetchAll();
|
||||
} else {
|
||||
// if not, clone the repo to remote-repo folder
|
||||
repo = await Git.Clone(url, remoteRepoDir);
|
||||
}
|
||||
let commit = await repo.getBranchCommit('master');
|
||||
// reset hard to this remote head
|
||||
await Git.Reset.reset(repo, commit, Git.Reset.TYPE.HARD);
|
||||
// then we checkout the latest master head
|
||||
await repo.checkoutBranch('master');
|
||||
// make sure we pull in the latest changes
|
||||
await repo.mergeBranches('master', 'origin/master');
|
||||
// then we check if we need to move the HEAD to the merge base
|
||||
if (commitId && commitId !== 'master') {
|
||||
// as the commitId probably came from our local repo
|
||||
// we use it to lookup the right commit in our remote repo
|
||||
commit = await Git.Commit.lookup(repo, commitId);
|
||||
// then we checkout the merge base
|
||||
await Git.Checkout.tree(repo, commit);
|
||||
}
|
||||
await buildAllBundles();
|
||||
}
|
||||
return getBundleResults();
|
||||
}
|
||||
|
||||
async function buildAllBundles(reactPath, skipBuild) {
|
||||
if (!skipBuild) {
|
||||
// build all bundles so we can get all stats and use bundles for benchmarks
|
||||
await buldAllBundles(reactPath);
|
||||
}
|
||||
return getBundleResults(reactPath);
|
||||
}
|
||||
|
||||
// if run directly via CLI
|
||||
if (require.main === module) {
|
||||
buildBenchmarkBundlesFromGitRepo();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildAllBundles,
|
||||
buildBenchmark,
|
||||
buildBenchmarkBundlesFromGitRepo,
|
||||
getMergeBaseFromLocalGitRepo,
|
||||
};
|
||||
@@ -1,207 +0,0 @@
|
||||
/*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;
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2015-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _run_js_in_jsc(jit, js, env):
|
||||
return subprocess.check_output(
|
||||
['jsc', '-e', """
|
||||
function now() {
|
||||
return preciseTime() * 1000;
|
||||
}
|
||||
function globalEval(code) {
|
||||
(0, eval)(code);
|
||||
}
|
||||
function report(label, time) {
|
||||
print(label + '_' + %(engine)s, time);
|
||||
}
|
||||
|
||||
this.ENV = %(env)s;
|
||||
%(js)s
|
||||
""" % {
|
||||
'env': json.dumps(env),
|
||||
'js': js,
|
||||
'engine': json.dumps('jsc_' + ('jit' if jit else 'nojit')),
|
||||
}],
|
||||
env=dict(os.environ, JSC_useJIT='yes' if jit else 'no'),
|
||||
)
|
||||
|
||||
_run_js_in_jsc_jit = functools.partial(_run_js_in_jsc, True)
|
||||
_run_js_in_jsc_nojit = functools.partial(_run_js_in_jsc, False)
|
||||
|
||||
|
||||
def _run_js_in_node(js, env):
|
||||
return subprocess.check_output(
|
||||
['node', '-e', """
|
||||
function now() {
|
||||
var hrTime = process.hrtime();
|
||||
return hrTime[0] * 1e3 + hrTime[1] * 1e-6;
|
||||
}
|
||||
function globalEval(code) {
|
||||
var vm = require('vm');
|
||||
// Hide "module" so UMD wrappers use the global
|
||||
vm.runInThisContext('(function(module){' + code + '\\n})()');
|
||||
}
|
||||
function readFile(filename) {
|
||||
var fs = require('fs');
|
||||
return fs.readFileSync(filename);
|
||||
}
|
||||
function report(label, time) {
|
||||
console.log(label + '_node', time);
|
||||
}
|
||||
|
||||
global.ENV = %(env)s;
|
||||
%(js)s
|
||||
""" % {
|
||||
'env': json.dumps(env),
|
||||
'js': js
|
||||
}]
|
||||
)
|
||||
|
||||
|
||||
def _measure_ssr_ms(engine, react_path, bench_name, bench_path, measure_warm):
|
||||
return engine(
|
||||
"""
|
||||
var reactCode = readFile(ENV.react_path + '/react.min.js');
|
||||
var reactDOMServerCode = readFile(ENV.react_path + '/react-dom-server.min.js');
|
||||
var START = now();
|
||||
globalEval(reactCode);
|
||||
globalEval(reactDOMServerCode);
|
||||
var END = now();
|
||||
if (typeof React !== 'object') throw new Error('React not loaded');
|
||||
if (typeof ReactDOMServer !== 'object') throw new Error('ReactDOMServer not loaded');
|
||||
report('factory_ms', END - START);
|
||||
|
||||
globalEval(readFile(ENV.bench_path));
|
||||
if (typeof Benchmark !== 'function') {
|
||||
throw new Error('benchmark not loaded');
|
||||
}
|
||||
var START = now();
|
||||
var html = ReactDOMServer.renderToString(React.createElement(Benchmark));
|
||||
html.charCodeAt(0); // flatten ropes
|
||||
var END = now();
|
||||
report('ssr_' + ENV.bench_name + '_cold_ms', END - START);
|
||||
|
||||
var warmup = ENV.measure_warm ? 80 : 0;
|
||||
var trials = ENV.measure_warm ? 40 : 0;
|
||||
|
||||
for (var i = 0; i < warmup; i++) {
|
||||
ReactDOMServer.renderToString(React.createElement(Benchmark));
|
||||
}
|
||||
|
||||
for (var i = 0; i < trials; i++) {
|
||||
var START = now();
|
||||
var html = ReactDOMServer.renderToString(React.createElement(Benchmark));
|
||||
html.charCodeAt(0); // flatten ropes
|
||||
var END = now();
|
||||
report('ssr_' + ENV.bench_name + '_warm_ms', END - START);
|
||||
}
|
||||
""",
|
||||
{
|
||||
'bench_name': bench_name,
|
||||
'bench_path': bench_path,
|
||||
'measure_warm': measure_warm,
|
||||
'react_path': react_path,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _main():
|
||||
if len(sys.argv) < 2 or len(sys.argv) % 2 == 0:
|
||||
sys.stderr.write("usage: measure.py build-folder-a a.txt build-folder-b b.txt\n")
|
||||
return 1
|
||||
# [(react_path, out_path)]
|
||||
react_paths = sys.argv[1::2]
|
||||
files = [open(out_path, 'w') for out_path in sys.argv[2::2]]
|
||||
|
||||
trials = 30
|
||||
sys.stderr.write("Measuring SSR for PE benchmark (%d trials)\n" % trials)
|
||||
sys.stderr.write("_" * trials + "\n")
|
||||
for i in range(trials):
|
||||
for engine in [
|
||||
_run_js_in_jsc_jit,
|
||||
_run_js_in_jsc_nojit,
|
||||
_run_js_in_node
|
||||
]:
|
||||
engines = range(len(react_paths))
|
||||
random.shuffle(engines)
|
||||
for i in engines:
|
||||
out = _measure_ssr_ms(engine, react_paths[i], 'pe', 'bench-pe-es5.js', False)
|
||||
files[i].write(out)
|
||||
sys.stderr.write(".")
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
# You can set this to a number of trials you want to do with warm JIT.
|
||||
# They are disabled by default because they are slower.
|
||||
trials = 0
|
||||
|
||||
sys.stderr.write("Measuring SSR for PE with warm JIT (%d slow trials)\n" % trials)
|
||||
sys.stderr.write("_" * trials + "\n")
|
||||
for i in range(trials):
|
||||
for engine in [
|
||||
_run_js_in_jsc_jit,
|
||||
_run_js_in_jsc_nojit,
|
||||
_run_js_in_node
|
||||
]:
|
||||
engines = range(len(react_paths))
|
||||
random.shuffle(engines)
|
||||
for i in engines:
|
||||
out = _measure_ssr_ms(engine, react_paths[i], 'pe', 'bench-pe-es5.js', True)
|
||||
files[i].write(out)
|
||||
sys.stderr.write(".")
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write("\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
for f in files:
|
||||
f.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(_main())
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
readdirSync,
|
||||
statSync,
|
||||
} = require('fs');
|
||||
const { join } = require('path');
|
||||
const runBenchmark = require('./benchmark');
|
||||
const {
|
||||
buildAllBundles,
|
||||
buildBenchmark,
|
||||
buildBenchmarkBundlesFromGitRepo,
|
||||
getMergeBaseFromLocalGitRepo,
|
||||
} = require('./build');
|
||||
const argv = require('minimist')(process.argv.slice(2));
|
||||
const chalk = require('chalk');
|
||||
const printResults = require('./stats');
|
||||
const serveBenchmark = require('./server');
|
||||
|
||||
function getBenchmarkNames() {
|
||||
return readdirSync(join(__dirname, 'benchmarks')).filter(
|
||||
file => statSync(join(__dirname, 'benchmarks', file)).isDirectory()
|
||||
);
|
||||
}
|
||||
|
||||
function wait(val) {
|
||||
return new Promise(resolve => setTimeout(resolve, val));
|
||||
}
|
||||
|
||||
const runRemote = argv.remote;
|
||||
const runLocal = argv.local;
|
||||
const benchmarkFilter = argv.benchmark;
|
||||
const headless = argv.headless;
|
||||
const skipBuild = argv['skip-build'];
|
||||
|
||||
async function runBenchmarks(reactPath) {
|
||||
const benchmarkNames = getBenchmarkNames();
|
||||
const results = {};
|
||||
const server = serveBenchmark();
|
||||
await wait(1000);
|
||||
|
||||
for (let i = 0; i < benchmarkNames.length; i++) {
|
||||
const benchmarkName = benchmarkNames[i];
|
||||
|
||||
if (
|
||||
!benchmarkFilter
|
||||
||
|
||||
(benchmarkFilter && benchmarkName.indexOf(benchmarkFilter) !== -1)
|
||||
) {
|
||||
console.log(chalk.gray(`- Building benchmark "${chalk.white(benchmarkName)}"...`));
|
||||
await buildBenchmark(reactPath, benchmarkName);
|
||||
console.log(chalk.gray(`- Running benchmark "${chalk.white(benchmarkName)}"...`));
|
||||
results[benchmarkName] = await runBenchmark(benchmarkName, headless);
|
||||
}
|
||||
}
|
||||
|
||||
server.close();
|
||||
// http-server.close() is async but they don't provide a callback..
|
||||
await wait(500);
|
||||
return results;
|
||||
}
|
||||
|
||||
// get the performance benchmark results
|
||||
// from remote master (default React repo)
|
||||
async function benchmarkRemoteMaster() {
|
||||
console.log(chalk.gray(`- Building React bundles...`));
|
||||
let commit = argv.remote;
|
||||
|
||||
if (!commit || typeof commit !== 'string') {
|
||||
commit = await getMergeBaseFromLocalGitRepo(join(__dirname, '..', '..'));
|
||||
console.log(chalk.gray(`- Merge base commit ${chalk.white(commit.tostrS())}`));
|
||||
}
|
||||
return {
|
||||
// we build the bundles from the React repo
|
||||
bundles: await buildBenchmarkBundlesFromGitRepo(
|
||||
commit,
|
||||
skipBuild
|
||||
),
|
||||
// we use these bundles to run the benchmarks
|
||||
benchmarks: await runBenchmarks(),
|
||||
};
|
||||
}
|
||||
|
||||
// get the performance benchmark results
|
||||
// of the local react repo
|
||||
async function benchmarkLocal(reactPath) {
|
||||
console.log(chalk.gray(`- Building React bundles...`));
|
||||
return {
|
||||
// we build the bundles from the React repo
|
||||
bundles: await buildAllBundles(reactPath, skipBuild),
|
||||
// we use these bundles to run the benchmarks
|
||||
benchmarks: await runBenchmarks(reactPath),
|
||||
};
|
||||
}
|
||||
|
||||
async function runLocalBenchmarks(showResults) {
|
||||
console.log(
|
||||
chalk.white.bold('Running benchmarks for ')
|
||||
+ chalk.green.bold('Local (Current Branch)')
|
||||
);
|
||||
const localResults = await benchmarkLocal(join(__dirname, '..', '..'));
|
||||
|
||||
if (showResults) {
|
||||
printResults(localResults, null);
|
||||
}
|
||||
return localResults;
|
||||
}
|
||||
|
||||
async function runRemoteBenchmarks(showResults) {
|
||||
console.log(
|
||||
chalk.white.bold('Running benchmarks for ')
|
||||
+ chalk.yellow.bold('Remote (Merge Base)')
|
||||
);
|
||||
const remoteMasterResults = await benchmarkRemoteMaster();
|
||||
|
||||
if (showResults) {
|
||||
printResults(null, remoteMasterResults);
|
||||
}
|
||||
return remoteMasterResults;
|
||||
}
|
||||
|
||||
async function compareLocalToMaster() {
|
||||
console.log(
|
||||
chalk.white.bold('Comparing ')
|
||||
+ chalk.green.bold('Local (Current Branch)')
|
||||
+ chalk.white.bold(' to ')
|
||||
+ chalk.yellow.bold('Remote (Merge Base)')
|
||||
);
|
||||
const localResults = await runLocalBenchmarks(false);
|
||||
const remoteMasterResults = await runRemoteBenchmarks(false);
|
||||
printResults(localResults, remoteMasterResults);
|
||||
}
|
||||
|
||||
if ((runLocal && runRemote) || (!runLocal && !runRemote)) {
|
||||
compareLocalToMaster().then(() => process.exit(0));
|
||||
} else if (runLocal) {
|
||||
runLocalBenchmarks(true).then(() => process.exit(0));
|
||||
} else if (runRemote) {
|
||||
runRemoteBenchmarks(true).then(() => process.exit(0));
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
const http2Server = require('http2');
|
||||
const httpServer = require('http-server');
|
||||
const {
|
||||
existsSync,
|
||||
statSync,
|
||||
createReadStream,
|
||||
} = require('fs');
|
||||
const { join } = require('path');
|
||||
const argv = require('minimist')(process.argv.slice(2));
|
||||
const mime = require('mime');
|
||||
|
||||
function sendFile(filename, response) {
|
||||
response.setHeader('Content-Type', mime.lookup(filename));
|
||||
response.writeHead(200);
|
||||
const fileStream = createReadStream(filename);
|
||||
fileStream.pipe(response);
|
||||
fileStream.on('finish', response.end);
|
||||
}
|
||||
|
||||
function createHTTP2Server(benchmark) {
|
||||
const server = http2Server.createServer({}, (request, response) => {
|
||||
const filename = join(__dirname, 'benchmarks', benchmark, request.url).replace(/\?.*/g, '');
|
||||
|
||||
if (existsSync(filename) && statSync(filename).isFile()) {
|
||||
sendFile(filename, response);
|
||||
} else {
|
||||
const indexHtmlPath = join(filename, 'index.html');
|
||||
|
||||
if (existsSync(indexHtmlPath)) {
|
||||
sendFile(indexHtmlPath, response);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
server.listen(8080);
|
||||
return server;
|
||||
}
|
||||
|
||||
function createHTTPServer() {
|
||||
const server = httpServer.createServer({
|
||||
root: join(__dirname, 'benchmarks'),
|
||||
robots: true,
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Credentials': 'true',
|
||||
},
|
||||
});
|
||||
server.listen(8080);
|
||||
return server;
|
||||
}
|
||||
|
||||
function serveBenchmark(benchmark, http2) {
|
||||
if (http2) {
|
||||
return createHTTP2Server(benchmark);
|
||||
} else {
|
||||
return createHTTPServer();
|
||||
}
|
||||
}
|
||||
|
||||
// if run directly via CLI
|
||||
if (require.main === module) {
|
||||
const benchmarkInput = argv._[0];
|
||||
|
||||
if (benchmarkInput) {
|
||||
serveBenchmark(benchmarkInput);
|
||||
} else {
|
||||
console.error('Please specifiy a benchmark directory to serve!');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = serveBenchmark;
|
||||
@@ -0,0 +1,163 @@
|
||||
'use strict';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const Table = require('cli-table');
|
||||
|
||||
function percentChange(prev, current, prevSem, currentSem) {
|
||||
const [mean, sd] = calculateMeanAndSdOfRatioFromDeltaMethod(
|
||||
prev,
|
||||
current,
|
||||
prevSem,
|
||||
currentSem
|
||||
);
|
||||
const pctChange = +(mean * 100).toFixed(1);
|
||||
const ci95 = +(100 * 1.96 * sd).toFixed(1);
|
||||
|
||||
const ciInfo = ci95 > 0 ? ` +- ${ci95} %` : '';
|
||||
const text = `${pctChange > 0 ? '+' : ''}${pctChange} %${ciInfo}`;
|
||||
if (pctChange + ci95 < 0) {
|
||||
return chalk.green(text);
|
||||
} else if (pctChange - ci95 > 0) {
|
||||
return chalk.red(text);
|
||||
} else {
|
||||
// Statistically insignificant.
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function calculateMeanAndSdOfRatioFromDeltaMethod(
|
||||
meanControl,
|
||||
meanTest,
|
||||
semControl,
|
||||
semTest
|
||||
) {
|
||||
const mean = (
|
||||
((meanTest - meanControl) / meanControl) -
|
||||
(Math.pow(semControl, 2) * meanTest / Math.pow(meanControl, 3))
|
||||
);
|
||||
const variance = (
|
||||
Math.pow(semTest / meanControl, 2) +
|
||||
(Math.pow(semControl * meanTest, 2) / Math.pow(meanControl, 4))
|
||||
);
|
||||
return [mean, Math.sqrt(variance)];
|
||||
}
|
||||
|
||||
function addBenchmarkResults(table, localResults, remoteMasterResults) {
|
||||
const benchmarks = Object.keys(
|
||||
(localResults && localResults.benchmarks) || (remoteMasterResults && remoteMasterResults.benchmarks)
|
||||
);
|
||||
benchmarks.forEach(benchmark => {
|
||||
const rowHeader = [chalk.white.bold(benchmark)];
|
||||
if (remoteMasterResults) {
|
||||
rowHeader.push(chalk.white.bold('Time'));
|
||||
}
|
||||
if (localResults) {
|
||||
rowHeader.push(chalk.white.bold('Time'));
|
||||
}
|
||||
if (localResults && remoteMasterResults) {
|
||||
rowHeader.push(chalk.white.bold('Diff'));
|
||||
}
|
||||
table.push(rowHeader);
|
||||
|
||||
const measurements = (
|
||||
(localResults && localResults.benchmarks[benchmark].averages)
|
||||
||
|
||||
(remoteMasterResults && remoteMasterResults.benchmarks[benchmark].averages)
|
||||
);
|
||||
measurements.forEach((measurement, i) => {
|
||||
const row = [
|
||||
chalk.gray(measurement.entry),
|
||||
];
|
||||
let remoteMean;
|
||||
let remoteSem;
|
||||
if (remoteMasterResults) {
|
||||
remoteMean = remoteMasterResults.benchmarks[benchmark].averages[i].mean;
|
||||
remoteSem = remoteMasterResults.benchmarks[benchmark].averages[i].sem;
|
||||
// https://en.wikipedia.org/wiki/1.96 gives a 99% confidence interval.
|
||||
const ci95 = remoteSem * 1.96;
|
||||
row.push(chalk.white(+remoteMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2)));
|
||||
}
|
||||
let localMean;
|
||||
let localSem;
|
||||
if (localResults) {
|
||||
localMean = localResults.benchmarks[benchmark].averages[i].mean;
|
||||
localSem = localResults.benchmarks[benchmark].averages[i].sem;
|
||||
const ci95 = localSem * 1.96;
|
||||
row.push(chalk.white(+localMean.toFixed(2) + ' ms +- ' + ci95.toFixed(2)));
|
||||
}
|
||||
if (localResults && remoteMasterResults) {
|
||||
row.push(percentChange(remoteMean, localMean, remoteSem, localSem));
|
||||
}
|
||||
table.push(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addBundleSizeComparions(table, localResults, remoteMasterResults) {
|
||||
const bundlesRowHeader = [chalk.white.bold('Bundles')];
|
||||
if (remoteMasterResults) {
|
||||
bundlesRowHeader.push(chalk.white.bold('Size'));
|
||||
}
|
||||
if (localResults) {
|
||||
bundlesRowHeader.push(chalk.white.bold('Size'));
|
||||
}
|
||||
if (localResults && remoteMasterResults) {
|
||||
bundlesRowHeader.push(chalk.white.bold('Diff'));
|
||||
}
|
||||
table.push(bundlesRowHeader);
|
||||
|
||||
const bundles = Object.keys(
|
||||
(localResults && localResults.bundles.bundleSizes)
|
||||
||
|
||||
(remoteMasterResults && remoteMasterResults.bundles.bundleSizes)
|
||||
);
|
||||
bundles.forEach(bundle => {
|
||||
const row = [
|
||||
chalk.gray(bundle),
|
||||
];
|
||||
let remoteSize = 0;
|
||||
if (remoteMasterResults) {
|
||||
const remoteBundle = remoteSize = remoteMasterResults.bundles.bundleSizes[bundle];
|
||||
|
||||
if (remoteBundle) {
|
||||
remoteSize = remoteSize.size;
|
||||
}
|
||||
row.push(chalk.white(remoteSize + ' kb'));
|
||||
}
|
||||
let localSize = 0;
|
||||
if (localResults) {
|
||||
const localBundle = localResults.bundles.bundleSizes[bundle];
|
||||
|
||||
if (localBundle) {
|
||||
localSize = localBundle.size;
|
||||
}
|
||||
localSize = localResults.bundles.bundleSizes[bundle].size;
|
||||
row.push(chalk.white(localSize + ' kb'));
|
||||
}
|
||||
if (localResults && remoteMasterResults) {
|
||||
row.push(percentChange(remoteSize, localSize, 0, 0));
|
||||
}
|
||||
table.push(row);
|
||||
});
|
||||
}
|
||||
|
||||
function printResults(localResults, remoteMasterResults) {
|
||||
const head = [''];
|
||||
if (remoteMasterResults) {
|
||||
head.push(chalk.yellow.bold('Remote (Merge Base)'));
|
||||
}
|
||||
if (localResults) {
|
||||
head.push(chalk.green.bold('Local (Current Branch)'));
|
||||
}
|
||||
if (localResults && remoteMasterResults) {
|
||||
head.push('');
|
||||
}
|
||||
const table = new Table({ head });
|
||||
|
||||
addBundleSizeComparions(table, localResults, remoteMasterResults);
|
||||
addBenchmarkResults(table, localResults, remoteMasterResults);
|
||||
|
||||
console.log(table.toString());
|
||||
}
|
||||
|
||||
module.exports = printResults;
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
npm run bench
|
||||
@@ -4,14 +4,6 @@ set -e
|
||||
|
||||
COMMANDS_TO_RUN=()
|
||||
|
||||
# We split these to be approximately equal chunks of four. As of this writing,
|
||||
# times were around:
|
||||
# - 3:30 test_coverage.sh
|
||||
# - 2:00 test_fiber.sh
|
||||
# - 1:15 test_html_generation.sh
|
||||
# - 1:15 grunt build
|
||||
# with everything else < 0:30.
|
||||
|
||||
if [ $((1 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
|
||||
COMMANDS_TO_RUN+=('./scripts/circleci/test_coverage.sh')
|
||||
fi
|
||||
@@ -33,6 +25,7 @@ if [ $((0 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
|
||||
COMMANDS_TO_RUN+=('./scripts/circleci/build.sh')
|
||||
COMMANDS_TO_RUN+=('./scripts/circleci/test_print_warnings.sh')
|
||||
COMMANDS_TO_RUN+=('./scripts/circleci/track_stats.sh')
|
||||
# COMMANDS_TO_RUN+=('./scripts/circleci/bench.sh')
|
||||
fi
|
||||
|
||||
RETURN_CODES=()
|
||||
|
||||
+31
-32
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"branch": "master",
|
||||
"bundleSizes": {
|
||||
"react.development.js (UMD_DEV)": {
|
||||
"size": 123496,
|
||||
@@ -10,20 +9,20 @@
|
||||
"gzip": 5824
|
||||
},
|
||||
"react-dom.development.js (UMD_DEV)": {
|
||||
"size": 591951,
|
||||
"gzip": 136503
|
||||
"size": 591916,
|
||||
"gzip": 136500
|
||||
},
|
||||
"react-dom.production.min.js (UMD_PROD)": {
|
||||
"size": 122170,
|
||||
"gzip": 38552
|
||||
"size": 122172,
|
||||
"gzip": 38559
|
||||
},
|
||||
"react-dom-server.development.js (UMD_DEV)": {
|
||||
"size": 499109,
|
||||
"gzip": 120706
|
||||
"size": 499074,
|
||||
"gzip": 120705
|
||||
},
|
||||
"react-dom-server.production.min.js (UMD_PROD)": {
|
||||
"size": 107574,
|
||||
"gzip": 33508
|
||||
"size": 107593,
|
||||
"gzip": 33517
|
||||
},
|
||||
"react-art.development.js (UMD_DEV)": {
|
||||
"size": 348302,
|
||||
@@ -46,56 +45,56 @@
|
||||
"gzip": 18181
|
||||
},
|
||||
"React-prod.js (FB_PROD)": {
|
||||
"size": 36483,
|
||||
"gzip": 9222
|
||||
"size": 36456,
|
||||
"gzip": 9216
|
||||
},
|
||||
"ReactDOMStack-dev.js (FB_DEV)": {
|
||||
"size": 494745,
|
||||
"gzip": 117995
|
||||
"size": 494710,
|
||||
"gzip": 117992
|
||||
},
|
||||
"ReactDOMStack-prod.js (FB_PROD)": {
|
||||
"size": 353177,
|
||||
"size": 353169,
|
||||
"gzip": 84692
|
||||
},
|
||||
"react-dom.development.js (NODE_DEV)": {
|
||||
"size": 549527,
|
||||
"gzip": 126729
|
||||
"size": 549492,
|
||||
"gzip": 126720
|
||||
},
|
||||
"react-dom.production.min.js (NODE_PROD)": {
|
||||
"size": 118379,
|
||||
"gzip": 37197
|
||||
"size": 118381,
|
||||
"gzip": 37204
|
||||
},
|
||||
"ReactDOMFiber-dev.js (FB_DEV)": {
|
||||
"size": 550449,
|
||||
"gzip": 127235
|
||||
"size": 550414,
|
||||
"gzip": 127232
|
||||
},
|
||||
"ReactDOMFiber-prod.js (FB_PROD)": {
|
||||
"size": 410322,
|
||||
"gzip": 94075
|
||||
"size": 410323,
|
||||
"gzip": 94077
|
||||
},
|
||||
"react-dom-server.development.js (NODE_DEV)": {
|
||||
"size": 447720,
|
||||
"gzip": 108154
|
||||
"size": 447685,
|
||||
"gzip": 108152
|
||||
},
|
||||
"react-dom-server.production.min.js (NODE_PROD)": {
|
||||
"size": 101947,
|
||||
"gzip": 31462
|
||||
"size": 101966,
|
||||
"gzip": 31470
|
||||
},
|
||||
"ReactDOMServerStack-dev.js (FB_DEV)": {
|
||||
"size": 446406,
|
||||
"gzip": 108005
|
||||
"size": 446371,
|
||||
"gzip": 108003
|
||||
},
|
||||
"ReactDOMServerStack-prod.js (FB_PROD)": {
|
||||
"size": 334132,
|
||||
"gzip": 80393
|
||||
"size": 334124,
|
||||
"gzip": 80394
|
||||
},
|
||||
"ReactARTStack-dev.js (FB_DEV)": {
|
||||
"size": 143166,
|
||||
"gzip": 32816
|
||||
},
|
||||
"ReactARTStack-prod.js (FB_PROD)": {
|
||||
"size": 101209,
|
||||
"gzip": 23045
|
||||
"size": 101182,
|
||||
"gzip": 23039
|
||||
},
|
||||
"react-art.development.js (NODE_DEV)": {
|
||||
"size": 269626,
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
const Table = require('cli-table');
|
||||
const filesize = require('filesize');
|
||||
const branch = require('git-branch');
|
||||
const chalk = require('chalk');
|
||||
const join = require('path').join;
|
||||
const fs = require('fs');
|
||||
const prevBuildResults = require('./results.json');
|
||||
|
||||
const currentBuildResults = {
|
||||
branch: branch.sync(),
|
||||
// Mutated during the build.
|
||||
bundleSizes: Object.assign({}, prevBuildResults.bundleSizes),
|
||||
};
|
||||
@@ -65,10 +63,7 @@ function printResults() {
|
||||
percentChange(prevGzip, gzip),
|
||||
]);
|
||||
});
|
||||
return (
|
||||
table.toString() +
|
||||
`\n\nThe difference was compared to the last build on "${chalk.green.bold(prevBuildResults.branch)}" branch.\n`
|
||||
);
|
||||
return table.toString();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user