diff --git a/.babelrc b/.babelrc
index c6708204e..2c99e8cfd 100644
--- a/.babelrc
+++ b/.babelrc
@@ -23,7 +23,19 @@
},
"test": {
"presets": [
- "env",
+ ["env", {
+ "exclude": ["transform-regenerator"]
+ }],
+ "react",
+ "stage-0"
+ ],
+ "plugins": ["transform-runtime"]
+ },
+ "benchmark": {
+ "presets": [
+ ["env", {
+ "exclude": ["transform-regenerator"]
+ }],
"react",
"stage-0"
],
diff --git a/.gitignore b/.gitignore
index 31a608d64..94b19ed8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ packages/*/yarn.lock
.DS_Store
.idea/
.vscode/
+
+# Editor files
+.tern-port
diff --git a/Contributing.md b/Contributing.md
index eb9fcb728..17a3a9f68 100644
--- a/Contributing.md
+++ b/Contributing.md
@@ -90,6 +90,16 @@ yarn benchmark
There will be some subtle changes in iteration speed always, but the comparison reporter will highlight any changes that seem meaningful. You can run `benchmark` multiple times to ensure the speed up persists.
+### Run Selected Benchmarks
+
+To run selected benchmarks, create `tmp/benchmark-config.js` with `module.exports.include`. For example, to run slate-core benchmarks only with `get-*`, we can create a `tmp/benchmark-config.js` as
+
+```
+module.exports.include = {
+ slate: /^get/
+}
+```
+
## Adding Browser Support
Slate aims to targeted all of the modern browsers, and eventually the modern mobile platforms. Right now browser support is limited to the latest versions of [Chrome](https://www.google.com/chrome/browser/desktop/), [Firefox](https://www.mozilla.org/en-US/firefox/new/), and [Safari](http://www.apple.com/safari/), but if you are interested in adding support for another modern platform, that is welcomed!
diff --git a/benchmark/compare.js b/benchmark/compare.js
new file mode 100644
index 000000000..7a9e87c20
--- /dev/null
+++ b/benchmark/compare.js
@@ -0,0 +1,115 @@
+/* eslint-disable no-console */
+
+const chalk = require('chalk')
+const figures = require('figures')
+const emojis = require('emojis')
+const { resolve } = require('path')
+
+const baseline = require(resolve(process.cwd(), 'tmp/benchmark-baseline'))
+const comparison = require(resolve(process.cwd(), 'tmp/benchmark-comparison'))
+const { existsSync } = require('fs')
+
+/**
+ * Constants.
+ */
+
+let THRESHOLD = 0.333
+const configPath = '../../tmp/benchmark-config.js'
+
+if (existsSync(configPath)) {
+ const alternative = require(configPath).THRESHOLD
+
+ if (typeof alternative === 'number' && alternative > 0) {
+ THRESHOLD = alternative
+ }
+}
+
+/**
+ * Print.
+ */
+
+console.log()
+console.log(` benchmarks`)
+
+baseline.forEach((suite, i) => {
+ console.log(` ${suite.name}`)
+
+ suite.benchmarks.forEach((base, j) => {
+ const compared = { user: {}, hr: {} }
+
+ for (const key of Object.keys(compared)) {
+ const comp = comparison[i].benchmarks[j]
+ if (!comp) return
+ const b = base.iterations / base[key] * 1000
+ const c = comp.iterations / comp[key] * 1000
+ const balancePercent =
+ b > c ? Math.round(Math.abs(b - c) / c * 100) : (c - b) / b * 100
+
+ const output = `${b.toFixed(2)} -> ${c.toFixed(2)} ops/sec`
+ compared[key].baseOutput = output
+
+ compared[key].percentOutput = `${balancePercent.toFixed(2)}% ${
+ c > b ? 'faster' : 'slower'
+ }`
+
+ compared[key].percentValue = balancePercent
+ compared[key].b = b
+ compared[key].c = c
+ compared[key].isFaster = c > b
+
+ if (balancePercent > 1000) {
+ compared[key].percentOutput += emojis.unicode(' :scream: ')
+ } else if (balancePercent > 100) {
+ if (c > b) {
+ compared[key].percentOutput += emojis.unicode(' :raised_hands: ')
+ } else {
+ compared[key].percentOutput += emojis.unicode(' :worried: ')
+ }
+ }
+ }
+
+ const { user, hr } = compared
+
+ if (
+ user.percentValue < THRESHOLD * 100 &&
+ hr.percentValue < THRESHOLD * 100
+ ) {
+ console.log(
+ chalk.grey(
+ ` ${figures.tick} ${base.name}: ${user.baseOutput} (${
+ user.percentOutput
+ })`
+ )
+ )
+ return
+ }
+
+ if (user.isFaster === hr.isFaster) {
+ if (user.isFaster) {
+ console.log(chalk.green(` ${figures.star} ${base.name}:`))
+
+ console.log(
+ ` user: ${user.baseOutput} (${user.percentOutput})`
+ )
+
+ console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
+ return
+ }
+
+ console.log(chalk.red(` ${figures.cross} ${base.name}:`))
+
+ console.log(
+ ` user: ${user.baseOutput} (${user.percentOutput})`
+ )
+
+ console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
+ return
+ }
+
+ console.log(chalk.red(` ${figures.questionMarkPrefix} ${base.name}:`))
+ console.log(` user: ${user.baseOutput} (${user.percentOutput})`)
+ console.log(` real: ${hr.baseOutput} (${hr.percentOutput})`)
+ })
+})
+
+console.log()
diff --git a/benchmark/config.js b/benchmark/config.js
new file mode 100644
index 000000000..3179a415c
--- /dev/null
+++ b/benchmark/config.js
@@ -0,0 +1,39 @@
+const { resolve } = require('path')
+const { existsSync } = require('fs')
+const program = require('commander')
+
+program
+ .option('-g, --grep []', 'Add grep pattern to filter running benchmarks')
+ .option('-c, --config [file]', 'Add config to filter running benchmarks')
+ .parse(process.argv)
+
+const { grep } = program
+
+if (grep) {
+ const pattern = new RegExp(grep)
+
+ module.exports.include = {
+ slate: pattern,
+ 'slate-html-serializer': pattern,
+ 'slate-plain-serializer': pattern,
+ 'slate-react': pattern,
+ }
+} else {
+ let { config = 'tmp/benchmark-config.js' } = program
+ config = resolve(config)
+
+ const userConfig = existsSync(config) ? require(config) : {}
+
+ if (userConfig.include) {
+ module.exports.include = userConfig.include
+ } else if (userConfig.default) {
+ module.exports.inlcude = userConfig.default
+ } else {
+ module.exports.include = {
+ slate: /^/,
+ 'slate-html-serializer': /^/,
+ 'slate-plain-serializer': /^/,
+ 'slate-react': /^/,
+ }
+ }
+}
diff --git a/benchmark/generate-report.js b/benchmark/generate-report.js
new file mode 100644
index 000000000..aac443d4a
--- /dev/null
+++ b/benchmark/generate-report.js
@@ -0,0 +1,57 @@
+const { writeFileSync } = require('fs')
+
+function convertRepo(report) {
+ const result = []
+
+ for (const name in report) {
+ const suite = report[name]
+
+ result.push({
+ name,
+ type: 'suite',
+ benchmarks: convertSuite(suite),
+ })
+ }
+ return result
+}
+
+function convertSuite(suite) {
+ const result = []
+
+ for (const name in suite) {
+ const bench = suite[name]
+ const { user, cycles } = bench
+
+ result.push({
+ name,
+ type: 'bench',
+ elapsed: user,
+ iterations: cycles,
+ ops: 1000 * cycles / user,
+ ...bench,
+ })
+ }
+ return result
+}
+
+const IS_COMPARE = process.env.COMPARE
+const filePath = IS_COMPARE
+ ? './tmp/benchmark-comparison.json'
+ : './tmp/benchmark-baseline.json'
+
+function generateReport(repo) {
+ repo
+ .run()
+ .then(report => {
+ const data = JSON.stringify(convertRepo(report))
+ writeFileSync(filePath, data)
+ return report
+ })
+ .then(report => {
+ if (IS_COMPARE) {
+ require('./compare')
+ }
+ })
+}
+
+module.exports = { generateReport }
diff --git a/benchmark/helpers/h.js b/benchmark/helpers/h.js
new file mode 100644
index 000000000..14e7f67c9
--- /dev/null
+++ b/benchmark/helpers/h.js
@@ -0,0 +1,45 @@
+/* eslint-disable import/no-extraneous-dependencies */
+const { createHyperscript } = require('slate-hyperscript')
+
+/**
+ * Define a hyperscript.
+ *
+ * @type {Function}
+ */
+
+const h = createHyperscript({
+ blocks: {
+ line: 'line',
+ paragraph: 'paragraph',
+ quote: 'quote',
+ code: 'code',
+ list: 'list',
+ item: 'item',
+ image: {
+ type: 'image',
+ isVoid: true,
+ },
+ },
+ inlines: {
+ link: 'link',
+ hashtag: 'hashtag',
+ comment: 'comment',
+ emoji: {
+ type: 'emoji',
+ isVoid: true,
+ },
+ },
+ marks: {
+ b: 'bold',
+ i: 'italic',
+ u: 'underline',
+ },
+})
+
+/**
+ * Export.
+ *
+ * @type {Function}
+ */
+
+module.exports = h
diff --git a/benchmark/index.js b/benchmark/index.js
new file mode 100644
index 000000000..4c02159eb
--- /dev/null
+++ b/benchmark/index.js
@@ -0,0 +1,20 @@
+const { repo } = require('slate-dev-benchmark')
+const { resolve } = require('path')
+const { readdirSync } = require('fs')
+const { generateReport } = require('./generate-report')
+const { include } = require('./config')
+
+const categoryDir = resolve(__dirname)
+
+const categories = readdirSync(categoryDir).filter(
+ c => c[0] != '.' && c.match(/^slate/)
+)
+
+categories.forEach(dir => {
+ if (include && include[dir]) {
+ const { run } = require(`./${dir}`)
+ run(include[dir])
+ }
+})
+
+generateReport(repo)
diff --git a/benchmark/slate-html-serializer/html-serializer/deserialize.js b/benchmark/slate-html-serializer/html-serializer/deserialize.js
new file mode 100644
index 000000000..43c3e1ce0
--- /dev/null
+++ b/benchmark/slate-html-serializer/html-serializer/deserialize.js
@@ -0,0 +1,58 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const Html = require('slate-html-serializer').default
+const { JSDOM } = require('jsdom') // eslint-disable-line import/no-extraneous-dependencies
+
+const html = new Html({
+ parseHtml: JSDOM.fragment,
+ rules: [
+ {
+ deserialize(el, next) {
+ switch (el.tagName.toLowerCase()) {
+ case 'blockquote':
+ return {
+ object: 'block',
+ type: 'quote',
+ nodes: next(el.childNodes),
+ }
+ case 'p': {
+ return {
+ object: 'block',
+ type: 'paragraph',
+ nodes: next(el.childNodes),
+ }
+ }
+ case 'strong': {
+ return {
+ object: 'mark',
+ type: 'bold',
+ nodes: next(el.childNodes),
+ }
+ }
+ case 'em': {
+ return {
+ object: 'mark',
+ type: 'italic',
+ nodes: next(el.childNodes),
+ }
+ }
+ }
+ },
+ },
+ ],
+})
+
+module.exports.default = function(string) {
+ html.deserialize(string)
+}
+
+module.exports.input = `
+
+
+ This is editable rich text, much better than a textarea!
+
+
+`
+ .trim()
+ .repeat(10)
diff --git a/packages/slate-html-serializer/benchmark/html-serializer/serialize.js b/benchmark/slate-html-serializer/html-serializer/serialize.js
similarity index 79%
rename from packages/slate-html-serializer/benchmark/html-serializer/serialize.js
rename to benchmark/slate-html-serializer/html-serializer/serialize.js
index b0a91ec4c..f2d3ea76a 100644
--- a/packages/slate-html-serializer/benchmark/html-serializer/serialize.js
+++ b/benchmark/slate-html-serializer/html-serializer/serialize.js
@@ -1,10 +1,10 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import Html from '../..'
-import React from 'react'
-import h from '../../test/helpers/h'
-import { JSDOM } from 'jsdom' // eslint-disable-line import/no-extraneous-dependencies
+const Html = require('slate-html-serializer').default
+const React = require('react')
+const h = require('../../helpers/h')
+const { JSDOM } = require('jsdom') // eslint-disable-line import/no-extraneous-dependencies
const html = new Html({
parseHtml: JSDOM.fragment,
@@ -34,11 +34,11 @@ const html = new Html({
],
})
-export default function(state) {
+module.exports.default = function(state) {
html.serialize(state)
}
-export const input = (
+module.exports.input = (
{Array.from(Array(10)).map(() => (
diff --git a/benchmark/slate-html-serializer/index.js b/benchmark/slate-html-serializer/index.js
new file mode 100644
index 000000000..84255e1ba
--- /dev/null
+++ b/benchmark/slate-html-serializer/index.js
@@ -0,0 +1,37 @@
+const { readdirSync } = require('fs')
+const { basename, extname, resolve } = require('path')
+const { resetMemoization } = require('slate')
+const { Suite, Bench } = require('slate-dev-benchmark')
+
+/**
+ * Benchmarks.
+ */
+
+module.exports.run = function(include) {
+ const categoryDir = resolve(__dirname)
+ const categories = readdirSync(categoryDir).filter(
+ c => c[0] != '.' && c != 'index.js'
+ )
+
+ categories.forEach(category => {
+ const suite = new Suite(category, { minTries: 100, minTime: 1000 })
+ const benchmarkDir = resolve(categoryDir, category)
+ const benchmarks = readdirSync(benchmarkDir)
+ .filter(b => b[0] != '.' && !!~b.indexOf('.js'))
+ .map(b => basename(b, extname(b)))
+
+ benchmarks.forEach(benchmark => {
+ if (include && !benchmark.match(include)) return
+ const bench = new Bench(suite, benchmark)
+ const dir = resolve(benchmarkDir, benchmark)
+ const module = require(dir)
+ const fn = module.default
+ bench.input(() => module.input)
+
+ bench.run(input => {
+ fn(input)
+ resetMemoization()
+ })
+ })
+ })
+}
diff --git a/benchmark/slate-plain-serializer/index.js b/benchmark/slate-plain-serializer/index.js
new file mode 100644
index 000000000..1958e5814
--- /dev/null
+++ b/benchmark/slate-plain-serializer/index.js
@@ -0,0 +1,40 @@
+const { basename, extname, resolve } = require('path')
+const { readdirSync } = require('fs')
+const { resetMemoization } = require('slate')
+const { Suite, Bench } = require('slate-dev-benchmark')
+
+/**
+ * Benchmarks.
+ */
+
+module.exports.run = function(include) {
+ const categoryDir = resolve(__dirname)
+ const categories = readdirSync(categoryDir).filter(
+ c => c[0] != '.' && c != 'index.js'
+ )
+
+ categories.forEach(category => {
+ const suite = new Suite(category, {
+ minTries: 100,
+ minTime: 1000,
+ })
+ const benchmarkDir = resolve(categoryDir, category)
+ const benchmarks = readdirSync(benchmarkDir)
+ .filter(b => b[0] != '.' && !!~b.indexOf('.js'))
+ .map(b => basename(b, extname(b)))
+
+ benchmarks.forEach(benchmark => {
+ if (include && !benchmark.match(include)) return
+ const bench = new Bench(suite, benchmark)
+ const dir = resolve(benchmarkDir, benchmark)
+ const module = require(dir)
+ const fn = module.default
+ bench.input(() => module.input)
+
+ bench.run(input => {
+ fn(input)
+ resetMemoization()
+ })
+ })
+ })
+}
diff --git a/packages/slate-plain-serializer/benchmark/plain-serializer/deserialize.js b/benchmark/slate-plain-serializer/plain-serializer/deserialize.js
similarity index 52%
rename from packages/slate-plain-serializer/benchmark/plain-serializer/deserialize.js
rename to benchmark/slate-plain-serializer/plain-serializer/deserialize.js
index cdcab1bf9..d8e4a724a 100644
--- a/packages/slate-plain-serializer/benchmark/plain-serializer/deserialize.js
+++ b/benchmark/slate-plain-serializer/plain-serializer/deserialize.js
@@ -1,14 +1,16 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import Plain from '../..'
+const Plain = require('slate-plain-serializer').default
-export default function(string) {
- Plain.deserialize(string)
-}
-
-export const input = `
+const input = `
This is editable plain text, just like a text area.
`
.trim()
.repeat(10)
+
+module.exports.input = input
+
+module.exports.default = function(string) {
+ Plain.deserialize(string)
+}
diff --git a/packages/slate-plain-serializer/benchmark/plain-serializer/serialize.js b/benchmark/slate-plain-serializer/plain-serializer/serialize.js
similarity index 71%
rename from packages/slate-plain-serializer/benchmark/plain-serializer/serialize.js
rename to benchmark/slate-plain-serializer/plain-serializer/serialize.js
index c1f2a60fe..eb70eab30 100644
--- a/packages/slate-plain-serializer/benchmark/plain-serializer/serialize.js
+++ b/benchmark/slate-plain-serializer/plain-serializer/serialize.js
@@ -1,14 +1,14 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import Plain from '../..'
-import h from '../../test/helpers/h'
+const Plain = require('slate-plain-serializer').default
+const h = require('../../helpers/h')
-export default function(state) {
+module.exports.default = function(state) {
Plain.serialize(state)
}
-export const input = (
+module.exports.input = (
{Array.from(Array(10)).map(() => (
diff --git a/benchmark/slate-react/index.js b/benchmark/slate-react/index.js
new file mode 100644
index 000000000..1958e5814
--- /dev/null
+++ b/benchmark/slate-react/index.js
@@ -0,0 +1,40 @@
+const { basename, extname, resolve } = require('path')
+const { readdirSync } = require('fs')
+const { resetMemoization } = require('slate')
+const { Suite, Bench } = require('slate-dev-benchmark')
+
+/**
+ * Benchmarks.
+ */
+
+module.exports.run = function(include) {
+ const categoryDir = resolve(__dirname)
+ const categories = readdirSync(categoryDir).filter(
+ c => c[0] != '.' && c != 'index.js'
+ )
+
+ categories.forEach(category => {
+ const suite = new Suite(category, {
+ minTries: 100,
+ minTime: 1000,
+ })
+ const benchmarkDir = resolve(categoryDir, category)
+ const benchmarks = readdirSync(benchmarkDir)
+ .filter(b => b[0] != '.' && !!~b.indexOf('.js'))
+ .map(b => basename(b, extname(b)))
+
+ benchmarks.forEach(benchmark => {
+ if (include && !benchmark.match(include)) return
+ const bench = new Bench(suite, benchmark)
+ const dir = resolve(benchmarkDir, benchmark)
+ const module = require(dir)
+ const fn = module.default
+ bench.input(() => module.input)
+
+ bench.run(input => {
+ fn(input)
+ resetMemoization()
+ })
+ })
+ })
+}
diff --git a/benchmark/slate-react/rendering/decoration.js b/benchmark/slate-react/rendering/decoration.js
new file mode 100644
index 000000000..40cd4651f
--- /dev/null
+++ b/benchmark/slate-react/rendering/decoration.js
@@ -0,0 +1,46 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const React = require('react')
+const ReactDOM = require('react-dom/server')
+const h = require('../../helpers/h')
+const { Editor } = require('slate-react')
+
+module.exports.default = function(value) {
+ const el = React.createElement(Editor, { value })
+ ReactDOM.renderToStaticMarkup(el)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map(() => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+
+
+
+ ))}
+
+
+)
+
+const texts = value.document.getTexts()
+const decorations = texts.flatMap((t, index) => {
+ if (index % 4 !== 0) return []
+ if (t.length === 0) return []
+ return [
+ {
+ anchorKey: t.key,
+ anchorOffset: 0,
+ focusKey: t.key,
+ focusOffset: 1,
+ marks: [{ type: 'underline' }],
+ },
+ ]
+})
+
+module.exports.input = value.change().setValue({ decorations }).value
diff --git a/packages/slate-react/benchmark/rendering/normal.js b/benchmark/slate-react/rendering/normal.js
similarity index 67%
rename from packages/slate-react/benchmark/rendering/normal.js
rename to benchmark/slate-react/rendering/normal.js
index 2dda5dc3b..42d9244b9 100644
--- a/packages/slate-react/benchmark/rendering/normal.js
+++ b/benchmark/slate-react/rendering/normal.js
@@ -1,17 +1,17 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import React from 'react'
-import ReactDOM from 'react-dom/server'
-import h from '../../test/helpers/h'
-import { Editor } from '../..'
+const React = require('react')
+const ReactDOM = require('react-dom/server')
+const h = require('../../helpers/h')
+const { Editor } = require('slate-react')
-export default function(value) {
+module.exports.default = function(value) {
const el = React.createElement(Editor, { value })
ReactDOM.renderToStaticMarkup(el)
}
-export const input = (
+module.exports.input = (
{Array.from(Array(10)).map(() => (
diff --git a/packages/slate/benchmark/changes/delete-backward.js b/benchmark/slate/changes/delete-backward.js
similarity index 72%
rename from packages/slate/benchmark/changes/delete-backward.js
rename to benchmark/slate/changes/delete-backward.js
index 5a698882c..db67adf99 100644
--- a/packages/slate/benchmark/changes/delete-backward.js
+++ b/benchmark/slate/changes/delete-backward.js
@@ -1,18 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(change) {
+module.exports.default = function(change) {
change.deleteBackward()
}
-export function before(value) {
- const change = value.change()
- return change
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -29,3 +24,7 @@ export const input = (
)
+
+module.exports.input = () => {
+ return value.change()
+}
diff --git a/packages/slate/benchmark/changes/delete-forward.js b/benchmark/slate/changes/delete-forward.js
similarity index 72%
rename from packages/slate/benchmark/changes/delete-forward.js
rename to benchmark/slate/changes/delete-forward.js
index a5fe3e995..fb1d665da 100644
--- a/packages/slate/benchmark/changes/delete-forward.js
+++ b/benchmark/slate/changes/delete-forward.js
@@ -1,18 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(change) {
+module.exports.default = function(change) {
change.deleteForward()
}
-export function before(value) {
- const change = value.change()
- return change
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -29,3 +24,7 @@ export const input = (
)
+
+module.exports.input = () => {
+ return value.change()
+}
diff --git a/benchmark/slate/changes/insert-node-by-key.js b/benchmark/slate/changes/insert-node-by-key.js
new file mode 100644
index 000000000..68642148e
--- /dev/null
+++ b/benchmark/slate/changes/insert-node-by-key.js
@@ -0,0 +1,32 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ change, block }) {
+ change.insertNodeByKey(block.key, 0, Hello world)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map((v, i) => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+ {i == 0 ? : ''}
+
+
+
+ ))}
+
+
+)
+const block = value.document.getBlocks().last()
+
+module.exports.input = function() {
+ const change = value.change()
+ return { change, block }
+}
diff --git a/packages/slate/benchmark/changes/insert-text-by-key-multiple.js b/benchmark/slate/changes/insert-text-by-key-multiple.js
similarity index 66%
rename from packages/slate/benchmark/changes/insert-text-by-key-multiple.js
rename to benchmark/slate/changes/insert-text-by-key-multiple.js
index 8fff7ce6e..9817670b0 100644
--- a/packages/slate/benchmark/changes/insert-text-by-key-multiple.js
+++ b/benchmark/slate/changes/insert-text-by-key-multiple.js
@@ -1,26 +1,15 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ change, keys }) {
+module.exports.default = function({ change, keys }) {
for (const key of keys) {
change.insertTextByKey(key, 0, 'a')
}
}
-export function before(value) {
- const change = value.change()
- const keys = value.document
- .getTexts()
- .toArray()
- .map(t => t.key)
- resetMemoization()
- return { change, keys }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -37,3 +26,12 @@ export const input = (
)
+const keys = value.document
+ .getTexts()
+ .toArray()
+ .map(t => t.key)
+
+module.exports.input = function() {
+ const change = value.change()
+ return { change, keys }
+}
diff --git a/packages/slate/benchmark/changes/insert-text-by-key.js b/benchmark/slate/changes/insert-text-by-key.js
similarity index 68%
rename from packages/slate/benchmark/changes/insert-text-by-key.js
rename to benchmark/slate/changes/insert-text-by-key.js
index 807d07667..f0d975091 100644
--- a/packages/slate/benchmark/changes/insert-text-by-key.js
+++ b/benchmark/slate/changes/insert-text-by-key.js
@@ -1,21 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ change, text }) {
+module.exports.default = function({ change, text }) {
change.insertTextByKey(text.key, 0, 'a')
}
-export function before(value) {
- const change = value.change()
- const text = value.document.getLastText()
- resetMemoization()
- return { change, text }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -32,3 +24,9 @@ export const input = (
)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ const change = value.change()
+ return { change, text }
+}
diff --git a/packages/slate/benchmark/changes/insert-text.js b/benchmark/slate/changes/insert-text.js
similarity index 72%
rename from packages/slate/benchmark/changes/insert-text.js
rename to benchmark/slate/changes/insert-text.js
index 66e3ea0f8..58a00f604 100644
--- a/packages/slate/benchmark/changes/insert-text.js
+++ b/benchmark/slate/changes/insert-text.js
@@ -1,18 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(change) {
+module.exports.default = function(change) {
change.insertText('a')
}
-export function before(value) {
- const change = value.change()
- return change
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -29,3 +24,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value.change()
+}
diff --git a/packages/slate/benchmark/changes/normalize.js b/benchmark/slate/changes/normalize.js
similarity index 78%
rename from packages/slate/benchmark/changes/normalize.js
rename to benchmark/slate/changes/normalize.js
index 2497fee16..a16d6a09c 100644
--- a/packages/slate/benchmark/changes/normalize.js
+++ b/benchmark/slate/changes/normalize.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(change) {
+module.exports.default = function(change) {
change.normalize()
}
-export function before(value) {
- return value.change()
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -28,3 +24,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value.change()
+}
diff --git a/benchmark/slate/changes/remove-node-by-key.js b/benchmark/slate/changes/remove-node-by-key.js
new file mode 100644
index 000000000..a9c316637
--- /dev/null
+++ b/benchmark/slate/changes/remove-node-by-key.js
@@ -0,0 +1,32 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ change, text }) {
+ change.removeNodeByKey(text.key)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map((v, i) => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+ {i == 0 ? : ''}
+
+
+
+ ))}
+
+
+)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ const change = value.change()
+ return { change, text }
+}
diff --git a/packages/slate/benchmark/changes/split-block.js b/benchmark/slate/changes/split-block.js
similarity index 72%
rename from packages/slate/benchmark/changes/split-block.js
rename to benchmark/slate/changes/split-block.js
index 060568690..b125d2e32 100644
--- a/packages/slate/benchmark/changes/split-block.js
+++ b/benchmark/slate/changes/split-block.js
@@ -1,18 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(change) {
+module.exports.default = function(change) {
change.splitBlock()
}
-export function before(value) {
- const change = value.change()
- return change
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map((v, i) => (
@@ -29,3 +24,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value.change()
+}
diff --git a/benchmark/slate/index.js b/benchmark/slate/index.js
new file mode 100644
index 000000000..3f6c47844
--- /dev/null
+++ b/benchmark/slate/index.js
@@ -0,0 +1,37 @@
+const { readdirSync } = require('fs')
+const { basename, extname, resolve } = require('path')
+const { resetMemoization } = require('slate')
+const { Suite, Bench } = require('slate-dev-benchmark')
+
+/**
+ * Benchmarks.
+ */
+
+module.exports.run = function(include) {
+ const categoryDir = resolve(__dirname)
+ const categories = readdirSync(categoryDir).filter(
+ c => c[0] != '.' && c != 'index.js'
+ )
+
+ categories.forEach(category => {
+ const suite = new Suite(category, { minTries: 100, minTime: 1000 })
+ const benchmarkDir = resolve(categoryDir, category)
+ const benchmarks = readdirSync(benchmarkDir)
+ .filter(b => b[0] != '.' && !!~b.indexOf('.js'))
+ .map(b => basename(b, extname(b)))
+
+ benchmarks.forEach(benchmark => {
+ if (include && !benchmark.match(include)) return
+ const bench = new Bench(suite, benchmark)
+ const dir = resolve(benchmarkDir, benchmark)
+ const module = require(dir)
+ const fn = module.default
+ bench.input(module.input)
+
+ bench.run(input => {
+ fn(input)
+ resetMemoization()
+ })
+ })
+ })
+}
diff --git a/packages/slate/benchmark/models/from-json-big.js b/benchmark/slate/models/from-json-big.js
similarity index 89%
rename from packages/slate/benchmark/models/from-json-big.js
rename to benchmark/slate/models/from-json-big.js
index a3d902c8a..d5bd1ccfa 100644
--- a/packages/slate/benchmark/models/from-json-big.js
+++ b/benchmark/slate/models/from-json-big.js
@@ -1,12 +1,12 @@
/* eslint-disable react/jsx-key */
-import { Value } from '../..'
+const { Value } = require('slate')
-export default function(json) {
+module.exports.default = function(json) {
Value.fromJSON(json)
}
-export const input = {
+const input = {
document: {
nodes: Array.from(Array(100)).map(() => ({
type: 'list',
@@ -64,3 +64,7 @@ export const input = {
})),
},
}
+
+module.exports.input = function() {
+ return input
+}
diff --git a/packages/slate/benchmark/models/from-json.js b/benchmark/slate/models/from-json.js
similarity index 85%
rename from packages/slate/benchmark/models/from-json.js
rename to benchmark/slate/models/from-json.js
index 661773253..71b2c21ce 100644
--- a/packages/slate/benchmark/models/from-json.js
+++ b/benchmark/slate/models/from-json.js
@@ -1,12 +1,12 @@
/* eslint-disable react/jsx-key */
-import { Value } from '../..'
+const { Value } = require('slate')
-export default function(json) {
+module.exports.default = function(json) {
Value.fromJSON(json)
}
-export const input = {
+const input = {
document: {
nodes: Array.from(Array(10)).map(() => ({
object: 'block',
@@ -44,3 +44,7 @@ export const input = {
})),
},
}
+
+module.exports.input = function() {
+ return input
+}
diff --git a/packages/slate/benchmark/models/get-characters-at-range.js b/benchmark/slate/models/get-active-marks-at-range.js
similarity index 62%
rename from packages/slate/benchmark/models/get-characters-at-range.js
rename to benchmark/slate/models/get-active-marks-at-range.js
index 4c34adae7..057f1ced7 100644
--- a/packages/slate/benchmark/models/get-characters-at-range.js
+++ b/benchmark/slate/models/get-active-marks-at-range.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
- value.document.getCharactersAtRange(value.selection)
+module.exports.default = function(value) {
+ value.document.getActiveMarksAtRange(value.selection)
}
-export function before(value) {
- return value.change().selectAll().value
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -27,3 +23,9 @@ export const input = (
)
+ .change()
+ .selectAll().value
+
+module.exports.input = function() {
+ return value
+}
diff --git a/benchmark/slate/models/get-ancestors.js b/benchmark/slate/models/get-ancestors.js
new file mode 100644
index 000000000..fbb39c743
--- /dev/null
+++ b/benchmark/slate/models/get-ancestors.js
@@ -0,0 +1,30 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ value, text }) {
+ value.document.getAncestors(text.key)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map(() => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+
+
+
+ ))}
+
+
+)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/get-blocks-at-range.js b/benchmark/slate/models/get-blocks-at-range.js
similarity index 71%
rename from packages/slate/benchmark/models/get-blocks-at-range.js
rename to benchmark/slate/models/get-blocks-at-range.js
index d07561873..ccbcdfe1c 100644
--- a/packages/slate/benchmark/models/get-blocks-at-range.js
+++ b/benchmark/slate/models/get-blocks-at-range.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getBlocksAtRange(value.selection)
}
-export function before(value) {
- return value.change().selectAll().value
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -27,3 +23,9 @@ export const input = (
)
+ .change()
+ .selectAll().value
+
+module.exports.input = () => {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-blocks.js b/benchmark/slate/models/get-blocks.js
similarity index 73%
rename from packages/slate/benchmark/models/get-blocks.js
rename to benchmark/slate/models/get-blocks.js
index dfe4ddfe1..dca5c8929 100644
--- a/packages/slate/benchmark/models/get-blocks.js
+++ b/benchmark/slate/models/get-blocks.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getBlocks()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-leaves.js b/benchmark/slate/models/get-common-ancestor.js
similarity index 54%
rename from packages/slate/benchmark/models/get-leaves.js
rename to benchmark/slate/models/get-common-ancestor.js
index 243eb7a5d..08d170415 100644
--- a/packages/slate/benchmark/models/get-leaves.js
+++ b/benchmark/slate/models/get-common-ancestor.js
@@ -1,20 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function(text) {
- text.getLeaves()
+module.exports.default = function({ value, first, last }) {
+ value.document.getCommonAncestor(first.key, last.key)
}
-export function before(value) {
- const text = value.document.getFirstText()
- resetMemoization()
- return text
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -30,3 +23,10 @@ export const input = (
)
+
+const first = value.document.getFirstText()
+const last = value.document.getLastText()
+
+module.exports.input = function() {
+ return { value, first, last }
+}
diff --git a/benchmark/slate/models/get-furthest-ancestor.js b/benchmark/slate/models/get-furthest-ancestor.js
new file mode 100644
index 000000000..e9f33bae7
--- /dev/null
+++ b/benchmark/slate/models/get-furthest-ancestor.js
@@ -0,0 +1,30 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ value, text }) {
+ value.document.getFurthestAncestor(text.key)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map(() => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+
+
+
+ ))}
+
+
+)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/get-inlines-at-range.js b/benchmark/slate/models/get-inlines-at-range.js
similarity index 70%
rename from packages/slate/benchmark/models/get-inlines-at-range.js
rename to benchmark/slate/models/get-inlines-at-range.js
index b31dfe4a8..d8d6b7a3d 100644
--- a/packages/slate/benchmark/models/get-inlines-at-range.js
+++ b/benchmark/slate/models/get-inlines-at-range.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getInlinesAtRange(value.selection)
}
-export function before(value) {
- return value.change().selectAll().value
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -27,3 +23,9 @@ export const input = (
)
+ .change()
+ .selectAll().value
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-inlines.js b/benchmark/slate/models/get-inlines.js
similarity index 73%
rename from packages/slate/benchmark/models/get-inlines.js
rename to benchmark/slate/models/get-inlines.js
index cef280292..04bc7c4e1 100644
--- a/packages/slate/benchmark/models/get-inlines.js
+++ b/benchmark/slate/models/get-inlines.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getInlines()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-characters.js b/benchmark/slate/models/get-leaves.js
similarity index 64%
rename from packages/slate/benchmark/models/get-characters.js
rename to benchmark/slate/models/get-leaves.js
index 6e194313a..d91ad2c1b 100644
--- a/packages/slate/benchmark/models/get-characters.js
+++ b/benchmark/slate/models/get-leaves.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
- value.document.getCharacters()
+module.exports.default = function(text) {
+ text.getLeaves()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,8 @@ export const input = (
)
+const text = value.document.getFirstText()
+
+module.exports.input = function() {
+ return text
+}
diff --git a/packages/slate/benchmark/models/get-marks-at-range.js b/benchmark/slate/models/get-marks-at-range.js
similarity index 70%
rename from packages/slate/benchmark/models/get-marks-at-range.js
rename to benchmark/slate/models/get-marks-at-range.js
index 701d58d84..389f1e940 100644
--- a/packages/slate/benchmark/models/get-marks-at-range.js
+++ b/benchmark/slate/models/get-marks-at-range.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getMarksAtRange(value.selection)
}
-export function before(value) {
- return value.change().selectAll().value
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -27,3 +23,9 @@ export const input = (
)
+ .change()
+ .selectAll().value
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-marks.js b/benchmark/slate/models/get-marks.js
similarity index 73%
rename from packages/slate/benchmark/models/get-marks.js
rename to benchmark/slate/models/get-marks.js
index 99fa6e0d8..530eda9bf 100644
--- a/packages/slate/benchmark/models/get-marks.js
+++ b/benchmark/slate/models/get-marks.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getMarks()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value
+}
diff --git a/benchmark/slate/models/get-parent.js b/benchmark/slate/models/get-parent.js
new file mode 100644
index 000000000..98159a19b
--- /dev/null
+++ b/benchmark/slate/models/get-parent.js
@@ -0,0 +1,30 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ value, text }) {
+ value.document.getParent(text.key)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map(() => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+
+
+
+ ))}
+
+
+)
+const text = value.document.getLastText()
+
+module.exports.input = () => {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/get-path.js b/benchmark/slate/models/get-path.js
similarity index 65%
rename from packages/slate/benchmark/models/get-path.js
rename to benchmark/slate/models/get-path.js
index 8f06f7910..20c81c6dc 100644
--- a/packages/slate/benchmark/models/get-path.js
+++ b/benchmark/slate/models/get-path.js
@@ -1,20 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ value, text }) {
+module.exports.default = function({ value, text }) {
value.document.getPath(text.key)
}
-export function before(value) {
- const text = value.document.getLastText()
- resetMemoization()
- return { value, text }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -30,3 +23,8 @@ export const input = (
)
+const text = value.document.getLastText()
+
+module.exports.input = () => {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/get-texts-at-range.js b/benchmark/slate/models/get-texts-at-range.js
similarity index 70%
rename from packages/slate/benchmark/models/get-texts-at-range.js
rename to benchmark/slate/models/get-texts-at-range.js
index 250c600f1..a1902e2a1 100644
--- a/packages/slate/benchmark/models/get-texts-at-range.js
+++ b/benchmark/slate/models/get-texts-at-range.js
@@ -1,17 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getTextsAtRange(value.selection)
}
-export function before(value) {
- return value.change().selectAll().value
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -27,3 +23,9 @@ export const input = (
)
+ .change()
+ .selectAll().value
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/get-texts.js b/benchmark/slate/models/get-texts.js
similarity index 73%
rename from packages/slate/benchmark/models/get-texts.js
rename to benchmark/slate/models/get-texts.js
index ead94832e..223ae1421 100644
--- a/packages/slate/benchmark/models/get-texts.js
+++ b/benchmark/slate/models/get-texts.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.document.getTexts()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value
+}
diff --git a/benchmark/slate/models/getDescendant.js b/benchmark/slate/models/getDescendant.js
new file mode 100644
index 000000000..ab2253511
--- /dev/null
+++ b/benchmark/slate/models/getDescendant.js
@@ -0,0 +1,30 @@
+/** @jsx h */
+/* eslint-disable react/jsx-key */
+
+const h = require('../../helpers/h')
+
+module.exports.default = function({ value, text }) {
+ value.document.getDescendant(text.key)
+}
+
+const value = (
+
+
+ {Array.from(Array(10)).map(() => (
+
+
+
+ This is editable rich text, much better than a
+ textarea!
+
+
+
+ ))}
+
+
+)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/has-node-multiple.js b/benchmark/slate/models/has-node-multiple.js
similarity index 62%
rename from packages/slate/benchmark/models/has-node-multiple.js
rename to benchmark/slate/models/has-node-multiple.js
index 30bf728a8..fb9ad0f68 100644
--- a/packages/slate/benchmark/models/has-node-multiple.js
+++ b/benchmark/slate/models/has-node-multiple.js
@@ -1,25 +1,15 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ value, keys }) {
+module.exports.default = function({ value, keys }) {
keys.forEach(key => {
value.document.hasNode(key)
})
}
-export function before(value) {
- const keys = value.document
- .getTexts()
- .toArray()
- .map(t => t.key)
- resetMemoization()
- return { value, keys }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -35,3 +25,11 @@ export const input = (
)
+const keys = value.document
+ .getTexts()
+ .toArray()
+ .map(t => t.key)
+
+module.exports.input = function() {
+ return { value, keys }
+}
diff --git a/packages/slate/benchmark/models/has-node.js b/benchmark/slate/models/has-node.js
similarity index 65%
rename from packages/slate/benchmark/models/has-node.js
rename to benchmark/slate/models/has-node.js
index 4c4253c88..a6d28f958 100644
--- a/packages/slate/benchmark/models/has-node.js
+++ b/benchmark/slate/models/has-node.js
@@ -1,20 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ value, text }) {
+module.exports.default = function({ value, text }) {
value.document.hasNode(text.key)
}
-export function before(value) {
- const text = value.document.getLastText()
- resetMemoization()
- return { value, text }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -30,3 +23,8 @@ export const input = (
)
+const text = value.document.getLastText()
+
+module.exports.input = function() {
+ return { value, text }
+}
diff --git a/packages/slate/benchmark/models/to-json.js b/benchmark/slate/models/to-json.js
similarity index 72%
rename from packages/slate/benchmark/models/to-json.js
rename to benchmark/slate/models/to-json.js
index 9a3b05e70..9400b7552 100644
--- a/packages/slate/benchmark/models/to-json.js
+++ b/benchmark/slate/models/to-json.js
@@ -1,13 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
+const h = require('../../helpers/h')
-export default function(value) {
+module.exports.default = function(value) {
value.toJSON()
}
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -23,3 +23,7 @@ export const input = (
)
+
+module.exports.input = function() {
+ return value
+}
diff --git a/packages/slate/benchmark/models/update-node.js b/benchmark/slate/models/update-node.js
similarity index 55%
rename from packages/slate/benchmark/models/update-node.js
rename to benchmark/slate/models/update-node.js
index 7b924b4af..a8a9666f6 100644
--- a/packages/slate/benchmark/models/update-node.js
+++ b/benchmark/slate/models/update-node.js
@@ -1,23 +1,13 @@
/** @jsx h */
/* eslint-disable react/jsx-key */
-import h from '../../test/helpers/h'
-import { resetMemoization } from '../..'
+const h = require('../../helpers/h')
-export default function({ value, next }) {
+module.exports.default = function({ value, next }) {
value.document.updateNode(next)
}
-export function before(value) {
- const texts = value.document.getTexts()
- const { size } = texts
- const text = texts.get(Math.round(size / 2))
- const next = text.insertText(0, 'some text')
- resetMemoization()
- return { value, next }
-}
-
-export const input = (
+const value = (
{Array.from(Array(10)).map(() => (
@@ -33,3 +23,12 @@ export const input = (
)
+
+const texts = value.document.getTexts()
+const { size } = texts
+const text = texts.get(Math.round(size / 2))
+const next = text.insertText(0, 'some text')
+
+module.exports.input = function() {
+ return { value, next }
+}
diff --git a/package.json b/package.json
index bfdcc516a..cb6295319 100644
--- a/package.json
+++ b/package.json
@@ -16,17 +16,20 @@
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"chalk": "^1.1.3",
+ "commander": "^2.15.1",
"copy-webpack-plugin": "^4.4.1",
"cross-env": "^5.1.3",
"css-loader": "^0.28.9",
"emotion": "^9.2.4",
"eslint": "^4.19.1",
+ "emojis": "^1.0.10",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.6.0",
"extract-text-webpack-plugin": "^3.0.2",
"faker": "^3.1.0",
+ "figures": "^2.0.0",
"fs-promise": "^1.0.0",
"gh-pages": "^0.11.0",
"html-webpack-plugin": "^2.30.1",
@@ -62,6 +65,7 @@
"rollup-plugin-sourcemaps": "^0.4.2",
"rollup-plugin-uglify": "^3.0.0",
"slate-collapse-on-escape": "^0.6.0",
+ "slate-dev-benchmark": "*",
"slate-soft-break": "^0.6.0",
"source-map-loader": "^0.2.3",
"source-map-support": "^0.4.0",
@@ -79,13 +83,15 @@
"slate-schema-violations": "*"
},
"scripts": {
- "benchmark": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-comparison.json && cross-env BABEL_ENV=test babel-node ./support/benchmark/compare",
- "benchmark:save": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-baseline.json",
+ "benchmark": "cross-env COMPARE=compare node --expose-gc ./tmp/benchmark/index.js",
+ "benchmark:save": " yarn benchmark:prepare && node --expose-gc ./tmp/benchmark/index.js",
+ "benchmark:prepare": "mkdir -p ./tmp && cross-env BABEL_ENV=benchmark babel benchmark --out-dir tmp/benchmark/",
+ "bootstrap": "lerna bootstrap && yarn build",
"build": "rollup --config ./support/rollup/config.js",
"build:production": "cross-env NODE_ENV=production rollup --config ./support/rollup/config.js && cross-env NODE_ENV=production webpack --config support/webpack/config.js",
"clean": "lerna run clean && rm -rf ./node_modules ./dist ./build",
"gh-pages": "gh-pages --dist ./build",
- "lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'",
+ "lint": "eslint benchmark packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'",
"open": "open http://localhost:8080",
"prettier": "prettier --write '**/*.{js,jsx,md,json,css}'",
"release": "yarn build:production && yarn test && yarn lint && lerna publish && yarn gh-pages",
diff --git a/packages/slate-dev-benchmark/Readme.md b/packages/slate-dev-benchmark/Readme.md
new file mode 100644
index 000000000..46021a7ce
--- /dev/null
+++ b/packages/slate-dev-benchmark/Readme.md
@@ -0,0 +1 @@
+This package contains 'Suite' and 'Bench' that Slate uses to compare computation efficiency under nodeJS.
diff --git a/packages/slate-dev-benchmark/package.json b/packages/slate-dev-benchmark/package.json
new file mode 100644
index 000000000..9beecb2af
--- /dev/null
+++ b/packages/slate-dev-benchmark/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "slate-dev-benchmark",
+ "description": "A simple, development-only benchmark for Slate",
+ "version": "0.0.1",
+ "license": "MIT",
+ "repository": "git://github.com/ianstormtaylor/slate.git",
+ "main": "src/index.js",
+ "devDependencies": {
+ "mocha": "^2.5.3"
+ },
+ "scripts": {
+ "clean": "rm -rf ./dist ./lib ./node_modules"
+ },
+ "keywords": ["slate"]
+}
diff --git a/packages/slate-dev-benchmark/src/Bench.js b/packages/slate-dev-benchmark/src/Bench.js
new file mode 100644
index 000000000..4bffc1cb3
--- /dev/null
+++ b/packages/slate-dev-benchmark/src/Bench.js
@@ -0,0 +1,227 @@
+/* global Promise */
+const { BenchType } = require('./types')
+const { makeOptions } = require('./makeOptions')
+const { Timer } = require('./Timer')
+const { logger } = require('./logger')
+
+const errorReport = {
+ cycles: NaN,
+ user: NaN,
+ system: NaN,
+ all: NaN,
+}
+
+/**
+ * Run a task and calculate the time consuming of tasks
+ */
+
+class Bench {
+ /**
+ * Construct a bench and register it to a Suite
+ * @param {Suite} suite
+ * @param {string} name
+ * @param {Object} options
+ */
+
+ constructor(suite, name, options = {}) {
+ this.name = name
+ this.options = makeOptions({ ...suite.options, ...options })
+ this.isFinished = false
+ this.inputer = () => undefined
+ this.runner = () => {}
+ this.report = { ...errorReport }
+ suite.addBench(this)
+ }
+
+ /**
+ * Is a Bench?
+ * @param {any} obj
+ * @return {boolean}
+ */
+
+ isBench(obj) {
+ return obj && obj[BenchType]
+ }
+
+ /**
+ * Set the method to generate (different} inputs for each run
+ * @param {Array|Function|Scalar} inputer
+ * @return {void}
+ */
+
+ input(inputer) {
+ if (Array.isArray(inputer)) {
+ this.inputer = index => inputer[index % inputer.length]
+ return
+ }
+
+ if (typeof inputer === 'function') {
+ this.inputer = inputer
+ return
+ }
+
+ this.inputer = () => inputer
+ }
+
+ /**
+ * Set the task runner
+ * @param {Function} runner
+ * @return {void}
+ */
+
+ run(runner) {
+ this.runner = runner
+ }
+
+ /**
+ * Tries to run tasks in `times`, if the time consuming excedes the max-time, then stop;
+ * After run, generate report and return
+ * If initial is the initial index to run the task, for continueing a task in adaptive mode
+ * @param {number} times
+ * @param {number} initial
+ */
+
+ async compose(times, initial) {
+ times = Math.floor(times)
+ const isAsync = this.options.async
+ const { runner, inputer } = this
+
+ const { maxTime } = this.options
+ let seq = Number.isFinite(this.options.maxTries) ? 1 : NaN
+ let nextCheckIndex = seq
+ const hrStart = process.hrtime()
+
+ if (global.gc) {
+ global.gc()
+ }
+
+ const report = { user: 0, system: 0, all: 0, hr: 0, cycles: 0 }
+
+ for (
+ let initialIndex = initial;
+ initialIndex < times;
+ initialIndex += this.options.allocationTries
+ ) {
+ const tries = Math.min(times - initialIndex, this.options.allocationTries)
+ const thisTryReport = await runBundleTasks.call(this, tries, initialIndex)
+
+ if (global.gc) {
+ global.gc()
+ }
+
+ for (const key in report) {
+ report[key] += thisTryReport[key]
+ }
+ }
+ return report
+
+ /**
+ * Run a bundle of tasks;
+ * the Bench estimate the time consuming of every `tries` tasks, then explictly run gc, and caculate the time consuming of next bundle tasks
+ * @param {number} tries
+ * @param {number} initialIndex
+ * @return {Promise< Object , *>}
+ */
+
+ function runBundleTasks(tries, initialIndex) {
+ const inputs = Array.from({ length: tries }).map(index =>
+ inputer(index + initialIndex)
+ )
+ const timer = new Timer()
+ timer.start()
+ return runFrom(0).then(cycles => {
+ timer.end()
+ const { elapsed } = timer
+ return { ...elapsed, cycles }
+ })
+
+ /**
+ * Run a single task run; If the task is end, return a Promise with the index when the task ends
+ * @param {number} index
+ * @return {Promise}
+ */
+
+ function runFrom(index) {
+ if (index === tries) return Promise.resolve(tries)
+
+ if (index === nextCheckIndex) {
+ const hrEnd = process.hrtime(hrStart)
+ const elapsed = hrEnd[0] * 1e3 + hrEnd[1] / 1e6
+
+ if (elapsed > maxTime) {
+ return Promise.resolve(index)
+ } else {
+ if (elapsed < maxTime / 20) {
+ seq *= 2
+ }
+
+ nextCheckIndex = seq + nextCheckIndex
+ }
+ }
+
+ if (!isAsync) {
+ const inputVar = inputs[index]
+ runner(inputVar)
+ return runFrom(index + 1)
+ } else {
+ return Promise.resolve(runner(inputs[index])).then(() =>
+ runFrom(index + 1)
+ )
+ }
+ }
+ }
+ }
+
+ /*
+ * Run the bench
+ * @return {void}
+ */
+
+ makeRun() {
+ if (this.isFinished) return true
+ logger(this)
+ const { options } = this
+ const { minTries, maxTime, maxTries } = options
+
+ let { minTime } = options
+ if (minTime > maxTime) minTime = maxTime
+
+ return this.compose(minTries, 0)
+ .then(report => {
+ if (this.options.mode === 'static') return report
+ const { all } = report
+ if (all > minTime) return report
+ const times = (minTime / all - 1) * minTries
+ return this.compose(Math.min(times, maxTries), minTries).then(
+ newReport => {
+ return mergeResults(report, newReport)
+ }
+ )
+ })
+ .then(report => {
+ this.report = report
+ this.isFinished = true
+ logger(this)
+ return true
+ })
+ }
+}
+
+Bench.prototype[BenchType] = true
+
+/*
+ * Merge two different report
+ * @param {Object} res1
+ * @param {Object} res2
+ */
+
+function mergeResults(res1, res2) {
+ const result = {}
+
+ for (const key in res1) {
+ result[key] = res1[key] + res2[key]
+ }
+ return result
+}
+
+module.exports = { Bench }
diff --git a/packages/slate-dev-benchmark/src/Repository.js b/packages/slate-dev-benchmark/src/Repository.js
new file mode 100644
index 000000000..729f199c8
--- /dev/null
+++ b/packages/slate-dev-benchmark/src/Repository.js
@@ -0,0 +1,73 @@
+/* global Promise */
+const { RepositoryType } = require('./types')
+const { logger } = require('./logger')
+const { compose } = require('./compose')
+
+/**
+ * Repository Class for holding Suite
+ */
+
+class Repository {
+ /**
+ * Construct a Repository with a name
+ * @param {string} name
+ */
+
+ constructor(name = 'default') {
+ this.name = name
+ this.suites = []
+ this.report = {}
+ this.isFinished = false
+ }
+
+ /**
+ * Check whether {obj} is repository
+ * @param {any} obj
+ * @return {boolean}
+ */
+
+ isRepository(obj) {
+ return obj && obj[RepositoryType]
+ }
+
+ /**
+ * Register a suite to the repository
+ * @param {Suite} suite
+ * @return {void}
+ */
+
+ addSuite(suite) {
+ this.isFinished = false
+ this.suites.push(suite)
+ }
+
+ /**
+ * Run all suites (and all benches under suites) and generate a report
+ * @return {Promise