Skip to content

Commit

Permalink
Require Node.js 18
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Sep 1, 2023
1 parent 356d61c commit a8a3a26
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 136 deletions.
1 change: 0 additions & 1 deletion .github/workflows/main.yml
Expand Up @@ -11,7 +11,6 @@ jobs:
matrix:
node-version:
- 18
- 16
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
45 changes: 19 additions & 26 deletions package.json
Expand Up @@ -11,11 +11,14 @@
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": {
"types": "./distribution/index.d.ts",
"default": "./distribution/index.js"
},
"main": "./distribution/index.js",
"exports": "./distribution/index.js",
"types": "./distribution/index.d.ts",
"engines": {
"node": ">=14.16"
"node": ">=18"
},
"scripts": {
"test": "xo && npm run build && ava --timeout=10m --serial",
Expand Down Expand Up @@ -50,38 +53,31 @@
"node-fetch"
],
"devDependencies": {
"@sindresorhus/tsconfig": "^3.0.1",
"@sindresorhus/tsconfig": "^4.0.0",
"@type-challenges/utils": "^0.1.1",
"@types/body-parser": "^1.19.2",
"@types/busboy": "^0.3.1",
"@types/express": "^4.17.14",
"@types/node": "^18.11.7",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"ava": "^5.3.0",
"body-parser": "^1.20.1",
"busboy": "^0.3.1",
"del-cli": "^4.0.1",
"delay": "^5.0.0",
"@types/busboy": "^1.5.0",
"@types/express": "^4.17.17",
"@types/node": "^20.5.7",
"ava": "^5.3.1",
"body-parser": "^1.20.2",
"busboy": "^1.6.0",
"del-cli": "^5.1.0",
"delay": "^6.0.0",
"expect-type": "^0.16.0",
"express": "^4.18.2",
"form-data": "^4.0.0",
"node-fetch": "^2.6.1",
"pify": "^6.1.0",
"playwright-chromium": "^1.27.1",
"raw-body": "^2.5.1",
"ts-node": "^10.8.1",
"typescript": "^5.1.3",
"xo": "^0.54.2"
"playwright-chromium": "^1.37.1",
"raw-body": "^2.5.2",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"xo": "^0.56.0"
},
"sideEffects": false,
"xo": {
"envs": [
"browser"
],
"globals": [
"globalThis"
],
"rules": {
"unicorn/filename-case": "off",
"@typescript-eslint/ban-ts-comment": "off",
Expand All @@ -93,9 +89,6 @@
}
},
"ava": {
"require": [
"./test/_require.ts"
],
"extensions": {
"ts": "module"
},
Expand Down
9 changes: 4 additions & 5 deletions readme.md
Expand Up @@ -40,7 +40,7 @@
[![Coverage Status](https://codecov.io/gh/sindresorhus/ky/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/ky)
[![](https://badgen.net/bundlephobia/minzip/ky)](https://bundlephobia.com/result?p=ky)

Ky targets [modern browsers](#browser-support) and [Deno](https://github.com/denoland/deno). For older browsers, you will need to transpile and use a [`fetch` polyfill](https://github.com/github/fetch) and [`globalThis` polyfill](https://github.com/es-shims/globalThis). For isomorphic needs (Node.js + browser, like SSR), check out [`ky-universal`](https://github.com/sindresorhus/ky-universal).
Ky targets [modern browsers](#browser-support), Node.js, and Deno.

It's just a tiny file with no dependencies.

Expand Down Expand Up @@ -618,11 +618,11 @@ try {

#### How do I use this in Node.js?

Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
Node.js 18 and later supports `fetch` natively, so you can just use this package directly.

#### How do I use this with a web app (React, Vue.js, etc.) that uses server-side rendering (SSR)?

Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
Same as above.

#### How do I test a browser library that uses this?

Expand Down Expand Up @@ -667,11 +667,10 @@ The latest version of Chrome, Firefox, and Safari.

## Node.js support

Polyfill the needed browser globals or just use [`ky-universal`](https://github.com/sindresorhus/ky-universal).
Node.js 18 and later.

## Related

- [ky-universal](https://github.com/sindresorhus/ky-universal) - Use Ky in both Node.js and browsers
- [got](https://github.com/sindresorhus/got) - Simplified HTTP requests for Node.js
- [ky-hooks-change-case](https://github.com/alice-health/ky-hooks-change-case) - Ky hooks to modify cases on requests and responses of objects

Expand Down
20 changes: 0 additions & 20 deletions source/errors/DOMException.ts

This file was deleted.

15 changes: 5 additions & 10 deletions source/utils/delay.ts
@@ -1,6 +1,5 @@
// https://github.com/sindresorhus/delay/tree/ab98ae8dfcb38e1593286c94d934e70d14a4e111

import {composeAbortError} from '../errors/DOMException.js';
import {type InternalOptions} from '../types/options.js';

export type DelayOptions = {
Expand All @@ -13,21 +12,17 @@ export default async function delay(
): Promise<void> {
return new Promise((resolve, reject) => {
if (signal) {
if (signal.aborted) {
reject(composeAbortError(signal));
return;
}

signal.addEventListener('abort', handleAbort, {once: true});
signal.throwIfAborted();
signal.addEventListener('abort', abortHandler, {once: true});
}

function handleAbort() {
reject(composeAbortError(signal!));
function abortHandler() {
clearTimeout(timeoutId);
reject(signal!.reason);
}

const timeoutId = setTimeout(() => {
signal?.removeEventListener('abort', handleAbort);
signal?.removeEventListener('abort', abortHandler);
resolve();
}, ms);
});
Expand Down
18 changes: 0 additions & 18 deletions test/_require.ts

This file was deleted.

20 changes: 11 additions & 9 deletions test/browser.ts
Expand Up @@ -88,15 +88,14 @@ test.serial('aborting a request', withPage, async (t: ExecutionContext, page: Pa
await page.goto(server.url);
await addKyScriptToPage(page);

const error = await page.evaluate(async (url: string) => {
const errorName = await page.evaluate(async (url: string) => {
const controller = new AbortController();
const request = window.ky(`${url}/test`, {signal: controller.signal}).text();
controller.abort('🦄');
return request.catch(error_ => error_.toString());
controller.abort();
return request.catch(error_ => error_.name);
}, server.url);

// TODO: When targeting Node.js 18, also assert that the error is a DOMException
t.is(error.split(': ')[1], '🦄');
t.is(errorName, 'AbortError');
});

test.serial('should copy origin response info when using `onDownloadProgress`', withPage, async (t: ExecutionContext, page: Page) => {
Expand Down Expand Up @@ -379,7 +378,7 @@ test.serial('FormData with searchParams ("multipart/form-data" parser)', withPag
fileContent += chunk; // eslint-disable-line @typescript-eslint/restrict-plus-operands
}

resolve([{fieldname, filename, encoding, mimetype, fileContent}, undefined]);
resolve([{fieldname, filename, fileContent}, undefined]);
} catch (error_: unknown) {
resolve([null, error_]);
}
Expand All @@ -399,11 +398,14 @@ test.serial('FormData with searchParams ("multipart/form-data" parser)', withPag

t.falsy(error);
t.deepEqual(request.query, {foo: '1'});

t.deepEqual(body, {
fieldname: 'file',
filename: 'my-file',
encoding: '7bit',
mimetype: 'text/plain',
filename: {
filename: 'my-file',
encoding: '7bit',
mimeType: 'text/plain',
},
fileContent: 'bubblegum pie',
});
});
Expand Down
40 changes: 11 additions & 29 deletions test/fetch.ts
@@ -1,25 +1,7 @@
import test from 'ava';
import ky from '../source/index.js';

test.serial('relative URLs are passed to fetch unresolved', async t => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async input => {
if (typeof input !== 'object') {
throw new TypeError('Expect to have an object request');
}

t.true(input.url.startsWith('/'));
return new Response(input.url);
};

t.is(await ky('/unicorn').text(), '/unicorn');
t.is(await ky('/unicorn', {searchParams: {foo: 'bar'}}).text(), '/unicorn?foo=bar');
t.is(await ky('/unicorn#hash', {searchParams: 'foo'}).text(), '/unicorn?foo#hash');
t.is(await ky('/unicorn?old', {searchParams: 'new'}).text(), '/unicorn?new');
t.is(await ky('/unicorn?old#hash', {searchParams: 'new'}).text(), '/unicorn?new#hash');
t.is(await ky('unicorn', {prefixUrl: '/api/'}).text(), '/api/unicorn');
globalThis.fetch = originalFetch;
});
const fixture = 'https://example.com/unicorn';

test('fetch option takes a custom fetch function', async t => {
t.plan(6);
Expand All @@ -32,34 +14,34 @@ test('fetch option takes a custom fetch function', async t => {
return new Response(input.url);
};

t.is(await ky('/unicorn', {fetch: customFetch}).text(), '/unicorn');
t.is(await ky(fixture, {fetch: customFetch}).text(), fixture);
t.is(
await ky('/unicorn', {
await ky(fixture, {
fetch: customFetch,
searchParams: {foo: 'bar'},
}).text(),
'/unicorn?foo=bar',
`${fixture}?foo=bar`,
);
t.is(
await ky('/unicorn#hash', {
await ky(`${fixture}#hash`, {
fetch: customFetch,
searchParams: 'foo',
}).text(),
'/unicorn?foo#hash',
`${fixture}?foo#hash`,
);
t.is(
await ky('/unicorn?old', {
await ky(`${fixture}?old`, {
fetch: customFetch,
searchParams: 'new',
}).text(),
'/unicorn?new',
`${fixture}?new`,
);
t.is(
await ky('/unicorn?old#hash', {
await ky(`${fixture}?old#hash`, {
fetch: customFetch,
searchParams: 'new',
}).text(),
'/unicorn?new#hash',
`${fixture}?new#hash`,
);
t.is(await ky('unicorn', {fetch: customFetch, prefixUrl: '/api/'}).text(), '/api/unicorn');
t.is(await ky('unicorn', {fetch: customFetch, prefixUrl: `${fixture}/api/`}).text(), `${fixture}/api/unicorn`);
});

0 comments on commit a8a3a26

Please sign in to comment.