diff --git a/README.md b/README.md index fd24a58..d9cc92d 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,35 @@ It's not trying to interfere with the changing spec by using other properties th ( The current minium supported browser I have chosen to support is the ones that can handle import/export )
( Some parts are lazy loaded when needed ) +### Updating from 2.x to 3.x +v3 removed all top level await that conditionally loaded polyfills like +WritableStream, DOMException, and Blob/File. considering that now all latest +up to date env have this built in globally on `globalThis` namespace. This makes +the file system adapter lighter for ppl who want a smaller bundle and supports +newer engines. + +But if you still need to provide polyfills for older environments +then you can provide your own polyfill and set it up with our config before any +other script is evaluated + +```js + +import config from 'native-file-system-adapter/config.js' +// This is the default config that you could override as needed. +Object.assign(config, { + ReadableStream: globalThis.ReadableStream, + WritableStream: globalThis.WritableStream, + TransformStream: globalThis.TransformStream, + DOMException: globalThis.DOMException, + Blob: globalThis.Blob, + File: globalThis.File +}) +// continue like normal. +import xyz from 'native-file-system-adapter' +``` + + + ### ES import in browser ```html diff --git a/package-lock.json b/package-lock.json index 821b919..61650b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,6 @@ } ], "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.5", - "node-domexception": "^1.0.0" - }, "devDependencies": { "@types/filesystem": "^0.0.32", "rollup": "^2.67.3", @@ -35,7 +31,7 @@ "node": ">=14.8.0" }, "optionalDependencies": { - "web-streams-polyfill": "^3.1.1" + "fetch-blob": "^3.2.0" } }, "node_modules/@babel/code-frame": { @@ -1282,9 +1278,9 @@ } }, "node_modules/fetch-blob": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz", - "integrity": "sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", @@ -1295,6 +1291,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "optional": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -2189,6 +2186,7 @@ } ], "license": "MIT", + "optional": true, "engines": { "node": ">=10.5.0" } @@ -3466,6 +3464,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz", "integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==", + "optional": true, "engines": { "node": ">= 8" } @@ -4517,9 +4516,10 @@ } }, "fetch-blob": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.5.tgz", - "integrity": "sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "optional": true, "requires": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -5172,7 +5172,8 @@ "node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "optional": true }, "normalize-package-data": { "version": "2.5.0", @@ -6097,7 +6098,8 @@ "web-streams-polyfill": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz", - "integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==" + "integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==", + "optional": true }, "which": { "version": "2.0.2", diff --git a/package.json b/package.json index b095c7c..18c0578 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "native-file-system-adapter", - "version": "2.0.3", + "version": "3.0.0", "description": "Native File System API", "main": "src/es6.js", "module": "./src/es6.js", @@ -49,11 +49,7 @@ ], "homepage": "https://github.com/jimmywarting/native-file-system-adapter#readme", "optionalDependencies": { - "web-streams-polyfill": "^3.1.1" - }, - "dependencies": { - "fetch-blob": "^3.1.5", - "node-domexception": "^1.0.0" + "fetch-blob": "^3.2.0" }, "standard": { "globals": [ diff --git a/src/FileSystemWritableFileStream.js b/src/FileSystemWritableFileStream.js index bb8158b..4d030da 100644 --- a/src/FileSystemWritableFileStream.js +++ b/src/FileSystemWritableFileStream.js @@ -1,8 +1,8 @@ -/** @type {typeof WritableStream} */ -const ws = globalThis.WritableStream || await import('https://cdn.jsdelivr.net/npm/web-streams-polyfill@3/dist/ponyfill.es2018.mjs').then(r => r.WritableStream).catch(() => import('web-streams-polyfill').then(r => r.WritableStream)) +import config from './config.js' -// TODO: add types for ws -class FileSystemWritableFileStream extends ws { +const { WritableStream } = config + +class FileSystemWritableFileStream extends WritableStream { constructor (...args) { super(...args) diff --git a/src/adapters/downloader.js b/src/adapters/downloader.js index a1009a1..faa5828 100644 --- a/src/adapters/downloader.js +++ b/src/adapters/downloader.js @@ -1,12 +1,18 @@ /* global Blob, DOMException, Response, MessageChannel */ import { errors } from '../util.js' +import config from '../config.js' + +const { + WritableStream, + TransformStream, + DOMException, + Blob +} = config const { GONE } = errors // @ts-ignore const isSafari = /constructor/i.test(window.HTMLElement) || window.safari || window.WebKitPoint -let TransformStream = globalThis.TransformStream -let WritableStream = globalThis.WritableStream export class FileHandle { constructor (name = 'unkown') { @@ -26,12 +32,6 @@ export class FileHandle { * @param {object} [options={}] */ async createWritable (options = {}) { - if (!TransformStream) { - // @ts-ignore - const ponyfill = await import('https://cdn.jsdelivr.net/npm/web-streams-polyfill@3/dist/ponyfill.es2018.mjs') - TransformStream = ponyfill.TransformStream - WritableStream = ponyfill.WritableStream - } const sw = await navigator.serviceWorker?.getRegistration() const link = document.createElement('a') const ts = new TransformStream() diff --git a/src/adapters/memory.js b/src/adapters/memory.js index f3aa1f8..ae82b3c 100644 --- a/src/adapters/memory.js +++ b/src/adapters/memory.js @@ -1,9 +1,7 @@ import { errors } from '../util.js' -/** @type {typeof window.File} */ -const File = globalThis.File || await import('fetch-blob/file.js').then(m => m.File) -/** @type {typeof window.Blob} */ -const Blob = globalThis.Blob || await import('fetch-blob').then(m => m.Blob) +import config from '../config.js' +const { File, Blob, DOMException } = config const { INVALID, GONE, MISMATCH, MOD_ERR, SYNTAX, SECURITY, DISALLOWED } = errors export class Sink { diff --git a/src/adapters/node.js b/src/adapters/node.js index 235ba9e..5491245 100644 --- a/src/adapters/node.js +++ b/src/adapters/node.js @@ -1,15 +1,34 @@ import fs from 'node:fs/promises' import { join } from 'node:path' -import 'node-domexception' -import Blob from 'fetch-blob' -import { fileFrom } from 'fetch-blob/from.js' import { errors } from '../util.js' -// import mime from 'mime-types' + +import config from '../config.js' + +const { + DOMException +} = config const { INVALID, GONE, MISMATCH, MOD_ERR, SYNTAX } = errors -export class Sink { +/** + * @see https://github.com/node-fetch/fetch-blob/blob/0455796ede330ecffd9eb6b9fdf206cc15f90f3e/index.js#L232 + * @param {*} object + * @returns {object is Blob} + */ +function isBlob (object) { + return ( + object && + typeof object === 'object' && + typeof object.constructor === 'function' && + ( + typeof object.stream === 'function' || + typeof object.arrayBuffer === 'function' + ) && + /^(Blob|File)$/.test(object[Symbol.toStringTag]) + ) +} +export class Sink { /** * @param {fs.FileHandle} fileHandle * @param {number} size @@ -65,7 +84,7 @@ export class Sink { chunk = new Uint8Array(chunk) } else if (typeof chunk === 'string') { chunk = Buffer.from(chunk) - } else if (chunk instanceof Blob) { + } else if (isBlob(chunk)) { for await (const data of chunk.stream()) { const res = await this._fileHandle.writev([data], this._position) this._position += res.bytesWritten @@ -101,6 +120,9 @@ export class FileHandle { await fs.stat(this._path).catch(err => { if (err.code === 'ENOENT') throw new DOMException(...GONE) }) + + // TODO: replace once https://github.com/nodejs/node/issues/37340 is fixed + const { fileFrom } = await import('fetch-blob/from.js') return fileFrom(this._path) } diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..0d7268c --- /dev/null +++ b/src/config.js @@ -0,0 +1,10 @@ +const config = { + ReadableStream: globalThis.ReadableStream, + WritableStream: globalThis.WritableStream, + TransformStream: globalThis.TransformStream, + DOMException: globalThis.DOMException, + Blob: globalThis.Blob, + File: globalThis.File, +} + +export default config diff --git a/test/test-node.js b/test/test-node.js index 14595ea..94f4e83 100644 --- a/test/test-node.js +++ b/test/test-node.js @@ -1,6 +1,8 @@ import { existsSync, mkdirSync, rmdirSync } from 'node:fs' -import { getOriginPrivateDirectory } from '../src/es6.js' +import config from '../src/config.js' import steps from './test.js' +import File from 'fetch-blob/file.js' +import { getOriginPrivateDirectory } from '../src/es6.js' import { cleanupSandboxedFileSystem } from '../test/util.js' let hasFailures = false @@ -12,6 +14,7 @@ async function test (fs, step, root) { console.log(`[OK]: ${fs} ${step.desc}`) } catch (err) { console.log(`[ERR]: ${fs} ${step.desc}\n\t-> ${err.message}`) + console.error(err.stack) hasFailures = true } } @@ -21,6 +24,7 @@ async function start () { if (!existsSync(testFolderPath)) { mkdirSync(testFolderPath) } + config.File = File const root = await getOriginPrivateDirectory(import('../src/adapters/node.js'), './testfolder') const memory = await getOriginPrivateDirectory(import('../src/adapters/memory.js'))