From da537c797d42fa22944f45524f7a612f64fb9ccb Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 10 Apr 2019 16:11:26 +0300 Subject: [PATCH] code formatting, and new version --- .gitignore | 3 +- module/umd.js | 243 +++++++++++++++++++++++++++ package-lock.json | 130 ++++---------- package.json | 11 +- src/Comparator.js | 4 +- src/__tests__/components/Checkbox.js | 6 +- src/__tests__/components/Counter.js | 8 +- src/__tests__/components/Input.js | 2 +- src/__tests__/patch.spec.js | 74 ++++---- src/logger.js | 2 +- src/patch.js | 6 +- 11 files changed, 347 insertions(+), 142 deletions(-) create mode 100644 module/umd.js diff --git a/.gitignore b/.gitignore index 1d9b00c..9011acb 100755 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /build npm-debug.log .idea -.DS_Store \ No newline at end of file +.DS_Store +.cache \ No newline at end of file diff --git a/module/umd.js b/module/umd.js new file mode 100644 index 0000000..673fc38 --- /dev/null +++ b/module/umd.js @@ -0,0 +1,243 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.clearRender = factory()); +}(this, function () { 'use strict'; + + const patchClass = (ClassComponent, comparator) => { + const originalRender = ClassComponent.prototype.render; + ClassComponent.prototype.render = function() { + comparator.processChanges(this.props, this.state); + + if (originalRender) { + return originalRender.call(this); + } + + return null; + }; + + return ClassComponent; + }; + + const patchFunction = (FunctionComponent, comparator) => { + const FunctionComponentWrap = (...args) => { + comparator.processChanges(args[0], {}); + + return FunctionComponent(...args); + }; + + return FunctionComponentWrap; + }; + + var patch = (Component, comparator) => { + const isClassComponent = !!( + Component.prototype.render && Component.prototype.isReactComponent + ); + + if (isClassComponent) { + return patchClass(Component, comparator); + } + + return patchFunction(Component, comparator); + }; + + class Logger { + constructor(componentName, log) { + this._componentName = componentName; + this._log = log; + } + + printInit() { + this._log.log( + '%c[clear-render] init for', + 'color: #848d95;', + this._componentName + ); + this._log.log( + '%c[clear-render] render', + 'color: #848d95;', + this._componentName + ); + } + + _printChange(change) { + if (typeof change === 'object') { + this._log.groupCollapsed( + `${change.key} %c [${change.type}]`, + 'color: #848d95; font-style: italic;' + ); + this._log.log( + '%c old ', + 'background: #ff6347; color: #fff', + change.oldValue + ); + this._log.log( + '%c new ', + 'background: #5fba7d; color: #fff', + change.nextValue + ); + this._log.groupEnd(); + } else { + this._log.log(change); + } + } + + printComparisonsResults(propsChanges, stateChanges) { + this._log.group( + `%c[clear-render] re-render #${this._renderCount}`, + 'color: #848d95;', + this._componentName + ); + this._printComparisonResult('props', propsChanges); + this._printComparisonResult('state', stateChanges); + + if (propsChanges.length === 0 && stateChanges.length === 0) { + this._log.log("maybe it's the hooks effect"); + } + + this._log.groupEnd(); + } + + _printComparisonResult(title, changes = []) { + if (changes.length === 0) { + return; + } + this._log.group(title); + changes.forEach(this._printChange.bind(this)); + this._log.groupEnd(); + } + + _differenceBetweenObjects(oldObj = {}, nextObj = {}) { + if (!nextObj || !oldObj) { + return []; + } + let difference = []; + Object.keys(nextObj).forEach(key => { + if (nextObj[key] !== oldObj[key]) { + const type = typeof nextObj[key]; + difference.push({ + key, + type, + oldValue: oldObj[key], + nextValue: nextObj[key], + }); + } + }); + return difference; + } + + _shallowCompareProps(prevProps, nextProps) { + this._propsChanges = this._differenceBetweenObjects(prevProps, nextProps); + } + + _shallowCompareState(prevState, nextState) { + this._stateChanges = this._differenceBetweenObjects(prevState, nextState); + } + + get _isFirstRender() { + return !this._renderCount; + } + + _incrementRenderCount() { + this._renderCount++; + } + } + + class Comparator { + constructor(logger) { + this._renderCount = 0; + + this._prevProps = null; + this._prevState = null; + + this._logger = logger; + } + + processChanges(nextProps, nextState) { + const isFirstRender = !this._renderCount; + if (isFirstRender) { + this._logger.printInit(); + } else { + const propsChanges = this._shallowCompare(this._prevProps, nextProps); + const stateChanges = this._shallowCompare(this._prevState, nextState); + + this._logger.printComparisonsResults(propsChanges, stateChanges); + } + + this._renderCount += 1; + this._prevState = nextState; + this._prevProps = nextProps; + } + + _shallowCompare(oldObj = {}, nextObj = {}) { + if (!nextObj || !oldObj || oldObj === nextObj) { + return []; + } + + let difference = []; + + Object.keys(nextObj).forEach(key => { + if (nextObj[key] !== oldObj[key]) { + const type = typeof nextObj[key]; + + difference.push({ + key, + type, + oldValue: oldObj[key], + nextValue: nextObj[key], + }); + } + }); + + return difference; + } + } + + var index = (...args) => { + if (args.length > 2) { + console.error('[clear-render] Error: Too many arguments'); + return null; + } + + if (args.length === 0) { + console.error('[clear-render] Error: extended React сomponent is requied'); + return null; + } + + let ComponentClass = null; + let forcedDisplayedName = null; + + args.forEach(argument => { + const type = typeof argument; + + switch (type) { + case 'symbol': + case 'string': + { + forcedDisplayedName = argument; + } + break; + default: + { + ComponentClass = argument; + } + break; + } + }); + + if (!ComponentClass) { + console.error('[clear-render] Error: need any React сomponent'); + return null; + } + + const name = forcedDisplayedName || ComponentClass.name; + + const logger = new Logger(name, console); + const comparator = new Comparator(logger); + + return patch(ComponentClass, comparator); + }; + + return index; + +})); diff --git a/package-lock.json b/package-lock.json index 41b6743..013d118 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2047,12 +2047,24 @@ "@babel/types": "^7.3.0" } }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==", "dev": true }, + "@types/node": { + "version": "11.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.2.tgz", + "integrity": "sha512-HOtU5KqROKT7qX/itKHuTtt5fV0iXbheQvrgbLNXFJQBY/eh+VS5vmmTAVlo3qIGMsypm0G4N1t2AXjy1ZicaQ==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -2125,15 +2137,6 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "append-transform": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", @@ -2523,17 +2526,6 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -2727,32 +2719,6 @@ "which": "^1.2.9" } }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", - "dev": true - }, "cssom": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", @@ -3448,12 +3414,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://npm.twiket.com/repository/ott-node-modules/inflight/-/inflight-1.0.6.tgz", @@ -4223,22 +4183,6 @@ "detect-newline": "^2.1.0" } }, - "jest-dom": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/jest-dom/-/jest-dom-3.1.3.tgz", - "integrity": "sha512-V9LdySiA74/spcAKEG3FRMRKnisKlcYr3EeCNYI4n7CWNE7uYg5WoBUHeGXirjWjRYLLZ5vx8rUaR/6x6o75oQ==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "css": "^2.2.3", - "css.escape": "^1.5.1", - "jest-diff": "^24.0.0", - "jest-matcher-utils": "^24.0.0", - "lodash": "^4.17.11", - "pretty-format": "^24.0.0", - "redent": "^2.0.0" - } - }, "jest-each": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", @@ -6818,6 +6762,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", + "dev": true + }, "pretty-format": { "version": "24.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", @@ -6993,16 +6943,6 @@ "util.promisify": "^1.0.0" } }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", - "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, "regenerate": { "version": "1.4.0", "resolved": "https://npm.twiket.com/repository/ott-node-modules/regenerate/-/regenerate-1.4.0.tgz", @@ -7181,6 +7121,25 @@ "glob": "^7.1.3" } }, + "rollup": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.9.3.tgz", + "integrity": "sha512-20iIOjee5n3H6W6CXsVdYs2xw86j4l+LQLM6yACynt+YJCwkqaYNHAjQ/dhVBIKsFpHwPqHamn/GHq+3Zp8ybQ==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "^11.13.2", + "acorn": "^6.1.1" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + } + } + }, "rsvp": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", @@ -7915,21 +7874,6 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", diff --git a/package.json b/package.json index 1a80a0d..98c3699 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "clear-render", "description": "Why did the rendering happen? Developer tool for react.", - "version": "0.1.18", + "version": "1.0.0", "homepage": "https://github.com/itwillwork/clear-render", "license": "MIT", "author": "itwillwork ", @@ -20,14 +20,15 @@ "@babel/preset-react": "^7.0.0", "babel-jest": "^24.1.0", "jest": "^24.1.0", + "prettier": "^1.16.4", "react": "^16.8.0", "react-dom": "^16.8.0", - "react-testing-library": "^6.1.2" + "react-testing-library": "^6.1.2", + "rollup": "^1.9.3" }, - "main": "build/index.js", + "main": "src/index.js", "scripts": { - "build": "babel src/index.js --no-babelrc --presets es2015 --out-file build/index.js", - "build-umd": "babel src/index.js --out-file build/umd/index.js", + "build-umd": "rollup src/index.js --file module/umd.js --format umd --name \"clearRender\"", "test": "jest", "format": "prettier --single-quote --trailing-comma es5 --write \"src/**/*.js\"" }, diff --git a/src/Comparator.js b/src/Comparator.js index 764a436..859026d 100644 --- a/src/Comparator.js +++ b/src/Comparator.js @@ -30,7 +30,7 @@ class Comparator { } let difference = []; - + Object.keys(nextObj).forEach(key => { if (nextObj[key] !== oldObj[key]) { const type = typeof nextObj[key]; @@ -43,7 +43,7 @@ class Comparator { }); } }); - + return difference; } } diff --git a/src/__tests__/components/Checkbox.js b/src/__tests__/components/Checkbox.js index 9ed45c6..13ccbca 100644 --- a/src/__tests__/components/Checkbox.js +++ b/src/__tests__/components/Checkbox.js @@ -23,7 +23,9 @@ class Checkbox extends React.Component { checked={this.state.isChecked} onChange={this.onChange} /> - + {title} {description} @@ -31,4 +33,4 @@ class Checkbox extends React.Component { } } -export default Checkbox; \ No newline at end of file +export default Checkbox; diff --git a/src/__tests__/components/Counter.js b/src/__tests__/components/Counter.js index 7e15820..4109d48 100644 --- a/src/__tests__/components/Counter.js +++ b/src/__tests__/components/Counter.js @@ -7,9 +7,11 @@ export default function Counter(props) { return (
- - {title} - {description} + + {title} + {description}
); } diff --git a/src/__tests__/components/Input.js b/src/__tests__/components/Input.js index de069b7..40e811d 100644 --- a/src/__tests__/components/Input.js +++ b/src/__tests__/components/Input.js @@ -23,4 +23,4 @@ class Input extends React.Component { } } -export default Input; \ No newline at end of file +export default Input; diff --git a/src/__tests__/patch.spec.js b/src/__tests__/patch.spec.js index bb3916d..a763c55 100644 --- a/src/__tests__/patch.spec.js +++ b/src/__tests__/patch.spec.js @@ -1,6 +1,6 @@ import React from 'react'; -import { render, fireEvent, cleanup } from 'react-testing-library' +import { render, fireEvent, cleanup } from 'react-testing-library'; // React class component with state import Checkbox from './components/Checkbox'; @@ -14,13 +14,11 @@ import Comparator from '../Comparator'; import patch from '../patch'; const defaultProps = { - title: "ping", - description: "text", -} + title: 'ping', + description: 'text', +}; -beforeEach(() => { - -}); +beforeEach(() => {}); afterEach(() => { cleanup(); @@ -43,9 +41,10 @@ test('Checkbox, detect changed state', () => { // Assert expect(fakeLogger.printInit.mock.calls.length).toBe(1); expect(fakeLogger.printComparisonsResults.mock.calls.length).toBe(1); - expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual( - [[], [{"key": "isChecked", "nextValue": true, "oldValue": false, "type": "boolean"}]] - ); + expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual([ + [], + [{ key: 'isChecked', nextValue: true, oldValue: false, type: 'boolean' }], + ]); expect(getByTestId('state-isChecked').textContent).toBe('1'); }); @@ -59,16 +58,19 @@ test('Checkbox, detect changed props', () => { const PatchedCheckbox = patch(Checkbox, comparator); // Act - const { rerender, getByTestId } = render(); + const { rerender, getByTestId } = render( + + ); - rerender(); + rerender(); // Assert expect(fakeLogger.printInit.mock.calls.length).toBe(1); expect(fakeLogger.printComparisonsResults.mock.calls.length).toBe(1); - expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual( - [[{"key":"title","type":"string","oldValue":"ping","nextValue":"pong"}],[]] - ); + expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual([ + [{ key: 'title', type: 'string', oldValue: 'ping', nextValue: 'pong' }], + [], + ]); expect(getByTestId('props-title').textContent).toBe('pong'); }); @@ -80,19 +82,22 @@ test('Counter, detect changed props re-render', () => { printComparisonsResults: jest.fn(), }; const comparator = new Comparator(fakeLogger); - const PatchedCounter= patch(Counter, comparator); + const PatchedCounter = patch(Counter, comparator); // Act - const { rerender, getByTestId } = render(); + const { rerender, getByTestId } = render( + + ); - rerender(); + rerender(); // Assert expect(fakeLogger.printInit.mock.calls.length).toBe(1); expect(fakeLogger.printComparisonsResults.mock.calls.length).toBe(1); - expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual( - [[{"key":"title","type":"string","oldValue":"ping","nextValue":"pong"}],[]] - ); + expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual([ + [{ key: 'title', type: 'string', oldValue: 'ping', nextValue: 'pong' }], + [], + ]); expect(getByTestId('props-title').textContent).toBe('pong'); }); @@ -103,10 +108,12 @@ test('Counter, used hooks', () => { printComparisonsResults: jest.fn(), }; const comparator = new Comparator(fakeLogger); - const PatchedCounter= patch(Counter, comparator); + const PatchedCounter = patch(Counter, comparator); // Act - const { rerender, getByTestId } = render(); + const { rerender, getByTestId } = render( + + ); fireEvent.click(getByTestId('button')); @@ -122,22 +129,25 @@ test('Input, detect changed props re-render', () => { printComparisonsResults: jest.fn(), }; const comparator = new Comparator(fakeLogger); - const PatchedInput= patch(Input, comparator); + const PatchedInput = patch(Input, comparator); const inputDefaultProps = { ...defaultProps, - value: "", + value: '', onChange: () => {}, - } + }; // Act - const { rerender, getByTestId } = render(); + const { rerender, getByTestId } = render( + + ); - rerender(); + rerender(); // Assert expect(fakeLogger.printInit.mock.calls.length).toBe(1); expect(fakeLogger.printComparisonsResults.mock.calls.length).toBe(1); - expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual( - [[{"key":"value","type":"string","oldValue":"","nextValue":"i type ..."}],[]] - ); -}); \ No newline at end of file + expect(fakeLogger.printComparisonsResults.mock.calls[0]).toEqual([ + [{ key: 'value', type: 'string', oldValue: '', nextValue: 'i type ...' }], + [], + ]); +}); diff --git a/src/logger.js b/src/logger.js index b372c96..a8365b5 100644 --- a/src/logger.js +++ b/src/logger.js @@ -49,7 +49,7 @@ class Logger { this._printComparisonResult('state', stateChanges); if (propsChanges.length === 0 && stateChanges.length === 0) { - this._log.log('maybe it\'s the hooks effect'); + this._log.log("maybe it's the hooks effect"); } this._log.groupEnd(); diff --git a/src/patch.js b/src/patch.js index 8a14a9e..a9f3367 100644 --- a/src/patch.js +++ b/src/patch.js @@ -24,11 +24,13 @@ const patchFunction = (FunctionComponent, comparator) => { }; export default (Component, comparator) => { - const isClassComponent = !!(Component.prototype.render && Component.prototype.isReactComponent); + const isClassComponent = !!( + Component.prototype.render && Component.prototype.isReactComponent + ); if (isClassComponent) { return patchClass(Component, comparator); } - return patchFunction(Component, comparator); + return patchFunction(Component, comparator); };