the latest code
This commit is contained in:
1 parent
d28bc1ed43
commit
9bbb24a52b
12226 files changed
+1455579
-21
No files matched your search
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Donald Chan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
# Browser Image Compression
|
||||
[](https://www.npmjs.com/package/browser-image-compression)
|
||||
[](https://github.com/Donaldcwl/browser-image-compression)
|
||||
[](https://github.com/Donaldcwl/browser-image-compression)
|
||||
|
||||
Javascript module to be run in the web browser for image compression.
|
||||
|
||||
## Features
|
||||
- You can use this module to compress jpeg, png, webp, and bmp images by reducing **resolution** or **storage size** before uploading to the application server to save bandwidth.
|
||||
- **Multi-thread** (web worker) non-blocking compression is supported through options.
|
||||
|
||||
|
||||
## Demo / Example
|
||||
open https://donaldcwl.github.io/browser-image-compression/example/basic.html
|
||||
|
||||
or check the "[example]" folder in this repo
|
||||
|
||||
|
||||
## Usage
|
||||
```html
|
||||
<input type="file" accept="image/*" onchange="handleImageUpload(event);">
|
||||
```
|
||||
### async await syntax:
|
||||
```javascript
|
||||
async function handleImageUpload(event) {
|
||||
|
||||
const imageFile = event.target.files[0];
|
||||
console.log('originalFile instanceof Blob', imageFile instanceof Blob); // true
|
||||
console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`);
|
||||
|
||||
const options = {
|
||||
maxSizeMB: 1,
|
||||
maxWidthOrHeight: 1920,
|
||||
useWebWorker: true,
|
||||
}
|
||||
try {
|
||||
const compressedFile = await imageCompression(imageFile, options);
|
||||
console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); // true
|
||||
console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB
|
||||
|
||||
await uploadToServer(compressedFile); // write your own logic
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
### Promise.then().catch() syntax:
|
||||
<details>
|
||||
<summary>Click to expand</summary>
|
||||
|
||||
```javascript
|
||||
function handleImageUpload(event) {
|
||||
|
||||
var imageFile = event.target.files[0];
|
||||
console.log('originalFile instanceof Blob', imageFile instanceof Blob); // true
|
||||
console.log(`originalFile size ${imageFile.size / 1024 / 1024} MB`);
|
||||
|
||||
var options = {
|
||||
maxSizeMB: 1,
|
||||
maxWidthOrHeight: 1920,
|
||||
useWebWorker: true
|
||||
}
|
||||
imageCompression(imageFile, options)
|
||||
.then(function (compressedFile) {
|
||||
console.log('compressedFile instanceof Blob', compressedFile instanceof Blob); // true
|
||||
console.log(`compressedFile size ${compressedFile.size / 1024 / 1024} MB`); // smaller than maxSizeMB
|
||||
|
||||
return uploadToServer(compressedFile); // write your own logic
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
## Installing
|
||||
### Use as ES module:
|
||||
You can install it via npm or yarn
|
||||
```bash
|
||||
npm install browser-image-compression --save
|
||||
# or
|
||||
yarn add browser-image-compression
|
||||
```
|
||||
```javascript
|
||||
import imageCompression from 'browser-image-compression';
|
||||
```
|
||||
(can be used in frameworks like React, Angular, Vue etc)
|
||||
|
||||
(work with bundlers like webpack and rollup)
|
||||
|
||||
### (or) Load UMD js file:
|
||||
You can download imageCompression from the [dist folder][dist].
|
||||
|
||||
Alternatively, you can use a CDN like [delivrjs]:
|
||||
```html
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.1/dist/browser-image-compression.js"></script>
|
||||
```
|
||||
|
||||
|
||||
## Support
|
||||
If this project helps you reduce the time to develop, you can buy me a cup of coffee :)
|
||||
|
||||
<a href="https://donaldcwl.github.io/donation/" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-red.png" alt="Buy Me A Coffee" height=60 width=217 ></a>
|
||||
|
||||
(powered by Stripe)
|
||||
|
||||
## API
|
||||
### Main function
|
||||
```javascript
|
||||
// you should provide one of maxSizeMB, maxWidthOrHeight in the options
|
||||
const options: Options = {
|
||||
maxSizeMB: number, // (default: Number.POSITIVE_INFINITY)
|
||||
maxWidthOrHeight: number, // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
|
||||
// but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
|
||||
// Please check the Caveat part for details.
|
||||
onProgress: Function, // optional, a function takes one progress argument (percentage from 0 to 100)
|
||||
useWebWorker: boolean, // optional, use multi-thread web worker, fallback to run in main-thread (default: true)
|
||||
libURL: string, // optional, the libURL of this library for importing script in Web Worker (default: https://cdn.jsdelivr.net/npm/browser-image-compression/dist/browser-image-compression.js)
|
||||
preserveExif: boolean, // optional, use preserve Exif metadata for JPEG image e.g., Camera model, Focal length, etc (default: false)
|
||||
|
||||
signal: AbortSignal, // optional, to abort / cancel the compression
|
||||
|
||||
// following options are for advanced users
|
||||
maxIteration: number, // optional, max number of iteration to compress the image (default: 10)
|
||||
exifOrientation: number, // optional, see https://stackoverflow.com/a/32490603/10395024
|
||||
fileType: string, // optional, fileType override e.g., 'image/jpeg', 'image/png' (default: file.type)
|
||||
initialQuality: number, // optional, initial quality value between 0 and 1 (default: 1)
|
||||
alwaysKeepResolution: boolean // optional, only reduce quality, always keep width and height (default: false)
|
||||
}
|
||||
|
||||
imageCompression(file: File, options: Options): Promise<File>
|
||||
```
|
||||
|
||||
#### Caveat
|
||||
Each browser limits [the maximum size](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size) of a browser Canvas object. <br/>
|
||||
So, we resize the image to less than the maximum size that each browser restricts. <br/>
|
||||
(However, the `proportion/ratio` of the image remains.)
|
||||
|
||||
#### Abort / Cancel Compression
|
||||
To use this feature, please check the browser compatibility: https://caniuse.com/?search=AbortController
|
||||
```javascript
|
||||
function handleImageUpload(event) {
|
||||
|
||||
var imageFile = event.target.files[0];
|
||||
|
||||
var controller = new AbortController();
|
||||
|
||||
var options = {
|
||||
// other options here
|
||||
signal: controller.signal,
|
||||
}
|
||||
imageCompression(imageFile, options)
|
||||
.then(function (compressedFile) {
|
||||
return uploadToServer(compressedFile); // write your own logic
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error.message); // output: I just want to stop
|
||||
});
|
||||
|
||||
// simulate abort the compression after 1.5 seconds
|
||||
setTimeout(function () {
|
||||
controller.abort(new Error('I just want to stop'));
|
||||
}, 1500);
|
||||
}
|
||||
```
|
||||
|
||||
### Helper function
|
||||
- for advanced users only, most users won't need to use the helper functions
|
||||
```javascript
|
||||
imageCompression.getDataUrlFromFile(file: File): Promise<base64 encoded string>
|
||||
imageCompression.getFilefromDataUrl(dataUrl: string, filename: string, lastModified?: number): Promise<File>
|
||||
imageCompression.loadImage(url: string): Promise<HTMLImageElement>
|
||||
imageCompression.drawImageInCanvas(img: HTMLImageElement, fileType?: string): HTMLCanvasElement | OffscreenCanvas
|
||||
imageCompression.drawFileInCanvas(file: File, options?: Options): Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement | OffscreenCanvas]>
|
||||
imageCompression.canvasToFile(canvas: HTMLCanvasElement | OffscreenCanvas, fileType: string, fileName: string, fileLastModified: number, quality?: number): Promise<File>
|
||||
imageCompression.getExifOrientation(file: File): Promise<number> // based on https://stackoverflow.com/a/32490603/10395024
|
||||
imageCompression.copyExifWithoutOrientation(copyExifFromFile: File, copyExifToFile: File): Promise<File> // based on https://gist.github.com/tonytonyjan/ffb7cd0e82cb293b843ece7e79364233
|
||||
```
|
||||
|
||||
|
||||
## Browsers support
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari-ios/safari-ios_48x48.png" alt="iOS Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>iOS Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Opera |
|
||||
| --------- | --------- | --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
### IE support
|
||||
This library uses ES features such as Promise API, globalThis. If you need to support browsers that do not support new ES features like IE. You can include the core-js polyfill in your project.
|
||||
|
||||
You can include the following script to load the core-js polyfill:
|
||||
```html
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/3.21.1/minified.min.js"></script>
|
||||
```
|
||||
|
||||
### Webp support
|
||||
The webp compression is supported on major browsers. Please see https://caniuse.com/mdn-api_offscreencanvas_converttoblob_option_type_parameter_webp for browser compatibility.
|
||||
|
||||
|
||||
## Remarks for compression to work in Web Worker
|
||||
The browser needs to support "OffscreenCanvas" API in order to take advantage of non-blocking compression. If the browser does not support "OffscreenCanvas" API, the main thread is used instead. See https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas#browser_compatibility for browser compatibility of "OffscreenCanvas" API.
|
||||
|
||||
|
||||
## Typescript type definitions
|
||||
Typescript definitions are included in the package & referenced in the `types` section of the `package.json`
|
||||
|
||||
|
||||
## Remarks on Content Security Policy (CSP)
|
||||
If your website has CSP enabled and you want to use Web Worker (useWebWorker: true), please add the following to the response header
|
||||
`content-security-policy: script-src 'self' blob: https://cdn.jsdelivr.net`
|
||||
|
||||
- `blob:` is for loading Web Worker script
|
||||
- `https://cdn.jsdelivr.net` is for importing this library from CDN inside Web Worker script. If you don't want to load this library from CDN, you can set your self hosted library URL in `options.libURL`.
|
||||
|
||||
|
||||
## Contribution
|
||||
1. fork the repo and git clone it
|
||||
2. run `npm run watch` # it will watch code change in lib/ folder and generate js in dist/ folder
|
||||
3. add/update code in lib/ folder
|
||||
4. try the code by opening example/development.html which will load the js in dist/ folder
|
||||
5. add/update test in test/ folder
|
||||
6. `npm run test`
|
||||
7. push to your forked repo on github
|
||||
8. make a pull request to dev branch of this repo
|
||||
|
||||
[dist]: https://github.com/Donaldcwl/browser-image-compression/tree/master/dist
|
||||
[example]: https://github.com/Donaldcwl/browser-image-compression/tree/master/example
|
||||
[delivrjs]: https://cdn.jsdelivr.net/
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Type definitions for browser-image-compression 2.0
|
||||
// Project: https://github.com/Donaldcwl/browser-image-compression
|
||||
// Definitions by: Donald <https://github.com/Donaldcwl> & Jamie Haywood <https://github.com/jamiehaywood>
|
||||
|
||||
export interface Options {
|
||||
/** @default Number.POSITIVE_INFINITY */
|
||||
maxSizeMB?: number;
|
||||
/** @default undefined */
|
||||
maxWidthOrHeight?: number;
|
||||
/** @default true */
|
||||
useWebWorker?: boolean;
|
||||
/** @default 10 */
|
||||
maxIteration?: number;
|
||||
/** Default to be the exif orientation from the image file */
|
||||
exifOrientation?: number;
|
||||
/** A function takes one progress argument (progress from 0 to 100) */
|
||||
onProgress?: (progress: number) => void;
|
||||
/** Default to be the original mime type from the image file */
|
||||
fileType?: string;
|
||||
/** @default 1.0 */
|
||||
initialQuality?: number;
|
||||
/** @default false */
|
||||
alwaysKeepResolution?: boolean;
|
||||
/** @default undefined */
|
||||
signal?: AbortSignal;
|
||||
/** @default false */
|
||||
preserveExif?: boolean;
|
||||
/** @default https://cdn.jsdelivr.net/npm/browser-image-compression/dist/browser-image-compression.js */
|
||||
libURL?: string;
|
||||
}
|
||||
|
||||
declare function imageCompression(image: File, options: Options): Promise<File>;
|
||||
|
||||
declare namespace imageCompression {
|
||||
function getDataUrlFromFile(file: File): Promise<string>;
|
||||
function getFilefromDataUrl(dataUrl: string, filename: string, lastModified?: number): Promise<File>;
|
||||
function loadImage(src: string): Promise<HTMLImageElement>;
|
||||
function drawImageInCanvas(img: HTMLImageElement, fileType?: string): HTMLCanvasElement;
|
||||
function drawFileInCanvas(file: File, options?: Options): Promise<[ImageBitmap | HTMLImageElement, HTMLCanvasElement]>;
|
||||
function canvasToFile(canvas: HTMLCanvasElement, fileType: string, fileName: string, fileLastModified: number, quality?: number): Promise<File>;
|
||||
function getExifOrientation(file: File): Promise<number>;
|
||||
}
|
||||
|
||||
export as namespace imageCompression;
|
||||
|
||||
export default imageCompression;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Browser Image Compression
|
||||
* v2.0.2
|
||||
* by Donald <[email protected]>
|
||||
* https://github.com/Donaldcwl/browser-image-compression
|
||||
*/
|
||||
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).imageCompression=t()}(this,(function(){"use strict";function _mergeNamespaces(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}}))})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=!0;break;case 19789:f=!1;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s}return t(new Blob)})),i.readAsArrayBuffer(e)}));var e={};!function(e){var t,r,UZIP={};e.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t)}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else{var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else{if(8!=l)throw"unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return e[0],e[1],UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=!1);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l}}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0)}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++])}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return-1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[Line truncated
|
||||
//# sourceMappingURL=browser-image-compression.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser-image-compression.js","sources":["../lib/copyExifWithoutOrientation.js","../node_modules/uzip/UZIP.js","../lib/UPNG.js","../lib/canvastobmp.js","../lib/config/browser-name.js","../lib/config/max-canvas-size.js","../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["// https://gist.github.com/tonytonyjan/ffb7cd0e82cb293b843ece7e79364233\n// Copyright (c) 2022 Weihang Jian <[email protected]>\n\nexport default async function copyExifWithoutOrientation(srcBlob, destBlob) {\n const exif = await getApp1Segment(srcBlob);\n return new Blob([destBlob.slice(0, 2), exif, destBlob.slice(2)], {\n type: 'image/jpeg',\n });\n}\n\nconst SOI = 0xffd8;\nconst SOS = 0xffda;\nconst APP1 = 0xffe1;\nconst EXIF = 0x45786966;\nconst LITTLE_ENDIAN = 0x4949;\nconst BIG_ENDIAN = 0x4d4d;\nconst TAG_ID_ORIENTATION = 0x0112;\nconst TAG_TYPE_SHORT = 3;\nconst getApp1Segment = (blob) => new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.addEventListener('load', ({ target: { result: buffer } }) => {\n const view = new DataView(buffer);\n let offset = 0;\n if (view.getUint16(offset) !== SOI) return reject('not a valid JPEG');\n offset += 2;\n\n while (true) {\n const marker = view.getUint16(offset);\n if (marker === SOS) break;\n\n const size = view.getUint16(offset + 2);\n if (marker === APP1 && view.getUint32(offset + 4) === EXIF) {\n const tiffOffset = offset + 10;\n let littleEndian;\n switch (view.getUint16(tiffOffset)) {\n case LITTLE_ENDIAN:\n littleEndian = true;\n break;\n case BIG_ENDIAN:\n littleEndian = false;\n break;\n default:\n return reject('TIFF header contains invalid endian');\n }\n if (view.getUint16(tiffOffset + 2, littleEndian) !== 0x2a) { return reject('TIFF header contains invalid version'); }\n\n const ifd0Offset = view.getUint32(tiffOffset + 4, littleEndian);\n const endOfTagsOffset = tiffOffset\n + ifd0Offset\n + 2\n + view.getUint16(tiffOffset + ifd0Offset, littleEndian) * 12;\n for (\n let i = tiffOffset + ifd0Offset + 2;\n i < endOfTagsOffset;\n i += 12\n ) {\n const tagId = view.getUint16(i, littleEndian);\n if (tagId == TAG_ID_ORIENTATION) {\n if (view.getUint16(i + 2, littleEndian) !== TAG_TYPE_SHORT) { return reject('Orientation data type is invalid'); }\n\n if (view.getUint32(i + 4, littleEndian) !== 1) { return reject('Orientation data count is invalid'); }\n\n view.setUint16(i + 8, 1, littleEndian);\n break;\n }\n }\n return resolve(buffer.slice(offset, offset + 2 + size));\n }\n offset += 2 + size;\n }\n return resolve(new Blob());\n });\n reader.readAsArrayBuffer(blob);\n});\n","\r\n\r\nvar UZIP = {};\r\nif(typeof module == \"object\") module.exports = UZIP;\r\n\r\n\r\nUZIP[\"parse\"] = function(buf, onlyNames)\t// ArrayBuffer\r\n{\r\n\tvar rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint, o = 0, out = {};\r\n\tvar data = new Uint8Array(buf);\r\n\tvar eocd = data.length-4;\r\n\t\r\n\twhile(rUi(data, eocd)!=0x06054b50) eocd--;\r\n\t\r\n\tvar o = eocd;\r\n\to+=4;\t// sign = 0x06054b50\r\n\to+=4; // disks = 0;\r\n\tvar cnu = rUs(data, o); o+=2;\r\n\tvar cnt = rUs(data, o); o+=2;\r\n\t\t\t\r\n\tvar csize = rUi(data, o); o+=4;\r\n\tvar coffs = rUi(data, o); o+=4;\r\n\t\r\n\to = coffs;\r\n\tfor(var i=0; i<cnu; i++)\r\n\t{\r\n\t\tvar sign = rUi(data, o); o+=4;\r\n\t\to += 4; // versions;\r\n\t\to += 4; // flag + compr\r\n\t\to += 4; // time\r\n\t\t\r\n\t\tvar crc32 = rUi(data, o); o+=4;\r\n\t\tvar csize = rUi(data, o); o+=4;\r\n\t\tvar usize = rUi(data, o); o+=4;\r\n\t\t\r\n\t\tvar nl = rUs(data, o), el = rUs(data, o+2), cl = rUs(data, o+4); o += 6; // name, extra, comment\r\n\t\to += 8; // disk, attribs\r\n\t\t\r\n\t\tvar roff = rUi(data, o); o+=4;\r\n\t\to += nl + el + cl;\r\n\t\t\r\n\t\tUZIP._readLocal(data, roff, out, csize, usize, onlyNames);\r\n\t}\r\n\t//console.log(out);\r\n\treturn out;\r\n}\r\n\r\nUZIP._readLocal = function(data, o, out, csize, usize, onlyNames)\r\n{\r\n\tvar rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint;\r\n\tvar sign = rUi(data, o); o+=4;\r\n\tvar ver = rUs(data, o); o+=2;\r\n\tvar gpflg = rUs(data, o); o+=2;\r\n\t//if((gpflg&8)!=0) throw \"unknown sizes\";\r\n\tvar cmpr = rUs(data, o); o+=2;\r\n\t\r\n\tvar time = rUi(data, o); o+=4;\r\n\t\r\n\tvar crc32 = rUi(data, o); o+=4;\r\n\t//var csize = rUi(data, o); o+=4;\r\n\t//var usize = rUi(data, o); o+=4;\r\n\to+=8;\r\n\t\t\r\n\tvar nlen = rUs(data, o); o+=2;\r\n\tvar elen = rUs(data, o); o+=2;\r\n\t\t\r\n\tvar name = UZIP.bin.readUTF8(data, o, nlen); o+=nlen; //console.log(name);\r\n\to += elen;\r\n\t\t\t\r\n\t//console.logLine truncated
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Browser Image Compression
|
||||
* v2.0.2
|
||||
* by Donald <[email protected]>
|
||||
* https://github.com/Donaldcwl/browser-image-compression
|
||||
*/
|
||||
|
||||
function _mergeNamespaces(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}}))})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=!0;break;case 19789:f=!1;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s}return t(new Blob)})),i.readAsArrayBuffer(e)}));var e={},t={get exports(){return e},set exports(t){e=t}};!function(e){var r,i,UZIP={};t.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t)}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else{var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else{if(8!=l)throw"unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return e[0],e[1],UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=!1);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l}}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0)}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++])}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return-1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[t])},writeUint:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255,e[t+2]=r>>16&255,e[t+3]=r>>24&255},readASCII:function(e,t,r){for(var i="",o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCLine truncated
|
||||
//# sourceMappingURL=browser-image-compression.mjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser-image-compression.mjs","sources":["../lib/copyExifWithoutOrientation.js","../node_modules/uzip/UZIP.js","../lib/UPNG.js","../lib/canvastobmp.js","../lib/config/browser-name.js","../lib/config/max-canvas-size.js","../lib/utils.js","../lib/image-compression.js","../lib/web-worker.js","../lib/index.js"],"sourcesContent":["// https://gist.github.com/tonytonyjan/ffb7cd0e82cb293b843ece7e79364233\n// Copyright (c) 2022 Weihang Jian <[email protected]>\n\nexport default async function copyExifWithoutOrientation(srcBlob, destBlob) {\n const exif = await getApp1Segment(srcBlob);\n return new Blob([destBlob.slice(0, 2), exif, destBlob.slice(2)], {\n type: 'image/jpeg',\n });\n}\n\nconst SOI = 0xffd8;\nconst SOS = 0xffda;\nconst APP1 = 0xffe1;\nconst EXIF = 0x45786966;\nconst LITTLE_ENDIAN = 0x4949;\nconst BIG_ENDIAN = 0x4d4d;\nconst TAG_ID_ORIENTATION = 0x0112;\nconst TAG_TYPE_SHORT = 3;\nconst getApp1Segment = (blob) => new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.addEventListener('load', ({ target: { result: buffer } }) => {\n const view = new DataView(buffer);\n let offset = 0;\n if (view.getUint16(offset) !== SOI) return reject('not a valid JPEG');\n offset += 2;\n\n while (true) {\n const marker = view.getUint16(offset);\n if (marker === SOS) break;\n\n const size = view.getUint16(offset + 2);\n if (marker === APP1 && view.getUint32(offset + 4) === EXIF) {\n const tiffOffset = offset + 10;\n let littleEndian;\n switch (view.getUint16(tiffOffset)) {\n case LITTLE_ENDIAN:\n littleEndian = true;\n break;\n case BIG_ENDIAN:\n littleEndian = false;\n break;\n default:\n return reject('TIFF header contains invalid endian');\n }\n if (view.getUint16(tiffOffset + 2, littleEndian) !== 0x2a) { return reject('TIFF header contains invalid version'); }\n\n const ifd0Offset = view.getUint32(tiffOffset + 4, littleEndian);\n const endOfTagsOffset = tiffOffset\n + ifd0Offset\n + 2\n + view.getUint16(tiffOffset + ifd0Offset, littleEndian) * 12;\n for (\n let i = tiffOffset + ifd0Offset + 2;\n i < endOfTagsOffset;\n i += 12\n ) {\n const tagId = view.getUint16(i, littleEndian);\n if (tagId == TAG_ID_ORIENTATION) {\n if (view.getUint16(i + 2, littleEndian) !== TAG_TYPE_SHORT) { return reject('Orientation data type is invalid'); }\n\n if (view.getUint32(i + 4, littleEndian) !== 1) { return reject('Orientation data count is invalid'); }\n\n view.setUint16(i + 8, 1, littleEndian);\n break;\n }\n }\n return resolve(buffer.slice(offset, offset + 2 + size));\n }\n offset += 2 + size;\n }\n return resolve(new Blob());\n });\n reader.readAsArrayBuffer(blob);\n});\n","\r\n\r\nvar UZIP = {};\r\nif(typeof module == \"object\") module.exports = UZIP;\r\n\r\n\r\nUZIP[\"parse\"] = function(buf, onlyNames)\t// ArrayBuffer\r\n{\r\n\tvar rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint, o = 0, out = {};\r\n\tvar data = new Uint8Array(buf);\r\n\tvar eocd = data.length-4;\r\n\t\r\n\twhile(rUi(data, eocd)!=0x06054b50) eocd--;\r\n\t\r\n\tvar o = eocd;\r\n\to+=4;\t// sign = 0x06054b50\r\n\to+=4; // disks = 0;\r\n\tvar cnu = rUs(data, o); o+=2;\r\n\tvar cnt = rUs(data, o); o+=2;\r\n\t\t\t\r\n\tvar csize = rUi(data, o); o+=4;\r\n\tvar coffs = rUi(data, o); o+=4;\r\n\t\r\n\to = coffs;\r\n\tfor(var i=0; i<cnu; i++)\r\n\t{\r\n\t\tvar sign = rUi(data, o); o+=4;\r\n\t\to += 4; // versions;\r\n\t\to += 4; // flag + compr\r\n\t\to += 4; // time\r\n\t\t\r\n\t\tvar crc32 = rUi(data, o); o+=4;\r\n\t\tvar csize = rUi(data, o); o+=4;\r\n\t\tvar usize = rUi(data, o); o+=4;\r\n\t\t\r\n\t\tvar nl = rUs(data, o), el = rUs(data, o+2), cl = rUs(data, o+4); o += 6; // name, extra, comment\r\n\t\to += 8; // disk, attribs\r\n\t\t\r\n\t\tvar roff = rUi(data, o); o+=4;\r\n\t\to += nl + el + cl;\r\n\t\t\r\n\t\tUZIP._readLocal(data, roff, out, csize, usize, onlyNames);\r\n\t}\r\n\t//console.log(out);\r\n\treturn out;\r\n}\r\n\r\nUZIP._readLocal = function(data, o, out, csize, usize, onlyNames)\r\n{\r\n\tvar rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint;\r\n\tvar sign = rUi(data, o); o+=4;\r\n\tvar ver = rUs(data, o); o+=2;\r\n\tvar gpflg = rUs(data, o); o+=2;\r\n\t//if((gpflg&8)!=0) throw \"unknown sizes\";\r\n\tvar cmpr = rUs(data, o); o+=2;\r\n\t\r\n\tvar time = rUi(data, o); o+=4;\r\n\t\r\n\tvar crc32 = rUi(data, o); o+=4;\r\n\t//var csize = rUi(data, o); o+=4;\r\n\t//var usize = rUi(data, o); o+=4;\r\n\to+=8;\r\n\t\t\r\n\tvar nlen = rUs(data, o); o+=2;\r\n\tvar elen = rUs(data, o); o+=2;\r\n\t\t\r\n\tvar name = UZIP.bin.readUTF8(data, o, nlen); o+=nlen; //console.log(name);\r\n\to += elen;\r\n\t\t\t\r\n\t//console.loLine truncated
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"name": "browser-image-compression",
|
||||
"version": "2.0.2",
|
||||
"description": "Compress images in the browser",
|
||||
"main": "dist/browser-image-compression.js",
|
||||
"module": "dist/browser-image-compression.mjs",
|
||||
"jsnext:main": "dist/browser-image-compression.mjs",
|
||||
"types": "dist/browser-image-compression.d.ts",
|
||||
"scripts": {
|
||||
"eslint": "eslint lib test --fix",
|
||||
"build": "rollup -c --environment BUILD:production --bundleConfigAsCjs",
|
||||
"watch": "rollup -c -w --environment BUILD:development --bundleConfigAsCjs",
|
||||
"dev": "npm run watch",
|
||||
"test": "cross-env NODE_ENV=test nyc mocha",
|
||||
"posttest": "npm run coverage-badges",
|
||||
"test:watch": "cross-env NODE_ENV=test nyc mocha -w",
|
||||
"prepublishOnly": "npm test && npm run build",
|
||||
"coverage-badges": "make-coverage-badge",
|
||||
"commit": "cz"
|
||||
},
|
||||
"homepage": "https://github.com/Donaldcwl/browser-image-compression#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Donaldcwl/browser-image-compression"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Donaldcwl/browser-image-compression/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"image compression",
|
||||
"browser",
|
||||
"image processing",
|
||||
"reduce resolution",
|
||||
"reduce size"
|
||||
],
|
||||
"author": "Donald <[email protected]>",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"uzip": "0.20201231.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/register": "^7.0.0",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-commonjs": "^24.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"babel-plugin-istanbul": "^6.0.0",
|
||||
"canvas": "2.6.1",
|
||||
"chai": "^4.1.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"cz-conventional-changelog": "3.3.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"jsdom": "^21.1.0",
|
||||
"make-coverage-badge": "^1.0.1",
|
||||
"mocha": "^10.2.0",
|
||||
"nyc": "^15.0.1",
|
||||
"rollup": "^3.15.0",
|
||||
"rollup-plugin-copy": "^3.3.0",
|
||||
"rollup-plugin-license": "^3.0.1",
|
||||
"rollup-plugin-nodent": "^0.2.2",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.6.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user