From 9448b766d0557900ab42974547e476c75c56e12c Mon Sep 17 00:00:00 2001 From: Glenn Dwiyatcita Date: Mon, 3 Oct 2016 16:55:03 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + 400.png | Bin 0 -> 12287 bytes README.md | 72 ++++++++++++++++++++++++ migrate-lodash.js | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 .gitignore create mode 100644 400.png create mode 100644 README.md create mode 100644 migrate-lodash.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce96fd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Created by .ignore support plugin (hsz.mobi) + +.idea diff --git a/400.png b/400.png new file mode 100644 index 0000000000000000000000000000000000000000..274eaed0f1c96efdd52558ccc3d4a9e6dff8b973 GIT binary patch literal 12287 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 j3iI8&e@xp~eXur2u%7q& Why? +- [lodash blows away the performance of native functional methods.](http://benmccormick.org/2014/11/12/underscore-vs-lodash/) +- lodash has more features for those functional methods such as _chaining_ and _iteratee shorthand_. +- lodash provides compatibility consistency for browser which hasn't really natively supported these functions yet (read: IE). +- lodash enables us to produce code in better clarity (and of course – improve style consistency :wink:), e.g. compare: `Object.hasOwnProperty` vs `_.has`, `angular.forEach` vs `_.forEach`. + +![http://cloud.highcharts.com/images/utusen/0/400.pdf](400.png) + + +Here are the two equivalence tables (note that the lists maybe incomplete, I only extract the common ones we use in our code): + +### Native +| Native method | lodash method | +| --- | --- | +| `Function.prototype.bind` | `_.bind` | +| `Array.prototype.forEach` | `_.forEach` | +| `Array.prototype.map` | `_.map` | +| `Array.prototype.filter` | `_.filter` | +| `Array.prototype.reduce` | `_.reduce` | +| `Object.hasOwnProperty` | `_.has` | +| `Object.keys` | `_.keys` | +| `Object.values` | `_.values` | + +### Angular +| Angular method | lodash method | +| --- | --- | +| `angular.bind` (beware that Angular [`.bind`](https://docs.angularjs.org/api/ng/function/angular.bind) function signature expects different arguments order) | `_.bind` | +| `angular.copy` | `_.cloneDeep` | +| `angular.equals` | `_.isEqual` | +| `angular.extend` | `_.assign` | +| `angular.forEach` | `_.forEach` | +| `angular.identity` | `_.identity` | +| `angular.isArray` | `_.isArray` | +| `angular.isDate` | `_.isDate` | +| `angular.isDefined` | `!_.isUndefined` | +| `angular.isElement` | `_.isElement` | +| `angular.isFunction` | `_.isFunction` | +| `angular.isNumber` | `_.isNumber` | +| `angular.isObject` | `_.isObject` | +| `angular.isString` | `_.isString` | +| `angular.isUndefined` | `_.isUndefined` | +| `angular.merge` | `_.merge` | +| `angular.noop` | `_.noop` | + +## Preferable lodash Functional Method Aliases +In our code, lodash functional method aliases have been used inconsistently. :disappointed: So, please help yourself to make it more consistent! + +| Alias | Favorable alias | +| --- | --- | +| `_.all` | `_.every` | +| `_.any` | `_.some` | +| `_.each` | `_.forEach` | +| `_.extend` (beware that lodash 4.x [`.extend`](https://lodash.com/docs/4.16.2#assignIn) behaves differently and **not** an alias for `.assign`) | `_.assign` | +| `_.unique` | `_.uniq` | +| `_.chain` | `_()` (yup, prefer implicit chaining. Also, beware that in implicit chaining, the wrapper [methods that are not chainable](https://lodash.com/docs/4.16.2#lodash) will end the chaining without the need of calling `.value()`, e.g. `.forEach`, `.reduce`) | + +## Breaking Changes in 4.x (Method Removals and Renames) +[R.T.F. Changelog](https://github.com/lodash/lodash/wiki/Changelog#v400). :shipit: + +Known to be used in our project are: +- `_.include` -> `_.includes` +- `_.contains` -> `_.includes` +- `_.pluck` -> `_.map` with iteratee shorthand +- `_.where` -> `_.filter` with iteratee shorthand +- `_.first` -> `_.head` +- `_.invoke` -> `_.invokeMap` +- `_.rest` -> `_.tail` diff --git a/migrate-lodash.js b/migrate-lodash.js new file mode 100644 index 0000000..c8aa26b --- /dev/null +++ b/migrate-lodash.js @@ -0,0 +1,140 @@ +const _ = require('lodash'); +const replace = require('replace'); + +const replacementSpecs = [ + { + regex: 'Object[.]hasOwnProperty', + replacement: '_.has', + }, + { + regex: 'Object[.]keys', + replacement: '_.keys', + }, + { + regex: 'Object[.]values', + replacement: '_.values', + }, + { + regex: 'angular[.]copy', + replacement: '_.cloneDeep', + }, + { + regex: 'angular[.]equals', + replacement: '_.isEqual', + }, + { + regex: 'angular[.]extend', + replacement: '_.assign', + }, + { + regex: 'angular[.]forEach', + replacement: '_.forEach', + }, + { + regex: 'angular[.]identity', + replacement: '_.identity', + }, + { + regex: 'angular[.]isArray', + replacement: '_.isArray', + }, + { + regex: 'angular[.]isDate', + replacement: '_.isDate', + }, + { + regex: 'angular[.]isDefined', + replacement: '!_.isUndefined', + }, + { + regex: 'angular[.]isElement', + replacement: '_.isElement', + }, + { + regex: 'angular[.]isFunction', + replacement: '_.isFunction', + }, + { + regex: 'angular[.]isNumber', + replacement: '_.isNumber', + }, + { + regex: 'angular[.]isObject', + replacement: '_.isObject', + }, + { + regex: 'angular[.]isString', + replacement: '_.isString', + }, + { + regex: 'angular[.]isUndefined', + replacement: '_.isUndefined', + }, + { + regex: 'angular[.]merge', + replacement: '_.merge', + }, + { + regex: 'angular[.]noop', + replacement: '_.noop', + }, + { + regex: '_[.]all', + replacement: '_.every', + }, + { + regex: '_[.]any', + replacement: '_.some', + }, + { + regex: '_[.]each', + replacement: '_.forEach', + }, + { + regex: '_[.]extend', + replacement: '_.assign', + }, + { + regex: '_[.]unique', + replacement: '_.uniq', + }, + { + regex: '_[.]chain', + replacement: '_', + }, + { + regex: '_[.]include', + replacement: '_.includes', + }, + { + regex: '_[.]contains', + replacement: '_.includes', + }, + { + regex: '_[.]pluck', + replacement: '_.map', + }, + { + regex: '_[.]where', + replacement: '_.filter', + }, + { + regex: '_[.]first', + replacement: '_.head', + }, + { + regex: '_[.]invoke', + replacement: '_.invokeMap', + }, + { + regex: '_[.]rest', + replacement: '_.tail', + }, +]; + +_.forEach(replacementSpecs, replacementSpec => replace({ + regex: replacementSpec.regex, + replacement: replacementSpec.replacement, + paths: ['/Users/glenn/cumulocity/cumulocity-ui/app/scripts'], + recursive: true, +}));