Skip to content

Commit 9040bd2

Browse files
committed
Require Node.js 20 and React 19
1 parent cc003a7 commit 9040bd2

6 files changed

Lines changed: 62 additions & 50 deletions

File tree

.github/workflows/main.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
node-version:
13-
- 16
14-
- 14
15-
- 12
13+
- 24
14+
- 20
1615
steps:
17-
- uses: actions/checkout@v2
18-
- uses: actions/setup-node@v2
16+
- uses: actions/checkout@v5
17+
- uses: actions/setup-node@v5
1918
with:
2019
node-version: ${{ matrix.node-version }}
2120
- run: npm install

index.d.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {Component as ReactComponent, ComponentClass, HTMLProps, ReactNode} from 'react';
1+
import {
2+
Component as ReactComponent,
3+
type ComponentClass,
4+
type HTMLProps,
5+
type ReactNode,
6+
} from 'react';
27

38
/**
49
Automatically binds your `Component` subclass methods to the instance.
@@ -89,7 +94,7 @@ Can be used to check if your component is running in the browser or if it's bein
8994
*/
9095
export const canUseDOM: boolean;
9196

92-
interface IfProps {
97+
type IfProps = {
9398
/**
9499
Condition to check. Children are only rendered if `true`.
95100
*/
@@ -104,7 +109,7 @@ interface IfProps {
104109
If you need the children to not be evaluated when `condition` is `false`, pass a function to the `render` prop that returns the children.
105110
*/
106111
readonly render?: () => ReactNode;
107-
}
112+
};
108113

109114
/**
110115
React component that renders the children if the `condition` prop is `true`.
@@ -139,7 +144,7 @@ import {If} from 'react-extras';
139144
*/
140145
export class If extends ReactComponent<IfProps> {}
141146

142-
interface ChooseOtherwiseProps {
147+
type ChooseOtherwiseProps = {
143148
/**
144149
Children to render in the default case.
145150
*/
@@ -149,7 +154,7 @@ interface ChooseOtherwiseProps {
149154
If you need the children to not be evaluated when a `<Condition.When>` component has a true condition, pass a function to the `render` prop that returns the children.
150155
*/
151156
readonly render?: () => ReactNode;
152-
}
157+
};
153158

154159
export class ChooseOtherwise extends ReactComponent<ChooseOtherwiseProps> {}
155160

@@ -213,7 +218,7 @@ export class Choose extends ReactComponent {
213218
static Otherwise: typeof ChooseOtherwise;
214219
}
215220

216-
interface ForProps<T> {
221+
type ForProps<T> = {
217222
/**
218223
Items to iterate over. `render` will be called once per item.
219224
*/
@@ -223,7 +228,7 @@ interface ForProps<T> {
223228
Returns the element to render corresponding to an `item`.
224229
*/
225230
readonly render?: (item: T, index: number) => ReactNode;
226-
}
231+
};
227232

228233
/**
229234
React component that iterates over the `of` prop and renders the `render` prop.
@@ -252,7 +257,7 @@ Or you could just use plain JavaScript:
252257
*/
253258
export class For<T> extends ReactComponent<ForProps<T>> {}
254259

255-
interface ImageProps extends HTMLProps<HTMLImageElement> {
260+
type ImageProps = {
256261
/**
257262
URL of the image. Use instead of `src`.
258263
*/
@@ -264,7 +269,7 @@ interface ImageProps extends HTMLProps<HTMLImageElement> {
264269
Default: Hide the image if it fails to load.
265270
*/
266271
readonly fallbackUrl?: string;
267-
}
272+
} & HTMLProps<HTMLImageElement>;
268273

269274
/**
270275
React component that improves the `<img>` element.
@@ -285,7 +290,7 @@ It supports all the props that `<img>` supports, but you use the prop `url` inst
285290
*/
286291
export class Image extends ReactComponent<ImageProps> {}
287292

288-
interface ElementClassProps {
293+
type ElementClassProps = {
289294
/**
290295
Classes to add to the root element.
291296
@@ -299,7 +304,7 @@ interface ElementClassProps {
299304
Either a single class or multiple classes separated by space.
300305
*/
301306
readonly remove?: string;
302-
}
307+
};
303308

304309
/**
305310
Renderless React component that can add and remove classes to the root `<html>` element. It accepts an `add` prop for adding classes, and a `remove` prop for removing classes. Both accept either a single class or multiple classes separated by space.

package.json

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
"types": "./index.d.ts",
1616
"default": "./dist/index.js"
1717
},
18-
"types": "./index.d.ts",
18+
"sideEffects": false,
1919
"engines": {
20-
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
20+
"node": ">=20"
2121
},
2222
"scripts": {
2323
"build": "babel 'source/*.js' --out-dir=dist",
@@ -54,23 +54,23 @@
5454
},
5555
"devDependencies": {
5656
"@ava/babel": "^2.0.0",
57-
"@babel/cli": "^7.17.0",
58-
"@babel/core": "^7.17.2",
59-
"@babel/preset-react": "^7.16.7",
60-
"@babel/register": "^7.17.0",
61-
"@types/react": "^17.0.39",
62-
"ava": "^3.15.0",
57+
"@babel/cli": "^7.28.3",
58+
"@babel/core": "^7.28.4",
59+
"@babel/preset-react": "^7.27.1",
60+
"@babel/register": "^7.28.3",
61+
"@types/react": "^19.1.12",
62+
"ava": "^6.4.1",
6363
"browser-env": "^3.3.0",
6464
"esm": "^3.2.25",
6565
"jest-prop-type-error": "^1.1.0",
66-
"react": "^17.0.2",
67-
"react-dom": "^17.0.2",
68-
"react-test-renderer": "^17.0.2",
69-
"tsd": "^0.19.1",
70-
"xo": "^0.48.0"
66+
"react": "^19.1.1",
67+
"react-dom": "^19.1.1",
68+
"react-test-renderer": "^19.1.1",
69+
"tsd": "^0.33.0",
70+
"xo": "^0.59.0"
7171
},
7272
"peerDependencies": {
73-
"react": ">=17"
73+
"react": ">=19"
7474
},
7575
"xo": {
7676
"envs": [
@@ -82,7 +82,15 @@
8282
"@typescript-eslint/no-unused-vars": "off",
8383
"@typescript-eslint/naming-convention": "off",
8484
"@typescript-eslint/no-unsafe-call": "off",
85-
"node/file-extension-in-import": "off"
85+
"node/file-extension-in-import": "off",
86+
"unicorn/prevent-abbreviations": [
87+
"error",
88+
{
89+
"replacements": {
90+
"props": false
91+
}
92+
}
93+
]
8694
}
8795
},
8896
"ava": {

source/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export {default as autoBind} from 'auto-bind/react';
33
export {default as classNames} from '@sindresorhus/class-names';
44

55
export const isStatelessComponent = Component => !(
6-
typeof Component.prototype !== 'undefined'
6+
Component.prototype !== undefined
77
&& typeof Component.prototype.render === 'function'
88
);
99

@@ -14,9 +14,9 @@ export const getDisplayName = Component => (
1414
);
1515

1616
export const canUseDOM = (
17-
typeof window !== 'undefined'
18-
&& 'document' in window
19-
&& 'createElement' in window.document
17+
globalThis.window !== undefined
18+
&& 'document' in globalThis
19+
&& 'createElement' in globalThis.document
2020
);
2121

2222
export {default as If} from './if.js';

test-d/index.test-d.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,20 @@ const ChooseTest = (props: {success: boolean; error: boolean}) => (
6868

6969
const ImageTest = (
7070
<Image
71-
url="https://sindresorhus.com/unicorn.jpg"
72-
fallbackUrl="https://sindresorhus.com/rainbow.jpg"
71+
url='https://sindresorhus.com/unicorn.jpg'
72+
fallbackUrl='https://sindresorhus.com/rainbow.jpg'
7373
/>
7474
);
7575

7676
const ImageTestNoFallback = (
77-
<Image url="https://sindresorhus.com/unicorn.jpg"/>
77+
<Image url='https://sindresorhus.com/unicorn.jpg'/>
7878
);
7979

8080
const RootTest = (props: {isDarkMode: boolean}) => (
8181
<If condition={props.isDarkMode}>
82-
<RootClass add="dark-mode"/>
83-
<RootClass add="logged-in paid-user" remove="promo"/>
84-
<BodyClass remove="dark-mode"/>
85-
<BodyClass add="logged-in paid-user" remove="promo"/>
82+
<RootClass add='dark-mode'/>
83+
<RootClass add='logged-in paid-user' remove='promo'/>
84+
<BodyClass remove='dark-mode'/>
85+
<BodyClass add='logged-in paid-user' remove='promo'/>
8686
</If>
8787
);

test/test.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ test('<For>', t => {
100100
}/>);
101101

102102
const error = t.throws(() => snapshotJSX(t, <For of={['🌈', '🦄', '😎']}/>), Error);
103-
error.message = error.message.replace(/\n|\r| +(?= )/g, '');
103+
error.message = error.message.replaceAll(/\n|\r| +(?= )/g, '');
104104

105105
const expectedErrorMessage = (
106106
'Warning: Failed prop type: The prop `render` is marked as required '
@@ -110,22 +110,22 @@ test('<For>', t => {
110110
});
111111

112112
test('<Image>', t => {
113-
snapshotJSX(t, <Image url="https://sindresorhus.com/unicorn"/>);
113+
snapshotJSX(t, <Image url='https://sindresorhus.com/unicorn'/>);
114114
});
115115

116116
test('<RootClass/>', t => {
117117
const element = document.documentElement;
118118

119-
renderIntoDocument(<RootClass add="foo"/>);
119+
renderIntoDocument(<RootClass add='foo'/>);
120120
t.true(element.classList.contains('foo'));
121-
renderIntoDocument(<RootClass remove="foo"/>);
121+
renderIntoDocument(<RootClass remove='foo'/>);
122122
t.false(element.classList.contains('foo'));
123123
element.className = '';
124124

125-
renderIntoDocument(<RootClass add="foo bar"/>);
125+
renderIntoDocument(<RootClass add='foo bar'/>);
126126
t.true(element.classList.contains('foo'));
127127
t.true(element.classList.contains('bar'));
128-
renderIntoDocument(<RootClass remove="foo"/>);
128+
renderIntoDocument(<RootClass remove='foo'/>);
129129
t.false(element.classList.contains('foo'));
130130
t.true(element.classList.contains('bar'));
131131
element.className = '';
@@ -134,9 +134,9 @@ test('<RootClass/>', t => {
134134
test('<BodyClass/>', t => {
135135
const element = document.body;
136136

137-
renderIntoDocument(<BodyClass add="foo"/>);
137+
renderIntoDocument(<BodyClass add='foo'/>);
138138
t.true(element.classList.contains('foo'));
139-
renderIntoDocument(<BodyClass remove="foo"/>);
139+
renderIntoDocument(<BodyClass remove='foo'/>);
140140
t.false(element.classList.contains('foo'));
141141
element.className = '';
142142
});

0 commit comments

Comments
 (0)