Skip to content

Commit

Permalink
feat: replace node-fetch with fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
darrenyong committed May 29, 2024
1 parent b8f492c commit bf7badc
Show file tree
Hide file tree
Showing 22 changed files with 310 additions and 2 deletions.
9 changes: 8 additions & 1 deletion src/helpers/__snapshots__/utils.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ exports[`availableTargets > returns all available targets 1`] = `
{
"cli": "node %s",
"clients": [
{
"description": "Perform asynchronous HTTP requests with the Fetch API",
"extname": ".js",
"key": "fetch",
"link": "https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch",
"title": "fetch",
},
{
"description": "Lightweight HTTP Request Client Library",
"extname": ".cjs",
Expand All @@ -194,7 +201,7 @@ exports[`availableTargets > returns all available targets 1`] = `
"title": "Axios",
},
],
"default": "axios",
"default": "fetch",
"key": "node",
"title": "Node.js",
},
Expand Down
130 changes: 130 additions & 0 deletions src/targets/node/fetch/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @description
* HTTP code snippet generator for fetch
*
* @author
* @pmdroid
*
* for any questions or issues regarding the generated code snippet, please open an issue mentioning the author.
*/
import type { Client } from '../../index.js';

import stringifyObject from 'stringify-object';

import { CodeBuilder } from '../../../helpers/code-builder.js';
import { getHeaderName } from '../../../helpers/headers.js';

interface FetchOptions {
credentials?: Record<string, string> | null;
}

export const fetch: Client<FetchOptions> = {
info: {
key: 'fetch',
title: 'fetch',
link: 'https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch',
description: 'Perform asynchronous HTTP requests with the Fetch API',
extname: '.js',
},
convert: ({ method, allHeaders, postData, fullUrl }, inputOpts) => {
const opts = {
indent: ' ',
credentials: null,
...inputOpts,
};

const { blank, join, push } = new CodeBuilder({ indent: opts.indent });

const options: Record<string, any> = {
method,
};

if (Object.keys(allHeaders).length) {
options.headers = allHeaders;
}

if (opts.credentials !== null) {
options.credentials = opts.credentials;
}

switch (postData.mimeType) {
case 'application/x-www-form-urlencoded':
options.body = postData.paramsObj ? postData.paramsObj : postData.text;
break;

case 'application/json':
if (postData.jsonObj) {
// Though `fetch` doesn't accept JSON objects in the `body` option we're going to
// stringify it when we add this into the snippet further down.
options.body = postData.jsonObj;
}
break;

case 'multipart/form-data':
if (!postData.params) {
break;
}

// The FormData API automatically adds a `Content-Type` header for `multipart/form-data` content and if we add our own here data won't be correctly transmitted.
// eslint-disable-next-line no-case-declarations -- We're only using `contentTypeHeader` within this block.
const contentTypeHeader = getHeaderName(allHeaders, 'content-type');
if (contentTypeHeader) {
delete allHeaders[contentTypeHeader];
}

push('const form = new FormData();');

postData.params.forEach(param => {
push(`form.append('${param.name}', '${param.value || param.fileName || ''}');`);
});

blank();
break;

default:
if (postData.text) {
options.body = postData.text;
}
}

// If we ultimately don't have any headers to send then we shouldn't add an empty object into the request options.
if (options.headers && !Object.keys(options.headers).length) {
delete options.headers;
}

push(
`const options = ${stringifyObject(options, {
indent: opts.indent,
inlineCharacterLimit: 80,
// The Fetch API body only accepts string parameters, but stringified JSON can be difficult
// to read, so we keep the object as a literal and use this transform function to wrap the
// literal in a `JSON.stringify` call.
transform: (object, property, originalResult) => {
if (property === 'body') {
if (postData.mimeType === 'application/x-www-form-urlencoded') {
return `new URLSearchParams(${originalResult})`;
} else if (postData.mimeType === 'application/json') {
return `JSON.stringify(${originalResult})`;
}
}
return originalResult;
},
})};`,
);
blank();

if (postData.params && postData.mimeType === 'multipart/form-data') {
push('options.body = form;');
blank();
}

push(`fetch('${fullUrl}', options)`);
push('.then(response => response.json())', 1);
push('.then(response => console.log(response))', 1);
push('.catch(err => console.error(err));', 1);

return join();
},
};
10 changes: 10 additions & 0 deletions src/targets/node/fetch/fixtures/application-form-encoded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const options = {
method: 'POST',
headers: {'content-type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({foo: 'bar', hello: 'world'})
};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
17 changes: 17 additions & 0 deletions src/targets/node/fetch/fixtures/application-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const options = {
method: 'POST',
headers: {'content-type': 'application/json'},
body: JSON.stringify({
number: 1,
string: 'f"oo',
arr: [1, 2, 3],
nested: {a: 'b'},
arr_mix: [1, 'a', {arr_mix_nested: []}],
boolean: false
})
};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/cookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET', headers: {cookie: 'foo=bar; bar=baz'}};

fetch('https://httpbin.org/cookies', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/custom-method.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'PROPFIND'};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
14 changes: 14 additions & 0 deletions src/targets/node/fetch/fixtures/full.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const options = {
method: 'POST',
headers: {
cookie: 'foo=bar; bar=baz',
accept: 'application/json',
'content-type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({foo: 'bar'})
};

fetch('https://httpbin.org/anything?foo=bar&foo=baz&baz=abc&key=value', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
14 changes: 14 additions & 0 deletions src/targets/node/fetch/fixtures/headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const options = {
method: 'GET',
headers: {
accept: 'application/json',
'x-foo': 'Bar',
'x-bar': 'Foo',
'quoted-value': '"quoted" \'string\''
}
};

fetch('https://httpbin.org/headers', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/http-insecure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET'};

fetch('http://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
10 changes: 10 additions & 0 deletions src/targets/node/fetch/fixtures/jsonObj-multiline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const options = {
method: 'POST',
headers: {'content-type': 'application/json'},
body: JSON.stringify({foo: 'bar'})
};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
10 changes: 10 additions & 0 deletions src/targets/node/fetch/fixtures/jsonObj-null-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const options = {
method: 'POST',
headers: {'content-type': 'application/json'},
body: JSON.stringify({foo: null})
};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
12 changes: 12 additions & 0 deletions src/targets/node/fetch/fixtures/multipart-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const form = new FormData();
form.append('foo', 'Hello World');
form.append('bar', 'Bonjour le monde');

const options = {method: 'POST'};

options.body = form;

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
11 changes: 11 additions & 0 deletions src/targets/node/fetch/fixtures/multipart-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const form = new FormData();
form.append('foo', 'src/fixtures/files/hello.txt');

const options = {method: 'POST'};

options.body = form;

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'POST', headers: {'Content-Type': 'multipart/form-data'}};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
11 changes: 11 additions & 0 deletions src/targets/node/fetch/fixtures/multipart-form-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const form = new FormData();
form.append('foo', 'bar');

const options = {method: 'POST'};

options.body = form;

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/nested.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET'};

fetch('https://httpbin.org/anything?foo%5Bbar%5D=baz%2Czap&fiz=buz&key=value', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/postdata-malformed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'POST', headers: {'content-type': 'application/json'}};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/query-encoded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET'};

fetch('https://httpbin.org/anything?startTime=2019-06-13T19%3A08%3A25.455Z&endTime=2015-09-15T14%3A00%3A12-04%3A00', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET'};

fetch('https://httpbin.org/anything?foo=bar&foo=baz&baz=abc&key=value', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/short.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'GET'};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
6 changes: 6 additions & 0 deletions src/targets/node/fetch/fixtures/text-plain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const options = {method: 'POST', headers: {'content-type': 'text/plain'}, body: 'Hello World'};

fetch('https://httpbin.org/anything', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
4 changes: 3 additions & 1 deletion src/targets/node/target.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { Target } from '../index.js';

import { axios } from './axios/client.js';
import { fetch } from './fetch/client.js';
import { unirest } from './unirest/client.js';

export const node: Target = {
info: {
key: 'node',
title: 'Node.js',
default: 'axios',
default: 'fetch',
cli: 'node %s',
},
clientsById: {
fetch,
unirest,
axios,
},
Expand Down

0 comments on commit bf7badc

Please sign in to comment.