├── .babelrc
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .huskyrc
├── .lintstagedrc
├── .prettierignore
├── LICENSE
├── README.md
├── benchmark-client
├── browser.json
├── client.js
├── components
│ ├── app
│ │ ├── component.js
│ │ └── index.marko
│ └── mount-container
│ │ ├── index.marko
│ │ └── style.css
├── createRoute.js
├── helpers.js
└── page.marko
├── benchmark-server
└── run.js
├── benchmarks.js
├── benchmarks
├── color-picker
│ ├── client.js
│ ├── colors.json
│ ├── createRoute.js
│ ├── inferno
│ │ ├── .babelrc
│ │ ├── client.jsx
│ │ ├── components
│ │ │ └── App.jsx
│ │ ├── page.marko
│ │ ├── rollup.config.js
│ │ ├── server.jsx
│ │ └── util
│ │ │ └── serverRender.jsx
│ ├── marko
│ │ ├── client.js
│ │ ├── components
│ │ │ └── app
│ │ │ │ └── index.marko
│ │ ├── page.marko
│ │ ├── rollup.config.js
│ │ └── server.js
│ ├── page.marko
│ ├── preact
│ │ ├── .babelrc
│ │ ├── client.jsx
│ │ ├── components
│ │ │ └── App.jsx
│ │ ├── page.marko
│ │ ├── rollup.config.js
│ │ ├── server.jsx
│ │ └── util
│ │ │ └── serverRender.jsx
│ ├── react
│ │ ├── .babelrc
│ │ ├── client.jsx
│ │ ├── components
│ │ │ └── App.jsx
│ │ ├── page.marko
│ │ ├── rollup.config.js
│ │ ├── server.jsx
│ │ └── util
│ │ │ └── serverRender.jsx
│ ├── server.js
│ └── vue
│ │ ├── .babelrc
│ │ ├── client.jsx
│ │ ├── components
│ │ ├── App.jsx
│ │ └── App.vue
│ │ ├── page.marko
│ │ ├── rollup.config.js
│ │ ├── server.jsx
│ │ ├── util
│ │ └── serverRender.jsx
│ │ └── webpack.config.js
└── search-results
│ ├── client.js
│ ├── createRoute.js
│ ├── inferno
│ ├── .babelrc
│ ├── client.jsx
│ ├── components
│ │ ├── App.jsx
│ │ ├── Footer.jsx
│ │ └── SearchResultsItem.jsx
│ ├── page.marko
│ ├── rollup.config.js
│ ├── server.jsx
│ └── util
│ │ └── serverRender.jsx
│ ├── marko
│ ├── client.js
│ ├── components
│ │ ├── app-footer
│ │ │ └── index.marko
│ │ ├── app-search-results-item
│ │ │ └── index.marko
│ │ └── app
│ │ │ └── index.marko
│ ├── page.marko
│ ├── rollup.config.js
│ └── server.js
│ ├── page.marko
│ ├── preact
│ ├── .babelrc
│ ├── client.jsx
│ ├── components
│ │ ├── App.jsx
│ │ ├── Footer.jsx
│ │ └── SearchResultsItem.jsx
│ ├── page.marko
│ ├── rollup.config.js
│ ├── server.jsx
│ └── util
│ │ └── serverRender.jsx
│ ├── react
│ ├── .babelrc
│ ├── client.jsx
│ ├── components
│ │ ├── App.jsx
│ │ ├── Footer.jsx
│ │ └── SearchResultsItem.jsx
│ ├── page.marko
│ ├── rollup.config.js
│ ├── server.jsx
│ └── util
│ │ └── serverRender.jsx
│ ├── server.js
│ ├── util
│ ├── search-results-data.json
│ └── search.js
│ └── vue
│ ├── .babelrc
│ ├── client.jsx
│ ├── components
│ ├── App.jsx
│ ├── App.vue
│ ├── Footer.jsx
│ ├── Footer.vue
│ ├── SearchResultsItem.jsx
│ └── SearchResultsItem.vue
│ ├── page.marko
│ ├── rollup.config.js
│ ├── server.jsx
│ ├── util
│ └── serverRender.jsx
│ └── webpack.config.js
├── eslintrc.json
├── index.marko
├── init.js
├── package-lock.json
├── package.json
├── scripts
├── bundle.js
├── minify.js
├── publish.js
├── rollup.js
└── static.js
├── server.js
├── static
├── images
│ ├── test-image-01.jpg
│ ├── test-image-02.jpg
│ ├── test-image-03.jpg
│ ├── test-image-04.jpg
│ ├── test-image-05.jpg
│ └── test-image-06.jpg
└── styles
│ ├── color-picker.css
│ └── search-results.css
└── util
└── runBenchmark.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "loose": true,
7 | "modules": false,
8 | "targets": {
9 | "node": "current",
10 | "browsers": "Last 1 Chrome version, Last 1 Safari version, Last 1 Firefox version"
11 | }
12 | }
13 | ]
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | package.json
3 | package-lock.json
4 | node_modules
5 | build
6 | *.marko.js
7 | App.server.js
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": true,
5 | "es6": true
6 | },
7 | "extends": ["eslint:recommended", "prettier"],
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "jsx": true
11 | },
12 | "ecmaVersion": 2018,
13 | "sourceType": "module"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.marko.js
2 | /node_modules
3 | /build
4 | /.cache
5 | .DS_Store
6 | npm-debug.log
7 | .idea
8 | /__publish
9 | App.server.js
10 |
--------------------------------------------------------------------------------
/.huskyrc:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": {
3 | "pre-commit": "lint-staged"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.js": ["eslint --fix", "prettier --write", "git add"],
3 | "*.{json,md}": ["prettier --write", "git add"]
4 | }
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | package.json
3 | package-lock.json
4 | node_modules
5 | build
6 | *.marko.js
7 | App.server.js
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 JS Foundation and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # isomorphic-ui-benchmarks
2 |
3 | This repo includes multiple benchmarks for various UI libraries. Each benchmark is designed to measure rendering performance (on the server and in the browser) and the time that it takes to update the DOM (client-side only).
4 |
5 | # Current results
6 |
7 | ## Search results
8 |
9 |
10 |
11 | ## Color picker
12 |
13 |
14 |
15 | ---
16 |
17 | Below are the results of a run on October 21, 2019
18 |
19 | Environment:
20 |
21 | - Node.js v10.16.1
22 | - MacBook Pro (13-inch, 2017)
23 | - Processor: 3.5 GHz Intel Core i7
24 | - Memory: 16 GB 2133 MHz LPDDR3
25 | - macOS Mojave: 10.14.6
26 | - Google Chrome Version 77.0.3865.120 (Official Build) (64-bit)
27 |
28 | ## Server-side
29 |
30 | ```
31 | Warming up...
32 |
33 | Warmup complete.
34 |
35 | Running "search-results"...
36 |
37 | Running benchmark "marko"...
38 |
39 | marko x 6,399 ops/sec ±2.71% (84 runs sampled)
40 |
41 | Running benchmark "preact"...
42 |
43 | preact x 746 ops/sec ±2.88% (81 runs sampled)
44 |
45 | Running benchmark "react"...
46 |
47 | react x 765 ops/sec ±5.02% (72 runs sampled)
48 |
49 | Running benchmark "vue"...
50 |
51 | vue x 2,657 ops/sec ±4.41% (60 runs sampled)
52 |
53 | Running benchmark "inferno"...
54 |
55 | inferno x 3,014 ops/sec ±1.78% (87 runs sampled)
56 |
57 | Fastest is marko
58 |
59 | --------------
60 |
61 |
62 | Warming up...
63 |
64 | Warmup complete.
65 |
66 | Running "color-picker"...
67 |
68 | Running benchmark "marko"...
69 |
70 | marko x 24,540 ops/sec ±1.48% (86 runs sampled)
71 |
72 | Running benchmark "preact"...
73 |
74 | preact x 4,587 ops/sec ±1.81% (85 runs sampled)
75 |
76 | Running benchmark "react"...
77 |
78 | react x 4,300 ops/sec ±4.72% (72 runs sampled)
79 |
80 | Running benchmark "vue"...
81 |
82 | vue x 9,120 ops/sec ±5.56% (70 runs sampled)
83 |
84 | Running benchmark "inferno"...
85 |
86 | inferno x 21,453 ops/sec ±2.12% (84 runs sampled)
87 |
88 | Fastest is marko
89 |
90 | --------------
91 |
92 |
93 | DONE!
94 |
95 | ~/marko-js/isomorphic-ui-benchmarks (master)> node -v
96 | v10.16.1
97 | ```
98 |
99 | # Client-side
100 |
101 | ## Search results
102 |
103 | ### Google Chrome
104 |
105 | ```
106 | Warming up...
107 | Warmup complete.
108 | Running "search-results"...
109 | Running benchmark "marko"...
110 | marko x 175 ops/sec ±1.84% (53 runs sampled)
111 | Running benchmark "preact"...
112 | preact x 132 ops/sec ±1.66% (48 runs sampled)
113 | Running benchmark "react"...
114 | react x 210 ops/sec ±1.36% (53 runs sampled)
115 | Running benchmark "vue"...
116 | vue x 142 ops/sec ±1.31% (52 runs sampled)
117 | Running benchmark "inferno"...
118 | inferno x 239 ops/sec ±1.24% (55 runs sampled)
119 | Fastest is inferno
120 | ```
121 |
122 | ## Color picker
123 |
124 | ### Google Chrome
125 |
126 | ```
127 | Warming up...
128 | Warmup complete.
129 | Running "color-picker"...
130 | Running benchmark "marko"...
131 | marko x 6,008 ops/sec ±1.66% (34 runs sampled)
132 | Running benchmark "preact"...
133 | preact x 6,435 ops/sec ±0.96% (59 runs sampled)
134 | Running benchmark "react"...
135 | react x 7,358 ops/sec ±1.43% (58 runs sampled)
136 | Running benchmark "vue"...
137 | vue x 4,291 ops/sec ±1.96% (55 runs sampled)
138 | Running benchmark "inferno"...
139 | inferno x 17,078 ops/sec ±2.17% (60 runs sampled)
140 | Fastest is inferno
141 | ```
142 |
143 | # Additional details
144 |
145 | ## Included libraries
146 |
147 | The following UI libraries are currently included:
148 |
149 | - [inferno](https://github.com/infernojs/inferno)
150 | - [marko](https://github.com/marko-js/marko)
151 | - [preact](https://github.com/developit/preact)
152 | - [react](https://github.com/facebook/react)
153 | - [vue](https://github.com/vuejs/vue)
154 |
155 | ## Included benchmarks
156 |
157 | This repo currently includes the following benchmarks
158 |
159 | ### Search Results
160 |
161 | This benchmark measures the time it takes to render pages of search results. Each page includes 100 search result items. Every iteration renders an entirely new set of search results. As a result of rendering new search results for every cycle, a significant number of DOM nodes must be updated.
162 |
163 | ### Color Picker
164 |
165 | This benchmark measures the time it takes to cycle through a selected color. The selected color index changes every cycle. When the selected color index changes two things happen:
166 |
167 | - The new selected color is highlighted
168 | - The old selected color is unhighlighted
169 | - The selected color is shown at the end
170 |
171 | Compared to the search results benchmark, there are a relatively small number of changes to the DOM for every cycle.
172 |
173 | # Running the benchmarks
174 |
175 | ## Install
176 |
177 | ```bash
178 | git clone https://github.com/marko-js/isomorphic-ui-benchmarks.git
179 | cd isomorphic-ui-benchmarks
180 | npm install
181 | npm run build # Build client-side JS bundles
182 | ```
183 |
184 | ## Run server-side benchmarks
185 |
186 | ```bash
187 | npm run benchmark
188 | ```
189 |
190 | ## Run client-side benchmarks
191 |
192 | Start
193 |
194 | ```bash
195 | npm start
196 | ```
197 |
198 | Open [http://localhost:8080/](http://localhost:8080/) in your browser and choose a benchmark to run.
199 |
200 | # Contributions and Feedback
201 |
202 | If you see any problems or have any suggestions please let us know. Every effort was made to be as fair and accurate as possible, but mistakes do happen. If you find a problem please open a Github issue to discuss.
203 |
--------------------------------------------------------------------------------
/benchmark-client/browser.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | "lodash/lodash.js",
4 | "benchmark/benchmark.js",
5 | "require: ./components/app"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/benchmark-client/client.js:
--------------------------------------------------------------------------------
1 | var helpers = require("./helpers");
2 |
3 | function addBench(libName, factoryFunc) {
4 | var benchmark = exports.benchmark;
5 | var bench = benchmark.createBench(libName, factoryFunc);
6 | benchmark.benches[libName] = bench;
7 | }
8 |
9 | function registerBenchmark(factoryFunc) {
10 | var benchmark = factoryFunc(helpers);
11 | benchmark.benches = {};
12 | exports.benchmark = benchmark;
13 | }
14 |
15 | if (typeof window !== "undefined") {
16 | window.addBench = addBench;
17 | window.registerBenchmark = registerBenchmark;
18 | window.onMount = function() {};
19 | }
20 |
--------------------------------------------------------------------------------
/benchmark-client/components/app/component.js:
--------------------------------------------------------------------------------
1 | var runBenchmark = require("../../../util/runBenchmark");
2 | var client = require("../../client");
3 |
4 | var Benchmark = typeof window !== "undefined" && window.Benchmark;
5 |
6 | module.exports = {
7 | onInput: function(input) {
8 | this.state = {
9 | running: false,
10 | benchmarkName: input.benchmark.name
11 | };
12 | },
13 |
14 | handleBenchmarkButtonClick: function(_, el) {
15 | if (this.state.running) {
16 | return;
17 | }
18 |
19 | var benchmarkName = this.state.benchmarkName;
20 |
21 | var oldButtonLabel = el.innerHTML;
22 | el.innerHTML = oldButtonLabel + " - running...";
23 |
24 | var resultsEl = this.getEl("results");
25 | resultsEl.innerHTML = "";
26 |
27 | var self = this;
28 |
29 | runBenchmark(benchmarkName, client.benchmark, Benchmark)
30 | .on("start", function() {
31 | resultsEl.innerHTML += 'Running "' + benchmarkName + '"...\n';
32 | })
33 | .on("startBench", function(bench) {
34 | resultsEl.innerHTML += 'Running benchmark "' + bench.name + '"...\n';
35 | })
36 | .on("warmup", function() {
37 | resultsEl.innerHTML += "Warming up...\n";
38 | })
39 | .on("warmupComplete", function() {
40 | resultsEl.innerHTML += "Warmup complete.\n";
41 | })
42 | .on("cycle", function(event) {
43 | resultsEl.innerHTML += event.resultsString + "\n";
44 | })
45 | .on("complete", function(event) {
46 | resultsEl.innerHTML += event.resultsString + "\n";
47 | el.innerHTML = oldButtonLabel;
48 | self.running = false;
49 | })
50 | .run()
51 | .catch(function(err) {
52 | resultsEl.innerHTML = err.stack || err;
53 | console.error("ERROR:", err.stack || err);
54 | });
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/benchmark-client/components/app/index.marko:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/benchmark-client/components/mount-container/index.marko:
--------------------------------------------------------------------------------
1 | class {
2 |
3 | }
4 |
5 |
6 | ${data.libName}
7 |
11 |
--------------------------------------------------------------------------------
/benchmark-client/components/mount-container/style.css:
--------------------------------------------------------------------------------
1 | .mount-container {
2 | height: 600px;
3 | width: 300px;
4 | overflow: scroll;
5 | display: inline-block;
6 | }
--------------------------------------------------------------------------------
/benchmark-client/createRoute.js:
--------------------------------------------------------------------------------
1 | var template = require("./page.marko");
2 |
3 | var isProduction = process.env.NODE_ENV === "production";
4 |
5 | function createRoute(benchmark) {
6 | var bundles = [];
7 | benchmark.benches.forEach(bench => {
8 | bundles.push(
9 | `${process.env.URL_PREFIX || ""}/bundles/${benchmark.name}/${bench.name}${
10 | isProduction ? ".min" : ""
11 | }.js`
12 | );
13 | });
14 |
15 | return function(req, res) {
16 | res.marko(template, {
17 | $global: {
18 | benchmark
19 | },
20 | bundles
21 | });
22 | };
23 | }
24 |
25 | module.exports = createRoute;
26 |
--------------------------------------------------------------------------------
/benchmark-client/helpers.js:
--------------------------------------------------------------------------------
1 | var mountContainer = require("./components/mount-container");
2 |
3 | var mountEls = {};
4 |
5 | function createMountEl(libName) {
6 | var key = libName;
7 | var mountedComponent = mountContainer
8 | .renderSync({
9 | libName: libName
10 | })
11 | .appendTo(document.getElementById("mount"))
12 | .getComponent();
13 |
14 | mountEls[key] = mountedComponent.el;
15 |
16 | return mountedComponent.getEl("output");
17 | }
18 |
19 | function showSingleMountEl(libName) {
20 | var key = libName;
21 |
22 | for (var curKey in mountEls) {
23 | var mountEl = mountEls[curKey];
24 | if (curKey === key) {
25 | mountEl.style.display = "inline-block";
26 | } else {
27 | mountEl.style.display = "none";
28 | }
29 | }
30 | }
31 |
32 | function showMountEl(libName) {
33 | var key = libName;
34 |
35 | var mountEl = mountEls[key];
36 | mountEl.style.display = "inline-block";
37 | }
38 |
39 | exports.createMountEl = createMountEl;
40 | exports.showSingleMountEl = showSingleMountEl;
41 | exports.showMountEl = showMountEl;
42 |
--------------------------------------------------------------------------------
/benchmark-client/page.marko:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import path from 'path'
3 |
4 | $ var dependencies = [
5 | require.resolve("./browser.json"),
6 | "require-run: " + require.resolve("./client"),
7 | "require-run: " +
8 | require.resolve(
9 | "../benchmarks/" + out.global.benchmark.name + "/client.js"
10 | )
11 | ];
12 |
16 |
17 |
18 |
19 |
20 | ${out.global.benchmark.name} | Marko Benchmark
21 |
22 |
27 |
28 |
29 | ${out.global.benchmark.name} | Marko Benchmark
30 |
31 |
34 |
35 |
36 |
37 |
22 | @body>
23 | >
--------------------------------------------------------------------------------
/benchmarks/color-picker/inferno/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
12 | nodeResolvePlugin({
13 | mainFields: ["browser", "module", "jsnext", "main"],
14 | preferBuiltins: false,
15 | extensions: [".js", ".jsx"]
16 | }),
17 | commonjsPlugin({
18 | extensions: [".js", ".jsx"]
19 | })
20 | ],
21 | output: {
22 | name: "app",
23 | format: "iife",
24 | file: path.join(process.env.BUNDLES_DIR, "inferno.js")
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/inferno/server.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var createVNode = Inferno.createVNode;
3 | var InfernoServer = require("inferno-server");
4 | var App = require("./components/App");
5 |
6 | module.exports = function(colors) {
7 | return function benchFn() {
8 | var html = InfernoServer.renderToString();
9 |
10 | return html;
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/inferno/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var InfernoServer = require("inferno-server");
3 |
4 | module.exports = function infernoRender(App, colors) {
5 | return InfernoServer.renderToString();
6 | };
7 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/marko/client.js:
--------------------------------------------------------------------------------
1 | var app = require("./components/app");
2 | require("marko/components").init();
3 |
4 | window.addBench("marko", function(el, colors) {
5 | var component = app
6 | .renderSync({ colors: colors })
7 | .appendTo(el)
8 | .getComponent();
9 |
10 | var selectedColorIndex = 0;
11 |
12 | return function(done) {
13 | component.state.selectedColorIndex = ++selectedColorIndex % colors.length;
14 | component.update();
15 | done();
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/marko/components/app/index.marko:
--------------------------------------------------------------------------------
1 | class {
2 | onCreate() {
3 | this.state = {
4 | selectedColorIndex: 0
5 | };
6 | }
7 |
8 | onMount() {
9 | window.onMount();
10 | }
11 |
12 | handleColorClick(colorIndex) {
13 | this.state.selectedColorIndex = colorIndex;
14 | }
15 | }
16 |
17 | $ var colors = input.colors;
18 | $ var selectedColorIndex = state.selectedColorIndex;
19 | $ var selectedColor = colors[selectedColorIndex];
20 |
21 | Choose your favorite color:
22 |
23 |
24 |
25 |
26 | $ {
27 | var className = "color";
28 | if (selectedColorIndex === i) {
29 | className += " selected";
30 | }
31 | }
32 | -
36 | ${color.name}
37 |
38 |
39 |
40 |
41 |
42 | No colors!
43 |
44 |
45 |
46 | You chose:
47 |
${selectedColor.name}
48 |
49 |
50 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/marko/page.marko:
--------------------------------------------------------------------------------
1 | <${input.pageLayout}>
2 | <@body>
3 |
4 | @body>
5 | >
6 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/marko/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import marko from "@marko/rollup";
4 | import path from "path";
5 |
6 | export default {
7 | input: path.join(__dirname, "client.js"),
8 | output: {
9 | name: "app",
10 | format: "iife",
11 | file: path.join(process.env.BUNDLES_DIR, "marko.js")
12 | },
13 | plugins: [
14 | marko(),
15 | nodeResolvePlugin({
16 | mainFields: ["browser", "module", "jsnext", "main"],
17 | preferBuiltins: false,
18 | extensions: [".js", ".marko"]
19 | }),
20 | commonjsPlugin({
21 | extensions: [".js", ".marko"]
22 | })
23 | ]
24 | };
25 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/marko/server.js:
--------------------------------------------------------------------------------
1 | var app = require("./components/app");
2 |
3 | module.exports = function(colors) {
4 | return function benchFn() {
5 | var html = app.renderToString({
6 | colors: colors
7 | });
8 |
9 | return html;
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/page.marko:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${out.global.title} • Isomorphic UI Benchmarks
6 |
10 |
11 |
12 | ${out.global.title} • Isomorphic UI Benchmarks
13 |
14 | ${input.body}
15 |
16 | <${input.body}/>
17 |
18 |
28 |
22 | @body>
23 | >
24 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/preact/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | process.env.NODE_ENV = "production";
8 |
9 | export default {
10 | input: path.join(__dirname, "client.jsx"),
11 | plugins: [
12 | babelPlugin({ runtimeHelpers: true }),
13 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
14 | nodeResolvePlugin({
15 | mainFields: ["browser", "module", "jsnext", "main"],
16 | preferBuiltins: false,
17 | extensions: [".js", ".jsx"]
18 | }),
19 | commonjsPlugin({
20 | extensions: [".js", ".jsx"]
21 | })
22 | ],
23 | output: {
24 | name: "app",
25 | format: "iife",
26 | file: path.join(process.env.BUNDLES_DIR, "preact.js")
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/preact/server.jsx:
--------------------------------------------------------------------------------
1 | const preact = require("preact");
2 | const renderToString = require("preact-render-to-string");
3 | const App = require("./components/App");
4 |
5 | module.exports = function(colors) {
6 | return function benchFn() {
7 | var html = renderToString();
8 |
9 | return html;
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/preact/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | const preact = require("preact");
2 | const renderToString = require("preact-render-to-string");
3 |
4 | module.exports = function reactRender(App, colors) {
5 | return renderToString();
6 | };
7 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react"],
3 | "plugins": ["babel-plugin-transform-react-constant-elements"]
4 | }
5 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/client.jsx:
--------------------------------------------------------------------------------
1 | const React = require("react");
2 | const ReactDOM = require("react-dom");
3 | const App = require("./components/App");
4 |
5 | const mountNode = document.getElementById("mount");
6 |
7 | if (window.colors) {
8 | ReactDOM.hydrate(, mountNode);
9 |
10 | console.log("Re-rendering on client completed");
11 | }
12 |
13 | window.addBench("react", function(el, colors) {
14 | let setSelectedColorIndex;
15 | let selectedColorIndex = 0;
16 |
17 | ReactDOM.render(
18 | (setSelectedColorIndex = _set)} />,
19 | el
20 | );
21 |
22 | return done => {
23 | setSelectedColorIndex(++selectedColorIndex % colors.length);
24 | done();
25 | };
26 | });
27 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/components/App.jsx:
--------------------------------------------------------------------------------
1 | const React = require("react");
2 | const { useState, useEffect } = React;
3 |
4 | function App({ onMount, colors }) {
5 | const [selectedColorIndex, setSelectedColorIndex] = useState(0);
6 | const selectedColor = colors[selectedColorIndex];
7 | useEffect(() => {
8 | if (onMount) {
9 | onMount(setSelectedColorIndex);
10 | }
11 | window.onMount();
12 | }, []);
13 |
14 | return (
15 |
16 |
Choose your favorite color:
17 |
18 | {colors.length ? (
19 |
20 | {colors.map((color, i) => (
21 | - setSelectedColorIndex(i)}
30 | >
31 | {color.name}
32 |
33 | ))}
34 |
35 | ) : (
36 |
No colors!
37 | )}
38 |
39 |
40 | You chose:
41 |
{selectedColor.name}
42 |
43 |
44 | );
45 | }
46 |
47 | module.exports = App;
48 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/page.marko:
--------------------------------------------------------------------------------
1 | import serverRender from './util/serverRender'
2 | import App from './components/App'
3 |
4 | <${input.pageLayout}>
5 | <@body>
6 | $ var renderedHTML = serverRender(App, input.colors);
7 | $!{renderedHTML}
8 |
19 |
22 | @body>
23 | >
24 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
12 | nodeResolvePlugin({
13 | mainFields: ["browser", "module", "jsnext", "main"],
14 | preferBuiltins: false,
15 | extensions: [".js", ".jsx"]
16 | }),
17 | commonjsPlugin({
18 | extensions: [".js", ".jsx"]
19 | })
20 | ],
21 | output: {
22 | name: "app",
23 | format: "iife",
24 | file: path.join(process.env.BUNDLES_DIR, "react.js")
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/server.jsx:
--------------------------------------------------------------------------------
1 | const React = require("react");
2 | const ReactDOMServer = require("react-dom/server");
3 |
4 | const App = require("./components/App");
5 |
6 | module.exports = function(colors) {
7 | return function benchFn() {
8 | return ReactDOMServer.renderToString();
9 | };
10 | };
11 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/react/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | const React = require("react");
2 | const ReactDOMServer = require("react-dom/server");
3 |
4 | module.exports = function reactRender(App, colors) {
5 | return ReactDOMServer.renderToString();
6 | };
7 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | require("marko/node-require");
3 |
4 | var colors = require("./colors.json");
5 | module.exports = function(bench) {
6 | var serverFactory = bench.serverFactory;
7 | var fn = serverFactory(colors);
8 | return {
9 | fn
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@vue/babel-preset-jsx"]
3 | }
4 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/client.jsx:
--------------------------------------------------------------------------------
1 | var Vue = require("vue");
2 | var App = require("./components/App");
3 |
4 | var mountNode = document.getElementById("mount");
5 |
6 | if (window.colors) {
7 | var colors = window.colors;
8 | const app = new Vue({
9 | render(h) {
10 | return ;
11 | }
12 | });
13 |
14 | app.$mount("#mount");
15 |
16 | console.log("Re-rendering on client completed");
17 | }
18 |
19 | window.addBench("vue", function(el, colors) {
20 | var widget;
21 | var currentDone;
22 | var selectedColorIndex = 0;
23 |
24 | function onMount(instance) {
25 | widget = instance;
26 | }
27 |
28 | // function onUpdate() {
29 | // currentDone();
30 | // }
31 |
32 | const app = new Vue({
33 | data: {
34 | colors: colors
35 | },
36 |
37 | render(h) {
38 | return ;
39 | }
40 | });
41 |
42 | app.$mount(el);
43 |
44 | return function(done) {
45 | widget.selectedColorIndex = ++selectedColorIndex % colors.length;
46 | widget.$nextTick(done);
47 | };
48 | });
49 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/components/App.jsx:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var Vue = require("vue");
4 |
5 | module.exports = {
6 | mounted() {
7 | if (this.handleMount) {
8 | this.handleMount(this);
9 | }
10 | window.onMount();
11 | },
12 | updated() {
13 | if (this.handleUpdate) {
14 | this.handleUpdate(this);
15 | }
16 | },
17 | props: ["colors", "handleMount", "handleUpdate"],
18 | data: function() {
19 | return {
20 | selectedColorIndex: 0
21 | };
22 | },
23 | methods: {
24 | handleColorClick: function(colorIndex) {
25 | this.selectedColorIndex = colorIndex;
26 | }
27 | },
28 | render(h) {
29 | var colors = this.colors;
30 | var handleColorClick = this.handleColorClick;
31 | var selectedColorIndex = this.selectedColorIndex;
32 | var selectedColor = colors[selectedColorIndex];
33 | var self = this;
34 |
35 | function renderColor(color, i) {
36 | var style = {
37 | backgroundColor: color.hex
38 | };
39 |
40 | return (
41 |
47 | {color.name}
48 |
49 | );
50 | }
51 |
52 | function renderColors(h, colors) {
53 | if (colors.length) {
54 | return (
55 |
56 | {colors.map(function(color, i) {
57 | return renderColor(color, i);
58 | })}
59 |
60 | );
61 | } else {
62 | return No colors!
;
63 | }
64 | }
65 |
66 | return (
67 |
68 |
Choose your favorite color:
69 |
{renderColors(h, colors)}
70 |
71 | You chose:
72 |
{selectedColor.name}
73 |
74 |
75 | );
76 | }
77 | };
78 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/components/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Choose your favorite color:
4 |
5 |
6 | -
9 | {{ color.name }}
10 |
11 |
12 |
13 | No colors!
14 |
15 |
16 |
17 | You chose:
18 |
{{ colors[selectedColorIndex].name }}
19 |
20 |
21 |
22 |
23 |
49 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/page.marko:
--------------------------------------------------------------------------------
1 | import serverRender from './util/serverRender'
2 | import App from './components/App'
3 |
4 | <${input.pageLayout}>
5 | <@body>
6 |
7 | <${function(out) {
8 | serverRender(App, input, out);
9 | }}/>
10 |
11 |
14 | @body>
15 | >
16 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import babelPlugin from "rollup-plugin-babel";
4 | import path from "path";
5 | import replace from "rollup-plugin-replace";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | nodeResolvePlugin({
12 | mainFields: ["browser", "module", "jsnext", "main"],
13 | preferBuiltins: false,
14 | extensions: [".js", ".jsx"]
15 | }),
16 | // replace({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) })
17 | commonjsPlugin({
18 | include: ["node_modules/**", "**/*.js", "**/*.jsx"],
19 | extensions: [".js", ".jsx"]
20 | }),
21 | replace({
22 | "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
23 | "process.env.VUE_ENV": JSON.stringify("browser")
24 | })
25 | ],
26 | output: {
27 | name: "app",
28 | format: "iife",
29 | file: path.join(process.env.BUNDLES_DIR, "vue.js")
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/server.jsx:
--------------------------------------------------------------------------------
1 | const Vue = require("vue");
2 | const renderToString = require("vue-server-renderer").createRenderer()
3 | .renderToString;
4 |
5 | var App = require("./components/App.server").default;
6 |
7 | module.exports = function(colors) {
8 | return function benchFn(done) {
9 | const vm = new Vue({
10 | render(h) {
11 | return ;
12 | }
13 | });
14 |
15 | renderToString(vm, function(err, html) {
16 | if (err) {
17 | throw err;
18 | }
19 |
20 | // If we just call `done` without process.nextTick() then we get
21 | // "Maximum call stack size exceeded"
22 | // ...and it is only for the color-picker benchmark
23 | // ¯\_(ツ)_/¯
24 | process.nextTick(done);
25 |
26 | return html;
27 | });
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | const Vue = require("vue");
2 | const renderToString = require("vue-server-renderer").createRenderer()
3 | .renderToString;
4 |
5 | module.exports = function serverRender(App, input, out) {
6 | const vm = new Vue({
7 | render(h) {
8 | return ;
9 | }
10 | });
11 |
12 | var asyncOut = out.beginAsync();
13 |
14 | return renderToString(vm, function(err, html) {
15 | if (err) {
16 | throw err;
17 | }
18 |
19 | asyncOut.end(html);
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/benchmarks/color-picker/vue/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const VueLoaderPlugin = require("vue-loader/lib/plugin");
4 |
5 | module.exports = {
6 | mode: "production",
7 | entry: path.resolve(__dirname, "components/App.vue"),
8 | target: "node",
9 | output: {
10 | filename: "App.server.js",
11 | path: path.resolve(__dirname, "components"),
12 | libraryTarget: "commonjs2"
13 | },
14 | module: {
15 | rules: [{ test: /\.vue$/, use: "vue-loader" }]
16 | },
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | "process.env": {
20 | NODE_ENV: '"production"'
21 | }
22 | }),
23 | new VueLoaderPlugin()
24 | ]
25 | };
26 |
--------------------------------------------------------------------------------
/benchmarks/search-results/client.js:
--------------------------------------------------------------------------------
1 | var searchService = require("./util/search");
2 |
3 | window.registerBenchmark(function(helpers) {
4 | return {
5 | createBench: function(libName, factoryFunc) {
6 | var mountEl = helpers.createMountEl(libName);
7 | var pageIndex = 0;
8 |
9 | function getNextSearchResults() {
10 | return searchService.performSearch({ pageIndex: pageIndex++ });
11 | }
12 |
13 | var fn = factoryFunc(mountEl, getNextSearchResults);
14 |
15 | return {
16 | onWarmup: function() {
17 | pageIndex = 0;
18 | helpers.showMountEl(libName);
19 | },
20 | onStart: function() {
21 | pageIndex = 0;
22 | helpers.showSingleMountEl(libName);
23 | },
24 | fn
25 | };
26 | }
27 | };
28 | });
29 |
--------------------------------------------------------------------------------
/benchmarks/search-results/createRoute.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | require("marko/node-require");
3 |
4 | var searchService = require("./util/search");
5 | var pageLayoutTemplate = require("./page.marko");
6 |
7 | module.exports = function createRoute(libName, options) {
8 | var pageTemplate = require(`./${libName}/page.marko`);
9 |
10 | return function(req, res) {
11 | res.setHeader("Content-Type", "text/html; charset=utf-8");
12 |
13 | pageTemplate.render(
14 | {
15 | $global: {
16 | jsBundle: options.jsBundle,
17 | title: libName
18 | },
19 | pageLayout: pageLayoutTemplate,
20 | searchResults: searchService.performSearch({})
21 | },
22 | res
23 | );
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [["babel-plugin-inferno", { "imports": false }]]
3 | }
4 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/client.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var hydrate = require("inferno-hydrate").hydrate;
3 | var createVNode = Inferno.createVNode;
4 |
5 | var App = require("./components/App");
6 |
7 | var mountNode = document.getElementById("searchResultsMount");
8 |
9 | if (mountNode) {
10 | hydrate(, mountNode);
11 |
12 | console.log("Re-rendering on client completed");
13 | }
14 |
15 | window.addBench("inferno", function(el, getNextSearchResults) {
16 | Inferno.render(, el);
17 |
18 | return function(done) {
19 | Inferno.render(, el);
20 |
21 | done();
22 | };
23 | });
24 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/components/App.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var Component = Inferno.Component;
3 | var createVNode = Inferno.createVNode;
4 | var createComponentVNode = Inferno.createComponentVNode;
5 | var SearchResultsItem = require("./SearchResultsItem");
6 | var Footer = require("./Footer");
7 |
8 | module.exports = class extends Component {
9 | componentDidMount() {
10 | window.onMount();
11 | }
12 |
13 | render() {
14 | var searchResultsData = this.props.searchResultsData;
15 |
16 | return (
17 |
18 |
19 | {searchResultsData.items.map(function(item, i) {
20 | return ;
21 | })}
22 |
23 |
24 |
25 | );
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/components/SearchResultsItem.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var Component = Inferno.Component;
3 | var createVNode = Inferno.createVNode;
4 | var linkEvent = Inferno.linkEvent;
5 |
6 | function handleBuyButtonClick(instance, event) {
7 | instance.setState({ purchased: true });
8 | }
9 |
10 | module.exports = class extends Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.state = {
15 | purchased: false
16 | };
17 | }
18 |
19 | componentWillReceiveProps(props) {
20 | this.setState({
21 | purchased: false
22 | });
23 | }
24 |
25 | render() {
26 | var item = this.props.item;
27 | var style = { backgroundColor: this.state.purchased ? "#f1c40f" : "" };
28 |
29 | return (
30 |
31 |
{item.title}
32 |
33 |
40 |
41 |
{item.price}
42 |
43 | {this.state.purchased ? (
44 |
Purchased!
45 | ) : (
46 |
53 | )}
54 |
55 | );
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/page.marko:
--------------------------------------------------------------------------------
1 | import serverRender from './util/serverRender'
2 | import App from './components/App'
3 |
4 | <${input.pageLayout}>
5 | <@body>
6 | $ var renderedHTML = serverRender(App, input.searchResults);
7 | $!{renderedHTML}
8 |
19 |
22 | @body>
23 | >
24 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
12 | nodeResolvePlugin({
13 | mainFields: ["browser", "module", "jsnext", "main"],
14 | preferBuiltins: false,
15 | extensions: [".js", ".jsx"]
16 | }),
17 | commonjsPlugin({
18 | extensions: [".js", ".jsx"]
19 | })
20 | ],
21 | output: {
22 | name: "app",
23 | format: "iife",
24 | file: path.join(process.env.BUNDLES_DIR, "inferno.js")
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/server.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var createVNode = Inferno.createVNode;
3 | var InfernoServer = require("inferno-server");
4 | var App = require("./components/App");
5 |
6 | module.exports = function(getNextSearchResults) {
7 | return function benchFn() {
8 | var html = InfernoServer.renderToString(
9 |
10 | );
11 |
12 | return html;
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/benchmarks/search-results/inferno/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | var Inferno = require("inferno");
2 | var InfernoServer = require("inferno-server");
3 |
4 | module.exports = function infernoRender(App, searchResultsData) {
5 | return InfernoServer.renderToString(
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/client.js:
--------------------------------------------------------------------------------
1 | var app = require("./components/app");
2 | require("marko/components").init();
3 |
4 | window.addBench("marko", function(el, getNextSearchResults) {
5 | var component = app
6 | .renderSync(getNextSearchResults())
7 | .appendTo(el)
8 | .getComponent();
9 |
10 | return function(done) {
11 | component.input = getNextSearchResults();
12 | component.update();
13 | done();
14 | };
15 | });
16 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/components/app-footer/index.marko:
--------------------------------------------------------------------------------
1 |
511 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/components/app-search-results-item/index.marko:
--------------------------------------------------------------------------------
1 | class {
2 | onInput(input) {
3 | this.state = {
4 | purchased: false
5 | };
6 | }
7 |
8 | handleBuyButtonClick() {
9 | this.state.purchased = true;
10 | }
11 | }
12 |
13 |
18 |
${input.title}
19 |
26 |
${input.price}
27 |
28 | Purchased!
29 |
30 |
31 | Buy now!
32 |
33 |
34 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/components/app/index.marko:
--------------------------------------------------------------------------------
1 | class {
2 | onInput() {
3 | this.input = {};
4 | }
5 |
6 | onMount() {
7 | window.onMount();
8 | }
9 | }
10 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/page.marko:
--------------------------------------------------------------------------------
1 | <${input.pageLayout}>
2 | <@body>
3 |
4 |
7 | @body>
8 | >
9 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import marko from "@marko/rollup";
5 | import path from "path";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.js"),
9 | plugins: [
10 | marko(),
11 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
12 | nodeResolvePlugin({
13 | mainFields: ["browser", "module", "jsnext", "main"],
14 | preferBuiltins: false,
15 | extensions: [".js", ".marko"]
16 | }),
17 | commonjsPlugin({
18 | include: [],
19 | extensions: [".js", ".marko"]
20 | })
21 | ],
22 | output: {
23 | name: "app",
24 | format: "iife",
25 | file: path.join(process.env.BUNDLES_DIR, "marko.js")
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/benchmarks/search-results/marko/server.js:
--------------------------------------------------------------------------------
1 | var app = require("./components/app");
2 |
3 | module.exports = function(getNextSearchResults) {
4 | return function benchFn() {
5 | var html = app.renderToString(getNextSearchResults());
6 | return html;
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/benchmarks/search-results/page.marko:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${out.global.title} • Isomorphic UI Benchmarks
6 |
10 |
11 |
12 | ${out.global.title} • Isomorphic UI Benchmarks
13 |
14 | ${input.body}
15 |
16 | <${input.body}/>
17 |
18 |
28 |
22 | @body>
23 | >
24 |
--------------------------------------------------------------------------------
/benchmarks/search-results/preact/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | process.env.NODE_ENV = "production";
8 |
9 | export default {
10 | input: path.join(__dirname, "client.jsx"),
11 | plugins: [
12 | babelPlugin({ runtimeHelpers: true }),
13 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
14 | nodeResolvePlugin({
15 | mainFields: ["browser", "module", "jsnext", "main"],
16 | preferBuiltins: false,
17 | extensions: [".js", ".jsx"]
18 | }),
19 | commonjsPlugin({
20 | extensions: [".js", ".jsx"]
21 | })
22 | ],
23 | output: {
24 | name: "app",
25 | format: "iife",
26 | file: path.join(process.env.BUNDLES_DIR, "preact.js")
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/benchmarks/search-results/preact/server.jsx:
--------------------------------------------------------------------------------
1 | var preact = require("preact");
2 | var h = preact.h;
3 | var renderToString = require("preact-render-to-string");
4 |
5 | var App = require("./components/App");
6 |
7 | module.exports = function(getNextSearchResults) {
8 | return function benchFn() {
9 | var html = renderToString(
10 |
11 | );
12 |
13 | return html;
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/benchmarks/search-results/preact/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | var preact = require("preact");
2 | var h = preact.h;
3 | var renderToString = require("preact-render-to-string");
4 |
5 | module.exports = function reactRender(App, searchResultsData) {
6 | return renderToString();
7 | };
8 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react"],
3 | "plugins": ["babel-plugin-transform-react-constant-elements"]
4 | }
5 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/client.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var ReactDOM = require("react-dom");
3 |
4 | var App = require("./components/App");
5 |
6 | var mountNode = document.getElementById("searchResultsMount");
7 |
8 | if (mountNode) {
9 | ReactDOM.hydrate(
10 | ,
11 | mountNode
12 | );
13 |
14 | console.log("Re-rendering on client completed");
15 | }
16 |
17 | window.addBench("react", function(el, getNextSearchResults) {
18 | ReactDOM.render(, el);
19 |
20 | return function(done) {
21 | ReactDOM.render(
22 | ,
23 | el,
24 | done
25 | );
26 | };
27 | });
28 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/components/App.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var SearchResultsItem = require("./SearchResultsItem");
3 | var Footer = require("./Footer");
4 |
5 | module.exports = class extends React.Component {
6 | componentDidMount() {
7 | window.onMount();
8 | }
9 |
10 | render() {
11 | var searchResultsData = this.props.searchResultsData;
12 |
13 | return (
14 |
15 |
16 | {searchResultsData.items.map(function(item, i) {
17 | return ;
18 | })}
19 |
20 |
21 |
22 | );
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/components/SearchResultsItem.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 |
3 | module.exports = class extends React.Component {
4 | constructor(props) {
5 | super(props);
6 |
7 | this.state = {
8 | purchased: false,
9 | item: this.props.item
10 | };
11 |
12 | this.handleBuyButtonClick = this.handleBuyButtonClick.bind(this);
13 | }
14 |
15 | componentWillReceiveProps(props) {
16 | this.setState({
17 | purchased: false
18 | });
19 | }
20 |
21 | handleBuyButtonClick() {
22 | this.setState({ purchased: true });
23 | }
24 |
25 | render() {
26 | var item = this.props.item;
27 | var style = { backgroundColor: this.state.purchased ? "#f1c40f" : "" };
28 |
29 | return (
30 |
31 |
{item.title}
32 |
33 |
40 |
41 |
{item.price}
42 |
43 | {this.state.purchased ? (
44 |
Purchased!
45 | ) : (
46 |
53 | )}
54 |
55 | );
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/page.marko:
--------------------------------------------------------------------------------
1 | import serverRender from './util/serverRender'
2 | import App from './components/App'
3 |
4 | <${input.pageLayout}>
5 | <@body>
6 | $ var renderedHTML = serverRender(App, input.searchResults);
7 | $!{renderedHTML}
8 |
19 |
22 | @body>
23 | >
24 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import replace from "rollup-plugin-replace";
4 | import babelPlugin from "rollup-plugin-babel";
5 | import path from "path";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
12 | nodeResolvePlugin({
13 | mainFields: ["browser", "module", "jsnext", "main"],
14 | preferBuiltins: false,
15 | extensions: [".js", ".jsx"]
16 | }),
17 | commonjsPlugin({
18 | extensions: [".js", ".jsx"]
19 | })
20 | ],
21 | output: {
22 | name: "app",
23 | format: "iife",
24 | file: path.join(process.env.BUNDLES_DIR, "react.js")
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/server.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var ReactDOMServer = require("react-dom/server");
3 |
4 | var App = require("./components/App");
5 |
6 | module.exports = function(getNextSearchResults) {
7 | return function benchFn() {
8 | var html = ReactDOMServer.renderToString(
9 |
10 | );
11 |
12 | return html;
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/benchmarks/search-results/react/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | var React = require("react");
2 | var ReactDOMServer = require("react-dom/server");
3 |
4 | module.exports = function reactRender(App, searchResultsData) {
5 | return ReactDOMServer.renderToString(
6 |
7 | );
8 | };
9 |
--------------------------------------------------------------------------------
/benchmarks/search-results/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | require("marko/node-require");
3 |
4 | var searchService = require("./util/search");
5 |
6 | module.exports = function(bench) {
7 | var serverFactory = bench.serverFactory;
8 |
9 | var pageIndex = 0;
10 |
11 | function getNextSearchResults() {
12 | return searchService.performSearch({ pageIndex: pageIndex++ });
13 | }
14 |
15 | var fn = serverFactory(getNextSearchResults);
16 |
17 | return {
18 | onWarmup() {
19 | pageIndex = 0;
20 | },
21 | onStart() {
22 | pageIndex = 0;
23 | },
24 | fn
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/benchmarks/search-results/util/search.js:
--------------------------------------------------------------------------------
1 | var searchResultsData = require("./search-results-data.json");
2 |
3 | exports.performSearch = function(input) {
4 | var pageIndex = input.pageIndex || 0;
5 | var pageSize = 100;
6 | var start = pageIndex * pageSize;
7 |
8 | var items = [];
9 |
10 | for (var i = start; i < start + pageSize; i++) {
11 | items.push(searchResultsData.items[i % searchResultsData.items.length]);
12 | }
13 |
14 | var results = {
15 | pageIndex: pageIndex,
16 | totalMatches: searchResultsData.items.length,
17 | items: items
18 | };
19 |
20 | return results;
21 | };
22 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@vue/babel-preset-jsx"
4 | ]
5 | }
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/client.jsx:
--------------------------------------------------------------------------------
1 | var Vue = require("vue");
2 | var App = require("./components/App");
3 |
4 | var mountNode = document.getElementById("searchResultsMount");
5 |
6 | if (mountNode) {
7 | const app = new Vue({
8 | render(h) {
9 | return ;
10 | }
11 | });
12 |
13 | app.$mount("#searchResultsMount");
14 |
15 | console.log("Re-rendering on client completed");
16 | }
17 |
18 | window.addBench("vue", function(el, getNextSearchResults) {
19 | const app = new Vue({
20 | data: {
21 | searchResultsData: getNextSearchResults()
22 | },
23 |
24 | render(h) {
25 | var searchResultsData = this.searchResultsData;
26 |
27 | return ;
28 | },
29 |
30 | methods: {
31 | updateSearchResults(searchResultsData, done) {
32 | this.searchResultsData = searchResultsData;
33 | this.$nextTick(done);
34 | }
35 | }
36 | });
37 |
38 | app.$mount(el);
39 |
40 | return function(done) {
41 | app.updateSearchResults(getNextSearchResults(), done);
42 | };
43 | });
44 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/components/App.jsx:
--------------------------------------------------------------------------------
1 | var Vue = require("vue");
2 | var SearchResultsItem = require("./SearchResultsItem");
3 | var Footer = require("./Footer");
4 |
5 | module.exports = {
6 | mounted() {
7 | window.onMount();
8 | },
9 | props: ["searchResultsData"],
10 | render(h) {
11 | var searchResultsData = this.searchResultsData;
12 |
13 | return (
14 |
15 |
16 | {searchResultsData.items.map(function(item, i) {
17 | return ;
18 | })}
19 |
20 |
21 |
22 | );
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/components/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
22 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
478 |
479 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/components/SearchResultsItem.jsx:
--------------------------------------------------------------------------------
1 | var Vue = require("vue");
2 |
3 | module.exports = {
4 | methods: {
5 | handleBuyButtonClick: function(instance) {
6 | this.purchased = true;
7 | }
8 | },
9 | props: ["item", "purchased"],
10 | render(h) {
11 | var item = this.item;
12 | var purchased = this.purchased;
13 | var style = { backgroundColor: this.purchased ? "#f1c40f" : "" };
14 | var handleBuyButtonClick = this.handleBuyButtonClick;
15 |
16 | return (
17 |
18 |
{item.title}
19 |
20 |
27 |
28 |
{item.price}
29 |
30 | {purchased ? (
31 |
Purchased!
32 | ) : (
33 |
40 | )}
41 |
42 | );
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/components/SearchResultsItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
{{ item.title }}
5 |
6 |
13 |
14 |
{{ item.price }}
15 |
16 |
Purchased!
17 |
20 |
21 |
22 |
23 |
24 |
37 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/page.marko:
--------------------------------------------------------------------------------
1 | import serverRender from './util/serverRender'
2 | import App from './components/App'
3 |
4 | <${input.pageLayout}>
5 | <@body>
6 |
7 | <${function(out) {
8 | serverRender(App, input.searchResults, out);
9 | }}/>
10 |
11 |
14 | @body>
15 | >
16 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjsPlugin from "rollup-plugin-commonjs";
2 | import nodeResolvePlugin from "rollup-plugin-node-resolve";
3 | import babelPlugin from "rollup-plugin-babel";
4 | import path from "path";
5 | import replace from "rollup-plugin-replace";
6 |
7 | export default {
8 | input: path.join(__dirname, "client.jsx"),
9 | plugins: [
10 | babelPlugin({ runtimeHelpers: true }),
11 | nodeResolvePlugin({
12 | mainFields: ["browser", "module", "jsnext", "main"],
13 | preferBuiltins: false,
14 | extensions: [".js", ".jsx"]
15 | }),
16 | commonjsPlugin({
17 | extensions: [".js", ".jsx"]
18 | }),
19 | replace({
20 | "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
21 | "process.env.VUE_ENV": JSON.stringify("browser")
22 | })
23 | ],
24 | output: {
25 | name: "app",
26 | format: "iife",
27 | file: path.join(process.env.BUNDLES_DIR, "vue.js")
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/server.jsx:
--------------------------------------------------------------------------------
1 | const Vue = require("vue");
2 | const renderToString = require("vue-server-renderer").createRenderer()
3 | .renderToString;
4 |
5 | var App = require("./components/App.server").default;
6 |
7 | module.exports = function(getNextSearchResults) {
8 | return function benchFn(done) {
9 | const vm = new Vue({
10 | render(h) {
11 | return ;
12 | }
13 | });
14 |
15 | renderToString(vm, function(err, html) {
16 | if (err) {
17 | throw err;
18 | }
19 |
20 | done();
21 |
22 | return html;
23 | });
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/util/serverRender.jsx:
--------------------------------------------------------------------------------
1 | const Vue = require("vue");
2 | const renderToString = require("vue-server-renderer").createRenderer()
3 | .renderToString;
4 |
5 | module.exports = function serverRender(App, searchResultsData, out) {
6 | const vm = new Vue({
7 | render(h) {
8 | return ;
9 | }
10 | });
11 |
12 | var asyncOut = out.beginAsync();
13 |
14 | return renderToString(vm, function(err, html) {
15 | if (err) {
16 | throw err;
17 | }
18 |
19 | asyncOut.end(html);
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/benchmarks/search-results/vue/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const VueLoaderPlugin = require("vue-loader/lib/plugin");
4 |
5 | module.exports = {
6 | mode: "production",
7 | entry: path.resolve(__dirname, "components/App.vue"),
8 | target: "node",
9 | output: {
10 | filename: "App.server.js",
11 | path: path.resolve(__dirname, "components"),
12 | libraryTarget: "commonjs2"
13 | },
14 | module: {
15 | rules: [{ test: /\.vue$/, use: "vue-loader" }]
16 | },
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | "process.env": {
20 | NODE_ENV: '"production"'
21 | }
22 | }),
23 | new VueLoaderPlugin()
24 | ]
25 | };
26 |
--------------------------------------------------------------------------------
/eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended", "prettier"]
3 | }
4 |
--------------------------------------------------------------------------------
/index.marko:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Isomorphic UI Benchmarks
6 |
7 |
8 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/init.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 |
3 | if (!process.env.NODE_ENV) {
4 | process.env.NODE_ENV = "production";
5 | }
6 |
7 | require("lasso").configure({
8 | plugins: [
9 | {
10 | plugin: "lasso-marko",
11 | config: {
12 | output: "vdom"
13 | }
14 | }
15 | ],
16 | bundlingEnabled: false,
17 | minify: false, //isProduction ? true : false,
18 | fingerprintsEnabled: false,
19 | urlPrefix: (process.env.URL_PREFIX || "") + "/static",
20 | outputDir: path.join(__dirname, "build/static")
21 | });
22 |
23 | require("lasso/node-require-no-op").enable(".less", ".css");
24 | require("marko/express");
25 | require("@babel/register")({
26 | // and .js so you'll have to add them back if you want them to be used again.
27 | extensions: [".jsx"]
28 | });
29 |
30 | require("marko/node-require").install();
31 |
32 | require("marko/compiler").configure({
33 | assumeUpToDate: false
34 | });
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isomorphic-ui-benchmarks",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "./server.js",
6 | "scripts": {
7 | "test": "npm run benchmark",
8 | "start": "node server.js",
9 | "benchmark": "npm run bundle-vue-server && node --expose-gc benchmark-server/run.js",
10 | "build": "npm run bundle --silent && npm run minify --silent",
11 | "bundle": "node ./scripts/bundle.js",
12 | "static": "node ./scripts/static.js",
13 | "publish": "node ./scripts/publish.js",
14 | "minify": "node ./scripts/minify.js",
15 | "build-inferno": "npm run bundle-inferno --silent && node ./scripts/minify.js inferno",
16 | "build-marko": "npm run bundle-marko --silent && node ./scripts/minify.js marko",
17 | "build-vue": "npm run bundle-vue --silent && node ./scripts/minify.js vue",
18 | "bundle-inferno": "node ./scripts/rollup.js inferno",
19 | "bundle-marko": "node ./scripts/rollup.js marko",
20 | "bundle-react": "node ./scripts/rollup.js react",
21 | "bundle-preact": "node ./scripts/rollup.js preact",
22 | "bundle-vue": "npm run bundle-vue-server && node ./scripts/rollup.js vue",
23 | "bundle-vue-server": "webpack --config benchmarks/color-picker/vue/webpack.config.js && webpack --config benchmarks/search-results/vue/webpack.config.js",
24 | "format": "prettier \"**/*.{json,md,js,jsx}\" --write && eslint ."
25 | },
26 | "author": "Patrick Steele-Idem {
9 | benchmark.benches.forEach(bench => {
10 | var libName = bench.name;
11 | if (targetLib && libName !== targetLib) {
12 | return;
13 | }
14 |
15 | console.log("------");
16 | console.log(`Bundling ${libName}...`);
17 | execSync(`npm run bundle-${libName}`);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/scripts/minify.js:
--------------------------------------------------------------------------------
1 | console.log("Minifying JavaScript bundles...");
2 |
3 | require("../init");
4 |
5 | const fs = require("fs");
6 | const path = require("path");
7 | const zlib = require("zlib");
8 | const Terser = require("terser");
9 | const formatNumber = require("format-number")();
10 |
11 | function leftPad(str, padding) {
12 | if (str.length < padding) {
13 | str = new Array(padding - str.length).join(" ") + str;
14 | }
15 |
16 | return str;
17 | }
18 |
19 | function minifier(src, file) {
20 | try {
21 | var result = Terser.minify(src);
22 |
23 | if (result.error) {
24 | throw result.error;
25 | }
26 |
27 | return result.code;
28 | } catch (e) {
29 | if (e.line != null) {
30 | console.error(`Failed to minify ${file}`);
31 | console.error(` Location: ${file}:${e.line}:${e.col}`);
32 | console.error(` Message: ${e.message}`);
33 | process.exit(1);
34 | }
35 | throw e;
36 | }
37 | }
38 |
39 | var promiseChain = Promise.resolve();
40 |
41 | var benchmarks = require("../benchmarks");
42 | var targetLib = process.argv[2];
43 | var sizes = {};
44 |
45 | benchmarks.forEach(benchmark => {
46 | var benchmarkName = benchmark.name;
47 |
48 | benchmark.benches.forEach(bench => {
49 | var libName = bench.name;
50 | if (targetLib && libName !== targetLib) {
51 | return;
52 | }
53 |
54 | var inputFile = path.join(
55 | __dirname,
56 | `../build/bundles/${benchmarkName}/${libName}.js`
57 | );
58 |
59 | if (!fs.existsSync(inputFile)) {
60 | return;
61 | }
62 |
63 | var outputFile = path.join(
64 | __dirname,
65 | `../build/bundles/${benchmarkName}/${libName}.min.js`
66 | );
67 |
68 | console.log(`Minifying ${inputFile}...`);
69 |
70 | var src = fs.readFileSync(inputFile, { encoding: "utf8" });
71 |
72 | var minifiedSrc = minifier(src, inputFile);
73 |
74 | console.log(`Done minifying ${inputFile}`);
75 |
76 | fs.writeFileSync(outputFile, minifiedSrc, { encoding: "utf8" });
77 |
78 | var sizeInfo = (sizes[libName] = {});
79 |
80 | promiseChain = promiseChain.then(() => {
81 | return new Promise((resolve, reject) => {
82 | console.log(`Compressing and calculating size of ${outputFile}...`);
83 | zlib.gzip(minifiedSrc, function(err, gzippedBuffer) {
84 | if (err) {
85 | return reject(err);
86 | }
87 |
88 | // Compare the sizes
89 | var minifiedBuffer = new Buffer(minifiedSrc, "utf8");
90 | // console.log(nodePath.basename(templateInfo.outputCompileMinifiedFile) + ': ' + gzippedBuffer.length + ' bytes gzipped (' + minifiedBuffer.length + ' bytes uncompressed)');
91 |
92 | sizeInfo.gzipped = gzippedBuffer.length;
93 | sizeInfo.min = minifiedBuffer.length;
94 |
95 | var sizeFilename = libName + ".json";
96 |
97 | fs.writeFileSync(
98 | path.join(
99 | __dirname,
100 | `../build/bundles/${benchmarkName}`,
101 | sizeFilename
102 | ),
103 | JSON.stringify(sizeInfo, null, 4),
104 | { encoding: "utf8" }
105 | );
106 |
107 | resolve();
108 | });
109 | });
110 | });
111 | });
112 | });
113 |
114 | promiseChain.then(() => {
115 | console.log();
116 |
117 | for (var lib in sizes) {
118 | var sizeInfo = sizes[lib];
119 | console.log("[" + lib + "]");
120 | console.log(
121 | " gzip: " + leftPad(formatNumber(sizeInfo.gzipped), 8) + " bytes"
122 | );
123 | console.log(" min: " + leftPad(formatNumber(sizeInfo.min), 8) + " bytes");
124 | console.log();
125 | }
126 |
127 | console.log("Minification complete.");
128 | });
129 |
--------------------------------------------------------------------------------
/scripts/publish.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = "production";
2 |
3 | const path = require("path");
4 | const exec = require("child_process").execSync;
5 | const gitUrl = "git@github.com:marko-js/isomorphic-ui-benchmarks.git";
6 | const gitBranch = "gh-pages";
7 |
8 | const buildDir = path.join(__dirname, "../build");
9 | const publishDir = path.join(__dirname, "../__publish");
10 |
11 | process.chdir(path.join(__dirname, ".."));
12 |
13 | exec("markoc . --clean");
14 | exec(`rm -rf ${path.join(__dirname, "../.cache")}`);
15 | exec(`rm -rf ${publishDir}`);
16 | exec(`rm -rf ${buildDir}`);
17 |
18 | console.log("Building bundles...");
19 | exec("npm run build");
20 | console.log("Done.");
21 |
22 | console.log("Building static site...");
23 | exec('URL_PREFIX="/isomorphic-ui-benchmarks" npm run static');
24 | console.log("Done.");
25 |
26 | // create publish directory
27 | exec(`mkdir ${publishDir}`);
28 |
29 | // clone the repo that is the publish target
30 | exec(
31 | `cd ${publishDir} && git init && git remote add origin ${gitUrl} && git fetch`
32 | );
33 |
34 | // switch to the target branch
35 | try {
36 | exec(`cd ${publishDir} && git checkout -t origin/${gitBranch}`);
37 | } catch (e) {
38 | exec(`cd ${publishDir} && git checkout -b ${gitBranch}`);
39 | }
40 |
41 | // steal the .git directory
42 | exec(`rm -rf ${buildDir}/.git`);
43 |
44 | exec(`mv ${publishDir + "/.git"} ${buildDir}`);
45 | exec(`rm -rf ${publishDir}`);
46 |
47 | // commit and push up the changes
48 | try {
49 | exec(
50 | `cd ${buildDir} && git add . --all && git commit -m "updated static site"`
51 | );
52 | exec(`cd ${buildDir} && git push origin ${gitBranch}`);
53 | console.log(
54 | "Static site successfully built and pushed to remote repository."
55 | );
56 | } catch (e) {
57 | if (e.cmd && e.cmd.indexOf("git commit")) {
58 | console.log("Static site successfully built. No changes to push.");
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/scripts/rollup.js:
--------------------------------------------------------------------------------
1 | require("../init");
2 |
3 | var execSync = require("child_process").execSync;
4 | var path = require("path");
5 |
6 | var benchmarks = require("../benchmarks");
7 | var targetLib = process.argv[2];
8 |
9 | benchmarks.forEach(benchmark => {
10 | var benchmarkName = benchmark.name;
11 |
12 | benchmark.benches.forEach(bench => {
13 | var libName = bench.name;
14 | if (targetLib && libName !== targetLib) {
15 | return;
16 | }
17 |
18 | process.env.BUNDLES_DIR = path.join(
19 | __dirname,
20 | `../build/bundles/${benchmarkName}`
21 | );
22 | execSync(`rollup -c ${bench.dir}/rollup.config.js`);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/scripts/static.js:
--------------------------------------------------------------------------------
1 | require("../init");
2 |
3 | const fs = require("fs");
4 | const path = require("path");
5 | const exec = require("child_process").execSync;
6 | const benchmarks = require("../benchmarks");
7 | const mkdirp = require("mkdirp");
8 | const http = require("http");
9 | const buildDir = path.join(__dirname, "../build");
10 |
11 | var baseUrl;
12 | var routes = ["/"];
13 |
14 | benchmarks.forEach(benchmark => {
15 | var benchmarkName = benchmark.name;
16 |
17 | benchmark.benches.forEach(bench => {
18 | var libName = bench.name;
19 | routes.push(`/${benchmarkName}/${libName}`);
20 | routes.push(`/benchmark/${benchmarkName}`);
21 | });
22 | });
23 |
24 | function buildRoute(routePath) {
25 | return new Promise((resolve, reject) => {
26 | var outputFile = path.join(buildDir, routePath, "index.html");
27 | mkdirp.sync(path.dirname(outputFile));
28 |
29 | var url = `${baseUrl}${routePath}`;
30 |
31 | console.log(`Building page "${outputFile}"...`);
32 | console.log(`URL:`, url);
33 |
34 | http.get(url, res => {
35 | const statusCode = res.statusCode;
36 |
37 | console.log("Status code:", statusCode);
38 |
39 | if (statusCode !== 200) {
40 | return reject(new Error(`Request Failed. Status Code: ${statusCode}`));
41 | }
42 |
43 | res
44 | .pipe(fs.createWriteStream(outputFile))
45 | .on("error", reject)
46 | .on("finish", resolve);
47 | });
48 | });
49 | }
50 |
51 | require("../server")
52 | .then(port => {
53 | baseUrl = "http://localhost:" + port;
54 | })
55 | .then(() => {
56 | var buildRoutesPromise = Promise.resolve();
57 | routes.forEach(routePath => {
58 | buildRoutesPromise = buildRoutesPromise.then(() => {
59 | return buildRoute(routePath);
60 | });
61 | });
62 |
63 | return buildRoutesPromise;
64 | })
65 | .then(() => {
66 | exec(`cp -R ${__dirname + "/../static"} ${buildDir + "/"}`);
67 | })
68 | .then(() => {
69 | console.log("Build complete!");
70 | process.exit(0);
71 | })
72 | .catch(err => {
73 | console.error("Build failed!", err);
74 | process.exit(1);
75 | });
76 |
77 | //
78 | // require('./project').build().then((buildResult) => {
79 | // // create publish directory
80 | // exec(`mkdir ${publishDir}`);
81 | //
82 | // // clone the repo that is the publish target
83 | // exec(`cd ${publishDir} && git init && git remote add origin ${gitUrl} && git fetch`);
84 | //
85 | // // switch to the target branch
86 | // try {
87 | // exec(`cd ${publishDir} && git checkout -t origin/${gitBranch}`);
88 | // } catch(e) {
89 | // exec(`cd ${publishDir} && git checkout -b ${gitBranch}`);
90 | // }
91 | //
92 | // // steal the .git directory
93 | // exec(`mv ${publishDir+'/.git'} ${buildDir}`);
94 | // exec(`rm -rf ${publishDir}`);
95 | //
96 | // // commit and push up the changes
97 | // try {
98 | // exec(`cd ${buildDir} && git add . --all && git commit -m "updated static site"`);
99 | // exec(`cd ${buildDir} && git push origin ${gitBranch}`);
100 | // console.log('Static site successfully built and pushed to remote repository.');
101 | // } catch(e) {
102 | // if(e.cmd && e.cmd.indexOf('git commit')) {
103 | // console.log('Static site successfully built. No changes to push.');
104 | // }
105 | // }
106 | // });
107 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require("./init");
2 |
3 | var express = require("express");
4 | var serveStatic = require("serve-static");
5 | var path = require("path");
6 | var compression = require("compression");
7 | var benchmarks = require("./benchmarks");
8 | var benchmarkClientCreateRoute = require("./benchmark-client/createRoute");
9 | var indexTemplate = require("./index.marko");
10 |
11 | var isProduction = process.env.NODE_ENV === "production";
12 | var minify = isProduction;
13 |
14 | var urlPrefix = process.env.URL_PREFIX || "";
15 |
16 | var app = express();
17 | app.use(compression());
18 |
19 | benchmarks.forEach(benchmark => {
20 | var benchmarkName = benchmark.name;
21 |
22 | benchmark.benches.forEach(bench => {
23 | var libName = bench.name;
24 | var jsBundle = `${urlPrefix}/bundles/${benchmarkName}/${libName}${
25 | minify ? ".min" : ""
26 | }.js`;
27 |
28 | var routeOptions = {
29 | jsBundle: jsBundle
30 | };
31 |
32 | app.get(
33 | `/${benchmarkName}/${libName}`,
34 | benchmark.createRoute(libName, routeOptions)
35 | );
36 | app.get(
37 | `/benchmark/${benchmarkName}`,
38 | benchmarkClientCreateRoute(benchmark, routeOptions)
39 | );
40 | });
41 | });
42 |
43 | app.use("/static", serveStatic(path.join(__dirname, "static")));
44 | app.use(
45 | "/isomorphic-ui-benchmarks/static",
46 | serveStatic(path.join(__dirname, "static"))
47 | );
48 |
49 | app.use(require("lasso/middleware").serveStatic());
50 |
51 | // app.get('/react', require('./src/react/pages/search-results'));
52 | app.use("/bundles", serveStatic(path.join(__dirname, "build/bundles")));
53 | app.use(
54 | "/isomorphic-ui-benchmarks/bundles",
55 | serveStatic(path.join(__dirname, "build/bundles"))
56 | );
57 |
58 | app.get("/", function(req, res) {
59 | res.marko(indexTemplate, {
60 | benchmarks
61 | });
62 | });
63 | // app.get('/', require('./src/shared/pages/index'));
64 |
65 | var port = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080;
66 |
67 | module.exports = new Promise((resolve, reject) => {
68 | app.listen(port, function(err) {
69 | if (err) {
70 | return reject(err);
71 | }
72 |
73 | console.log("Listening on port " + port);
74 |
75 | if (process.send) {
76 | console.log("Server online");
77 | process.send("online"); // Let browser-refresh know we are ready to serve traffic
78 | }
79 |
80 | resolve(port);
81 | });
82 | }).catch(err => {
83 | console.error("Failed to start server:", err);
84 | process.exit(1);
85 | });
86 |
87 | process.on("SIGTERM", function() {
88 | process.exit(0);
89 | });
90 |
--------------------------------------------------------------------------------
/static/images/test-image-01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-01.jpg
--------------------------------------------------------------------------------
/static/images/test-image-02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-02.jpg
--------------------------------------------------------------------------------
/static/images/test-image-03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-03.jpg
--------------------------------------------------------------------------------
/static/images/test-image-04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-04.jpg
--------------------------------------------------------------------------------
/static/images/test-image-05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-05.jpg
--------------------------------------------------------------------------------
/static/images/test-image-06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/images/test-image-06.jpg
--------------------------------------------------------------------------------
/static/styles/color-picker.css:
--------------------------------------------------------------------------------
1 | .colors .color {
2 | box-sizing: border-box;
3 | border: 2px solid white;
4 | cursor: pointer;
5 | }
6 |
7 | .colors .color.selected {
8 | border: 2px solid black;
9 | }
10 | .colors ul {
11 | margin: 0;
12 | padding-left: 0;
13 | list-style: none;
14 | }
15 |
16 | .colors li {
17 | margin: 0;
18 | display: inline-block;
19 | width: 150px;
20 | height: 3em;
21 | text-align: center;
22 | line-height: 3em;
23 | vertical-align: middle;
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/static/styles/search-results.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansolid/isomorphic-ui-benchmarks/3bf71d8c192e6eb58c66708bfa49dfc926ae6749/static/styles/search-results.css
--------------------------------------------------------------------------------
/util/runBenchmark.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require("events").EventEmitter;
2 |
3 | function addListeners(eventEmitter, config) {
4 | Object.keys(config).forEach(function(name) {
5 | if (name.startsWith("on")) {
6 | var listener = config[name];
7 | var eventName = name.charAt(2).toLowerCase() + name.substring(3);
8 | eventEmitter.on(eventName, listener);
9 | }
10 | });
11 | }
12 |
13 | function delay(durationMillis) {
14 | return new Promise(function(resolve) {
15 | setTimeout(resolve, durationMillis);
16 | });
17 | }
18 |
19 | module.exports = function runBenchmark(name, options, Benchmark) {
20 | var Suite = Benchmark.Suite;
21 | var suite = new Suite(name);
22 |
23 | var benches = [];
24 |
25 | var suiteEvents = new EventEmitter();
26 | var userSetup;
27 |
28 | function load() {
29 | return Promise.resolve()
30 | .then(function() {
31 | if (typeof options === "function") {
32 | return options();
33 | } else {
34 | return options;
35 | }
36 | })
37 | .then(function(options) {
38 | addListeners(suiteEvents, options);
39 |
40 | var benchPromiseChain = Promise.resolve();
41 |
42 | Object.keys(options.benches).forEach(function(name) {
43 | benchPromiseChain = benchPromiseChain
44 | .then(function() {
45 | var bench = options.benches[name];
46 | if (typeof bench === "function") {
47 | return bench(name);
48 | } else {
49 | return bench;
50 | }
51 | })
52 | .then(function(bench) {
53 | var benchFn = bench.fn;
54 | userSetup = bench.setup;
55 |
56 | var benchEvents = new EventEmitter();
57 |
58 | addListeners(benchEvents, bench);
59 |
60 | var actualFn;
61 |
62 | var defer = benchFn.length === 1;
63 |
64 | if (defer) {
65 | actualFn = function(deferred) {
66 | function done() {
67 | deferred.resolve();
68 | }
69 |
70 | benchFn(done);
71 | };
72 | } else {
73 | actualFn = function() {
74 | benchFn();
75 | };
76 | }
77 |
78 | bench = Object.assign({ name: name }, bench);
79 | bench.events = benchEvents;
80 |
81 | suite.add(name, {
82 | // a flag to indicate the benchmark is deferred
83 | defer: defer,
84 | // benchmark test function
85 | fn: actualFn,
86 |
87 | onStart: function() {
88 | benchEvents.emit("start");
89 | suiteEvents.emit("startBench", bench);
90 | }
91 | });
92 |
93 | benches.push(bench);
94 | });
95 | });
96 |
97 | return benchPromiseChain;
98 | });
99 | }
100 |
101 | function setup() {
102 | var promiseChain = Promise.resolve();
103 |
104 | if (userSetup) {
105 | promiseChain = promiseChain.then(userSetup);
106 | }
107 |
108 | benches.forEach(function(bench) {
109 | if (bench.setup) {
110 | promiseChain = promiseChain.then(function() {
111 | bench.setup();
112 | });
113 | }
114 | });
115 |
116 | return promiseChain;
117 | }
118 |
119 | function warmupCycle() {
120 | var promiseChain = Promise.resolve();
121 | benches.forEach(function(bench) {
122 | var benchFn = bench.fn;
123 | promiseChain = promiseChain.then(function() {
124 | if (benchFn.length === 1) {
125 | return new Promise(function(resolve) {
126 | benchFn(resolve);
127 | });
128 | } else {
129 | benchFn();
130 | }
131 | });
132 | });
133 |
134 | return promiseChain;
135 | }
136 |
137 | function warmup() {
138 | suiteEvents.emit("warmup");
139 |
140 | benches.forEach(function(bench) {
141 | bench.events.emit("warmup");
142 | });
143 |
144 | var index = 0;
145 | var totalCount = 100;
146 |
147 | function next() {
148 | return warmupCycle().then(function() {
149 | if (++index === totalCount) {
150 | suiteEvents.emit("warmupComplete");
151 | return delay(1000);
152 | } else {
153 | return delay(10).then(next);
154 | }
155 | });
156 | }
157 |
158 | return next();
159 | }
160 |
161 | function run() {
162 | return new Promise(function(resolve, reject) {
163 | suite
164 | .on("start", function() {
165 | suiteEvents.emit("start", {
166 | suite: suite
167 | });
168 | })
169 | .on("cycle", function(event) {
170 | suiteEvents.emit("cycle", {
171 | suite: suite,
172 | resultsString: String(event.target)
173 | });
174 | })
175 | .on("complete", function() {
176 | suiteEvents.emit("complete", {
177 | suite: suite,
178 | resultsString:
179 | "Fastest is " +
180 | this.filter("fastest").map("name") +
181 | "\n\n--------------\n"
182 | });
183 |
184 | suite.off("start cycle complete");
185 | resolve();
186 | })
187 | .on("error", function(e) {
188 | suite.off("start cycle complete error");
189 | reject(e.target.error);
190 | })
191 | .run({ async: true });
192 | });
193 | }
194 |
195 | return {
196 | on: function(eventName, listener) {
197 | suiteEvents.on(eventName, listener);
198 | return this;
199 | },
200 |
201 | run: function() {
202 | return Promise.resolve()
203 | .then(load)
204 | .then(setup)
205 | .then(warmup)
206 | .then(run);
207 | }
208 | };
209 | };
210 |
--------------------------------------------------------------------------------