From 03ab170a7fbfb7522173a8800d2152afe37f74c1 Mon Sep 17 00:00:00 2001 From: "G. Grau" Date: Fri, 17 Aug 2018 13:37:33 +0200 Subject: [PATCH] Convert benchmarks to JavaScript (#18) --- package.json | 3 +- tools/benchmarks.coffee | 271 ---------------- tools/benchmarks.js | 325 +++++++++++++++++++ tools/exampleWebpackConfigTreeShaking.js | 48 --- tools/oldTimm.coffee | 397 ----------------------- yarn.lock | 4 - 6 files changed, 326 insertions(+), 722 deletions(-) delete mode 100755 tools/benchmarks.coffee create mode 100644 tools/benchmarks.js delete mode 100644 tools/exampleWebpackConfigTreeShaking.js delete mode 100644 tools/oldTimm.coffee diff --git a/package.json b/package.json index 518e512..0c6f646 100755 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build": "yarn lint && yarn flow && yarn compile && yarn uglify && yarn testCovFull && yarn docs && yarn xxl", "travis": "yarn compile && yarn testCovNoMin", "xxl": "xxl", - "benchmarks": "coffee tools/benchmarks.coffee" + "benchmarks": "node tools/benchmarks" }, "dependencies": {}, "devDependencies": { @@ -36,7 +36,6 @@ "babel-preset-react": "^6.16.0", "babel-preset-stage-2": "^6.24.1", "chalk": "1.1.3", - "coffee-script": "1.11.1", "coveralls": "^3.0.2", "envify": "3.4.1", "eslint": "^5.3.0", diff --git a/tools/benchmarks.coffee b/tools/benchmarks.coffee deleted file mode 100755 index 9fadccf..0000000 --- a/tools/benchmarks.coffee +++ /dev/null @@ -1,271 +0,0 @@ -process.env.NODE_ENV = 'production' -_ = require 'lodash' -chalk = require 'chalk' -# Seamless = require 'seamless-immutable' -Seamless = require 'seamless-immutable/seamless-immutable.production.min' -Immutable = require 'immutable' -timm = require '../lib/timm.min' - -INITIAL_OBJECT = - toggle: false - b: 3 - str: 'foo' - d: - d1: 6 - d2: 'foo' - toggle: false - d9: b: b: b: 1 - e: - e1: 18 - e2: 'foo' -DEEP_PATH = ['d', 'd9', 'b', 'b', 'b'] -ARRAY_LENGTH = 1000 -INITIAL_ARRAY = new Array(ARRAY_LENGTH) -for n in [0...ARRAY_LENGTH] - INITIAL_ARRAY[n] = {a: 1, b: 2} -N = 2e5 - -_getIn = (obj, path) -> - out = obj - out = out[key] for key in path - out - -_mergeDeepInPlace = (obj1, obj2) -> - for key in Object.keys obj2 - val = obj2[key] - if (not val?) - obj1[key] = val - else if (typeof val is 'object') and (val.length?) - obj1[key] = _mergeDeepInPlace [], val - else if (typeof val is 'object') - obj1[key] = _mergeDeepInPlace {}, val - else - obj1[key] = val - obj1 - -_solMutable = - init: -> _.cloneDeep INITIAL_OBJECT - get: (obj, key) -> obj[key] - set: (obj, key, val) -> obj[key] = val; obj - getDeep: (obj, key1, key2) -> obj[key1][key2] - setDeep: (obj, key1, key2, val) -> obj[key1][key2] = val; obj - getIn: _getIn - setIn: (obj, path, val) -> - ptr = obj - for idx in [0...(path.length - 1)] - ptr = ptr[path[idx]] - ptr[path[path.length - 1]] = val - obj - merge: (obj1, obj2) -> - for key in Object.keys obj2 - obj1[key] = obj2[key] - mergeDeep: (obj1, obj2) -> _mergeDeepInPlace(obj1, obj2) - initArr: -> _.cloneDeep INITIAL_ARRAY - getAt: (arr, idx) -> arr[idx] - setAt: (arr, idx, val) -> arr[idx] = val; arr - -_solImmutableTimm = - init: -> _.cloneDeep INITIAL_OBJECT - get: (obj, key) -> obj[key] - set: (obj, key, val) -> timm.set obj, key, val - getDeep: (obj, key1, key2) -> obj[key1][key2] - setDeep: (obj, key1, key2, val) -> - return timm.set obj, key1, \ - timm.set(obj[key1], key2, val) - getIn: _getIn - setIn: (obj, path, val) -> timm.setIn obj, path, val - merge: (obj1, obj2) -> timm.merge obj1, obj2 - mergeDeep: (obj1, obj2) -> timm.mergeDeep obj1, obj2 - initArr: -> _.cloneDeep INITIAL_ARRAY - getAt: (arr, idx) -> arr[idx] - setAt: (arr, idx, val) -> timm.replaceAt arr, idx, val - -_solImmutableJs = - init: -> Immutable.fromJS INITIAL_OBJECT # deep - get: (obj, key) -> obj.get key - set: (obj, key, val) -> obj.set key, val - getDeep: (obj, key1, key2) -> obj.getIn [key1, key2] - setDeep: (obj, key1, key2, val) -> obj.setIn [key1, key2], val - getIn: (obj, path) -> obj.getIn path - setIn: (obj, path, val) -> obj.setIn path, val - merge: (obj1, obj2) -> obj1.merge obj2 - mergeDeep: (obj1, obj2) -> obj1.mergeDeep obj2 - initArr: -> Immutable.List INITIAL_ARRAY # shallow - getAt: (arr, idx) -> arr.get idx - setAt: (arr, idx, val) -> arr.set idx, val - -_solImmutableSeamless = - init: -> Seamless INITIAL_OBJECT - get: (obj, key) -> obj[key] - set: (obj, key, val) -> obj.set key, val - getDeep: (obj, key1, key2) -> obj[key1][key2] - setDeep: (obj, key1, key2, val) -> obj.setIn [key1, key2], val - getIn: _getIn - setIn: (obj, path, val) -> obj.setIn path, val - merge: (obj1, obj2) -> obj1.merge obj2 - mergeDeep: (obj1, obj2) -> obj1.merge obj2, {deep: true} - initArr: -> Seamless INITIAL_ARRAY - getAt: (arr, idx) -> arr[idx] - setAt: (arr, idx, val) -> arr.set idx, val - -_toggle = (solution, obj) -> - return solution.set obj, 'toggle', not(solution.get obj, 'toggle') - -_addResult = (results, condition) -> - results.push if condition then chalk.green.bold('P') else chalk.red('F') -_verify = (solution) -> - results = [] - {init, get, set, setDeep, getIn, setIn, merge, mergeDeep, initArr, getAt, setAt} = solution - - # Initial conditions - obj = init() - _addResult results, (get(obj, 'toggle') is false) - results.push '-' - - # Changes to root attributes create a new object - # (but keep the nested object untouched) - obj2 = set obj, 'toggle', true - _addResult results, (get(obj, 'toggle') is false) - _addResult results, (get(obj2, 'toggle') is true) - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - results.push '-' - obj2 = set(obj, 'str', 'foo') - _addResult results, (obj2 is obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - results.push '-' - - # Same for deep attributes - obj2 = setDeep(obj, 'd', 'd1', 3) - _addResult results, (solution.getDeep(obj, 'd', 'd1') is 6) - _addResult results, (solution.getDeep(obj2, 'd', 'd1') is 3) - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') isnt get(obj, 'd')) - _addResult results, (get(obj2, 'e') is get(obj, 'e')) - results.push '-' - - # If we change an attribute to exactly the same value, - # no new object is created - obj2 = set(obj, 'b', get(obj, 'b')) - _addResult results, (obj2 is obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - results.push '-' - obj2 = set(obj, 'str', 'bar') - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - - # Same for deep attributes - obj = init() - obj2 = setDeep(obj, 'd', 'd1', 6) - _addResult results, (solution.getDeep(obj, 'd', 'd1') is 6) - _addResult results, (solution.getDeep(obj2, 'd', 'd1') is 6) - _addResult results, (obj2 is obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - results.push '-' - - # Deep writes - obj2 = setIn(obj, DEEP_PATH, 3) - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') isnt get(obj, 'd')) - _addResult results, (get(obj2, 'e') is get(obj, 'e')) - _addResult results, (getIn(obj, DEEP_PATH) is 1) - _addResult results, (getIn(obj2, DEEP_PATH) is 3) - results.push '-' - - # Merge - obj2 = merge obj, {c: 5, f: null} - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') is get(obj, 'd')) - _addResult results, (get(obj2, 'c') is 5) - _addResult results, (get(obj2, 'f') is null) - results.push '-' - - # Deep merge - obj2 = mergeDeep obj, {c: 5, f: null, d: {d9: {b: {a: 1}}}} - changedPath = ['d', 'd9', 'b', 'a'] - unchangedPath = ['d', 'd9', 'b', 'b'] - _addResult results, (obj2 isnt obj) - _addResult results, (get(obj2, 'd') isnt get(obj, 'd')) - _addResult results, (getIn(obj2, changedPath) is 1) - _addResult results, (getIn(obj2, unchangedPath) is getIn(obj, unchangedPath)) - _addResult results, (get(obj2, 'c') is 5) - _addResult results, (get(obj2, 'f') is null) - results.push '-' - - # Array writes - arr = initArr() - arr2 = setAt(arr, 1, {b: 3}) - _addResult results, (arr2 isnt arr) - _addResult results, (getAt(arr, 1).b is 2) - _addResult results, (getAt(arr2, 1).b is 3) - arr2 = setAt(arr, 1, getAt(arr, 1)) - _addResult results, (arr2 is arr) - - console.log " Verification: #{results.join ''}" - -_test = (desc, cb) -> - tic = new Date().getTime() - cb() - tac = new Date().getTime() - console.log " #{desc}: " + chalk.bold "#{tac - tic} ms" - -_allTests = (desc, solution) -> - console.log chalk.bold desc - _verify solution - obj = solution.init() - _test "Object: read (x#{N})", -> - for n in [0...N] - val = solution.get(obj, 'toggle') - return - obj = solution.init() - _test "Object: write (x#{N})", -> - for n in [0...N] - obj2 = solution.set(obj, 'b', n) - return - obj = solution.init() - _test "Object: deep read (x#{N})", -> - for n in [0...N] - val = solution.getDeep(obj, 'd', 'd1') - return - obj = solution.init() - _test "Object: deep write (x#{N})", -> - for n in [0...N] - obj2 = solution.setDeep(obj, 'd', 'd1', n) - return - obj = solution.init() - _test "Object: very deep read (x#{N})", -> - for n in [0...N] - val = solution.getIn(obj, DEEP_PATH) - return - obj = solution.init() - _test "Object: very deep write (x#{N})", -> - for n in [0...N] - obj2 = solution.setIn(obj, DEEP_PATH, n) - return - obj = solution.init() - MERGE_OBJ = {c: 5, f: null} - _test "Object: merge (x#{N})", -> - for n in [0...N] - obj2 = solution.merge(obj, MERGE_OBJ) - return - obj = solution.init() - MERGE_DEEP_OBJ = {c: 5, f: null, d: {d9: {b: {a: 1}}}} - _test "Object: mergeDeep (x#{N})", -> - for n in [0...N] - obj2 = solution.mergeDeep(obj, MERGE_DEEP_OBJ) - return - arr = solution.initArr() - _test "Array: read (x#{N})", -> - for n in [0...N] - val = solution.getAt(arr, 1) - return - arr = solution.initArr() - _test "Array: write (x#{N})", -> - for n in [0...N] - arr2 = solution.setAt(arr, 1, n) - return - -_allTests "Mutable", _solMutable -_allTests "Immutable (ImmutableJS)", _solImmutableJs -_allTests "Immutable (timm)", _solImmutableTimm -_allTests "Immutable (seamless-immutable)", _solImmutableSeamless diff --git a/tools/benchmarks.js b/tools/benchmarks.js new file mode 100644 index 0000000..8b4b8cc --- /dev/null +++ b/tools/benchmarks.js @@ -0,0 +1,325 @@ +process.env.NODE_ENV = 'production'; +const _ = require('lodash'); +const chalk = require('chalk'); +// const Seamless = require('seamless-immutable') +const Seamless = require('seamless-immutable/seamless-immutable.production.min'); +const Immutable = require('immutable'); +const timm = require('../lib/timm.min'); + +const INITIAL_OBJECT = { + toggle: false, + b: 3, + str: 'foo', + d: { + d1: 6, + d2: 'foo', + toggle: false, + d9: { b: { b: { b: 1 } } }, + }, + e: { + e1: 18, + e2: 'foo', + }, +}; +const DEEP_PATH = ['d', 'd9', 'b', 'b', 'b']; +const ARRAY_LENGTH = 1000; +const INITIAL_ARRAY = new Array(ARRAY_LENGTH); +for (let n = 0; n < ARRAY_LENGTH; n++) { + INITIAL_ARRAY[n] = { a: 1, b: 2 }; +} +const N = 2e5; + +const _getIn = (obj, path) => { + let out = obj; + path.forEach(key => { + out = out[key]; + }); + return out; +}; + +const _mergeDeepInPlace = (obj1, obj2) => { + Object.keys(obj2).forEach(key => { + const val = obj2[key]; + if (val == null) obj1[key] = val; + else if (typeof val === 'object' && val.length != null) + obj1[key] = _mergeDeepInPlace([], val); + else if (typeof val === 'object') obj1[key] = _mergeDeepInPlace({}, val); + else obj1[key] = val; + }); + return obj1; +}; + +const _solMutable = { + init: () => _.cloneDeep(INITIAL_OBJECT), + get: (obj, key) => obj[key], + set: (obj, key, val) => { + obj[key] = val; + return obj; + }, + getDeep: (obj, key1, key2) => obj[key1][key2], + setDeep: (obj, key1, key2, val) => { + obj[key1][key2] = val; + return obj; + }, + getIn: _getIn, + setIn: (obj, path, val) => { + let ptr = obj; + for (let idx = 0; idx < path.length - 1; idx++) { + ptr = ptr[path[idx]]; + } + ptr[path[path.length - 1]] = val; + return obj; + }, + merge: (obj1, obj2) => { + Object.keys(obj2).forEach(key => { + obj1[key] = obj2[key]; + }); + return obj1; + }, + mergeDeep: (obj1, obj2) => _mergeDeepInPlace(obj1, obj2), + initArr: () => _.cloneDeep(INITIAL_ARRAY), + getAt: (arr, idx) => arr[idx], + setAt: (arr, idx, val) => { + arr[idx] = val; + return arr; + }, +}; + +_solImmutableTimm = { + init: () => _.cloneDeep(INITIAL_OBJECT), + get: (obj, key) => obj[key], + set: (obj, key, val) => timm.set(obj, key, val), + getDeep: (obj, key1, key2) => obj[key1][key2], + setDeep: (obj, key1, key2, val) => + timm.set(obj, key1, timm.set(obj[key1], key2, val)), + getIn: _getIn, + setIn: (obj, path, val) => timm.setIn(obj, path, val), + merge: (obj1, obj2) => timm.merge(obj1, obj2), + mergeDeep: (obj1, obj2) => timm.mergeDeep(obj1, obj2), + initArr: () => _.cloneDeep(INITIAL_ARRAY), + getAt: (arr, idx) => arr[idx], + setAt: (arr, idx, val) => timm.replaceAt(arr, idx, val), +}; + +_solImmutableJs = { + init: () => Immutable.fromJS(INITIAL_OBJECT), // deep + get: (obj, key) => obj.get(key), + set: (obj, key, val) => obj.set(key, val), + getDeep: (obj, key1, key2) => obj.getIn([key1, key2]), + setDeep: (obj, key1, key2, val) => obj.setIn([key1, key2], val), + getIn: (obj, path) => obj.getIn(path), + setIn: (obj, path, val) => obj.setIn(path, val), + merge: (obj1, obj2) => obj1.merge(obj2), + mergeDeep: (obj1, obj2) => obj1.mergeDeep(obj2), + initArr: () => Immutable.List(INITIAL_ARRAY), // shallow + getAt: (arr, idx) => arr.get(idx), + setAt: (arr, idx, val) => arr.set(idx, val), +}; + +_solImmutableSeamless = { + init: () => Seamless(INITIAL_OBJECT), + get: (obj, key) => obj[key], + set: (obj, key, val) => obj.set(key, val), + getDeep: (obj, key1, key2) => obj[key1][key2], + setDeep: (obj, key1, key2, val) => obj.setIn([key1, key2], val), + getIn: _getIn, + setIn: (obj, path, val) => obj.setIn(path, val), + merge: (obj1, obj2) => obj1.merge(obj2), + mergeDeep: (obj1, obj2) => obj1.merge(obj2, { deep: true }), + initArr: () => Seamless(INITIAL_ARRAY), + getAt: (arr, idx) => arr[idx], + setAt: (arr, idx, val) => arr.set(idx, val), +}; + +const _toggle = (solution, obj) => + solution.set(obj, 'toggle', !solution.get(obj, 'toggle')); + +const _addResult = (results, condition) => { + results.push(condition ? chalk.green.bold('P') : chalk.red('F')); +}; + +const _verify = solution => { + const results = []; + const { + init, + get, + set, + setDeep, + getIn, + setIn, + merge, + mergeDeep, + initArr, + getAt, + setAt, + } = solution; + let obj; + let obj2; + let arr; + let arr2; + + // Initial conditions + obj = init(); + _addResult(results, get(obj, 'toggle') === false); + results.push('-'); + + // Changes to root attributes create a new object + // (but keep the nested object untouched) + obj2 = set(obj, 'toggle', true); + _addResult(results, get(obj, 'toggle') === false); + _addResult(results, get(obj2, 'toggle') === true); + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + results.push('-'); + obj2 = set(obj, 'str', 'foo'); + _addResult(results, obj2 === obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + results.push('-'); + + // Same for deep attributes + obj2 = setDeep(obj, 'd', 'd1', 3); + _addResult(results, solution.getDeep(obj, 'd', 'd1') === 6); + _addResult(results, solution.getDeep(obj2, 'd', 'd1') === 3); + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') !== get(obj, 'd')); + _addResult(results, get(obj2, 'e') === get(obj, 'e')); + results.push('-'); + + // If we change an attribute to exactly the same value, + // no new object is created + obj2 = set(obj, 'b', get(obj, 'b')); + _addResult(results, obj2 === obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + results.push('-'); + obj2 = set(obj, 'str', 'bar'); + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + + // Same for deep attributes + obj = init(); + obj2 = setDeep(obj, 'd', 'd1', 6); + _addResult(results, solution.getDeep(obj, 'd', 'd1') === 6); + _addResult(results, solution.getDeep(obj2, 'd', 'd1') === 6); + _addResult(results, obj2 === obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + results.push('-'); + + // Deep writes + obj2 = setIn(obj, DEEP_PATH, 3); + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') !== get(obj, 'd')); + _addResult(results, get(obj2, 'e') === get(obj, 'e')); + _addResult(results, getIn(obj, DEEP_PATH) === 1); + _addResult(results, getIn(obj2, DEEP_PATH) === 3); + results.push('-'); + + // Merge + obj2 = merge(obj, { c: 5, f: null }); + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') === get(obj, 'd')); + _addResult(results, get(obj2, 'c') === 5); + _addResult(results, get(obj2, 'f') === null); + results.push('-'); + + // Deep merge + obj2 = mergeDeep(obj, { c: 5, f: null, d: { d9: { b: { a: 1 } } } }); + const changedPath = ['d', 'd9', 'b', 'a']; + const unchangedPath = ['d', 'd9', 'b', 'b']; + _addResult(results, obj2 !== obj); + _addResult(results, get(obj2, 'd') !== get(obj, 'd')); + _addResult(results, getIn(obj2, changedPath) === 1); + _addResult(results, getIn(obj2, unchangedPath) === getIn(obj, unchangedPath)); + _addResult(results, get(obj2, 'c') === 5); + _addResult(results, get(obj2, 'f') === null); + results.push('-'); + + // Array writes + arr = initArr(); + arr2 = setAt(arr, 1, { b: 3 }); + _addResult(results, arr2 !== arr); + _addResult(results, getAt(arr, 1).b === 2); + _addResult(results, getAt(arr2, 1).b === 3); + arr2 = setAt(arr, 1, getAt(arr, 1)); + _addResult(results, arr2 === arr); + + console.log(` Verification: ${results.join('')}`); +}; + +const _test = (desc, cb) => { + const tic = new Date().getTime(); + cb(); + const tac = new Date().getTime(); + console.log(` ${desc}: ` + chalk.bold(`${tac - tic} ms`)); +}; + +const _allTests = (desc, solution) => { + console.log(chalk.bold(desc)); + _verify(solution); + obj = solution.init(); + _test(`Object: read (x${N})`, () => { + for (let n = 0; n < N; n++) { + const val = solution.get(obj, 'toggle'); + } + }); + obj = solution.init(); + _test(`Object: write (x${N})`, () => { + for (let n = 0; n < N; n++) { + const obj2 = solution.set(obj, 'b', n); + } + }); + obj = solution.init(); + _test(`Object: deep read (x${N})`, () => { + for (let n = 0; n < N; n++) { + const val = solution.getDeep(obj, 'd', 'd1'); + } + }); + obj = solution.init(); + _test(`Object: deep write (x${N})`, () => { + for (let n = 0; n < N; n++) { + const obj2 = solution.setDeep(obj, 'd', 'd1', n); + } + }); + obj = solution.init(); + _test(`Object: very deep read (x${N})`, () => { + for (let n = 0; n < N; n++) { + const val = solution.getIn(obj, DEEP_PATH); + } + }); + obj = solution.init(); + _test(`Object: very deep write (x${N})`, () => { + for (let n = 0; n < N; n++) { + const obj2 = solution.setIn(obj, DEEP_PATH, n); + } + }); + obj = solution.init(); + MERGE_OBJ = { c: 5, f: null }; + _test(`Object: merge (x${N})`, () => { + for (let n = 0; n < N; n++) { + const obj2 = solution.merge(obj, MERGE_OBJ); + } + }); + obj = solution.init(); + MERGE_DEEP_OBJ = { c: 5, f: null, d: { d9: { b: { a: 1 } } } }; + _test(`Object: mergeDeep (x${N})`, () => { + for (let n = 0; n < N; n++) { + const obj2 = solution.mergeDeep(obj, MERGE_DEEP_OBJ); + } + }); + arr = solution.initArr(); + _test(`Array: read (x${N})`, () => { + for (let n = 0; n < N; n++) { + const val = solution.getAt(arr, 1); + } + }); + arr = solution.initArr(); + _test(`Array: write (x${N})`, () => { + for (let n = 0; n < N; n++) { + const arr2 = solution.setAt(arr, 1, n); + } + }); +}; + +_allTests('Mutable', _solMutable); +_allTests('Immutable (ImmutableJS)', _solImmutableJs); +_allTests('Immutable (timm)', _solImmutableTimm); +_allTests('Immutable (seamless-immutable)', _solImmutableSeamless); diff --git a/tools/exampleWebpackConfigTreeShaking.js b/tools/exampleWebpackConfigTreeShaking.js deleted file mode 100644 index 94ed38a..0000000 --- a/tools/exampleWebpackConfigTreeShaking.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - entry: { - test: "./test.js", - }, - output: { - // Make sure to use [name] or [id] in output.filename - // when using multiple entry points - filename: "test.bundle.js", - }, - module: { - loaders: [ - { - loader: 'babel', - test: /\.js$/, - query: { - babelrc: false, - // presets: ['es2015'], - presets: [], - - // All of the plugins of babel-preset-es2015, - // minus babel-plugin-transform-es2015-modules-commonjs - plugins: [ - 'transform-es2015-template-literals', - 'transform-es2015-literals', - 'transform-es2015-function-name', - 'transform-es2015-arrow-functions', - 'transform-es2015-block-scoped-functions', - 'transform-es2015-classes', - 'transform-es2015-object-super', - 'transform-es2015-shorthand-properties', - 'transform-es2015-computed-properties', - 'transform-es2015-for-of', - 'transform-es2015-sticky-regex', - 'transform-es2015-unicode-regex', - 'check-es2015-constants', - 'transform-es2015-spread', - 'transform-es2015-parameters', - 'transform-es2015-destructuring', - 'transform-es2015-block-scoping', - 'transform-es2015-typeof-symbol', - ['transform-regenerator', { async: false, asyncGenerators: false }], - "transform-flow-strip-types" - ], - } - } - ] - } -} \ No newline at end of file diff --git a/tools/oldTimm.coffee b/tools/oldTimm.coffee deleted file mode 100644 index 293e05b..0000000 --- a/tools/oldTimm.coffee +++ /dev/null @@ -1,397 +0,0 @@ -### -| Timm -| (c) Guillermo Grau Panea 2016 -| License: MIT -### - -INVALID_ARGS = 'INVALID_ARGS' - -#----------------------------------------------- -#- ### Helpers -#----------------------------------------------- -_throw = (msg) -> throw new Error msg - -_clone = (obj) -> - if Array.isArray obj then return [].concat obj - keys = Object.keys obj - out = {} - out[key] = obj[key] for key in keys - out - -_merge = (fAddDefaults) -> - args = arguments - len = args.length - out = args[1] - not(out?) and _throw \ - if process.env.NODE_ENV isnt 'production' \ - then "At least one object should be provided to merge()" \ - else INVALID_ARGS - fChanged = false - for idx in [2...len] by 1 - obj = args[idx] - continue if not obj? - keys = Object.keys obj - continue if not keys.length - for key in keys - continue if fAddDefaults and out[key] isnt undefined - nextVal = obj[key] - continue if nextVal is undefined - continue if nextVal is out[key] - if not fChanged - fChanged = true - out = _clone out - out[key] = nextVal - out - -_isObject = (o) -> - type = typeof o - return o? and (type is 'object' or type is 'function') - -## _deepFreeze = (obj) -> -## Object.freeze obj -## for key in Object.getOwnPropertyNames obj -## val = obj[key] -## if _isObject(val) and not Object.isFrozen val -## _deepFreeze val -## obj - -#----------------------------------------------- -# ### Arrays -#----------------------------------------------- - -# #### addLast() -# Returns a new array with an appended item or items. -# -# Usage: `addLast(array: Array, val: Array | any): Array` -# -# ```js -# arr = ['a', 'b'] -# arr2 = addLast(arr, 'c') -# // ['a', 'b', 'c'] -# arr2 === arr -# // false -# arr3 = addLast(arr, ['c', 'd']) -# // ['a', 'b', 'c', 'd'] -# ``` -## `array.concat(val)` also handles the array case, -## but is apparently very slow -addLast = (array, val) -> - if Array.isArray val then return array.concat val - return array.concat [val] - -# #### addFirst() -# Returns a new array with a prepended item or items. -# -# Usage: `addFirst(array: Array, val: Array | any): Array` -# -# ```js -# arr = ['a', 'b'] -# arr2 = addFirst(arr, 'c') -# // ['c', 'a', 'b'] -# arr2 === arr -# // false -# arr3 = addFirst(arr, ['c', 'd']) -# // ['c', 'd', 'a', 'b'] -# ``` -addFirst = (array, val) -> - if Array.isArray val then return val.concat array - return [val].concat array - -# #### insert() -# Returns a new array obtained by inserting an item or items -# at a specified index. -# -# Usage: `insert(array: Array, idx: number, val: Array | any): Array` -# -# ```js -# arr = ['a', 'b', 'c'] -# arr2 = insert(arr, 1, 'd') -# // ['a', 'd', 'b', 'c'] -# arr2 === arr -# // false -# insert(arr, 1, ['d', 'e']) -# // ['a', 'd', 'e', 'b', 'c'] -# ``` -insert = (array, idx, val) -> - return array.slice(0, idx) - .concat if Array.isArray val then val else [val] - .concat array.slice(idx) - -# #### removeAt() -# Returns a new array obtained by removing an item at -# a specified index. -# -# Usage: `removeAt(array: Array, idx: number): Array` -# -# ```js -# arr = ['a', 'b', 'c'] -# arr2 = removeAt(arr, 1) -# // ['a', 'c'] -# arr2 === arr -# // false -# ``` -removeAt = (array, idx) -> array.slice(0, idx).concat array.slice(idx + 1) - -# #### replaceAt() -# Returns a new array obtained by replacing an item at -# a specified index. If the provided item is the same -# (*referentially equal to*) the previous item at that position, -# the original array is returned. -# -# Usage: `replaceAt(array: Array, idx: number, newItem: any): Array` -# -# ```js -# arr = ['a', 'b', 'c'] -# arr2 = replaceAt(arr, 1, 'd') -# // ['a', 'd', 'c'] -# arr2 === arr -# // false -# -# // ... but the same object is returned if there are no changes: -# replaceAt(arr, 1, 'b') === arr -# // true -# ``` -replaceAt = (array, idx, newItem) -> - return array if array[idx] is newItem - return array.slice(0, idx) - .concat [newItem] - .concat array.slice(idx + 1) - -#----------------------------------------------- -# ### Collections (objects and arrays) -#----------------------------------------------- - -# #### getIn() -# Returns a value from an object at a given path. Works with -# nested arrays and objects. If the path does not exist, it returns -# `undefined`. -# -# Usage: `getIn(obj: Object, path: Array): any` -# -# ```js -# obj = {a: 1, b: 2, d: {d1: 3, d2: 4}, e: ['a', 'b', 'c']} -# getIn(obj, ['d', 'd1']) -# // 3 -# getIn(obj, ['e', 1]) -# // 'b' -# ``` -getIn = (obj, path) -> - not(Array.isArray path) and _throw \ - if process.env.NODE_ENV isnt 'production' \ - then "A path array should be provided when calling getIn()" \ - else INVALID_ARGS - ptr = obj - return undefined if not ptr? - for segment in path - ptr = ptr?[segment] - return ptr if ptr is undefined - ptr - -# #### set() -# Returns a new object with a modified attribute. -# If the provided value is the same (*referentially equal to*) -# the previous value, the original object is returned. -# -# Usage: `set(obj: Object, key: string, val: any): Object` -# -# ```js -# obj = {a: 1, b: 2, c: 3} -# obj2 = set(obj, 'b', 5) -# // {a: 1, b: 5, c: 3} -# obj2 === obj -# // false -# -# // ... but the same object is returned if there are no changes: -# set(obj, 'b', 2) === obj -# // true -# ``` -set = (obj, key, val) -> - obj ?= {} - return obj if obj[key] is val - obj2 = _clone obj - obj2[key] = val - obj2 - -# #### setIn() -# Returns a new object with a modified **nested** attribute. -# -# Notes: -# -# * If the provided value is the same (*referentially equal to*) -# the previous value, the original object is returned. -# -# * If the path does not exist, it will be created before setting -# the new value. -# -# Usage: `setIn(obj: Object, path: Array, val: any): Object` -# -# ```js -# obj = {a: 1, b: 2, d: {d1: 3, d2: 4}, e: {e1: 'foo', e2: 'bar'}} -# obj2 = setIn(obj, ['d', 'd1'], 4) -# // {a: 1, b: 2, d: {d1: 4, d2: 4}, e: {e1: 'foo', e2: 'bar'}} -# obj2 === obj -# // false -# obj2.d === obj.d -# // false -# obj2.e === obj.e -# // true -# -# // ... but the same object is returned if there are no changes: -# obj3 = setIn(obj, ['d', 'd1'], 3) -# // {a: 1, b: 2, d: {d1: 3, d2: 4}, e: {e1: 'foo', e2: 'bar'}} -# obj3 === obj -# // true -# obj3.d === obj.d -# // true -# obj3.e === obj.e -# // true -# -# // ... unknown paths create intermediate keys: -# setIn({a: 3}, ['unknown', 'path'], 4) -# // {a: 3, unknown: {path: 4}} -# ``` -setIn = (obj, path, val) -> - return val if not path.length - return _setIn obj, path, val, 0 - -_setIn = (obj, path, val, idx) -> - key = path[idx] - if idx is path.length - 1 - newValue = val - else - nestedObj = if _isObject obj then obj[key] else {} - newValue = _setIn nestedObj, path, val, idx + 1 - return set obj, key, newValue - -# #### updateIn() -# Returns a new object with a modified **nested** attribute, -# calculated via a user-provided callback based on the current value. -# If the calculated value is the same (*referentially equal to*) -# the previous value, the original object is returned. -# -# Usage: `updateIn(obj: Object, path: Array, fnUpdate: (prevValue: any) => any): Object` -# -# ```js -# obj = {a: 1, d: {d1: 3, d2: 4}} -# obj2 = updateIn(obj, ['d', 'd1'], function(val){return val + 1}) -# // {a: 1, d: {d1: 4, d2: 4}} -# obj2 === obj -# // false -# -# // ... but the same object is returned if there are no changes: -# obj3 = updateIn(obj, ['d', 'd1'], function(val){return val}) -# // {a: 1, d: {d1: 3, d2: 4}} -# obj3 === obj -# // true -# ``` -updateIn = (obj, path, fnUpdate) -> - prevVal = getIn obj, path - nextVal = fnUpdate prevVal - return setIn obj, path, nextVal - -# #### merge() -# Returns a new object built as follows: the overlapping keys from the -# second one overwrite the corresponding entries from the first one. -# Similar to `Object.assign()`, but immutable. -# -# Usage: `merge(obj1: Object, obj2: Object): Object` -# -# Variadic: `merge(obj1: Object, ...objects: Object[]): Object` -# -# The unmodified `obj1` is returned if `obj2` does not *provide something -# new to* `obj1`, i.e. if either of the following -# conditions are true: -# -# * `obj2` is `null` or `undefined` -# * `obj2` is an object, but it is empty -# * All attributes of `obj2` are referentially equal to the -# corresponding attributes of `obj` -# -# ```js -# obj1 = {a: 1, b: 2, c: 3} -# obj2 = {c: 4, d: 5} -# obj3 = merge(obj1, obj2) -# // {a: 1, b: 2, c: 4, d: 5} -# obj3 === obj1 -# // false -# -# // ... but the same object is returned if there are no changes: -# merge(obj1, {c: 3}) === obj1 -# // true -# ``` -merge = (a, b, c, d, e, f) -> - if arguments.length <= 6 - return _merge false, a, b, c, d, e, f - else - return _merge false, arguments... - -# #### mergeIn() -# Similar to `merge()`, but merging the value at a given nested path. -# -# Usage: `mergeIn(obj1: Object, path: Array, obj2: Object): Object` -# -# Variadic: `mergeIn(obj1: Object, path: Array, ...objects: Object[]): Object` -# -# ```js -# obj1 = {a: 1, d: {b: {d1: 3, d2: 4}}} -# obj2 = {d3: 5} -# obj2 = mergeIn(obj1, ['d', 'b'], obj2) -# // {a: 1, d: {b: {d1: 3, d2: 4, d3: 5}}} -# obj3 === obj1 -# // false -# -# // ... but the same object is returned if there are no changes: -# mergeIn(obj1, ['d', 'b'], {d2: 4}) === obj1 -# // true -# ``` -mergeIn = (a, path, b, c, d, e, f) -> - prevVal = getIn a, path - prevVal ?= {} - if arguments.length <= 7 - nextVal = _merge false, prevVal, b, c, d, e, f - else - mergeArgs = [false, prevVal].concat [].slice.call(arguments, 2) - nextVal = _merge.apply null, mergeArgs - return setIn a, path, nextVal - -# #### addDefaults() -# Returns a new object built as follows: `undefined` keys in the first one -# are filled in with the corresponding values from the second one -# (even if they are `null`). -# -# Usage: `addDefaults(obj: Object, defaults: Object): Object` -# -# Variadic: `addDefaults(obj: Object, ...defaultObjects: Object[]): Object` -# -# ```js -# obj1 = {a: 1, b: 2, c: 3} -# obj2 = {c: 4, d: 5, e: null} -# obj3 = addDefaults(obj1, obj2) -# // {a: 1, b: 2, c: 3, d: 5, e: null} -# obj3 === obj1 -# // false -# -# // ... but the same object is returned if there are no changes: -# addDefaults(obj1, {c: 4}) === obj1 -# // true -# ``` -addDefaults = (a, b, c, d, e, f) -> - if arguments.length <= 6 - return _merge true, a, b, c, d, e, f - else - return _merge true, arguments... - -#----------------------------------------------- -#- ### Public API -#----------------------------------------------- -module.exports = { - addLast, addFirst, - insert, - removeAt, replaceAt, - - getIn, - set, setIn, - updateIn, - merge, mergeIn, - addDefaults, -} diff --git a/yarn.lock b/yarn.lock index cfba99e..dad259b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1618,10 +1618,6 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -coffee-script@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.11.1.tgz#bf1c47ad64443a0d95d12df2b147cc0a4daad6e9" - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"