Repository: node-fetch/node-fetch (js)

Issue:
Expose more stuff (Blob, File & FormData)
In other help section i have mention ways of posting a FormData, Blob, and a File

Some solutions involves adding same extra dependency on the same thing that node-fetch also depends on and other solutions involves more hacky stuff to get the internals of what node-fetch is using internally.

like: 
```js
import fetch, { Headers, Request, Response } from 'node-fetch'

// Get the internals fetch depends on
const { constructor: Blob } = await new Response().blob()
const { constructor: FormData } = await new Response(new URLSearchParams).formData()
const fd = new FormData(); fd.set('a', new Blob())
const { constructor: File } = fd.get('a')

const file = new File(['hello world'], 'sample.txt', { type: 'text/plain' })
fd.set('a', file)
fetch(url, { method: 'POST', body: fd })
```

it feels very hacky and unnecessary to go around the problem of using the same dependency as node-fetch is using. 
It also requires top level await to work...

I think that we should expose this 3 classes as well so you can just do:

```js
import fetch, { Headers, Request, Response, FormData, Blob, File } from 'node-fetch'

const file = new File(['hello world'], 'sample.txt', { type: 'text/plain' })
const fd = new FormData()
fd.set('a', file)
fetch(url, { method: 'POST', body: fd })
```
it would also be worth exposing the `fetch-blob/from.js` utility functions that turns a file into a Blob or a File without actually reading the hole content into memory... 

i think this would simplify the developers experiences and also making it easier to keep things up to date and avoiding dedupe and using different versions

---

For the v4 milestone, would it also be worth exposing the whatwg Readable/Wrtiable Stream as well?
(So that we take care of exposing nodes builtin whatwg stream whenever possible and falling back to web-streams-polyfill otherwise)

```js
import { stream } from 'node-fetch'
const { TransformStream, ReadableStream, WritableStream, ... } = stream
```
maybe it's too much exposure?
it is also possible to do the same hack here (but you only get ReadableStream)
```js
const ReadableStream = new Response('').body.constructor
const rs = new ReadableStream({ /* ... */ })
fetch(url, { method: 'POST', body: rs })
```
it would be nice to have the same instances of streams so they can be piped to...
piping from a native ReadableStream to a polyfilled WritableStream don't work well together...

Pull request description:
feat: export Blob, File and FormData + utilities
<!-- Thanks for contributing! -->

## Purpose
Making it easier to import and use `FormData, Blob & File`. Installing same dependency as `node-fetch` or otherwise getting them in a round-about way was hacky.

## Changes
This re-export the stuff so it is available to use

___

<!-- Mark the ones you have done and remove unnecessary ones. Add new tasks that fit (like TODOs). -->
- [ ] I updated readme
- [x] I added unit tests (for FormData, Blob, and fileFrom only) 

___

<!-- Add `- Fixes #_NUMBER_` line for every PR/Issue this PR solves. Do not comma separate them. -->
- fix #1454 


Patch:
diff --git a/.github/semantic.yml b/.github/semantic.yml
new file mode 100644
index 0000000..5992a2b
--- /dev/null
+++ b/.github/semantic.yml
@@ -0,0 +1,4 @@
+# https://github.com/zeke/semantic-pull-requests#configuration
+
+# Always validate the PR title, and ignore the commits
+titleOnly: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..881b4cb
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,22 @@
+name: Release
+on:
+  push:
+    branches:
+      - main
+      - next
+      - beta
+      - "*.x" # maintenance releases such as 2.x
+
+jobs:
+  release:
+    name: release
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-node@v2
+        with:
+          node-version: 16
+      - run: npx semantic-release
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..4e20528
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,66 @@
+# Contributing
+
+Thank you for considering to contribute to `node-fetch` 💖
+
+Please note that this project is released with a [Contributor Code of Conduct][./CODE_OF_CONDUCT.md].
+By participating you agree to abide by its terms.
+
+## Setup
+
+Node.js 12.20 or higher is required. Install it from https://nodejs.org/en/. [GitHub's `gh` CLI](https://cli.github.com/) is recommended for the initial setup
+
+1. Fork this repository and clone it to your local machine. Using `gh` you can do this
+
+   ```
+   gh repo fork node-fetch/node-fetch
+   ```
+
+2. After cloning and changing into the `node-fetch` directory, install dependencies and run the tests
+
+   ```
+   npm install
+   npm test
+   ```
+
+## Issues before pull requests
+
+Unless the change is trivial such as a type, please [open an issue first](https://github.com/node-fetch/node-fetch/issues/new) before starting a pull request for a bug fix or a new feature.
+
+After you cloned your fork, create a new branch and implement the changes in them. To start a pull request, you can use the `gh` CLI
+
+```
+gh pr create
+```
+
+## Maintainers only
+
+### Merging the Pull Request & releasing a new version
+
+Releases are automated using [semantic-release](https://github.com/semantic-release/semantic-release).
+The following commit message conventions determine which version is released:
+
+1. `fix: ...` or `fix(scope name): ...` prefix in subject: bumps fix version, e.g. `1.2.3` → `1.2.4`
+2. `feat: ...` or `feat(scope name): ...` prefix in subject: bumps feature version, e.g. `1.2.3` → `1.3.0`
+3. `BREAKING CHANGE:` in body: bumps breaking version, e.g. `1.2.3` → `2.0.0`
+
+Only one version number is bumped at a time, the highest version change trumps the others.
+Besides, publishing a new version to npm, semantic-release also creates a git tag and release
+on GitHub, generates changelogs from the commit messages and puts them into the release notes.
+
+If the pull request looks good but does not follow the commit conventions, update the pull request title and use the <kbd>Squash & merge</kbd> button, at which point you can set a custom commit message.
+
+### Beta/Next/Maintenance releases
+
+`semantic-release` supports pre-releases and maintenance releases.
+
+In order to release a maintenance version, open a pull request against a `[VERSION].x` branch, e.g. `2.x`. As long as the commit conventions documented above are followed, a maintenance version will be released. Breaking changes are not permitted.
+
+In order to release a beta version, create or re-create the `beta` branch based on the latest `main` branch. Then create a pull request against the `beta` branch. When merged into the `beta` branch, a new `...-beta.X` release will be created. Once ready, create a pull request against `main` (or `next` if you prefer). This pull request be merged using `squash & merge`.
+
+**Important**: do not rebase & force push to the `beta` branch while working on pre-releases. Do only use force push to reset the `beta` branch in order to sync it with the latest `main`.
+
+To release a `next` version, create or re-create the `next` branch based on the latest `main` branch. Then create a pull request against the `next` branch. When merged into the `next` branch, a new version is created based on the commit conventions, but published using the `next` dist-tag to npm and marked as pre-release on GitHub.
+
+**Important**: do not rebase & force push to the `next` branch while working on pre-releases. Do only use force push to reset the `next` branch in order to sync it with the latest `main`. Also, when merging `next` into `main`, **do not use squash & merge**! In this particular case, the traditional "Create a merge commit" merge button has to be used, otherwise semantic-release will not be able to match the commit history and won't be able to promote the existing releases in npm or GitHub. If the button is disabled, temporarily enable it in the repository settings. 
+
+For any semantic-release questions, ping [@gr2m](https://github.com/gr2m).
diff --git a/package.json b/package.json
index 57260d3..ae5e196 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,7 @@
   },
   "dependencies": {
     "data-uri-to-buffer": "^4.0.0",
-    "fetch-blob": "^3.1.3",
+    "fetch-blob": "^3.1.4",
     "formdata-polyfill": "^4.0.10"
   },
   "tsd": {
@@ -116,5 +116,16 @@
       }
     ]
   },
-  "runkitExampleFilename": "example.js"
+  "runkitExampleFilename": "example.js",
+  "release": {
+    "branches": [
+      "+([0-9]).x",
+      "main",
+      "next",
+      {
+        "name": "beta",
+        "prerelease": true
+      }
+    ]
+  }
 }
diff --git a/src/index.js b/src/index.js
index 36dd199..f5f3984 100644
--- a/src/index.js
+++ b/src/index.js
@@ -21,10 +21,20 @@ import Request, {getNodeRequestOptions} from './request.js';
 import {FetchError} from './errors/fetch-error.js';
 import {AbortError} from './errors/abort-error.js';
 import {isRedirect} from './utils/is-redirect.js';
+import {FormData} from 'formdata-polyfill/esm.min.js';
 import {isDomainOrSubdomain} from './utils/is.js';
 import {parseReferrerPolicyFromHeader} from './utils/referrer.js';
-
-export {Headers, Request, Response, FetchError, AbortError, isRedirect};
+import {
+	Blob,
+	File,
+	fileFromSync,
+	fileFrom,
+	blobFromSync,
+	blobFrom
+} from 'fetch-blob/from.js';
+
+export {FormData, Headers, Request, Response, FetchError, AbortError, isRedirect};
+export {Blob, File, fileFromSync, fileFrom, blobFromSync, blobFrom};
 
 const supportedSchemas = new Set(['data:', 'http:', 'https:']);
 


Test patch:
diff --git a/test/form-data.js b/test/form-data.js
index 9acbab9..7d81edc 100644
--- a/test/form-data.js
+++ b/test/form-data.js
@@ -1,8 +1,6 @@
 import {FormData as FormDataNode} from 'formdata-node';
-import {FormData} from 'formdata-polyfill/esm.min.js';
-import {Blob} from 'fetch-blob/from.js';
 import chai from 'chai';
-import {Request, Response} from '../src/index.js';
+import {Request, Response, FormData, Blob} from '../src/index.js';
 
 const {expect} = chai;
 
diff --git a/test/main.js b/test/main.js
index cd58e65..5df12c4 100644
--- a/test/main.js
+++ b/test/main.js
@@ -1,28 +1,27 @@
 // Test tools
-import zlib from 'node:zlib';
+import {lookup} from 'node:dns';
 import crypto from 'node:crypto';
-import http from 'node:http';
 import fs from 'node:fs';
-import stream from 'node:stream';
+import http from 'node:http';
 import path from 'node:path';
-import {lookup} from 'node:dns';
+import stream from 'node:stream';
 import vm from 'node:vm';
+import zlib from 'node:zlib';
+
+import {text} from 'stream-consumers';
+import AbortControllerMysticatea from 'abort-controller';
+import abortControllerPolyfill from 'abortcontroller-polyfill/dist/abortcontroller.js';
 import chai from 'chai';
-import chaiPromised from 'chai-as-promised';
 import chaiIterator from 'chai-iterator';
+import chaiPromised from 'chai-as-promised';
 import chaiString from 'chai-string';
 import FormData from 'form-data';
-import {FormData as FormDataNode} from 'formdata-polyfill/esm.min.js';
-import AbortControllerMysticatea from 'abort-controller';
-import abortControllerPolyfill from 'abortcontroller-polyfill/dist/abortcontroller.js';
-import {text} from 'stream-consumers';
-
-// Test subjects
-import Blob from 'fetch-blob';
-import {fileFromSync} from 'fetch-blob/from.js';
 
 import fetch, {
+	Blob,
 	FetchError,
+	fileFromSync,
+	FormData as FormDataNode,
 	Headers,
 	Request,
 	Response
diff --git a/test/response.js b/test/response.js
index 34db312..714cda1 100644
--- a/test/response.js
+++ b/test/response.js
@@ -1,7 +1,6 @@
 import * as stream from 'node:stream';
 import chai from 'chai';
-import Blob from 'fetch-blob';
-import {Response} from '../src/index.js';
+import {Response, Blob} from '../src/index.js';
 import TestServer from './utils/server.js';
 
 const {expect} = chai;