diff --git a/package.json b/package.json index 9a23be0a..9644af10 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "colors": "^1.1.2", "flow-parser": "^0.*", "graceful-fs": "^4.1.11", - "lodash": "^4.13.1", "micromatch": "^2.3.7", "neo-async": "^2.5.0", "node-dir": "^0.1.17", diff --git a/src/Collection.js b/src/Collection.js index 7992aaca..5a17cfd3 100644 --- a/src/Collection.js +++ b/src/Collection.js @@ -11,8 +11,9 @@ 'use strict'; const assert = require('assert'); +const intersection = require('./utils/intersection'); const recast = require('recast'); -const _ = require('lodash'); +const union = require('./utils/union'); const astTypes = recast.types; var types = astTypes.namedTypes; @@ -260,8 +261,7 @@ function _inferTypes(paths) { ); } else { // try to find a common type - _types = _.intersection.apply( - null, + _types = intersection( paths.map(path => astTypes.getSupertypeNames(path.node.type)) ); } @@ -274,10 +274,9 @@ function _toTypeArray(value) { value = !Array.isArray(value) ? [value] : value; value = value.map(v => v.toString()); if (value.length > 1) { - return _.union(value, _.intersection.apply( - null, - value.map(_getSupertypeNames) - )); + return union( + [value].concat(intersection(value.map(_getSupertypeNames))) + ); } else { return value.concat(_getSupertypeNames(value[0])); } diff --git a/src/collections/JSXElement.js b/src/collections/JSXElement.js index 34665056..90ea076e 100644 --- a/src/collections/JSXElement.js +++ b/src/collections/JSXElement.js @@ -10,11 +10,11 @@ 'use strict'; -const _ = require('lodash'); const Collection = require('../Collection'); const NodeCollection = require('./Node'); const assert = require('assert'); +const once = require('../utils/once'); const recast = require('recast'); const requiresModule = require('./VariableDeclarator').filters.requiresModule; @@ -199,6 +199,6 @@ function register() { Collection.registerMethods(traversalMethods, JSXElement); } -exports.register = _.once(register); +exports.register = once(register); exports.filters = filterMethods; exports.mappings = mappingMethods; diff --git a/src/collections/Node.js b/src/collections/Node.js index ec36578f..482e9700 100644 --- a/src/collections/Node.js +++ b/src/collections/Node.js @@ -10,10 +10,10 @@ 'use strict'; -const _ = require('lodash'); const Collection = require('../Collection'); const matchNode = require('../matchNode'); +const once = require('../utils/once'); const recast = require('recast'); const Node = recast.types.namedTypes.Node; @@ -186,4 +186,4 @@ function register() { Collection.setDefaultCollectionType(Node); } -exports.register = _.once(register); +exports.register = once(register); diff --git a/src/collections/VariableDeclarator.js b/src/collections/VariableDeclarator.js index ffa2952f..8344cb3f 100644 --- a/src/collections/VariableDeclarator.js +++ b/src/collections/VariableDeclarator.js @@ -10,9 +10,9 @@ 'use strict'; -const _ = require('lodash'); const Collection = require('../Collection'); const NodeCollection = require('./Node'); +const once = require('../utils/once'); const recast = require('recast'); const astNodesAreEquivalent = recast.types.astNodesAreEquivalent; @@ -134,8 +134,8 @@ const transformMethods = { scope = scope.parent; } if (scope) { // identifier must refer to declared variable - - // It may look like we filtered out properties, + + // It may look like we filtered out properties, // but the filter only ignored property "keys", not "value"s // In shorthand properties, "key" and "value" both have an // Identifier with the same structure. @@ -163,5 +163,5 @@ function register() { Collection.registerMethods(transformMethods, VariableDeclarator); } -exports.register = _.once(register); +exports.register = once(register); exports.filters = filterMethods; diff --git a/src/utils/__tests__/intersection-test.js b/src/utils/__tests__/intersection-test.js new file mode 100644 index 00000000..db31f21f --- /dev/null +++ b/src/utils/__tests__/intersection-test.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const intersection = require('../intersection'); + +function test(testCases) { + for (const testName in testCases) { + const testCase = testCases[testName]; + it(testName, function() { + expect(intersection(testCase.input)).toEqual(testCase.output); + }); + } +} + +describe('intersection', function() { + test({ + 'intersects string values': { + input: [['foo', 'bar', 'baz'], ['foo', 'bar'], ['bar', 'baz']], + output: ['bar'], + }, + + 'returns empty list if no intersection': { + input: [['foo', 'bar', 'baz'], ['foo'], ['bar']], + output: [], + }, + }); +}); + diff --git a/src/utils/__tests__/once-test.js b/src/utils/__tests__/once-test.js new file mode 100644 index 00000000..563f5d63 --- /dev/null +++ b/src/utils/__tests__/once-test.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const once = require('../once'); + +describe('once', function() { + it('executes the function only once', function() { + const mock = jest.fn().mockImplementation(foo => foo); + const wrapped = once(mock); + + wrapped('foo'); + const result = wrapped('bar'); + + expect(result).toEqual('foo'); + expect(mock).toHaveBeenCalledTimes(1); + }); +}); + diff --git a/src/utils/__tests__/union-test.js b/src/utils/__tests__/union-test.js new file mode 100644 index 00000000..e5710ed7 --- /dev/null +++ b/src/utils/__tests__/union-test.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const union = require('../union'); + +function test(testCases) { + for (const testName in testCases) { + const testCase = testCases[testName]; + it(testName, function() { + expect(union(testCase.input)).toEqual(testCase.output); + }); + } +} + +describe('union', function() { + test({ + 'unions string values': { + input: [['foo', 'bar', 'baz'], ['foo', 'bar'], ['bar', 'baz']], + output: ['foo', 'bar', 'baz'], + }, + + 'understands empty input arrays': { + input: [[], ['foo'], ['bar']], + output: ['foo', 'bar'], + }, + }); +}); + diff --git a/src/utils/intersection.js b/src/utils/intersection.js new file mode 100644 index 00000000..1fce2014 --- /dev/null +++ b/src/utils/intersection.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +module.exports = function(arrays) { + const result = new Set(arrays[0]); + let resultSize = result.length; + + let i, value, valuesToCheck; + for (i = 1; i < arrays.length; i++) { + valuesToCheck = new Set(arrays[i]); + for (value of result) { + if (!valuesToCheck.has(value)) { + result.delete(value); + resultSize -= 1; + } + if (resultSize === 0) { + return []; + } + } + } + + return Array.from(result); +}; diff --git a/src/utils/once.js b/src/utils/once.js new file mode 100644 index 00000000..d451d0e4 --- /dev/null +++ b/src/utils/once.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +/** + * This replicates lodash's once functionality for our purposes. + */ +module.exports = function(func) { + let called = false; + let result; + return function(...args) { + if (called) { + return result; + } + called = true; + return result = func.apply(this, args); + }; +}; diff --git a/src/utils/union.js b/src/utils/union.js new file mode 100644 index 00000000..1455c067 --- /dev/null +++ b/src/utils/union.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +module.exports = function(arrays) { + const result = new Set(arrays[0]); + + let i,j, array; + for (i = 1; i < arrays.length; i++) { + array = arrays[i]; + for (j = 0; j < array.length; j++) { + result.add(array[j]); + } + } + + return Array.from(result); +}; diff --git a/yarn.lock b/yarn.lock index a0d7efbc..46f84edd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2197,7 +2197,7 @@ lodash.pickby@^4.0.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"