Skip to content

Commit 09bdedd

Browse files
Implement Babel plugin, Jest tests and add readme file
1 parent d6e093f commit 09bdedd

File tree

3 files changed

+1494
-1
lines changed

3 files changed

+1494
-1
lines changed

README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# babel-plugin-check-prop-types
2+
3+
[Babel](https://babeljs.io) plugin to typecheck [React](https://react.dev) components with [legacy PropTypes](https://legacy.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper) compatible with [React 19](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-deprecated-react-apis).
4+
5+
Works with [Function](https://react.dev/learn/your-first-component#defining-a-component) (including Arrow Function) and [Class](https://react.dev/reference/react/Component) (including [@decorated](https://github.com/tc39/proposal-decorators)) components, [React](https://react.dev/learn/extracting-state-logic-into-a-reducer) or [Redux](https://redux.js.org/usage/structuring-reducers/basic-reducer-structure) reducers.
6+
7+
Supports defining [extend](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) class names with `classComponentExtends` (`Component` and `PureComponent` by default) and `classComponentExtendsObject` (`React` by default) array options.
8+
9+
Supports debugging skipped components with `logIgnoredBinding` or `logIgnoredClassComponentExtends` boolean options (enabled by default).
10+
11+
## Examples
12+
13+
Input:
14+
15+
```js
16+
import PropTypes from 'prop-types';
17+
import PropTypesExact from 'prop-types-exact';
18+
import { forbidExtraProps } from 'prop-types-tools';
19+
20+
function FunctionComponent() {}
21+
FunctionComponent.propTypes = { // classical object without wrapper
22+
myProp: PropTypes.number
23+
};
24+
25+
const ArrowFunctionComponent = () => {};
26+
ArrowFunctionComponent.propTypes = PropTypes.exact({ // you can use "exact" on root level from now
27+
myProp: PropTypes.number
28+
});
29+
30+
class ClassComponent extends React.PureComponent {
31+
render() {}
32+
}
33+
ClassComponent.propTypes = PropTypesExact({ // just drop your "PropTypesExact" dependency
34+
myProp: PropTypes.number
35+
});
36+
37+
const anonymousFunction = function () {};
38+
anonymousFunction.displayName = "MyComponent";
39+
anonymousFunction.propTypes = forbidExtraProps({ // "forbidExtraProps" makes no sense anymore
40+
myProp: PropTypes.number
41+
});
42+
```
43+
44+
Output:
45+
46+
```js
47+
import _checkPropTypes from "prop-types/checkPropTypes";
48+
49+
import PropTypes from 'prop-types';
50+
import PropTypesExact from 'prop-types-exact';
51+
import { forbidExtraProps } from 'prop-types-tools';
52+
53+
function FunctionComponent() {
54+
_checkPropTypes(FunctionComponent.propTypes, arguments[0], "prop", FunctionComponent.displayName || "FunctionComponent");
55+
}
56+
FunctionComponent.propTypes = {
57+
myProp: PropTypes.number
58+
};
59+
60+
const ArrowFunctionComponent = _props => {
61+
_checkPropTypes(ArrowFunctionComponent.propTypes, _props, "prop", ArrowFunctionComponent.displayName || "ArrowFunctionComponent");
62+
};
63+
ArrowFunctionComponent.propTypes = PropTypes.exact({
64+
myProp: PropTypes.number
65+
});
66+
67+
class ClassComponent extends React.PureComponent {
68+
render() {
69+
_checkPropTypes(this.constructor.propTypes, this.props, "prop", this.constructor.displayName || "ClassComponent");
70+
}
71+
}
72+
ClassComponent.propTypes = PropTypesExact({
73+
myProp: PropTypes.number
74+
});
75+
76+
const anonymousFunction = function () {
77+
_checkPropTypes(anonymousFunction.propTypes, arguments[0], "prop", anonymousFunction.displayName || "anonymousFunction");
78+
};
79+
anonymousFunction.displayName = "MyComponent";
80+
anonymousFunction.propTypes = forbidExtraProps({
81+
myProp: PropTypes.number
82+
});
83+
```
84+
85+
In addition you can typecheck reducers (actually first argument of everything what looks like function):
86+
87+
```js
88+
import { useReducer } from "react";
89+
import { createStore } from "redux";
90+
91+
function counter(state = 0, action) {
92+
if (action.type === "INCREMENT") {
93+
return state + 1;
94+
} else if (action.type === "DECREMENT") {
95+
return state - 1;
96+
} else {
97+
return state;
98+
}
99+
}
100+
counter.propTypes = PropTypes.number.isRequired;
101+
102+
const ReactComponent = () => {
103+
const [state, dispatch] = useReducer(counter, 0);
104+
};
105+
106+
const reduxStore = createStore(counter, 0);
107+
```
108+
109+
See [tests](https://github.com/NikolayFrantsev/babel-plugin-check-prop-types/blob/master/test.js) for more examples.
110+
111+
## Usage
112+
113+
Install plugin package:
114+
115+
```sh
116+
yarn add --dev babel-plugin-check-prop-types
117+
```
118+
119+
Update [Babel configuration](https://babeljs.io/docs/configuration#javascript-configuration-files):
120+
121+
```js
122+
export default () => {
123+
const plugins = [];
124+
125+
if (process.env.NODE_ENV === "development") { // enable plugin only for development bundle
126+
plugins.push(["check-prop-types", {
127+
// classComponentExtendsObject: ["UI"],
128+
// classComponentExtends: ["App"],
129+
// logIgnoredBinding: false,
130+
// logIgnoredClassComponentExtends: false,
131+
}]);
132+
}
133+
134+
return { plugins };
135+
};
136+
```
137+
138+
Update `react-is` for `prop-types` with [Yarn resolutions](https://classic.yarnpkg.com/lang/en/docs/selective-version-resolutions/) (otherwise you’ll get validation errors for `PropTypes.node`):
139+
```json
140+
{
141+
"resolutions": {
142+
"prop-types/react-is": "19"
143+
}
144+
}
145+
```

0 commit comments

Comments
 (0)