Skip to content

Commit b09adf7

Browse files
committed
Require Node.js 20 and Ink 6
1 parent 81a606e commit b09adf7

File tree

5 files changed

+80
-104
lines changed

5 files changed

+80
-104
lines changed

.github/workflows/main.yml

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

package.json

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"scripts": {
2222
"pretest": "npm run build",
2323
"prepublish": "npm run build",
24-
"test": "xo && NODE_OPTIONS='--import=tsx/esm' FORCE_COLOR=1 ava",
24+
"test": "xo --react && NODE_OPTIONS='--import=tsx/esm' FORCE_COLOR=1 node --test test.tsx",
2525
"build": "tsc"
2626
},
2727
"files": [
@@ -42,36 +42,32 @@
4242
"command-line"
4343
],
4444
"dependencies": {
45-
"prop-types": "^15.8.1",
46-
"terminal-link": "^3.0.0"
45+
"terminal-link": "^5.0.0"
4746
},
4847
"peerDependencies": {
49-
"ink": ">=4"
48+
"ink": ">=6"
5049
},
5150
"devDependencies": {
52-
"@sindresorhus/tsconfig": "^5.0.0",
53-
"@types/react": "^18.3.3",
54-
"ava": "^6.1.3",
55-
"eslint-config-xo-react": "^0.27.0",
56-
"eslint-plugin-react": "^7.34.2",
57-
"eslint-plugin-react-hooks": "^4.6.2",
58-
"ink": "^5.0.0",
51+
"@sindresorhus/tsconfig": "^8.0.1",
52+
"@types/prop-types": "^15.7.15",
53+
"@types/react": "^19.1.13",
54+
"eslint-config-xo-react": "^0.28.0",
55+
"eslint-plugin-react": "^7.37.5",
56+
"eslint-plugin-react-hooks": "^5.2.0",
57+
"ink": "^6.3.0",
5958
"ink-testing-library": "^4.0.0",
60-
"react": "^18.3.1",
61-
"tsx": "^4.11.0",
62-
"typescript": "^5.4.5",
63-
"xo": "^0.58.0"
64-
},
65-
"ava": {
66-
"extensions": {
67-
"ts": "module",
68-
"tsx": "module"
69-
},
70-
"workerThreads": false
59+
"react": "^19.1.1",
60+
"tsx": "^4.20.5",
61+
"typescript": "^5.9.2",
62+
"xo": "^1.2.2"
7163
},
7264
"xo": {
73-
"extends": [
74-
"xo-react"
75-
]
65+
"rules": {
66+
"@typescript-eslint/no-unsafe-call": "off",
67+
"@typescript-eslint/no-unsafe-assignment": "off",
68+
"react/function-component-definition": "off",
69+
"react/react-in-jsx-scope": "off",
70+
"react/jsx-closing-tag-location": "off"
71+
}
7672
}
7773
}

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ render(
3232

3333
[Supported terminals.](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
3434

35-
For unsupported terminals, the link will be printed in parens after the text: `My website (https://sindresorhus.com)`.
35+
For unsupported terminals, the link will be printed after the text: `My website https://sindresorhus.com`.
3636

3737
#### url
3838

@@ -45,7 +45,7 @@ The URL to link to.
4545
Type: `boolean | (text: string, url: string) => string`\
4646
Default: `true`
4747

48-
Determines whether the URL should be printed in parens after the text for unsupported terminals: `My website (https://sindresorhus.com)`.
48+
Determines whether the URL should be printed after the text for unsupported terminals: `My website https://sindresorhus.com`.
4949

5050
Can also be a function that receives the text and URL and returns a custom fallback string.
5151

source/index.tsx

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import React, {type FC as ReactFC, type ReactNode} from 'react';
1+
import {type FC as ReactFC, type ReactNode} from 'react';
22
import {Transform, Text} from 'ink';
3-
import PropTypes from 'prop-types';
43
import terminalLink from 'terminal-link';
54

6-
export type Props = { // eslint-disable-line unicorn/prevent-abbreviations
5+
export type Props = {
76
readonly children: ReactNode;
87

98
/**
@@ -22,7 +21,7 @@ export type Props = { // eslint-disable-line unicorn/prevent-abbreviations
2221
readonly url: string;
2322

2423
/**
25-
Determines whether the URL should be printed in parens after the text for unsupported terminals: `My website (https://sindresorhus.com)`.
24+
Determines whether the URL should be printed after the text for unsupported terminals: `My website https://sindresorhus.com`.
2625
2726
Can be a boolean or a function that receives the text and URL and returns a custom fallback string.
2827
@@ -50,7 +49,7 @@ An Ink component that creates clickable links in the terminal.
5049
5150
[Supported terminals.](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
5251
53-
For unsupported terminals, the link will be printed in parens after the text: `My website (https://sindresorhus.com)`.
52+
For unsupported terminals, the link will be printed after the text: `My website https://sindresorhus.com`.
5453
5554
@example
5655
```
@@ -65,21 +64,12 @@ render(
6564
);
6665
```
6766
*/
68-
const Link: ReactFC<Props> = ({children, url, fallback = true}) => ( // eslint-disable-line react/function-component-definition
67+
const Link: ReactFC<Props> = ({children, url, fallback = true}) => (
6968
<Transform transform={children => terminalLink(children, url, {fallback})}>
7069
<Text>
7170
{children}
7271
</Text>
7372
</Transform>
7473
);
7574

76-
Link.propTypes = {
77-
children: PropTypes.oneOfType([
78-
PropTypes.arrayOf(PropTypes.node),
79-
PropTypes.node,
80-
]).isRequired,
81-
url: PropTypes.string.isRequired,
82-
fallback: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
83-
};
84-
8575
export default Link;

test.tsx

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,83 @@
11
import process from 'node:process';
2-
import test from 'ava';
3-
import React, {type FC as ReactFC, type ReactNode} from 'react';
2+
import {test, afterEach} from 'node:test';
3+
import assert from 'node:assert/strict';
4+
import React from 'react';
45
import {Text} from 'ink';
56
import {render} from 'ink-testing-library';
67
import Link from './source/index.js';
78

8-
test.afterEach(() => {
9+
afterEach(() => {
910
delete process.env.FORCE_HYPERLINK;
1011
});
1112

12-
test('render', t => {
13+
test('render', () => {
1314
if (process.env.CI) {
14-
t.pass();
1515
return;
1616
}
1717

18-
process.env.FORCE_HYPERLINK = 1;
18+
process.env.FORCE_HYPERLINK = '1';
1919

20-
const {lastFrame} = render(
21-
<Link url='https://sindresorhus.com'>
22-
My{' '}<Text color='green'>Website</Text>
23-
</Link>,
24-
);
25-
console.log(lastFrame());
26-
t.snapshot(lastFrame());
20+
const result = render(<Link url='https://sindresorhus.com'>
21+
My{' '}<Text color='green'>Website</Text>
22+
</Link>);
23+
console.log('render:', result.lastFrame());
24+
// With hyperlinks enabled, should contain the hyperlink escape sequences
25+
assert.ok(result.lastFrame().includes('sindresorhus.com'));
2726
});
2827

29-
test('render fallback', t => {
30-
process.env.FORCE_HYPERLINK = 0;
28+
test('render fallback', () => {
29+
process.env.FORCE_HYPERLINK = '0';
3130

32-
const {lastFrame} = render(
33-
<Link url='https://sindresorhus.com'>
34-
My Website
35-
</Link>,
36-
);
37-
console.log(lastFrame());
38-
t.snapshot(lastFrame());
31+
const result = render(<Link url='https://sindresorhus.com'>
32+
My Website
33+
</Link>);
34+
console.log('render fallback:', result.lastFrame());
35+
// New terminal-link v5 format: "text url" (no parens)
36+
assert.strictEqual(result.lastFrame(), 'My Website https://sindresorhus.com');
3937
});
4038

41-
test('exclude fallback if disabled', t => {
42-
process.env.FORCE_HYPERLINK = 0;
39+
test('exclude fallback if disabled', () => {
40+
process.env.FORCE_HYPERLINK = '0';
4341

44-
const {lastFrame} = render(
45-
<Link url='https://sindresorhus.com' fallback={false}>
46-
My Website
47-
</Link>,
48-
);
49-
console.log(lastFrame());
50-
t.snapshot(lastFrame());
42+
const result = render(<Link url='https://sindresorhus.com' fallback={false}>
43+
My Website
44+
</Link>);
45+
console.log('exclude fallback:', result.lastFrame());
46+
assert.strictEqual(result.lastFrame(), 'My Website');
5147
});
5248

53-
test('include fallback if explicitly enabled', t => {
54-
process.env.FORCE_HYPERLINK = 0;
49+
test('include fallback if explicitly enabled', () => {
50+
process.env.FORCE_HYPERLINK = '0';
5551

56-
const {lastFrame} = render(
57-
<Link fallback url='https://sindresorhus.com'>
58-
My Website
59-
</Link>,
60-
);
61-
console.log(lastFrame());
62-
t.snapshot(lastFrame());
52+
const result = render(<Link fallback url='https://sindresorhus.com'>
53+
My Website
54+
</Link>);
55+
console.log('include fallback:', result.lastFrame());
56+
// New terminal-link v5 format: "text url" (no parens)
57+
assert.strictEqual(result.lastFrame(), 'My Website https://sindresorhus.com');
6358
});
6459

65-
test('custom fallback function', t => {
66-
process.env.FORCE_HYPERLINK = 0;
60+
test('custom fallback function', () => {
61+
process.env.FORCE_HYPERLINK = '0';
6762

6863
const customFallback = (text: string, url: string) => `[${text}](${url})`;
6964

70-
const {lastFrame} = render(
71-
<Link url='https://sindresorhus.com' fallback={customFallback}>
72-
My Website
73-
</Link>,
74-
);
75-
console.log(lastFrame());
76-
t.is(lastFrame(), '[My Website](https://sindresorhus.com)');
65+
const result = render(<Link url='https://sindresorhus.com' fallback={customFallback}>
66+
My Website
67+
</Link>);
68+
console.log('custom fallback:', result.lastFrame());
69+
assert.strictEqual(result.lastFrame(), '[My Website](https://sindresorhus.com)');
7770
});
7871

79-
test('custom fallback function with complex text', t => {
80-
process.env.FORCE_HYPERLINK = 0;
72+
test('custom fallback function with complex text', () => {
73+
process.env.FORCE_HYPERLINK = '0';
8174

8275
const customFallback = (text: string, url: string) => `${text} -> ${url}`;
8376

84-
const {lastFrame} = render(
85-
<Link url='https://example.com/path?query=value' fallback={customFallback}>
86-
Visit <Text color='cyan'>our site</Text>
87-
</Link>,
88-
);
89-
console.log(lastFrame());
77+
const result = render(<Link url='https://example.com/path?query=value' fallback={customFallback}>
78+
Visit <Text color='cyan'>our site</Text>
79+
</Link>);
80+
console.log('complex fallback:', result.lastFrame());
9081
// The text includes ANSI color codes from the cyan Text component
91-
t.regex(lastFrame(), /Visit.*our site.* -> https:\/\/example\.com\/path\?query=value/);
82+
assert.match(result.lastFrame(), /Visit.*our site.* -> https:\/\/example\.com\/path\?query=value/);
9283
});

0 commit comments

Comments
 (0)