From 8e1959ca001235feee71b5320167989b1810bf73 Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Fri, 23 Jun 2017 15:01:36 -0700 Subject: [PATCH] Simplify to enumerables + symbols and inherit statics (#24) --- .travis.yml | 3 +-- index.js | 51 +++++++++++++++++++++++++++------------------ package.json | 18 +++++++++++----- tests/.babelrc | 6 ++++++ tests/unit/index.js | 40 ++++++++++++++++++++++++++++------- 5 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 tests/.babelrc diff --git a/.travis.yml b/.travis.yml index 248821a..f2f8b15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ sudo: false language: node_js node_js: + - "8" - "6" - "4" - - "0.12" - - "0.10" after_success: - "npm run cover" - "cat artifacts/lcov.info | ./node_modules/coveralls/bin/coveralls.js" diff --git a/index.js b/index.js index 76b6893..1998daa 100644 --- a/index.js +++ b/index.js @@ -15,35 +15,46 @@ var REACT_STATICS = { type: true }; -var KNOWN_STATICS = { - name: true, - length: true, - prototype: true, - caller: true, - arguments: true, - arity: true -}; - -var isGetOwnPropertySymbolsAvailable = typeof Object.getOwnPropertySymbols === 'function'; +var getOwnPropertySymbols = Object.getOwnPropertySymbols; +var hasOwnProperty = Object.prototype.hasOwnProperty; +var propIsEnumerable = Object.prototype.propertyIsEnumerable; +var getPrototypeOf = Object.getPrototypeOf; +var objectPrototype = getPrototypeOf && getPrototypeOf(Object); -module.exports = function hoistNonReactStatics(targetComponent, sourceComponent, customStatics) { +module.exports = function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) { if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components - var keys = Object.getOwnPropertyNames(sourceComponent); - /* istanbul ignore else */ - if (isGetOwnPropertySymbolsAvailable) { - keys = keys.concat(Object.getOwnPropertySymbols(sourceComponent)); + if (objectPrototype) { + var inheritedComponent = getPrototypeOf(sourceComponent); + if (inheritedComponent && inheritedComponent !== objectPrototype) { + hoistNonReactStatics(targetComponent, inheritedComponent, blacklist); + } } - for (var i = 0; i < keys.length; ++i) { - if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]] && (!customStatics || !customStatics[keys[i]])) { - try { - targetComponent[keys[i]] = sourceComponent[keys[i]]; - } catch (error) { + for (var key in sourceComponent) { + if (!REACT_STATICS[key] && (!blacklist || !blacklist[key])) { + if (hasOwnProperty.call(sourceComponent, key)) { + try { // Avoid failures from read-only properties + targetComponent[key] = sourceComponent[key]; + } catch (e) {} + } + } + } + if (getOwnPropertySymbols) { + var symbols = getOwnPropertySymbols(sourceComponent); + for (var i = 0; i < symbols.length; i++) { + if (!REACT_STATICS[symbols[i]] && (!blacklist || !blacklist[symbols[i]])) { + if (propIsEnumerable.call(sourceComponent, symbols[i])) { + try { // Avoid failures from read-only properties + targetComponent[symbols[i]] = sourceComponent[symbols[i]]; + } catch(e) {} + } } } } + + return targetComponent; } return targetComponent; diff --git a/package.json b/package.json index fa9b542..e9383ad 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,25 @@ "scripts": { "cover": "node node_modules/istanbul/lib/cli.js cover --dir artifacts -- ./node_modules/mocha/bin/_mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec", "lint": "eslint ./index.js", - "test": "mocha tests/unit/ --recursive --compilers js:babel/register --reporter spec" + "test": "mocha tests/unit/ --recursive --compilers js:babel-register --reporter spec" }, "author": "Michael Ridgway ", "license": "BSD-3-Clause", "devDependencies": { - "babel": "^5.0.7", - "chai": "^3.0.0", + "babel": "^6.23.0", + "babel-cli": "^6.24.1", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-react-jsx-source": "^6.22.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-es2016": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-register": "^6.24.1", + "chai": "^4.0.1", "coveralls": "^2.11.1", + "create-react-class": "^15.5.3", "eslint": "^3.8.0", - "istanbul": "^0.3.2", - "mocha": "^2.0.1", + "istanbul": "^0.4.5", + "mocha": "^3.4.2", "pre-commit": "^1.0.7", "react": "^15.0.0" }, diff --git a/tests/.babelrc b/tests/.babelrc new file mode 100644 index 0000000..2603c50 --- /dev/null +++ b/tests/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": ["es2015", "es2016", "react"], + "plugins": [ + "transform-class-properties" + ] +} diff --git a/tests/unit/index.js b/tests/unit/index.js index 4adc152..f61b3ae 100644 --- a/tests/unit/index.js +++ b/tests/unit/index.js @@ -3,12 +3,13 @@ var expect = require('chai').expect; var React = require('react'); +var createReactClass = require('create-react-class'); var hoistNonReactStatics = require('../../index'); describe('hoist-non-react-statics', function () { it('should hoist non react statics', function () { - var Component = React.createClass({ + var Component = createReactClass({ displayName: 'Foo', statics: { foo: 'bar' @@ -18,7 +19,7 @@ describe('hoist-non-react-statics', function () { } }); - var Wrapper = React.createClass({ + var Wrapper = createReactClass({ displayName: 'Bar', render: function () { return ; @@ -32,7 +33,7 @@ describe('hoist-non-react-statics', function () { }); it('should not hoist custom statics', function () { - var Component = React.createClass({ + var Component = createReactClass({ displayName: 'Foo', statics: { foo: 'bar' @@ -42,7 +43,7 @@ describe('hoist-non-react-statics', function () { } }); - var Wrapper = React.createClass({ + var Wrapper = createReactClass({ displayName: 'Bar', render: function () { return ; @@ -55,7 +56,7 @@ describe('hoist-non-react-statics', function () { it('should not hoist statics from strings', function() { var Component = 'input'; - var Wrapper = React.createClass({ + var Wrapper = createReactClass({ render: function() { return ; } @@ -68,17 +69,17 @@ describe('hoist-non-react-statics', function () { it('should hoist symbols', function() { var foo = Symbol('foo'); - var Component = React.createClass({ + var Component = createReactClass({ render: function() { return null; } }); // Manually set static property using Symbol - // since React.createClass doesn't handle symbols passed to static + // since createReactClass doesn't handle symbols passed to static Component[foo] = 'bar'; - var Wrapper = React.createClass({ + var Wrapper = createReactClass({ render: function() { return ; } @@ -89,4 +90,27 @@ describe('hoist-non-react-statics', function () { expect(Wrapper[foo]).to.equal('bar'); }); + it('should inherit class properties', () => { + class A extends React.Component { + static test3 = 'A'; + static test4 = 'D'; + test5 = 'foo'; + } + class B extends A { + static test2 = 'B'; + static test4 = 'DD'; + } + class C { + static test1 = 'C'; + } + const D = hoistNonReactStatics(C, B); + + + expect(D.test1).to.equal('C'); + expect(D.test2).to.equal('B'); + expect(D.test3).to.equal('A'); + expect(D.test4).to.equal('DD'); + expect(D.test5).to.equal(undefined); + }); + });