Skip to content

Commit d0a0b5a

Browse files
Kylie Stewartdios-davidryan-roemer
authored
Add Preact support (#76)
Co-authored-by: David Dios <dios.david@thedevcore.net> Co-authored-by: Ryan Roemer <ryan.roemer@formidable.com>
1 parent c3c752b commit d0a0b5a

10 files changed

Lines changed: 2024 additions & 1141 deletions

File tree

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ dist: trusty
33
language: node_js
44

55
node_js:
6-
- "8"
76
- "10"
87
- "12"
9-
- "13"
8+
- "14"
109

1110
branches:
1211
only:
@@ -30,7 +29,7 @@ script:
3029
- yarn run test
3130

3231
after_script:
33-
- coveralls < coverage/lcov.info
32+
- yarn codecov
3433

3534
cache:
3635
yarn: true

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased - 3.1.0 (2020-05-08)
4+
5+
- [#76](https://github.com/FormidableLabs/react-fast-compare/pull/76). Add support for preact/compat.
6+
- [#75](https://github.com/FormidableLabs/react-fast-compare/pull/75). Drop test support for Node 8.
7+
- [#62](https://github.com/FormidableLabs/react-fast-compare/pull/62). Fix TypeScript types by declaring a function instead of a module.
8+
39
## 3.0.2 (2020-05-01)
410

511
- [#71](https://github.com/FormidableLabs/react-fast-compare/pull/71). Extend the `hasArrayBuffer` check to support older IE 11 versions.

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![Bundle Size][bundle_img]](#bundle-size)
55
[![Travis Status][trav_img]][trav_site]
66
[![AppVeyor Status][appveyor_img]][appveyor_site]
7+
[![Coverage Status][cov_img]][cov_site]
78
[![npm version][npm_img]][npm_site]
89
[![Maintenance Status][maintenance_img]](#maintenance-status)
910

@@ -154,12 +155,12 @@ Please see our [contributions guide](./CONTRIBUTING.md).
154155

155156
[trav_img]: https://api.travis-ci.com/FormidableLabs/react-fast-compare.svg
156157
[trav_site]: https://travis-ci.com/FormidableLabs/react-fast-compare
157-
[cov_img]: https://img.shields.io/coveralls/FormidableLabs/react-fast-compare.svg
158-
[cov_site]: https://coveralls.io/r/FormidableLabs/react-fast-compare
158+
[cov_img]: https://codecov.io/gh/FormidableLabs/react-fast-compare/branch/master/graph/badge.svg
159+
[cov_site]: https://codecov.io/gh/FormidableLabs/react-fast-compare
159160
[npm_img]: https://badge.fury.io/js/react-fast-compare.svg
160161
[npm_site]: http://badge.fury.io/js/react-fast-compare
161162
[appveyor_img]: https://ci.appveyor.com/api/projects/status/github/formidablelabs/react-fast-compare?branch=master&svg=true
162163
[appveyor_site]: https://ci.appveyor.com/project/FormidableLabs/react-fast-compare
163-
[bundle_img]: https://img.shields.io/badge/minzipped%20size-627%20B-flatgreen.svg
164+
[bundle_img]: https://img.shields.io/badge/minzipped%20size-639%20B-flatgreen.svg
164165
[downloads_img]: https://img.shields.io/npm/dm/react-fast-compare.svg
165166
[maintenance_img]: https://img.shields.io/badge/maintenance-active-flatgreen.svg

appveyor.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
# Test against this version of Node.js
22
environment:
33
matrix:
4-
- nodejs_version: "8"
54
- nodejs_version: "10"
65
- nodejs_version: "12"
7-
- nodejs_version: "13"
6+
- nodejs_version: "14"
87

98
# Install scripts. (runs after repo cloning)
109
install:

index.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,17 @@ function equal(a, b) {
8888
// custom handling for DOM elements
8989
if (hasElementType && a instanceof Element) return false;
9090

91-
// custom handling for React
91+
// custom handling for React/Preact
9292
for (i = length; i-- !== 0;) {
93-
if (keys[i] === '_owner' && a.$$typeof) {
94-
// React-specific: avoid traversing React elements' _owner.
95-
// _owner contains circular references
96-
// and is not needed when comparing the actual elements (and not their owners)
97-
// .$$typeof and ._store on just reasonable markers of a react element
93+
if ((keys[i] === '_owner' || keys[i] === '__v' || keys[i] === '__o') && a.$$typeof) {
94+
// React-specific: avoid traversing React elements' _owner
95+
// Preact-specific: avoid traversing Preact elements' __v and __o
96+
// __v = $_original / $_vnode
97+
// __o = $_owner
98+
// These properties contain circular references and are not needed when
99+
// comparing the actual elements (and not their owners)
100+
// .$$typeof and ._store on just reasonable markers of elements
101+
98102
continue;
99103
}
100104

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"test-browser": "karma start test/browser/karma.conf.js",
1111
"test-browser-ie": "karma start test/browser/karma.conf.ie.js",
1212
"test-node": "mocha \"test/node/*.spec.js\"",
13-
"test-node-cov": "nyc yarn test-node",
13+
"test-node-cov": "nyc mocha \"test/node/*.spec.js\"",
1414
"test-ts": "tsc --target ES5 --noImplicitAny index.d.ts",
1515
"test": "builder concurrent --buffer eslint test-ts test-node-cov test-browser",
1616
"test-ie": "builder concurrent --buffer eslint test-ts test-node-cov test-browser-ie",
@@ -38,14 +38,18 @@
3838
"devDependencies": {
3939
"@babel/core": "^7.7.5",
4040
"@babel/preset-env": "^7.7.6",
41+
"@testing-library/dom": "^7.5.1",
42+
"@testing-library/preact": "^1.0.2",
4143
"babel-loader": "^8.0.6",
4244
"benchmark": "^2.1.4",
4345
"builder": "^5.0.0",
46+
"codecov": "^3.6.5",
4447
"core-js": "^3.5.0",
45-
"coveralls": "^3.0.9",
4648
"eslint": "^6.7.2",
4749
"fast-deep-equal": "3.1.1",
4850
"fast-deep-equal-git": "epoberezkin/fast-deep-equal#v3.1.1",
51+
"jsdom": "^16.2.2",
52+
"jsdom-global": "^3.0.2",
4953
"karma": "^4.4.1",
5054
"karma-chrome-launcher": "^3.1.0",
5155
"karma-firefox-launcher": "^1.1.0",
@@ -58,8 +62,9 @@
5862
"mocha": "^6.2.2",
5963
"nano-equal": "^2.0.2",
6064
"nyc": "^14.1.1",
65+
"preact": "^10.4.1",
6166
"react": "^16.3.1",
62-
"react-test-renderer": "^16.3.1",
67+
"react-test-renderer": "^16.13.1",
6368
"shallow-equal-fuzzy": "0.0.2",
6469
"sinon": "^7.5.0",
6570
"terser": "^4.4.3",

test/browser/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
'use strict';
22

3-
// Polyfills for IE in React 16.
3+
// Polyfills for IE in React 16 and other dependencies.
4+
require('core-js/features/array/from');
5+
require('core-js/features/object/entries');
6+
require('core-js/features/promise');
47
require('core-js/features/map');
58
require('core-js/features/set');
69
require('core-js/features/weak-map');
7-
require('core-js/features/symbol');
810
require('core-js/features/regexp/flags');
11+
require('core-js/features/symbol');
912

1013
// Re-use node tests.
1114
const testsContext = require.context('..', true, /\.spec\.js$/);

test/browser/karma.conf.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,38 @@
22

33
const path = require('path');
44

5+
// **Debugging Help**
6+
// We normally dislike commented out code, but as Karma doesn't easily produce
7+
// a bundle, debugging bundle errors is a pain, particularly on ie11.
8+
// To help with this, uncomment this plugin to write out all Karma webpack
9+
// assets like `test/browser/index.js` to disk as `~/Desktops/test-browser-index.js`
10+
// and then inspect the failing lines you need.
11+
//
12+
// Add the plugin into config for `webpack` below with:
13+
// ```
14+
// plugins: [
15+
// new WriteAssetPlugin()
16+
// ]
17+
// ```
18+
//
19+
// const fs = require('fs').promises;
20+
// const os = require('os');
21+
//
22+
// class WriteAssetPlugin {
23+
// apply(compiler) {
24+
// compiler.hooks.emit.tapPromise("WriteAssetPlugin", this.writeAsset.bind(this));
25+
// }
26+
//
27+
// async writeAsset(compiler) {
28+
// const { assets } = compiler;
29+
//
30+
// await Promise.all(Object.entries(assets).map(async ([file, src]) => {
31+
// const outFile = path.join(os.homedir(), "Desktop", file.split(path.sep).join("-"));
32+
// await fs.writeFile(outFile, src.source());
33+
// }));
34+
// }
35+
// }
36+
537
module.exports = function(config) {
638
config.set({
739
basePath: '../..',
@@ -31,7 +63,9 @@ module.exports = function(config) {
3163
path.join(
3264
path.dirname(require.resolve('fast-deep-equal-git/package.json')),
3365
'spec'
34-
)
66+
),
67+
// Transpile all of testing-library's preact stuff.
68+
path.dirname(require.resolve('@testing-library/preact/package.json'))
3569
],
3670
loader: 'babel-loader',
3771
options: {

test/node/advanced.spec.js

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
'use strict';
22

33
const assert = require('assert');
4+
const jsdom = require('jsdom-global');
45
const sinon = require('sinon');
6+
57
const React = require('react');
68
const ReactTestRenderer = require('react-test-renderer');
79

10+
const Preact = require('preact/compat');
11+
const PreactTestRenderer = require('@testing-library/preact');
12+
813
const equal = require('../..');
914
const tests = require('./tests');
1015

11-
class ChildWithShouldComponentUpdate extends React.Component {
16+
class ReactChild extends React.Component {
1217
shouldComponentUpdate(nextProps) {
1318
// this.props.children is a h1 with a circular reference to its owner, Container
1419
return !equal(this.props, nextProps);
@@ -18,72 +23,151 @@ class ChildWithShouldComponentUpdate extends React.Component {
1823
}
1924
}
2025

21-
class Container extends React.Component {
26+
class ReactContainer extends React.Component {
2227
render() {
23-
return React.createElement(ChildWithShouldComponentUpdate, {
28+
return React.createElement(ReactChild, {
2429
children: [
2530
React.createElement('h1', this.props.title || ''),
26-
React.createElement('h2', this.props.subtitle || '')
27-
]
31+
React.createElement('h2', this.props.subtitle || ''),
32+
],
33+
});
34+
}
35+
}
36+
37+
class PreactChild extends Preact.Component {
38+
shouldComponentUpdate(nextProps) {
39+
// this.props.children is a h1 with a circular reference to its owner, Container
40+
return !equal(this.props, nextProps);
41+
}
42+
render() {
43+
return null;
44+
}
45+
}
46+
47+
class PreactContainer extends Preact.Component {
48+
render() {
49+
return Preact.createElement(PreactChild, {
50+
children: [
51+
Preact.createElement('h1', this.props.title || ''),
52+
Preact.createElement('h2', this.props.subtitle || ''),
53+
],
2854
});
2955
}
3056
}
3157

3258
describe('advanced', () => {
3359
let sandbox;
3460
let warnStub;
35-
let childRenderSpy;
3661

3762
beforeEach(() => {
3863
sandbox = sinon.createSandbox();
3964
warnStub = sandbox.stub(console, 'warn');
40-
childRenderSpy = sandbox.spy(ChildWithShouldComponentUpdate.prototype, 'render');
4165
});
4266

4367
afterEach(() => {
4468
sandbox.restore();
4569
});
4670

4771
describe('React', () => {
72+
let reactChildRenderSpy;
73+
74+
beforeEach(() => {
75+
reactChildRenderSpy = sandbox.spy(ReactChild.prototype, 'render');
76+
});
77+
78+
describe('element (with circular references)', () => {
79+
it('compares without warning or errors', () => {
80+
const reactApp = ReactTestRenderer.create(
81+
React.createElement(ReactContainer)
82+
);
83+
reactApp.update(React.createElement(ReactContainer));
84+
assert.strictEqual(warnStub.callCount, 0);
85+
});
86+
it('elements of same type and props are equal', () => {
87+
const reactApp = ReactTestRenderer.create(
88+
React.createElement(ReactContainer)
89+
);
90+
reactApp.update(React.createElement(ReactContainer));
91+
assert.strictEqual(reactChildRenderSpy.callCount, 1);
92+
});
93+
it('elements of same type with different props are not equal', () => {
94+
const reactApp = ReactTestRenderer.create(
95+
React.createElement(ReactContainer)
96+
);
97+
reactApp.update(React.createElement(ReactContainer, { title: 'New' }));
98+
assert.strictEqual(reactChildRenderSpy.callCount, 2);
99+
});
100+
});
101+
});
102+
103+
describe('Preact', () => {
104+
let cleanupJsDom;
105+
let preactChildRenderSpy;
106+
107+
beforeEach(() => {
108+
cleanupJsDom = jsdom();
109+
preactChildRenderSpy = sandbox.spy(PreactChild.prototype, 'render');
110+
});
111+
112+
afterEach(() => {
113+
PreactTestRenderer.cleanup();
114+
if (cleanupJsDom) cleanupJsDom();
115+
});
116+
48117
describe('element (with circular references)', () => {
49118
it('compares without warning or errors', () => {
50-
const testRenderer = ReactTestRenderer.create(React.createElement(Container));
51-
testRenderer.update(React.createElement(Container));
119+
const { rerender } = PreactTestRenderer.render(
120+
Preact.createElement(PreactContainer)
121+
);
122+
rerender(Preact.createElement(PreactContainer));
52123
assert.strictEqual(warnStub.callCount, 0);
53124
});
54125
it('elements of same type and props are equal', () => {
55-
const testRenderer = ReactTestRenderer.create(React.createElement(Container));
56-
testRenderer.update(React.createElement(Container));
57-
assert.strictEqual(childRenderSpy.callCount, 1);
126+
const { rerender } = PreactTestRenderer.render(
127+
Preact.createElement(PreactContainer)
128+
);
129+
rerender(Preact.createElement(PreactContainer));
130+
assert.strictEqual(preactChildRenderSpy.callCount, 1);
58131
});
59132
it('elements of same type with different props are not equal', () => {
60-
const testRenderer = ReactTestRenderer.create(React.createElement(Container));
61-
testRenderer.update(React.createElement(Container, { title: 'New' }));
62-
assert.strictEqual(childRenderSpy.callCount, 2);
133+
const { rerender } = PreactTestRenderer.render(
134+
Preact.createElement(PreactContainer)
135+
);
136+
rerender(Preact.createElement(PreactContainer, { title: 'New' }));
137+
assert.strictEqual(preactChildRenderSpy.callCount, 2);
63138
});
64139
});
65140
});
66141

67142
describe('warnings', () => {
68143
describe('circular reference', () => {
69144
it('warns on circular refs but do not throw', () => {
70-
const circularA = {a: 1};
145+
const circularA = { a: 1 };
71146
circularA.self = circularA;
72-
const circularB = {a: 1};
147+
const circularB = { a: 1 };
73148
circularB.self = circularB;
74149
equal(circularA, circularB);
75150
assert.strictEqual(warnStub.callCount, 1);
76151
});
77152
});
78153
describe('basics usage', () => {
79154
it('never warns', () => {
80-
tests.generic.forEach( (suite) => {
81-
suite.tests.forEach( (test) => {
82-
assert.strictEqual(equal(test.value1, test.value2), test.equal, test.description);
155+
tests.generic.forEach((suite) => {
156+
suite.tests.forEach((test) => {
157+
assert.strictEqual(
158+
equal(test.value1, test.value2),
159+
test.equal,
160+
test.description
161+
);
83162
});
84163
});
85-
assert.strictEqual(warnStub.callCount, 0,
86-
`console.warn called ${warnStub.callCount} with arguments: ${JSON.stringify(warnStub.args)}`);
164+
assert.strictEqual(
165+
warnStub.callCount,
166+
0,
167+
`console.warn called ${
168+
warnStub.callCount
169+
} with arguments: ${JSON.stringify(warnStub.args)}`
170+
);
87171
});
88172
});
89173
});

0 commit comments

Comments
 (0)