浏览代码

Add true ESM

Arjun Barrett 4 年之前
父节点
当前提交
9e442e99c5

+ 2 - 0
CHANGELOG.md

@@ -1,3 +1,5 @@
+## 0.3.6
+- Allowed true ESM imports
 ## 0.3.4
 - Fixed rare overflow bug causing corruption
 - Added async stream termination

+ 7 - 0
README.md

@@ -56,6 +56,13 @@ Note that tree shaking is completely unsupported from the CDN
 <!-- Now, the global variable fflate contains the library -->
 ```
 
+If you want to avoid using build tools entirely:
+```js
+// This should only ever be done if you have no build tools, otherwise the
+// standard ESM import is more stable.
+import * as fflate from 'fflate/esm/browser.js';
+```
+
 And use:
 ```js
 // This is an ArrayBuffer of data

+ 1 - 1
docs/interfaces/asyncgzipoptions.md

@@ -89,4 +89,4 @@ ___
 *Inherited from [GzipOptions](gzipoptions.md).[mtime](gzipoptions.md#mtime)*
 
 When the file was last modified. Defaults to the current time.
-Set this to 0 to avoid specifying a modification date entirely.
+If you're using GZIP, set this to 0 to avoid revealing a modification date entirely.

+ 1 - 1
docs/interfaces/gzipoptions.md

@@ -73,4 +73,4 @@ ___
 • `Optional` **mtime**: Date \| string \| number
 
 When the file was last modified. Defaults to the current time.
-Set this to 0 to avoid specifying a modification date entirely.
+If you're using GZIP, set this to 0 to avoid revealing a modification date entirely.

+ 9 - 8
package.json

@@ -1,15 +1,15 @@
 {
   "name": "fflate",
-  "version": "0.3.5",
+  "version": "0.3.6",
   "description": "High performance (de)compression in an 8kB package",
-  "main": "lib/index.js",
-  "module": "esm/index.js",
-  "types": "lib/index.d.ts",
-  "unpkg": "umd/index.js",
-  "jsdelivr": "umd/index.js",
+  "main": "./lib/index.js",
+  "module": "./esm/index.mjs",
+  "types": "./lib/index.d.ts",
+  "unpkg": "./umd/index.js",
+  "jsdelivr": "./umd/index.js",
   "browser": {
     "./lib/node-worker.js": "./lib/worker.js",
-    "./esm/node-worker.js": "./esm/worker.js"
+    "./esm/index.mjs": "./esm/browser.js"
   },
   "targets": {
     "main": false,
@@ -47,7 +47,7 @@
     "build:lib": "tsc && tsc --project tsconfig.esm.json && yarn build:umd",
     "build:umd": "SC=buildUMD yarn script",
     "build:rewrite": "SC=rewriteBuilds yarn script",
-    "build:demo": "parcel build src/demo/index.html --dist-dir demo",
+    "build:demo": "tsc --project tsconfig.demo.json && parcel build src/demo/index.html --dist-dir demo",
     "build:docs": "typedoc --mode library --plugin typedoc-plugin-markdown --hideProjectName --hideBreadcrumbs --readme none --disableSources --excludePrivate --excludeProtected --out docs/ src/index.ts",
     "test": "TS_NODE_PROJECT=test/tsconfig.json uvu -b -r ts-node/register test",
     "prepack": "yarn build && yarn test"
@@ -64,6 +64,7 @@
     "preact": "^10.5.5",
     "react": "^17.0.1",
     "react-dom": "^17.0.1",
+    "rmwc": "^6.1.4",
     "terser": "^5.3.8",
     "tiny-inflate": "*",
     "ts-node": "^9.0.0",

+ 17 - 5
scripts/rewriteBuilds.ts

@@ -1,6 +1,18 @@
-import { readFileSync, writeFileSync } from 'fs';
+import { readFileSync, writeFileSync, unlinkSync } from 'fs';
 import { join } from 'path';
-for (const d of ['lib', 'esm']) {
-  const f = join(__dirname, '..', d, 'index.js');
-  writeFileSync(f, readFileSync(f, 'utf-8').replace(/\/\*\* \@class \*\//g, '/*#__PURE__*/'));
-}
+const atClass = /\/\*\* \@class \*\//g, pure = '/*#__PURE__*/';
+const libIndex = join(__dirname, '..', 'lib', 'index.js');
+writeFileSync(libIndex, readFileSync(libIndex, 'utf-8').replace(atClass, pure));
+const esmDir = join(__dirname, '..', 'esm');
+const esmIndex = join(esmDir, 'index.js'),
+      esmWK = join(esmDir, 'worker.js'),
+      esmNWK = join(esmDir, 'node-worker.js');
+const esm = readFileSync(esmIndex, 'utf-8').replace(atClass, pure);
+const wk = readFileSync(esmWK, 'utf-8'),
+      nwk = readFileSync(esmNWK, 'utf-8');
+unlinkSync(esmIndex), unlinkSync(esmWK), unlinkSync(esmNWK);
+const workerImport = /import wk from '\.\/node-worker';/;
+const defaultExport = /export default/;
+const constDecl = 'var wk =';
+writeFileSync(join(esmDir, 'index.mjs'), esm.replace(workerImport, nwk.replace(defaultExport, constDecl)));
+writeFileSync(join(esmDir, 'browser.js'), esm.replace(workerImport, wk.replace(defaultExport, constDecl)));

+ 51 - 5
src/demo/App.tsx

@@ -3,11 +3,57 @@ import FilePicker from './components/file-picker';
 
 const App: FC = () => {
   return (
-    <div>
-      <FilePicker onFiles={f => {
-        console.log(f);
-      }} onError={console.log} onDrag={() => {}}>Hi</FilePicker>
-    </div>
+    <>
+      <div style={{
+        display: 'flex',
+        fontSize: '70px',
+        justifyContent: 'space-between',
+        flexDirection: 'row',
+        overflow: 'hidden',
+        width: '100%',
+        fontWeight: 'bold'
+      }}>
+        <div style={{ paddingLeft: '0.25em' }}>
+          fflate
+          <div style={{
+            color: 'gray',
+            fontSize: '0.25em',
+            fontWeight: 'lighter'
+          }}>a fast compression library by <a href="//github.com/101arrowz" style={{ color: 'gray' }}>101arrowz</a></div>
+        </div>
+        <a href="//github.com/101arrowz/fflate">
+          <svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="white">
+            <path d="M0 0l115 115h15l12 27 108 108V0z" fill="black"/>
+            <path d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16" style={{ transformOrigin: '130px 106px' }} />
+            <path d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"/>
+          </svg>
+        </a>
+      </div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'center',
+        alignItems: 'center',
+        flexDirection: 'column',
+        textAlign: 'center',
+        width: '100%',
+        flex: 1
+      }}>
+        <div style={{ maxWidth: '80%', fontSize: 'calc(18px + 0.6vw)', paddingTop: '4vh', paddingBottom: '2vh' }}>
+          You've found <a href="//npmjs.com/package/fflate">fflate</a>, the fastest pure JavaScript compression library in existence.
+          <br /><br />
+          You can both pack and expand Zlib, GZIP, DEFLATE, or ZIP files very quickly with just a few lines of code.
+          <br /><br />
+          Weighing in at a measly 8kB for basic (de)compression, you don't need to worry about your bundle size ballooning.
+          <br /><br />
+          Despite utilizing multiple cores, supporting data streams, and being very memory efficient, fflate is compatible with both Node.js and browsers as old as IE11.
+          <br /><br />
+          You can read more about fflate on <a href="//github.com/package/fflate">GitHub</a>. Try the demo below to see its performance for yourself.
+        </div>
+        <FilePicker allowDirs onFiles={f => {
+          console.log(f);
+        }} onError={console.log} onDrag={() => {}} />
+      </div>
+    </>
   );
 }
 

+ 14 - 0
src/demo/components/code-box/index.tsx

@@ -0,0 +1,14 @@
+import React, { FC } from 'react';
+import streamify from './stream-adapter';
+
+const canStream = 'stream' in File.prototype;
+
+type Preset = {
+  fflate: string;
+  uzip: string;
+  jszip: string;
+};
+
+const presets: Record<string, string> = {
+  
+}

+ 17 - 0
src/demo/components/code-box/stream-adapter.tsx

@@ -0,0 +1,17 @@
+import { AsyncDeflate } from '../../..';
+export default (stream: AsyncDeflate) => {
+  const writable = new WritableStream({
+    write(dat: Uint8Array) { stream.push(dat); },
+    close() { stream.push(new Uint8Array(0), true); }
+  });
+  const readable = new ReadableStream({
+    start(controller: ReadableStreamDefaultController<Uint8Array>) {
+      stream.ondata = (err, chunk, final) => {
+        if (err) writable.abort(err.message);
+        controller.enqueue(chunk);
+        if (final) controller.close();
+      }
+    }
+  });
+  return { readable, writable };
+}

+ 25 - 9
src/demo/components/file-picker/index.tsx

@@ -1,4 +1,4 @@
-import React, { FC, HTMLAttributes, InputHTMLAttributes, useEffect, useRef } from 'react';
+import React, { CSSProperties, FC, HTMLAttributes, InputHTMLAttributes, useEffect, useRef } from 'react';
 
 const supportsInputDirs = 'webkitdirectory' in HTMLInputElement.prototype;
 const supportsRelativePath = 'webkitRelativePath' in File.prototype;
@@ -30,7 +30,7 @@ const readRecurse = (dir: FileSystemDirectoryEntry, onComplete: (files: File[])
       if (entry.isFile) entry.file(f => onDone([
         new File([f], entry.fullPath.slice(1), f)
       ]), onErr);
-      else readRecurse(entry, onDone, onErr);
+      else readRecurse(entry as FileSystemDirectoryEntry, onDone, onErr);
     }
   };
   reader.readEntries(onRead, onError);
@@ -40,8 +40,9 @@ const FilePicker: FC<{
   onFiles(files: File[]): void;
   onDrag(on: boolean): void;
   onError(err: string | Error): void;
+  allowDirs: boolean;
 } & HTMLAttributes<HTMLDivElement>
-> = ({ onFiles, onDrag, onError, style, ...props }) => {
+> = ({ onFiles, onDrag, onError, style, allowDirs, children, ...props }) => {
   const inputRef = useRef<HTMLInputElement>(null);
   const dirInputRef = useRef<HTMLInputElement>(null);
   useEffect(() => {
@@ -56,7 +57,7 @@ const FilePicker: FC<{
       const tf = ev.dataTransfer;
       if (!tf.files.length) onError('Please drop some files in');
       else {
-        if (supportsDirs) {
+        if (supportsDirs && allowDirs) {
           let outFiles: File[] = [];
           let lft = tf.items.length;
           let errored = false;
@@ -73,7 +74,7 @@ const FilePicker: FC<{
           for (let i = 0; i < tf.items.length; ++i) {
             const entry = tf.items[i].webkitGetAsEntry();
             if (entry.isFile) entry.file(f => onDone([f]), onErr);
-            else readRecurse(entry, onDone, onErr);
+            else readRecurse(entry as FileSystemDirectoryEntry, onDone, onErr);
           }
         } else onFiles(Array.prototype.slice.call(tf.files));
       }
@@ -90,7 +91,8 @@ const FilePicker: FC<{
     style: {
       display: 'flex',
       flexDirection: 'row',
-      flex: 1,
+      alignItems: 'center',
+      justifyContent: 'spac',
       ...style
     }
   };
@@ -110,14 +112,28 @@ const FilePicker: FC<{
     style: { display: 'none' },
     multiple: true
   };
+  const buttonStyles: CSSProperties = {
+    cursor: 'grab'
+  };
   return (
     <div {...props} {...rootProps}>
+      {children}
       <input type="file" ref={inputRef} {...inputProps} />
-      <div onClick={() => inputRef.current!.click()}>Files</div>
-      {supportsInputDirs && 
+      <div onClick={() => inputRef.current!.click()} style={buttonStyles}>Files</div>
+      {supportsInputDirs && allowDirs &&
         <>
+          <div style={{
+            borderLeft: '1px solid gray',
+            color: 'gray',
+            marginLeft: '2vw',
+            height: '100%',
+            display: 'flex',
+            alignItems: 'center'
+          }}>
+            <span style={{ position: 'relative', left: '-1vw', background: 'white' }}>OR</span>
+          </div>
           <input type="file" ref={dirInputRef} {...inputProps} />
-          <div onClick={() => dirInputRef.current!.click()}>Folders</div>
+          <div onClick={() => dirInputRef.current!.click()} style={buttonStyles}>Folders</div>
         </>
       }
     </div>

+ 1 - 0
src/demo/index.css

@@ -1,6 +1,7 @@
 html, body {
   margin: 0;
   padding: 0;
+  font-family: Arial, Helvetica, sans-serif;
 }
 
 #app {

+ 15 - 7
src/demo/util/workers.ts

@@ -1,8 +1,11 @@
-/// <reference lib="webworker" />
 import pako from 'pako';
 import * as UZIP from 'uzip';
 import JSZip from 'jszip';
 
+const wk = self as unknown as {
+  postMessage(b: unknown, bufs: ArrayBuffer[]): void;
+};
+
 const dcmp = ['inflate', 'gunzip', 'unzlib'];
 
 const concat = (chunks: Uint8Array[]) => {
@@ -56,7 +59,7 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
           type: 'uint8array',
           compressionOptions: { level: 6 }
         }).then(buf => {
-          postMessage(buf, [buf.buffer]);
+          wk.postMessage(buf, [buf.buffer]);
         })
       };
     } else if (type == 'unzip') {
@@ -72,7 +75,7 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
             }));
           }
           Promise.all(bufs).then(res => {
-            postMessage(out, res);
+            wk.postMessage(out, res);
           });
         })
       }
@@ -87,7 +90,7 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
         raw: type == 'deflate'
       });
       strm.onData = (chunk: Uint8Array) => {
-        postMessage(chunk, [chunk.buffer]);
+        wk.postMessage(chunk, [chunk.buffer]);
       };
       onmessage = (ev: MessageEvent<[Uint8Array, boolean]>) => {
         strm.push(ev.data[0], ev.data[1]);
@@ -101,12 +104,17 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
           zip[ev.data[0]] = ev.data[1];
         } else {
           const buf = UZIP.encode(zip);
-          postMessage(buf, [buf.buffer]);
+          wk.postMessage(buf, [buf.buffer]);
         }
       };
     } else if (type == 'unzip') {
       onmessage = (ev: MessageEvent<Uint8Array>) => {
-        postMessage(UZIP.parse(ev.data));
+        const bufs = UZIP.parse(ev.data);
+        const outBufs: ArrayBuffer[] = [];
+        for (const k in bufs) {
+          outBufs.push(bufs[k]);
+        }
+        wk.postMessage(bufs, outBufs);
       }
     } else {
       const chunks: Uint8Array[] = [];
@@ -126,7 +134,7 @@ onmessage = (ev: MessageEvent<[string, string]>) => {
                     ? uzGzip(out)
                     // we can pray that there's no special header
                     : UZIP.inflateRaw(out.subarray(10, -8));
-          postMessage(buf, [buf.buffer]);
+          wk.postMessage(buf, [buf.buffer]);
         }
       }
     }

+ 3 - 8
src/index.ts

@@ -831,14 +831,9 @@ export interface AsyncTerminable {
   (): void;
 }
 
-// for implementing classes
-type Public<T> = { [K in keyof T]: T[K] };
-
-const gmem = (len: number, mem: number) => mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(len))) * 1.5) : (12 + mem);
-
 // deflate with opts
 const dopt = (dat: Uint8Array, opt: DeflateOptions, pre: number, post: number, st?: boolean) =>
-  dflt(dat, opt.level == null ? 6 : opt.level, gmem(dat.length, opt.mem), pre, post, !st as unknown as 0 | 1);
+  dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + opt.mem), pre, post, !st as unknown as 0 | 1);
 
 // Walmart object spread
 const mrg = <T extends {}>(a: T, b: T) => {
@@ -912,7 +907,7 @@ const wrkr = <T, R>(fns: (() => unknown[])[], init: (ev: MessageEvent<T>) => voi
 
 // base async inflate fn
 const bInflt = () => [u8, u16, fleb, flebmsk, fdeb, fdebmsk, clim, fl, fd, flrm, fdrm, rev, hMap, max, bits, bits16, shft, slc, inflt, inflateSync, pbf, gu8];
-const bDflt = () => [u8, u16, u32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, gmem, dopt, deflateSync, pbf]
+const bDflt = () => [u8, u16, u32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]
 
 // gzip extra
 const gze = () => [gzh, gzhl, wbytes, crc, crct];
@@ -963,7 +958,7 @@ const astrmify = <T>(fns: (() => unknown[])[], strm: Astrm, opts: T | 0, init: (
     init,
     id,
     (err, dat) => {
-      if (err) strm.ondata.call(strm, err);
+      if (err) w.terminate(), strm.ondata.call(strm, err);
       else {
         if (dat[1]) w.terminate();
         strm.ondata.call(strm, err, dat[0], dat[1]);

+ 5 - 5
src/node-worker.ts

@@ -5,16 +5,16 @@ const workerAdd = ";var __w=require('worker_threads');__w.parentPort.on('message
 
 export default <T>(c: string, _: number, msg: unknown, transfer: ArrayBuffer[], cb: (err: Error, msg: T) => void) => {
   let done = false;
-  const wk = new Worker(c + workerAdd, { eval: true })
+  const w = new Worker(c + workerAdd, { eval: true })
     .on('error', e => cb(e, null))
     .on('message', m => cb(null, m))
     .on('exit', c => {
       if (c && !done) cb(new Error('Exited with code ' + c), null);
     });
-  wk.postMessage(msg, transfer);
-  wk.terminate = () => {
+  w.postMessage(msg, transfer);
+  w.terminate = () => {
     done = true;
-    return Worker.prototype.terminate.call(wk);
+    return Worker.prototype.terminate.call(w);
   }
-  return wk;
+  return w;
 }

+ 2 - 2
src/worker.ts

@@ -1,7 +1,7 @@
-const ch: Record<string, string> = {};
+const ch2: Record<string, string> = {};
 
 export default <T>(c: string, id: number, msg: unknown, transfer: ArrayBuffer[], cb: (err: Error, msg: T) => void) => {
-  const u = ch[id] ||= URL.createObjectURL(new Blob([c], { type: 'text/javascript' }));
+  const u = ch2[id] ||= URL.createObjectURL(new Blob([c], { type: 'text/javascript' }));
   const w = new Worker(u);
   w.onerror = e => cb(e.error, null);
   w.onmessage = e => cb(null, e.data);

+ 4 - 1
tsconfig.demo.json

@@ -2,7 +2,10 @@
   "compilerOptions": {
     "noEmit": true,
     "target": "ESNext",
+    "jsx": "preserve",
+    "allowSyntheticDefaultImports": true,
     "moduleResolution": "node"
   },
-  "include": ["src/demo/**/*.ts", "src/demo/**/*.tsx", "src/demo/augment.d.ts"]
+  "include": ["src/demo/**/*.ts", "src/demo/**/*.tsx", "src/demo/augment.d.ts"],
+  "exclude": ["src/demo/sw.ts"]
 }

文件差异内容过多而无法显示
+ 1005 - 0
yarn.lock


部分文件因为文件数量过多而无法显示