Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a1a182e

Browse files
authoredMay 18, 2023
breaking: Remove dependency on streams polyfill (#149)
* Remove dependency on streams polyfill * require node v16.7 * change test version * only test ubuntu * remove changelog
1 parent 57e4dae commit a1a182e

File tree

12 files changed

+65
-231
lines changed

12 files changed

+65
-231
lines changed
 

‎.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212

1313
-------------------------------------------------------------------------------------------------
1414

15-
<!-- Mark what you have done, Remove unnecessary ones. Add new tasks that may fit (like TODO's) -->
15+
<!-- Mark what you have done with [x], Remove unnecessary ones. Add new tasks that may fit (like TODO's) -->
1616
- [ ] I prefixed the PR-title with `docs: `, `fix(area): `, `feat(area): ` or `breaking(area): `
17-
- [ ] I updated ./CHANGELOG.md with a link to this PR or Issue
1817
- [ ] I updated the README.md
1918
- [ ] I Added unit test(s)
2019

‎.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ jobs:
1313
test:
1414
strategy:
1515
matrix:
16-
os: [ubuntu-latest, windows-latest, macOS-latest]
17-
node: ["17.3"]
16+
os: [ubuntu-latest]
17+
node: ["16", "18", "20"]
1818

1919
runs-on: ${{ matrix.os }}
2020

2121
steps:
2222
- uses: actions/checkout@v2
2323
- uses: actions/setup-node@v2
2424
with:
25-
node-version: '17.3'
25+
node-version: ${{ matrix.node }}
2626
- run: npm install
2727
- run: npm test
2828
- run: npm run report -- --colors

‎CHANGELOG.md

Lines changed: 0 additions & 98 deletions
This file was deleted.

‎README.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ npm install fetch-blob
2626
- CommonJS was replaced with ESM
2727
- The node stream returned by calling `blob.stream()` was replaced with whatwg streams
2828
- (Read "Differences from other blobs" for more info.)
29-
3029
</details>
3130

3231
<details>
@@ -48,14 +47,10 @@ npm install fetch-blob
4847

4948
```js
5049
// Ways to import
51-
// (PS it's dependency free ESM package so regular http-import from CDN works too)
52-
import Blob from 'fetch-blob'
53-
import File from 'fetch-blob/file.js'
54-
55-
import {Blob} from 'fetch-blob'
56-
import {File} from 'fetch-blob/file.js'
50+
import { Blob } from 'fetch-blob'
51+
import { File } from 'fetch-blob/file.js'
5752

58-
const {Blob} = await import('fetch-blob')
53+
const { Blob } = await import('fetch-blob')
5954

6055

6156
// Ways to read the blob:
@@ -75,7 +70,6 @@ It will not read the content into memory. It will only stat the file for last mo
7570

7671
```js
7772
// The default export is sync and use fs.stat to retrieve size & last modified as a blob
78-
import blobFromSync from 'fetch-blob/from.js'
7973
import {File, Blob, blobFrom, blobFromSync, fileFrom, fileFromSync} from 'fetch-blob/from.js'
8074

8175
const fsFile = fileFromSync('./2-GiB-file.bin', 'application/octet-stream')
@@ -119,7 +113,8 @@ blob = undefined // loosing references will delete the file from disk
119113

120114
### Creating Blobs backed up by other async sources
121115
Our Blob & File class are more generic then any other polyfills in the way that it can accept any blob look-a-like item
122-
An example of this is that our blob implementation can be constructed with parts coming from [BlobDataItem](https://github.com/node-fetch/fetch-blob/blob/8ef89adad40d255a3bbd55cf38b88597c1cd5480/from.js#L32) (aka a filepath) or from [buffer.Blob](https://nodejs.org/api/buffer.html#buffer_new_buffer_blob_sources_options), It dose not have to implement all the methods - just enough that it can be read/understood by our Blob implementation. The minium requirements is that it has `Symbol.toStringTag`, `size`, `slice()` and either a `stream()` or a `arrayBuffer()` method. If you then wrap it in our Blob or File `new Blob([blobDataItem])` then you get all of the other methods that should be implemented in a blob or file
116+
An example of this is that our blob implementation can be constructed with parts coming from [BlobDataItem](https://github.com/node-fetch/fetch-blob/blob/8ef89adad40d255a3bbd55cf38b88597c1cd5480/from.js#L32) (aka a filepath) or from [buffer.Blob](https://nodejs.org/api/buffer.html#buffer_new_buffer_blob_sources_options), It dose not have to implement all the methods - just enough that it can be read/understood by our Blob implementation. The minium requirements is that it has `Symbol.toStringTag`, `size`, `slice()`, `stream()` methods (the stream method
117+
can be as simple as being a sync or async iterator that yields Uint8Arrays. If you then wrap it in our Blob or File `new Blob([blobDataItem])` then you get all of the other methods that should be implemented in a blob or file (aka: text(), arrayBuffer() and type and a ReadableStream)
123118

124119
An example of this could be to create a file or blob like item coming from a remote HTTP request. Or from a DataBase
125120

‎file.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Blob from './index.js'
1+
import { Blob } from './index.js'
22

33
const _File = class File extends Blob {
44
#lastModified = 0
@@ -46,4 +46,3 @@ const _File = class File extends Blob {
4646

4747
/** @type {typeof globalThis.File} */// @ts-ignore
4848
export const File = _File
49-
export default File

‎from.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { tmpdir } from 'node:os'
1010
import process from 'node:process'
1111
import DOMException from 'node-domexception'
1212

13-
import File from './file.js'
14-
import Blob from './index.js'
13+
import { File } from './file.js'
14+
import { Blob } from './index.js'
1515

1616
const { stat, mkdtemp } = fs
1717
let i = 0, tempDir, registry

‎index.js

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
/*! fetch-blob. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
22

3-
// TODO (jimmywarting): in the feature use conditional loading with top level await (requires 14.x)
4-
// Node has recently added whatwg stream into core
5-
6-
import './streams.cjs'
3+
if (!globalThis.ReadableStream) {
4+
try {
5+
const process = await import('node:process').then(m => m.default)
6+
const { emitWarning } = process
7+
try {
8+
process.emitWarning = () => {}
9+
const streams = await import('node:stream/web').then(m => m.default)
10+
Object.assign(globalThis, streams)
11+
process.emitWarning = emitWarning
12+
} catch (error) {
13+
process.emitWarning = emitWarning
14+
throw error
15+
}
16+
} catch (error) {}
17+
}
718

819
// 64 KiB (same size chrome slice theirs blob into Uint8array's)
920
const POOL_SIZE = 65536
1021

11-
/** @param {(Blob | Uint8Array)[]} parts */
12-
async function * toIterator (parts, clone = true) {
22+
/**
23+
* @param {(Blob | Uint8Array)[]} parts
24+
* @param {boolean} clone
25+
* @returns {AsyncIterableIterator<Uint8Array>}
26+
*/
27+
async function * toIterator (parts, clone) {
1328
for (const part of parts) {
14-
if ('stream' in part) {
15-
yield * (/** @type {AsyncIterableIterator<Uint8Array>} */ (part.stream()))
16-
} else if (ArrayBuffer.isView(part)) {
29+
if (ArrayBuffer.isView(part)) {
1730
if (clone) {
1831
let position = part.byteOffset
1932
const end = part.byteOffset + part.byteLength
@@ -26,16 +39,9 @@ async function * toIterator (parts, clone = true) {
2639
} else {
2740
yield part
2841
}
29-
/* c8 ignore next 10 */
3042
} else {
31-
// For blobs that have arrayBuffer but no stream method (nodes buffer.Blob)
32-
let position = 0, b = (/** @type {Blob} */ (part))
33-
while (position !== b.size) {
34-
const chunk = b.slice(position, Math.min(b.size, position + POOL_SIZE))
35-
const buffer = await chunk.arrayBuffer()
36-
position += buffer.byteLength
37-
yield new Uint8Array(buffer)
38-
}
43+
// @ts-ignore TS Think blob.stream() returns a node:stream
44+
yield * part.stream()
3945
}
4046
}
4147
}
@@ -139,11 +145,6 @@ const _Blob = class Blob {
139145
* @return {Promise<ArrayBuffer>}
140146
*/
141147
async arrayBuffer () {
142-
// Easier way... Just a unnecessary overhead
143-
// const view = new Uint8Array(this.size);
144-
// await this.stream().getReader({mode: 'byob'}).read(view);
145-
// return view.buffer;
146-
147148
const data = new Uint8Array(this.size)
148149
let offset = 0
149150
for await (const chunk of toIterator(this.#parts, false)) {
@@ -218,7 +219,7 @@ const _Blob = class Blob {
218219
}
219220
}
220221

221-
const blob = new Blob([], { type: String(type).toLowerCase() })
222+
const blob = new Blob([], { type: `${type}` })
222223
blob.#size = span
223224
blob.#parts = blobParts
224225

@@ -251,4 +252,3 @@ Object.defineProperties(_Blob.prototype, {
251252

252253
/** @type {typeof globalThis.Blob} */
253254
export const Blob = _Blob
254-
export default Blob

‎package.json

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fetch-blob",
3-
"version": "3.1.5",
3+
"version": "4.0.0",
44
"description": "Blob & File implementation in Node.js, originally from node-fetch.",
55
"main": "index.js",
66
"type": "module",
@@ -10,8 +10,7 @@
1010
"file.d.ts",
1111
"index.js",
1212
"index.d.ts",
13-
"from.d.ts",
14-
"streams.cjs"
13+
"from.d.ts"
1514
],
1615
"scripts": {
1716
"test": "node --experimental-loader ./test/http-loader.js ./test/test-wpt-in-node.js",
@@ -26,7 +25,7 @@
2625
"node-fetch"
2726
],
2827
"engines": {
29-
"node": "^12.20 || >= 14.13"
28+
"node": ">=16.7"
3029
},
3130
"author": "Jimmy Wärting <jimmy@warting.se> (https://jimmy.warting.se)",
3231
"license": "MIT",
@@ -35,9 +34,9 @@
3534
},
3635
"homepage": "https://github.com/node-fetch/fetch-blob#readme",
3736
"devDependencies": {
38-
"@types/node": "^18.0.2",
39-
"c8": "^7.11.0",
40-
"typescript": "^4.5.4"
37+
"@types/node": "^16.5.0",
38+
"c8": "^7.13.0",
39+
"typescript": "^5.0.4"
4140
},
4241
"funding": [
4342
{
@@ -50,7 +49,6 @@
5049
}
5150
],
5251
"dependencies": {
53-
"node-domexception": "^1.0.0",
54-
"web-streams-polyfill": "^3.0.3"
52+
"node-domexception": "^1.0.0"
5553
}
5654
}

‎streams.cjs

Lines changed: 0 additions & 51 deletions
This file was deleted.

‎test/http-loader.js

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,29 @@ import { get } from 'node:https'
55
const fetch = url => new Promise(rs => get(url, rs))
66
const cache = new URL('./.cache/', import.meta.url)
77

8-
/**
9-
* @param {string} specifier
10-
* @param {{
11-
* conditions: !Array<string>,
12-
* parentURL: !(string | undefined),
13-
* }} context
14-
* @param {Function} defaultResolve
15-
* @returns {Promise<{ url: string }>}
16-
*/
17-
export async function resolve (specifier, context, defaultResolve) {
18-
const { parentURL = null } = context
8+
export function resolve(specifier, context, nextResolve) {
9+
const { parentURL = null } = context;
1910

2011
// Normally Node.js would error on specifiers starting with 'https://', so
2112
// this hook intercepts them and converts them into absolute URLs to be
2213
// passed along to the later hooks below.
2314
if (specifier.startsWith('https://')) {
2415
return {
25-
url: specifier
26-
}
16+
shortCircuit: true,
17+
url: specifier,
18+
};
2719
} else if (parentURL && parentURL.startsWith('https://')) {
2820
return {
29-
url: new URL(specifier, parentURL).href
30-
}
21+
shortCircuit: true,
22+
url: new URL(specifier, parentURL).href,
23+
};
3124
}
3225

3326
// Let Node.js handle all other specifiers.
34-
return defaultResolve(specifier, context, defaultResolve)
27+
return nextResolve(specifier);
3528
}
3629

37-
export async function load (url, context, defaultLoad) {
30+
export async function load(url, context, nextLoad) {
3831
// For JavaScript to be loaded over the network, we need to fetch and
3932
// return it.
4033
if (url.startsWith('https://')) {
@@ -52,14 +45,15 @@ export async function load (url, context, defaultLoad) {
5245
fs.writeFileSync(cachedFile, data)
5346
}
5447

48+
// This example assumes all network-provided JavaScript is ES module
49+
// code.
5550
return {
56-
// This example assumes all network-provided JavaScript is ES module
57-
// code.
5851
format: 'module',
59-
source: data
52+
shortCircuit: true,
53+
source: data,
6054
}
6155
}
6256

6357
// Let Node.js handle all other URLs.
64-
return defaultLoad(url, context, defaultLoad)
65-
}
58+
return nextLoad(url);
59+
}

‎test/own-misc-test.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,6 @@ test(() => {
6363
assert_equals(blobFromSync, syncBlob)
6464
}, 'default export is named exported blobFromSync')
6565

66-
promise_test(async () => {
67-
const { Blob, default: def } = await import('../index.js')
68-
assert_equals(Blob, def)
69-
}, 'Can use named import - as well as default')
70-
7166
// This was necessary to avoid large ArrayBuffer clones (slice)
7267
promise_test(async t => {
7368
const buf = new Uint8Array(65590)

‎test/test-wpt-in-node.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function test_blob (fn, expectations) {
2222
const blob = fn()
2323
assert_true(blob instanceof Blob)
2424
assert_false(blob instanceof File)
25-
assert_equals(blob.type.toLowerCase(), type)
25+
assert_equals(blob.type.toLowerCase(), type.toLowerCase())
2626
assert_equals(await blob.text(), expected)
2727
t.done()
2828
})
@@ -140,4 +140,7 @@ import('https://wpt.live/FileAPI/blob/Blob-stream.any.js')
140140
import('https://wpt.live/FileAPI/blob/Blob-text.any.js')
141141
import('./own-misc-test.js')
142142

143-
hasFailed && process.exit(1)
143+
if (hasFailed) {
144+
console.log('Tests failed')
145+
process.exit(1)
146+
}

0 commit comments

Comments
 (0)
Please sign in to comment.