@samitouri / QOS-React-2 / commits / 41b8c65f1e

Add react-is package (#12199)

Authoritative brand checking library. Can be used without any dependency on React. Plausible replacement for `React.isValidElement.`

Brian Vaughn committed Feb 11, 2018 at 14:08 UTC 41b8c65f1e81ba48192fbb65fd5c742638ef4f82
9 files changed +353
packages/react-is/README.md new
+83
@@ -0,0 +1,83 @@
1 +# `react-is`
2 +
3 +This package allows you to test arbitrary values and see if they're a particular React type, e.g. React Elements.
4 +
5 +## Installation
6 +
7 +```sh
8 +# Yarn
9 +yarn add react-is
10 +
11 +# NPM
12 +npm install react-is --save
13 +```
14 +
15 +## Usage
16 +
17 +### AsyncMode
18 +
19 +```js
20 +import React from "react";
21 +import * as ReactIs from 'react-is';
22 +
23 +ReactIs.isAsyncMode(<React.unstable_AsyncMode />); // true
24 +ReactIs.typeOf(<React.unstable_AsyncMode />) === ReactIs.AsyncMode; // true
25 +```
26 +
27 +### Context
28 +
29 +```js
30 +import React from "react";
31 +import * as ReactIs from 'react-is';
32 +
33 +const ThemeContext = React.createContext("blue");
34 +
35 +ReactIs.isContextConsumer(<ThemeContext.Consumer />); // true
36 +ReactIs.isContextProvider(<ThemeContext.Provider />); // true
37 +ReactIs.typeOf(<ThemeContext.Provider />) === ReactIs.ContextProvider; // true
38 +ReactIs.typeOf(<ThemeContext.Consumer />) === ReactIs.ContextConsumer; // true
39 +```
40 +
41 +### Element
42 +
43 +```js
44 +import React from "react";
45 +import * as ReactIs from 'react-is';
46 +
47 +ReactIs.isElement(<div />); // true
48 +ReactIs.typeOf(<div />) === ReactIs.Element; // true
49 +```
50 +
51 +### Fragment
52 +
53 +```js
54 +import React from "react";
55 +import * as ReactIs from 'react-is';
56 +
57 +ReactIs.isFragment(<></>); // true
58 +ReactIs.typeOf(<></>) === ReactIs.Fragment; // true
59 +```
60 +
61 +### Portal
62 +
63 +```js
64 +import React from "react";
65 +import ReactDOM from "react-dom";
66 +import * as ReactIs from 'react-is';
67 +
68 +const div = document.createElement("div");
69 +const portal = ReactDOM.createPortal(<div />, div);
70 +
71 +ReactIs.isPortal(portal); // true
72 +ReactIs.typeOf(portal) === ReactIs.Portal; // true
73 +```
74 +
75 +### StrictMode
76 +
77 +```js
78 +import React from "react";
79 +import * as ReactIs from 'react-is';
80 +
81 +ReactIs.isStrictMode(<React.StrictMode />); // true
82 +ReactIs.typeOf(<React.StrictMode />) === ReactIs.StrictMode; // true
83 +```
packages/react-is/index.js new
+12
@@ -0,0 +1,12 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +'use strict';
11 +
12 +export * from './src/ReactIs';
packages/react-is/npm/index.js new
+7
@@ -0,0 +1,7 @@
1 +'use strict';
2 +
3 +if (process.env.NODE_ENV === 'production') {
4 + module.exports = require('./cjs/react-is.production.min.js');
5 +} else {
6 + module.exports = require('./cjs/react-is.development.js');
7 +}
packages/react-is/package.json new
+23
@@ -0,0 +1,23 @@
1 +{
2 + "name": "react-is",
3 + "version": "16.3.0-alpha.0",
4 + "description": "Brand checking of React Elements.",
5 + "main": "index.js",
6 + "repository": "facebook/react",
7 + "keywords": ["react"],
8 + "license": "MIT",
9 + "bugs": {
10 + "url": "https://github.com/facebook/react/issues"
11 + },
12 + "homepage": "https://reactjs.org/",
13 + "peerDependencies": {
14 + "react": "^16.0.0 || 16.3.0-alpha.0"
15 + },
16 + "files": [
17 + "LICENSE",
18 + "README.md",
19 + "index.js",
20 + "cjs/",
21 + "umd/"
22 + ]
23 +}
\ No newline at end of file
packages/react-is/src/ReactIs.js new
+86
@@ -0,0 +1,86 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +'use strict';
11 +
12 +import {
13 + REACT_ASYNC_MODE_TYPE,
14 + REACT_CONTEXT_TYPE,
15 + REACT_ELEMENT_TYPE,
16 + REACT_FRAGMENT_TYPE,
17 + REACT_PORTAL_TYPE,
18 + REACT_PROVIDER_TYPE,
19 + REACT_STRICT_MODE_TYPE,
20 +} from 'shared/ReactSymbols';
21 +
22 +export function typeOf(object: any) {
23 + if (typeof object === 'object' && object !== null) {
24 + const $$typeof = object.$$typeof;
25 +
26 + switch ($$typeof) {
27 + case REACT_ELEMENT_TYPE:
28 + const type = object.type;
29 +
30 + switch (type) {
31 + case REACT_ASYNC_MODE_TYPE:
32 + case REACT_FRAGMENT_TYPE:
33 + case REACT_STRICT_MODE_TYPE:
34 + return type;
35 + default:
36 + const $$typeofType = type.$$typeof;
37 +
38 + switch ($$typeofType) {
39 + case REACT_CONTEXT_TYPE:
40 + case REACT_PROVIDER_TYPE:
41 + return $$typeofType;
42 + default:
43 + return $$typeof;
44 + }
45 + }
46 + case REACT_PORTAL_TYPE:
47 + return $$typeof;
48 + }
49 + }
50 +
51 + return undefined;
52 +}
53 +
54 +export const AsyncMode = REACT_ASYNC_MODE_TYPE;
55 +export const ContextConsumer = REACT_CONTEXT_TYPE;
56 +export const ContextProvider = REACT_PROVIDER_TYPE;
57 +export const Element = REACT_ELEMENT_TYPE;
58 +export const Fragment = REACT_FRAGMENT_TYPE;
59 +export const Portal = REACT_PORTAL_TYPE;
60 +export const StrictMode = REACT_STRICT_MODE_TYPE;
61 +
62 +export function isAsyncMode(object: any) {
63 + return typeOf(object) === REACT_ASYNC_MODE_TYPE;
64 +}
65 +export function isContextConsumer(object: any) {
66 + return typeOf(object) === REACT_CONTEXT_TYPE;
67 +}
68 +export function isContextProvider(object: any) {
69 + return typeOf(object) === REACT_PROVIDER_TYPE;
70 +}
71 +export function isElement(object: any) {
72 + return (
73 + typeof object === 'object' &&
74 + object !== null &&
75 + object.$$typeof === REACT_ELEMENT_TYPE
76 + );
77 +}
78 +export function isFragment(object: any) {
79 + return typeOf(object) === REACT_FRAGMENT_TYPE;
80 +}
81 +export function isPortal(object: any) {
82 + return typeOf(object) === REACT_PORTAL_TYPE;
83 +}
84 +export function isStrictMode(object: any) {
85 + return typeOf(object) === REACT_STRICT_MODE_TYPE;
86 +}
packages/react-is/src/__tests__/ReactIs-test.js new
+103
@@ -0,0 +1,103 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +let React;
13 +let ReactDOM;
14 +let ReactIs;
15 +
16 +describe('ReactIs', () => {
17 + beforeEach(() => {
18 + jest.resetModules();
19 +
20 + React = require('react');
21 + ReactDOM = require('react-dom');
22 + ReactIs = require('react-is');
23 + });
24 +
25 + it('should return undefined for unknown/invalid types', () => {
26 + expect(ReactIs.typeOf('abc')).toBe(undefined);
27 + expect(ReactIs.typeOf(true)).toBe(undefined);
28 + expect(ReactIs.typeOf(123)).toBe(undefined);
29 + expect(ReactIs.typeOf({})).toBe(undefined);
30 + expect(ReactIs.typeOf(null)).toBe(undefined);
31 + expect(ReactIs.typeOf(undefined)).toBe(undefined);
32 + });
33 +
34 + it('should identify async mode', () => {
35 + expect(ReactIs.typeOf(<React.unstable_AsyncMode />)).toBe(
36 + ReactIs.AsyncMode,
37 + );
38 + expect(ReactIs.isAsyncMode(<React.unstable_AsyncMode />)).toBe(true);
39 + expect(ReactIs.isAsyncMode({type: ReactIs.AsyncMode})).toBe(false);
40 + expect(ReactIs.isAsyncMode(<React.StrictMode />)).toBe(false);
41 + expect(ReactIs.isAsyncMode(<div />)).toBe(false);
42 + });
43 +
44 + it('should identify context consumers', () => {
45 + const Context = React.createContext(false);
46 + expect(ReactIs.typeOf(<Context.Consumer />)).toBe(ReactIs.ContextConsumer);
47 + expect(ReactIs.isContextConsumer(<Context.Consumer />)).toBe(true);
48 + expect(ReactIs.isContextConsumer(<Context.Provider />)).toBe(false);
49 + expect(ReactIs.isContextConsumer(<div />)).toBe(false);
50 + });
51 +
52 + it('should identify context providers', () => {
53 + const Context = React.createContext(false);
54 + expect(ReactIs.typeOf(<Context.Provider />)).toBe(ReactIs.ContextProvider);
55 + expect(ReactIs.isContextProvider(<Context.Provider />)).toBe(true);
56 + expect(ReactIs.isContextProvider(<Context.Consumer />)).toBe(false);
57 + expect(ReactIs.isContextProvider(<div />)).toBe(false);
58 + });
59 +
60 + it('should identify elements', () => {
61 + expect(ReactIs.typeOf(<div />)).toBe(ReactIs.Element);
62 + expect(ReactIs.isElement(<div />)).toBe(true);
63 + expect(ReactIs.isElement('div')).toBe(false);
64 + expect(ReactIs.isElement(true)).toBe(false);
65 + expect(ReactIs.isElement(123)).toBe(false);
66 + expect(ReactIs.isElement(null)).toBe(false);
67 + expect(ReactIs.isElement(undefined)).toBe(false);
68 + expect(ReactIs.isElement({})).toBe(false);
69 +
70 + // It should also identify more specific types as elements
71 + const Context = React.createContext(false);
72 + expect(ReactIs.isElement(<Context.Provider />)).toBe(true);
73 + expect(ReactIs.isElement(<Context.Consumer />)).toBe(true);
74 + expect(ReactIs.isElement(<React.Fragment />)).toBe(true);
75 + expect(ReactIs.isElement(<React.unstable_AsyncMode />)).toBe(true);
76 + expect(ReactIs.isElement(<React.StrictMode />)).toBe(true);
77 + });
78 +
79 + it('should identify fragments', () => {
80 + expect(ReactIs.typeOf(<React.Fragment />)).toBe(ReactIs.Fragment);
81 + expect(ReactIs.isFragment(<React.Fragment />)).toBe(true);
82 + expect(ReactIs.isFragment({type: ReactIs.Fragment})).toBe(false);
83 + expect(ReactIs.isFragment('React.Fragment')).toBe(false);
84 + expect(ReactIs.isFragment(<div />)).toBe(false);
85 + expect(ReactIs.isFragment([])).toBe(false);
86 + });
87 +
88 + it('should identify portals', () => {
89 + const div = document.createElement('div');
90 + const portal = ReactDOM.createPortal(<div />, div);
91 + expect(ReactIs.typeOf(portal)).toBe(ReactIs.Portal);
92 + expect(ReactIs.isPortal(portal)).toBe(true);
93 + expect(ReactIs.isPortal(div)).toBe(false);
94 + });
95 +
96 + it('should identify strict mode', () => {
97 + expect(ReactIs.typeOf(<React.StrictMode />)).toBe(ReactIs.StrictMode);
98 + expect(ReactIs.isStrictMode(<React.StrictMode />)).toBe(true);
99 + expect(ReactIs.isStrictMode({type: ReactIs.StrictMode})).toBe(false);
100 + expect(ReactIs.isStrictMode(<React.unstable_AsyncMode />)).toBe(false);
101 + expect(ReactIs.isStrictMode(<div />)).toBe(false);
102 + });
103 +});
scripts/rollup/bundles.js
+10
@@ -234,6 +234,16 @@ const bundles = [
234 global: 'ReactCallReturn',
235 externals: [],
236 },
237 +
238 + /******* React Is *******/
239 + {
240 + label: 'react-is',
241 + bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD],
242 + moduleType: ISOMORPHIC,
243 + entry: 'react-is',
244 + global: 'ReactIs',
245 + externals: [],
246 + },
247 ];
248
249 // Based on deep-freeze by substack (public domain)
scripts/rollup/results.json
+28
@@ -398,6 +398,34 @@
398 "packageName": "react-reconciler",
399 "size": 41327,
400 "gzip": 13133
401 + },
402 + {
403 + "filename": "react-is.development.js",
404 + "bundleType": "NODE_DEV",
405 + "packageName": "react-is",
406 + "size": 3358,
407 + "gzip": 1015
408 + },
409 + {
410 + "filename": "react-is.production.min.js",
411 + "bundleType": "NODE_PROD",
412 + "packageName": "react-is",
413 + "size": 1433,
414 + "gzip": 607
415 + },
416 + {
417 + "filename": "react-is.development.js",
418 + "bundleType": "UMD_DEV",
419 + "packageName": "react-is",
420 + "size": 3547,
421 + "gzip": 1071
422 + },
423 + {
424 + "filename": "react-is.production.min.js",
425 + "bundleType": "UMD_PROD",
426 + "packageName": "react-is",
427 + "size": 1515,
428 + "gzip": 670
429 }
430 ]
431 }
\ No newline at end of file
scripts/rollup/validate/eslintrc.umd.js
+1
@@ -17,6 +17,7 @@ module.exports = {
17 // UMD wrapper code
18 // TODO: this is too permissive.
19 // Ideally we should only allow these *inside* the UMD wrapper.
20 + exports: true,
21 module: true,
22 define: true,
23 require: true,