diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fba7d21..e9f73213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.0.9 + +- Refactor +- Introduces svgo svg processing + ## 0.0.8 - Updates README.md diff --git a/README.md b/README.md index fe553874..10a5d536 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This **[Astro integration][astro-integration]** brings compression utilities to your Astro project. -[csso] [html-minifier-terser] [terser] [sharp] +[csso] [html-minifier-terser] [terser] [sharp] [svgo] ## Installation @@ -58,12 +58,36 @@ export default defineConfig({ The utility should now automatically compress all your CSS, HTML and JavaScript files in the dist folder. +The following image file types will also be compressed. + +- avci +- avcs +- avif +- avifs +- gif +- heic +- heics +- heif +- heifs +- jfif +- jif +- jpe +- jpeg +- jpg +- png +- raw +- tiff +- webp + +SVG compression is supported, as well via [svgo]. + You can override any of the default options from the configurations of: -- [csso](src/options/csso.ts) -- [html-minifier-terser](src/options/html-minifier-terser.ts) -- [terser](src/options/terser.ts) -- [sharp](src/options/sharp.ts) +- [csso](src/options/css.ts) +- [html-minifier-terser](src/options/html.ts) +- [terser](src/options/js.ts) +- [sharp](src/options/img.ts) +- [svgo](src/options/svg.ts) or disable them entirely: @@ -78,6 +102,7 @@ export default defineConfig({ html: false, js: false, img: false, + svg: false, }), ], }); @@ -99,9 +124,25 @@ export default defineConfig({ }); ``` +Set logger to 0 if you do not want to see debug messages. Default is 2. + +```js +import { defineConfig } from "astro/config"; +import compress from "astro-compress"; + +export default defineConfig({ + integrations: [ + compress({ + logger: 0, + }), + ], +}); +``` + [astro-compress]: https://npmjs.org/astro-compress [csso]: https://npmjs.org/csso [html-minifier-terser]: https://npmjs.org/html-minifier-terser [terser]: https://npmjs.org/terser [sharp]: https://npmjs.org/sharp +[svgo]: https://npmjs.org/svgo [astro-integration]: https://docs.astro.build/en/guides/integrations-guide/ diff --git a/dist/index.js b/dist/index.js index a11ba085..2eb0edbe 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1 +1 @@ -import f from"fs";import g from"fast-glob";import*as y from"csso";import*as d from"html-minifier-terser";import{minify as v}from"terser";import w from"sharp";function h(o,r=2){if(o===0)return"0 Bytes";const e=1024,s=r<0?0:r,t=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],i=Math.floor(Math.log(o)/Math.log(e));return parseFloat((o/Math.pow(e,i)).toFixed(s))+" "+t[i]}const c=async(o,r)=>{const e=await g(o);for(const s of e)try{await f.promises.writeFile(s,await r(await f.promises.readFile(s,"utf-8")),"utf-8")}catch{console.log("Error: Cannot minify file "+s+"!")}},j=async(o,r,e=2)=>{const s=await g(o);let t={files:0,size:0};for(const i of s)try{const a=i.split(".").pop();if(typeof a!="string")return;const m={avci:"avif",avcs:"avif",avifs:"avif",heic:"heif",heics:"heif",heifs:"heif",jfif:"jpeg",jif:"jpeg",jpe:"jpeg",jpg:"jpeg"},n=typeof m[a]<"u"?m[a]:typeof r[a]<"u"?a:!1;if(["avif","gif","heif","jpeg","png","raw","tiff","webp"].includes(n)&&r[n]!==!1){const l=(await f.promises.stat(i)).size,u=await(await w(i))[n](r[n]).toBuffer();if(l>Buffer.byteLength(u)){await f.promises.writeFile(i,u,"utf-8");const p=(await f.promises.stat(i)).size;t.files++,t.size+=l-p,e>1&&console.info("\x1B[32mCompressed "+i.replace(/^.*[\\\/]/,"")+" for "+h(l-p)+" ("+(p/l*100).toFixed(2)+"% compression ratio).\x1B[39m")}}}catch{console.log("Error: Cannot compress file "+i+"!")}e>0&&t.files>0&&console.info("\x1B[32mSuccessfully compressed a total of "+t.files+" images for "+h(t.size)+".\x1B[39m")};function b(o={}){var s;const e=Object.assign({path:"./dist/",css:{clone:!1,comments:!1,debug:!1,forceMediaMerge:!0,restructure:!0,sourceMap:!1},html:{caseSensitive:!0,collapseBooleanAttributes:!0,collapseInlineTagWhitespace:!1,collapseWhitespace:!0,conservativeCollapse:!1,continueOnParseError:!1,customAttrAssign:[],customAttrCollapse:"",customAttrSurround:[],customEventAttributes:[/^on[a-z]{3,}$/],decodeEntities:!1,html5:!0,ignoreCustomComments:[],ignoreCustomFragments:[],includeAutoGeneratedTags:!0,keepClosingSlash:!0,maxLineLength:null,minifyCSS:!0,minifyJS:!0,minifyURLs:!1,preserveLineBreaks:!1,preventAttributesEscaping:!1,processConditionalComments:!0,processScripts:["module"],quoteCharacter:"",removeAttributeQuotes:!0,removeComments:!0,removeEmptyAttributes:!0,removeEmptyElements:!1,removeOptionalTags:!1,removeRedundantAttributes:!0,removeScriptTypeAttributes:!0,removeStyleLinkTypeAttributes:!0,removeTagWhitespace:!0,sortAttributes:!0,sortClassName:!0,trimCustomFragments:!1,useShortDoctype:!1},js:{ecma:5,enclose:!1,keep_classnames:!1,keep_fnames:!1,ie8:!1,module:!1,safari10:!1,toplevel:!1},img:{fit:{width:1920,height:1080},avif:{chromaSubsampling:"4:4:4",effort:9},gif:{effort:10},heif:{chromaSubsampling:"4:4:4"},jpeg:{chromaSubsampling:"4:4:4",mozjpeg:!0,trellisQuantisation:!0,overshootDeringing:!0,optimiseScans:!0},png:{compressionLevel:9,palette:!0},raw:{},tiff:{compression:"lzw"},webp:{effort:6}},logger:2},o);return e.path=(s=e.path)!=null&&s.endsWith("/")?e.path:`${e.path}/`,{name:"astro-compress",hooks:{"astro:config:done":async t=>{e.path=e.path?e.path:t.config.outDir.toString()},"astro:build:done":async()=>{e.css&&await c(`${e.path}**/*.css`,t=>y.minify(t,e.css).css),e.html&&await c(`${e.path}**/*.html`,async t=>await d.minify(t,e.html)),e.js&&await c(`${e.path}**/*.{js,mjs,cjs}`,async t=>(await v(t,e.js)).code),e.img&&await j(`${e.path}**/*.{avci,avcs,avif,avifs,gif,heic,heics,heif,heifs,jfif,jif,jpe,jpeg,jpg,png,raw,tiff,webp}`,e.img,e.logger)}}}}export{b as default}; +import l from"fs";import u from"fast-glob";import*as g from"csso";import*as h from"html-minifier-terser";import{minify as y}from"terser";import d from"sharp";import v from"svgo";function m(t,a=2){if(t===0)return"0 Bytes";const e=1024,s=a<0?0:a,i=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],r=Math.floor(Math.log(t)/Math.log(e));return parseFloat((t/Math.pow(e,r)).toFixed(s))+" "+i[r]}const b=async(t,a={})=>{const e=t.options.input.file.split(".").pop();if(!e)return;const s={avci:"avif",avcs:"avif",avifs:"avif",heic:"heif",heics:"heif",heifs:"heif",jfif:"jpeg",jif:"jpeg",jpe:"jpeg",jpg:"jpeg"},i=typeof s[e]<"u"?s[e]:typeof a[e]<"u"?e:!1;if(["avif","gif","heif","jpeg","png","raw","tiff","webp"].includes(i)&&a[i]!==!1)return await t[i](a[i]).toBuffer()},w=async t=>{for(const a in t)if(Object.prototype.hasOwnProperty.call(t,a)){const e=t[a];switch(a){case"css":await f(`${t.path}**/*.css`,t.logger,s=>g.minify(s,e).css);break;case"html":await f(`${t.path}**/*.html`,t.logger,async s=>await h.minify(s,e));break;case"js":await f(`${t.path}**/*.{js,mjs,cjs}`,t.logger,async s=>{const i=await y(s,e);return i.code?i.code:s});break;case"img":await f(`${t.path}**/*.{avci,avcs,avif,avifs,gif,heic,heics,heif,heifs,jfif,jif,jpe,jpeg,jpg,png,raw,tiff,webp}`,t.logger,async s=>await b(s,e),async s=>await d(s));break;case"svg":await f(`${t.path}**/*.svg`,t.logger,async s=>v.optimize(s,e).data);break;default:break}}},f=async(t,a=2,e=async i=>i,s=async i=>await l.promises.readFile(i,"utf-8"))=>{const i=await u(t);let r={files:0,size:0};for(const o of i)try{const n=(await l.promises.stat(o)).size,p=await e(await s(o));if(!p)return;if(n>Buffer.byteLength(p)){await l.promises.writeFile(o,p,"utf-8");const c=(await l.promises.stat(o)).size;r.files++,r.size+=n-c,a>1&&console.info("\x1B[32mCompressed "+o.replace(/^.*[\\\/]/,"")+" for "+m(n-c)+" ("+(c/n*100).toFixed(2)+"% compression rate).\x1B[39m")}}catch{console.log("Error: Cannot compress file "+o+"!")}a>0&&r.files>0&&console.info("\x1B[32mSuccessfully compressed a total of "+r.files+" files for "+m(r.size)+".\x1B[39m")};function j(t={}){var s;const e=Object.assign({path:"./dist/",css:{clone:!1,comments:!1,debug:!1,forceMediaMerge:!0,restructure:!0,sourceMap:!1},html:{caseSensitive:!0,collapseBooleanAttributes:!0,collapseInlineTagWhitespace:!1,collapseWhitespace:!0,conservativeCollapse:!1,continueOnParseError:!1,customAttrAssign:[],customAttrCollapse:"",customAttrSurround:[],customEventAttributes:[/^on[a-z]{3,}$/],decodeEntities:!1,html5:!0,ignoreCustomComments:[],ignoreCustomFragments:[],includeAutoGeneratedTags:!0,keepClosingSlash:!0,maxLineLength:null,minifyCSS:!0,minifyJS:!0,minifyURLs:!1,preserveLineBreaks:!1,preventAttributesEscaping:!1,processConditionalComments:!0,processScripts:["module"],quoteCharacter:"",removeAttributeQuotes:!0,removeComments:!0,removeEmptyAttributes:!0,removeEmptyElements:!1,removeOptionalTags:!1,removeRedundantAttributes:!0,removeScriptTypeAttributes:!0,removeStyleLinkTypeAttributes:!0,removeTagWhitespace:!0,sortAttributes:!0,sortClassName:!0,trimCustomFragments:!1,useShortDoctype:!1},js:{ecma:5,enclose:!1,keep_classnames:!1,keep_fnames:!1,ie8:!1,module:!1,safari10:!1,toplevel:!1},img:{avif:{chromaSubsampling:"4:4:4",effort:9},gif:{effort:10},heif:{chromaSubsampling:"4:4:4"},jpeg:{chromaSubsampling:"4:4:4",mozjpeg:!0,trellisQuantisation:!0,overshootDeringing:!0,optimiseScans:!0},png:{compressionLevel:9,palette:!0},raw:{},tiff:{compression:"lzw"},webp:{effort:6}},svg:{multipass:!0,datauri:"base64",js2svg:{indent:0,pretty:!1},plugins:["preset-default"]},logger:2},t);return e.path=(s=e.path)!=null&&s.endsWith("/")?e.path:`${e.path}/`,{name:"astro-compress",hooks:{"astro:config:done":async i=>{e.path=e.path?e.path:i.config.outDir.toString()},"astro:build:done":async()=>{await w(e)}}}}export{j as default}; diff --git a/dist/options.d.ts b/dist/options.d.ts index e3e11a4d..1fc73859 100644 --- a/dist/options.d.ts +++ b/dist/options.d.ts @@ -1,29 +1,35 @@ -import CSS from "./options/csso"; -import HTML from "./options/html-minifier-terser"; -import JS from "./options/terser"; -import IMG from "./options/sharp"; +import CSS from "./options/css"; +import HTML from "./options/html"; +import JS from "./options/js"; +import IMG from "./options/img"; +import SVG from "./options/svg"; export default interface Options { + [key: string]: any; /** * Astro build path. - * Default: "./dist/" + * @default "./dist/" */ path?: string; /** * [csso] options. */ - css?: CSS; + css?: boolean | CSS; /** * [html-minifier-terser] options. */ - html?: HTML; + html?: boolean | HTML; /** * [terser] options. */ - js?: JS; + js?: boolean | JS; /** * [sharp] options. */ - img?: IMG; + img?: boolean | IMG; + /** + * [svgo] options. + */ + svg?: boolean | SVG; /** * Logger level. * Default: 2 diff --git a/dist/options/csso.d.ts b/dist/options/css.d.ts similarity index 86% rename from dist/options/csso.d.ts rename to dist/options/css.d.ts index 86737417..c432cfbf 100644 --- a/dist/options/csso.d.ts +++ b/dist/options/css.d.ts @@ -7,62 +7,63 @@ export interface afterCompressOptions { options?: {}; } export default interface CSS { + [key: string]: any; /** * Generate a source map when true. - * Default: false + * @default false */ sourceMap?: Boolean; /** * Filename of input CSS, uses for source map generation. - * Default: '' + * @default '' */ filename?: String; /** * Output debug information to stderr. - * Default: false + * @default false */ debug?: Boolean; /** * Called right after parse is run. - * Default: null + * @default null */ beforeCompress?: ({}: beforeCompressOptions) => void | Array<({}: beforeCompressOptions) => void> | null; /** * Called right after syntax.compress() is run. - * Default: null + * @default null */ afterCompress?: ({}: afterCompressOptions) => void | Array<({}: afterCompressOptions) => void> | null; /** * Disable | enable a structure optimisations. - * Default: true + * @default true */ restructure?: Boolean; /** * Enables merging of @media rules with the same media query by splitted by other rules. * The optimisation is unsafe in general, but should work fine in most cases. Use it on your own risk. - * Default: false + * @default false */ forceMediaMerge?: Boolean; /** * Transform a copy of input AST if true. Useful in case of AST reuse. - * Default: false + * @default false */ clone?: Boolean; /** * Specify what comments to leave: * 'exclamation' | true – leave all exclamation comments (i.e. /*! .. *\/) * 'first-exclamation' – remove every comment except first one false – remove all comments - * Default: true + * @default true */ comments?: String | Boolean; /** * Usage data for advanced optimisations. - * Default: null + * @default null */ usage?: {} | null; /** * Function to track every step of transformation. - * Default: null + * @default null */ logger?: () => {} | null; } diff --git a/dist/options/html-minifier-terser.d.ts b/dist/options/html.d.ts similarity index 81% rename from dist/options/html-minifier-terser.d.ts rename to dist/options/html.d.ts index b41bc25a..02006c5c 100644 --- a/dist/options/html-minifier-terser.d.ts +++ b/dist/options/html.d.ts @@ -1,39 +1,40 @@ export default interface HTML { + [key: string]: any; /** * Treat attributes in case sensitive manner (useful for custom HTML tags). - * Default: false + * @default false */ caseSensitive?: Boolean; /** * Omit attribute values from boolean attributes. - * Default: false + * @default false */ collapseBooleanAttributes?: Boolean; /** * Don't leave any spaces between `display:inline;` elements when collapsing. * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ collapseInlineTagWhitespace?: Boolean; /** * Collapse white space that contributes to text nodes in a document tree. - * Default: false + * @default false */ collapseWhitespace?: Boolean; /** * Always collapse to 1 space (never remove it entirely). * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ conservativeCollapse?: Boolean; /** * Handle parse errors instead of aborting. - * Default: false + * @default false */ continueOnParseError?: Boolean; /** - * Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'
'`). - * Default: [ ] + * Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'
'`). + * @default [] */ customAttrAssign?: RegExp[]; /** @@ -42,53 +43,53 @@ export default interface HTML { customAttrCollapse?: String; /** * Arrays of regex'es that allow to support custom attribute surround expressions (e.g. ``). - * Default: [ ] + * @default [] */ customAttrSurround?: RegExp[]; /** * Arrays of regex'es that allow to support custom event attributes for `minifyJS` (e.g. `ng-click`). - * Default: [ /^on[a-z]{3,}$/ ] + * @default [/^on[a-z]{3,}$/] */ customEventAttributes?: RegExp[]; /** * Use direct Unicode characters whenever possible. - * Default: false + * @default false */ decodeEntities?: Boolean; /** * Parse input according to HTML5 specifications. - * Default: true + * @default true */ html5?: Boolean; /** * Array of regex'es that allow to ignore certain comments, when matched. - * Default: [ /^!/, /^\s*#/ ] + * @default [/^!/, /^\s*#/] */ ignoreCustomComments?: RegExp[]; /** * Array of regex'es that allow to ignore certain fragments, when matched (e.g. ``, `{{ ... }}`, etc.). - * Default: [ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ] + * @default [/<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/] */ ignoreCustomFragments?: RegExp[]; /** * Insert tags generated by HTML parser. - * Default: true + * @default true */ includeAutoGeneratedTags?: Boolean; /** * Keep the trailing slash on singleton elements. - * Default: false + * @default false */ keepClosingSlash?: Boolean; /** * Specify a maximum line length. * Compressed output will be split by newlines at valid HTML split-points. - * Default: null + * @default null */ maxLineLength?: Number | null; /** * Minify CSS in style elements and style attributes (uses clean-css). - * Default: false (could be `true`, `Object`, `Function(text, type)` + * @default false (could be `true`, `Object`, `Function(text, type)` */ minifyCSS?: Boolean | Object | (({}: { text: string; @@ -96,7 +97,7 @@ export default interface HTML { }) => void); /** * Minify JavaScript in script elements and event attributes (uses Terser). - * Default: false (could be `true`, `Object`, `Function(text, inline)` + * @default false (could be `true`, `Object`, `Function(text, inline)` */ minifyJS?: Boolean | Object | (({}: { text: string; @@ -104,55 +105,55 @@ export default interface HTML { }) => void); /** * Minify URLs in various attributes (uses relateurl). - * Default: false (could be `String`, `Object`, `Function(text)` + * @default false (could be `String`, `Object`, `Function(text)` */ minifyURLs?: Boolean | String | Object | (({}: { text: string; }) => void); /** * Never add a newline before a tag that closes an element. - * Default: false + * @default false */ noNewlinesBeforeTagClose?: Boolean; /** * Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ preserveLineBreaks?: Boolean; /** * Prevents the escaping of the values of attributes. - * Default: false + * @default false */ preventAttributesEscaping?: Boolean; /** * Process contents of conditional comments through minifier. - * Default: false + * @default false */ processConditionalComments?: Boolean; /** * Array of strings corresponding to types of script elements to process through minifier (e.g. `text/ng-template`, `text/x-handlebars-template`, etc.). - * Default: [ ] + * @default [] */ processScripts?: String[]; /** * Type of quote to use for attribute values (' or "). - * Default: "" + * @default "" */ quoteCharacter?: String; /** * Remove quotes around attributes when possible. - * Default: false + * @default false */ removeAttributeQuotes?: Boolean; /** * Strip HTML comments. - * Default: false + * @default false */ removeComments?: Boolean; /** * Remove all attributes with whitespace-only values. - * Default: false (could be `true`, `Function(attrName, tag)` + * @default false (could be `true`, `Function(attrName, tag)` */ removeEmptyAttributes?: Boolean | (({}: { attrName: string; @@ -160,55 +161,55 @@ export default interface HTML { }) => void); /** * Remove all elements with empty contents. - * Default: false + * @default false */ removeEmptyElements?: Boolean; /** * Remove optional tags. - * Default: false + * @default false */ removeOptionalTags?: Boolean; /** * Remove attributes when value matches default. - * Default: false + * @default false */ removeRedundantAttributes?: Boolean; /** * Remove `type="text/javascript"` from `script` tags. * Other `type` attribute values are left intact - * Default: false + * @default false */ removeScriptTypeAttributes?: Boolean; /** * Remove `type="text/css"` from `style` and `link` tags. * Other `type` attribute values are left intact - * Default: false + * @default false */ removeStyleLinkTypeAttributes?: Boolean; /** * Remove space between attributes whenever possible. * Note that this will result in invalid HTML! - * Default: false + * @default false */ removeTagWhitespace?: Boolean; /** * Sort attributes by frequency. - * Default: false + * @default false */ sortAttributes?: Boolean; /** * Sort style classes by frequency. - * Default: false + * @default false */ sortClassName?: Boolean; /** * Trim white space around `ignoreCustomFragments` - * Default: false + * @default false */ trimCustomFragments?: Boolean; /** * Replaces the `doctype` with the short (HTML5) doctype. - * Default: false + * @default false */ useShortDoctype?: Boolean; } diff --git a/dist/options/img.d.ts b/dist/options/img.d.ts new file mode 100644 index 00000000..59d2deab --- /dev/null +++ b/dist/options/img.d.ts @@ -0,0 +1,346 @@ +export default interface IMG { + [key: string]: any; + /** + * [avif] + */ + avif?: { + /** + * Quality, integer 1-100 + * @default 50 + */ + quality?: number; + /** + * Use lossless compression + * @default false + */ + lossless?: boolean; + /** + * CPU effort, between 0 (fastest) and 9 (slowest) + * @default 4 + */ + effort?: number; + /** + * Set to '4:2:0' to use chroma subsampling + * @default '4:4:4' + */ + chromaSubsampling?: string; + }; + /** + * [gif] + */ + gif?: { + /** + * Maximum number of palette entries, including transparency, between 2 and 256 + * @default 256 + */ + colours?: number; + /** + * Alternative spelling of options.colours + * @default 256 + */ + colors?: number; + /** + * CPU effort, between 1 (fastest) and 10 (slowest) + * @default 7 + */ + effort?: number; + /** + * Level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) + * @default 1.0 + */ + dither?: number; + /** + * Number of animation iterations, use 0 for infinite animation + * @default 0 + */ + loop?: number; + /** + * Delay(s) between animation frames (in milliseconds) + */ + delay?: number | Array; + /** + * Force GIF output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + /** + * [heif] + */ + heif?: { + /** + * Quality, integer 1-100 + * @default 50 + */ + quality?: number; + /** + * Compression format: av1, hevc + * @default 'av1' + */ + compression?: string; + /** + * Use lossless compression + * @default false + */ + lossless?: boolean; + /** + * CPU effort, between 0 (fastest) and 9 (slowest) + * @default 4 + */ + effort?: number; + /** + * Set to '4:2:0' to use chroma subsampling + * @default '4:4:4' + */ + chromaSubsampling?: string; + }; + /** + * [jpeg] + */ + jpeg?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + /** + * Use progressive (interlace) scan + * @default false + */ + progressive?: boolean; + /** + * Set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling/** + * @default '4:2:0' + */ + chromaSubsampling?: string; + /** + * Optimise Huffman coding tables + * @default true + */ + optimiseCoding?: boolean; + /** + * Alternative spelling of optimiseCoding + * @default true + */ + optimizeCoding?: boolean; + /** + * Use mozjpeg defaults, equivalent to { trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 } + * @default false + */ + mozjpeg?: boolean; + /** + * Apply trellis quantisation + * @default false + */ + trellisQuantisation?: boolean; + /** + * Apply overshoot deringing + * @default false + */ + overshootDeringing?: boolean; + /** + * Optimise progressive scans, forces progressive + * @default false + */ + optimiseScans?: boolean; + /** + * Alternative spelling of optimiseScans + * @default false + */ + optimizeScans?: boolean; + /** + * Quantization table to use, integer 0-8 + * @default 0 + */ + quantisationTable?: number; + /** + * Alternative spelling of quantisationTable + * @default 0 + */ + quantizationTable?: number; + /** + * Force JPEG output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + /** + * [png] + */ + png?: { + /** + * Use progressive (interlace) scan + * @default false + */ + progressive?: boolean; + /** + * Zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) + * @default 6 + */ + compressionLevel?: number; + /** + * Use adaptive row filtering + * @default false + */ + adaptiveFiltering?: boolean; + /** + * Quantise to a palette-based image with alpha transparency support + * @default false + */ + palette?: boolean; + /** + * Use the lowest number of colours needed to achieve given quality, sets palette to true + * @default 100 + */ + quality?: number; + /** + * CPU effort, between 1 (fastest) and 10 (slowest), sets palette to true + * @default 7 + */ + effort?: number; + /** + * Maximum number of palette entries, sets palette to true + * @default 256 + */ + colours?: number; + /** + * Alternative spelling of options.colours, sets palette to true + * @default 256 + */ + colors?: number; + /** + * Level of Floyd-Steinberg error diffusion, sets palette to true + * @default 1.0 + */ + dither?: number; + /** + * Force PNG output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + /** + * [raw] + */ + raw?: { + /** + * Bit depth, one of: char, uchar, short, ushort, int, uint, float, complex, double, dpcomplex + * @default 'uchar' + */ + depth?: string; + }; + /** + * [tiff] + */ + tiff?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + /** + * Force TIFF output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + /** + * Compression options: lzw, deflate, jpeg, ccittfax4 + * @default 'jpeg' + */ + compression?: string; + /** + * Compression predictor options: none, horizontal, float + * @default 'horizontal' + */ + predictor?: string; + /** + * Write an image pyramid + * @default false + */ + pyramid?: boolean; + /** + * Write a tiled tiff + * @default false + */ + tile?: boolean; + /** + * Horizontal tile size + * @default 256 + */ + tileWidth?: number; + /** + * Vertical tile size + * @default 256 + */ + tileHeight?: number; + /** + * Horizontal resolution in pixels/mm + * @default 1.0 + */ + xres?: number; + /** + * Vertical resolution in pixels/mm + * @default 1.0 + */ + yres?: number; + /** + * Resolution unit options: inch, cm + * @default 'inch' + */ + resolutionUnit?: string; + /** + * Reduce bitdepth to 1, 2 or 4 bit + * @default 8 + */ + bitdepth?: number; + }; + /** + * [webp] + */ + webp?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + /** + * Quality of alpha layer, integer 0-100 + * @default 100 + */ + alphaQuality?: number; + /** + * Use lossless compression mode + * @default false + */ + lossless?: boolean; + /** + * Use near_lossless compression mode + * @default false + */ + nearLossless?: boolean; + /** + * Use high quality chroma subsampling + * @default false + */ + smartSubsample?: boolean; + /** + * CPU effort, between 0 (fastest) and 6 (slowest) + * @default 4 + */ + effort?: number; + /** + * Number of animation iterations, use 0 for infinite animation + * @default 0 + */ + loop?: number; + /** + * Delay(s) between animation frames (in milliseconds) + */ + delay?: number | Array; + /** + * Force WebP output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; +} diff --git a/dist/options/js.d.ts b/dist/options/js.d.ts new file mode 100644 index 00000000..87a91801 --- /dev/null +++ b/dist/options/js.d.ts @@ -0,0 +1,4 @@ +import { MinifyOptions } from "terser"; +export default interface JS extends MinifyOptions { + [key: string]: any; +} diff --git a/dist/options/sharp.d.ts b/dist/options/sharp.d.ts deleted file mode 100644 index 0086db52..00000000 --- a/dist/options/sharp.d.ts +++ /dev/null @@ -1,291 +0,0 @@ -export default interface IMG { - [key: string]: any; - fit?: { - width?: number; - height?: number; - }; - /** - * [avif] - */ - avif?: { - /** - * quality, integer 1-100 (optional, default 50) - */ - quality?: number; - /** - * use lossless compression (optional, default false) - */ - lossless?: boolean; - /** - * CPU effort, between 0 (fastest) and 9 (slowest) (optional, default 4) - */ - effort?: number; - /** - * set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') - */ - chromaSubsampling?: string; - }; - /** - * [gif] - */ - gif?: { - /** - * maximum number of palette entries, including transparency, between 2 and 256 (optional, default 256) - */ - colours?: number; - /** - * alternative spelling of options.colours (optional, default 256) - */ - colors?: number; - /** - * CPU effort, between 1 (fastest) and 10 (slowest) (optional, default 7) - */ - effort?: number; - /** - * level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default 1.0) - */ - dither?: number; - /** - * number of animation iterations, use 0 for infinite animation (optional, default 0) - */ - loop?: number; - /** - * delay(s) between animation frames (in milliseconds) - */ - delay?: number | Array; - /** - * force GIF output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - /** - * [heif] - */ - heif?: { - /** - * quality, integer 1-100 (optional, default 50) - */ - quality?: number; - /** - * compression format: av1, hevc (optional, default 'av1') - */ - compression?: string; - /** - * use lossless compression (optional, default false) - */ - lossless?: boolean; - /** - * CPU effort, between 0 (fastest) and 9 (slowest) (optional, default 4) - */ - effort?: number; - /** - * set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') - */ - chromaSubsampling?: string; - }; - /** - * [jpeg] - */ - jpeg?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - /** - * use progressive (interlace) scan (optional, default false) - */ - progressive?: boolean; - /** - * set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling/** (optional, default '4:2:0') - */ - chromaSubsampling?: string; - /** - * optimise Huffman coding tables (optional, default true) - */ - optimiseCoding?: boolean; - /** - * alternative spelling of optimiseCoding (optional, default true) - */ - optimizeCoding?: boolean; - /** - * use mozjpeg defaults, equivalent to { trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 } (optional, default false) - */ - mozjpeg?: boolean; - /** - * apply trellis quantisation (optional, default false) - */ - trellisQuantisation?: boolean; - /** - * apply overshoot deringing (optional, default false) - */ - overshootDeringing?: boolean; - /** - * optimise progressive scans, forces progressive (optional, default false) - */ - optimiseScans?: boolean; - /** - * alternative spelling of optimiseScans (optional, default false) - */ - optimizeScans?: boolean; - /** - * quantization table to use, integer 0-8 (optional, default 0) - */ - quantisationTable?: number; - /** - * alternative spelling of quantisationTable (optional, default 0) - */ - quantizationTable?: number; - /** - * force JPEG output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - /** - * [png] - */ - png?: { - /** - * use progressive (interlace) scan (optional, default false) - */ - progressive?: boolean; - /** - * zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default 6) - */ - compressionLevel?: number; - /** - * use adaptive row filtering (optional, default false) - */ - adaptiveFiltering?: boolean; - /** - * quantise to a palette-based image with alpha transparency support (optional, default false) - */ - palette?: boolean; - /** - * use the lowest number of colours needed to achieve given quality, sets palette to true (optional, default 100) - */ - quality?: number; - /** - * CPU effort, between 1 (fastest) and 10 (slowest), sets palette to true (optional, default 7) - */ - effort?: number; - /** - * maximum number of palette entries, sets palette to true (optional, default 256) - */ - colours?: number; - /** - * alternative spelling of options.colours, sets palette to true (optional, default 256) - */ - colors?: number; - /** - * level of Floyd-Steinberg error diffusion, sets palette to true (optional, default 1.0) - */ - dither?: number; - /** - * force PNG output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - /** - * [raw] - */ - raw?: { - /** - * bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex (optional, default 'uchar') - */ - depth?: string; - }; - /** - * [tiff] - */ - tiff?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - /** - * force TIFF output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - /** - * compression options: lzw, deflate, jpeg, ccittfax4 (optional, default 'jpeg') - */ - compression?: string; - /** - * compression predictor options: none, horizontal, float (optional, default 'horizontal') - */ - predictor?: string; - /** - * write an image pyramid (optional, default false) - */ - pyramid?: boolean; - /** - * write a tiled tiff (optional, default false) - */ - tile?: boolean; - /** - * horizontal tile size (optional, default 256) - */ - tileWidth?: number; - /** - * vertical tile size (optional, default 256) - */ - tileHeight?: number; - /** - * horizontal resolution in pixels/mm (optional, default 1.0) - */ - xres?: number; - /** - * vertical resolution in pixels/mm (optional, default 1.0) - */ - yres?: number; - /** - * resolution unit options: inch, cm (optional, default 'inch') - */ - resolutionUnit?: string; - /** - * reduce bitdepth to 1, 2 or 4 bit (optional, default 8) - */ - bitdepth?: number; - }; - /** - * [webp] - */ - webp?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - /** - * quality of alpha layer, integer 0-100 (optional, default 100) - */ - alphaQuality?: number; - /** - * use lossless compression mode (optional, default false) - */ - lossless?: boolean; - /** - * use near_lossless compression mode (optional, default false) - */ - nearLossless?: boolean; - /** - * use high quality chroma subsampling (optional, default false) - */ - smartSubsample?: boolean; - /** - * CPU effort, between 0 (fastest) and 6 (slowest) (optional, default 4) - */ - effort?: number; - /** - * number of animation iterations, use 0 for infinite animation (optional, default 0) - */ - loop?: number; - /** - * delay(s) between animation frames (in milliseconds) - */ - delay?: number | Array; - /** - * force WebP output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; -} diff --git a/dist/options/svg.d.ts b/dist/options/svg.d.ts new file mode 100644 index 00000000..ca5f7636 --- /dev/null +++ b/dist/options/svg.d.ts @@ -0,0 +1,31 @@ +export interface js2svgOptions { + /** + * String with spaces or number of spaces + * @default 4 + */ + indent?: string | number; + /** + * @default false + */ + pretty?: boolean; +} +export default interface SVG { + [key: string]: any; + /** + * @default false + */ + multipass?: boolean; + /** + * 'enc' or 'unenc' + * @default "base64" + */ + datauri?: string; + /** + * @default { indent: 4, pretty: false } + */ + js2svg?: js2svgOptions; + /** + * @default ["preset-default"] + */ + plugins?: any; +} diff --git a/dist/util.d.ts b/dist/util.d.ts deleted file mode 100644 index 710b8615..00000000 --- a/dist/util.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export declare const defaultLogger: { - [x: string]: (message: string) => void; - trace(msg: string): void; - debug(msg: string): void; - warn(msg: string): void; - error(msg: string): void; - info(msg: string): void; - silent(): void; -}; -export declare function createLogger(logLevel: string): {}; diff --git a/package.json b/package.json index 929dca2f..2fb35482 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "astro-compress", - "version": "0.0.8", + "version": "0.0.9", "type": "module", "description": "🗜️ AstroJS compression utilities. Compress HTML, CSS, JavaScript and more.", "repository": { @@ -22,7 +22,9 @@ "css", "html", "img", - "performance" + "javascript", + "performance", + "svg" ], "scripts": { "build": "esbuild --format=esm --platform=node --target=node14 --sources-content=false --minify --outdir=dist src/index.ts && tsc" @@ -36,8 +38,8 @@ "terser": "5.14.0" }, "devDependencies": { - "@types/node": "17.0.39", - "astro": "1.0.0-beta.40", + "@types/node": "17.0.40", + "astro": "1.0.0-beta.41", "esbuild": "0.14.42", "typescript": "4.7.3" } diff --git a/src/index.ts b/src/index.ts index a9c20cf6..adad25a7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,15 +2,17 @@ import fs from "fs"; import FastGlob from "fast-glob"; import type { AstroIntegration } from "astro"; import Options from "./options"; -import IMG from "./options/sharp"; +import IMG from "./options/img"; // @ts-ignore -import * as csso from "csso"; +import * as cssoMinify from "csso"; // @ts-ignore -import * as htmlMinifierTerser from "html-minifier-terser"; +import * as htmlMinifierTerserMinify from "html-minifier-terser"; import { minify as terserMinify } from "terser"; // @ts-ignore -import sharp from "sharp"; +import sharpMinify from "sharp"; +// @ts-ignore +import svgoMinify from "svgo"; function formatBytes(bytes: number, decimals = 2) { if (bytes === 0) return "0 Bytes"; @@ -24,23 +26,119 @@ function formatBytes(bytes: number, decimals = 2) { return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; } -const parse = async (glob: string, write: (data: string) => any) => { - const files = await FastGlob(glob); +const sharp = async (pipe: any, options: IMG = {}) => { + const fileType = pipe.options.input.file.split(".").pop(); - for (const file of files) { - try { - await fs.promises.writeFile( - file, - await write(await fs.promises.readFile(file, "utf-8")), - "utf-8" - ); - } catch (error) { - console.log("Error: Cannot minify file " + file + "!"); + if (!fileType) { + return; + } + + const typeToOption: { + [key: string]: any; + } = { + "avci": "avif", + "avcs": "avif", + "avifs": "avif", + "heic": "heif", + "heics": "heif", + "heifs": "heif", + "jfif": "jpeg", + "jif": "jpeg", + "jpe": "jpeg", + "jpg": "jpeg", + }; + + const optionType = + typeof typeToOption[fileType] !== "undefined" + ? typeToOption[fileType] + : typeof options[fileType] !== "undefined" + ? fileType + : false; + + const validOptionCalls = [ + "avif", + "gif", + "heif", + "jpeg", + "png", + "raw", + "tiff", + "webp", + ]; + + if ( + validOptionCalls.includes(optionType) && + options[optionType] !== false + ) { + return await pipe[optionType](options[optionType]).toBuffer(); + } +}; + +const pipeAll = async (settings: Options) => { + for (const fileType in settings) { + if (Object.prototype.hasOwnProperty.call(settings, fileType)) { + const setting = settings[fileType]; + + switch (fileType) { + case "css": + await parse( + `${settings.path}**/*.css`, + settings.logger, + (data) => cssoMinify.minify(data, setting).css + ); + break; + + case "html": + await parse( + `${settings.path}**/*.html`, + settings.logger, + async (data) => + await htmlMinifierTerserMinify.minify(data, setting) + ); + break; + + case "js": + await parse( + `${settings.path}**/*.{js,mjs,cjs}`, + settings.logger, + async (data) => { + const result = await terserMinify(data, setting); + return result.code ? result.code : data; + } + ); + break; + + case "img": + await parse( + `${settings.path}**/*.{avci,avcs,avif,avifs,gif,heic,heics,heif,heifs,jfif,jif,jpe,jpeg,jpg,png,raw,tiff,webp}`, + settings.logger, + async (sharpFile) => await sharp(sharpFile, setting), + async (file) => await sharpMinify(file) + ); + break; + + case "svg": + await parse( + `${settings.path}**/*.svg`, + settings.logger, + async (data) => svgoMinify.optimize(data, setting).data + ); + break; + + default: + break; + } } } }; -const sharpParse = async (glob: string, options: IMG, debug = 2) => { +const parse = async ( + glob: string, + debug = 2, + write: (data: string) => Promise = async (data) => data, + read: (file: string) => Promise = async (file) => + await fs.promises.readFile(file, "utf-8") +) => { const files = await FastGlob(glob); let totalDifference = { files: 0, @@ -49,79 +147,34 @@ const sharpParse = async (glob: string, options: IMG, debug = 2) => { for (const file of files) { try { - const fileType = file.split(".").pop(); + const fileSizeBefore = (await fs.promises.stat(file)).size; + const writeBuffer = await write(await read(file)); - if (typeof fileType !== "string") { + if (!writeBuffer) { return; } - const typeToOption: { - [key: string]: any; - } = { - "avci": "avif", - "avcs": "avif", - "avifs": "avif", - "heic": "heif", - "heics": "heif", - "heifs": "heif", - "jfif": "jpeg", - "jif": "jpeg", - "jpe": "jpeg", - "jpg": "jpeg", - }; - - const optionType = - typeof typeToOption[fileType] !== "undefined" - ? typeToOption[fileType] - : typeof options[fileType] !== "undefined" - ? fileType - : false; - - const validOptionCalls = [ - "avif", - "gif", - "heif", - "jpeg", - "png", - "raw", - "tiff", - "webp", - ]; - - if ( - validOptionCalls.includes(optionType) && - options[optionType] !== false - ) { - const fileSizeBefore = (await fs.promises.stat(file)).size; - - const sharpFile = await sharp(file); - const sharpBuffer = await sharpFile[optionType]( - options[optionType] - ).toBuffer(); + if (fileSizeBefore > Buffer.byteLength(writeBuffer)) { + await fs.promises.writeFile(file, writeBuffer, "utf-8"); - if (fileSizeBefore > Buffer.byteLength(sharpBuffer)) { - await fs.promises.writeFile(file, sharpBuffer, "utf-8"); + const fileSizeAfter = (await fs.promises.stat(file)).size; - const fileSizeAfter = (await fs.promises.stat(file)).size; + totalDifference.files++; + totalDifference.size += fileSizeBefore - fileSizeAfter; - totalDifference.files++; - totalDifference.size += fileSizeBefore - fileSizeAfter; - - if (debug > 1) { - console.info( - "\u001b[32mCompressed " + - file.replace(/^.*[\\\/]/, "") + - " for " + - formatBytes(fileSizeBefore - fileSizeAfter) + - " (" + - ( - (fileSizeAfter / fileSizeBefore) * - 100 - ).toFixed(2) + - "% compression ratio)" + - ".\u001b[39m" - ); - } + if (debug > 1) { + console.info( + "\u001b[32mCompressed " + + file.replace(/^.*[\\\/]/, "") + + " for " + + formatBytes(fileSizeBefore - fileSizeAfter) + + " (" + + ((fileSizeAfter / fileSizeBefore) * 100).toFixed( + 2 + ) + + "% compression rate)" + + ".\u001b[39m" + ); } } } catch (error) { @@ -133,7 +186,7 @@ const sharpParse = async (glob: string, options: IMG, debug = 2) => { console.info( "\u001b[32mSuccessfully compressed a total of " + totalDifference.files + - " images for " + + " files for " + formatBytes(totalDifference.size) + ".\u001b[39m" ); @@ -204,10 +257,6 @@ export default function createPlugin( toplevel: false, }, img: { - fit: { - width: 1920, - height: 1080, - }, avif: { chromaSubsampling: "4:4:4", effort: 9, @@ -237,6 +286,15 @@ export default function createPlugin( effort: 6, }, }, + svg: { + multipass: true, + datauri: "base64", + js2svg: { + indent: 0, + pretty: false, + }, + plugins: ["preset-default"], + }, logger: 2, }; @@ -255,38 +313,7 @@ export default function createPlugin( : _options.path; }, "astro:build:done": async () => { - if (_options.css) { - await parse( - `${_options.path}**/*.css`, - (data) => csso.minify(data, _options.css).css - ); - } - - if (_options.html) { - await parse( - `${_options.path}**/*.html`, - async (data) => - await htmlMinifierTerser.minify(data, _options.html) - ); - } - - if (_options.js) { - await parse( - `${_options.path}**/*.{js,mjs,cjs}`, - async (data) => - ( - await terserMinify(data, _options.js) - ).code - ); - } - - if (_options.img) { - await sharpParse( - `${_options.path}**/*.{avci,avcs,avif,avifs,gif,heic,heics,heif,heifs,jfif,jif,jpe,jpeg,jpg,png,raw,tiff,webp}`, - _options.img, - _options.logger - ); - } + await pipeAll(_options); }, }, }; diff --git a/src/options.ts b/src/options.ts index fa7a6b3f..af59db2b 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,34 +1,42 @@ -import CSS from "./options/csso"; -import HTML from "./options/html-minifier-terser"; -import JS from "./options/terser"; -import IMG from "./options/sharp"; +import CSS from "./options/css"; +import HTML from "./options/html"; +import JS from "./options/js"; +import IMG from "./options/img"; +import SVG from "./options/svg"; export default interface Options { + [key: string]: any; + /** * Astro build path. - * Default: "./dist/" + * @default "./dist/" */ path?: string; /** * [csso] options. */ - css?: CSS; + css?: boolean | CSS; /** * [html-minifier-terser] options. */ - html?: HTML; + html?: boolean | HTML; /** * [terser] options. */ - js?: JS; + js?: boolean | JS; /** * [sharp] options. */ - img?: IMG; + img?: boolean | IMG; + + /** + * [svgo] options. + */ + svg?: boolean | SVG; /** * Logger level. diff --git a/src/options/csso.ts b/src/options/css.ts similarity index 87% rename from src/options/csso.ts rename to src/options/css.ts index 7133ae1c..36512fb8 100644 --- a/src/options/csso.ts +++ b/src/options/css.ts @@ -9,27 +9,29 @@ export interface afterCompressOptions { } export default interface CSS { + [key: string]: any; + /** * Generate a source map when true. - * Default: false + * @default false */ sourceMap?: Boolean; /** * Filename of input CSS, uses for source map generation. - * Default: '' + * @default '' */ filename?: String; /** * Output debug information to stderr. - * Default: false + * @default false */ debug?: Boolean; /** * Called right after parse is run. - * Default: null + * @default null */ beforeCompress?: ({}: beforeCompressOptions) => void | Array< ({}: beforeCompressOptions) => void @@ -37,7 +39,7 @@ export default interface CSS { /** * Called right after syntax.compress() is run. - * Default: null + * @default null */ afterCompress?: ({}: afterCompressOptions) => void | Array< ({}: afterCompressOptions) => void @@ -45,20 +47,20 @@ export default interface CSS { /** * Disable | enable a structure optimisations. - * Default: true + * @default true */ restructure?: Boolean; /** * Enables merging of @media rules with the same media query by splitted by other rules. * The optimisation is unsafe in general, but should work fine in most cases. Use it on your own risk. - * Default: false + * @default false */ forceMediaMerge?: Boolean; /** * Transform a copy of input AST if true. Useful in case of AST reuse. - * Default: false + * @default false */ clone?: Boolean; @@ -66,19 +68,19 @@ export default interface CSS { * Specify what comments to leave: * 'exclamation' | true – leave all exclamation comments (i.e. /*! .. *\/) * 'first-exclamation' – remove every comment except first one false – remove all comments - * Default: true + * @default true */ comments?: String | Boolean; /** * Usage data for advanced optimisations. - * Default: null + * @default null */ usage?: {} | null; /** * Function to track every step of transformation. - * Default: null + * @default null */ logger?: () => {} | null; } diff --git a/src/options/html-minifier-terser.ts b/src/options/html.ts similarity index 81% rename from src/options/html-minifier-terser.ts rename to src/options/html.ts index ae8bf8c0..b392a07a 100644 --- a/src/options/html-minifier-terser.ts +++ b/src/options/html.ts @@ -1,45 +1,47 @@ export default interface HTML { + [key: string]: any; + /** * Treat attributes in case sensitive manner (useful for custom HTML tags). - * Default: false + * @default false */ caseSensitive?: Boolean; /** * Omit attribute values from boolean attributes. - * Default: false + * @default false */ collapseBooleanAttributes?: Boolean; /** * Don't leave any spaces between `display:inline;` elements when collapsing. * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ collapseInlineTagWhitespace?: Boolean; /** * Collapse white space that contributes to text nodes in a document tree. - * Default: false + * @default false */ collapseWhitespace?: Boolean; /** * Always collapse to 1 space (never remove it entirely). * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ conservativeCollapse?: Boolean; /** * Handle parse errors instead of aborting. - * Default: false + * @default false */ continueOnParseError?: Boolean; /** - * Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'
'`). - * Default: [ ] + * Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'
'`). + * @default [] */ customAttrAssign?: RegExp[]; @@ -50,62 +52,62 @@ export default interface HTML { /** * Arrays of regex'es that allow to support custom attribute surround expressions (e.g. ``). - * Default: [ ] + * @default [] */ customAttrSurround?: RegExp[]; /** * Arrays of regex'es that allow to support custom event attributes for `minifyJS` (e.g. `ng-click`). - * Default: [ /^on[a-z]{3,}$/ ] + * @default [/^on[a-z]{3,}$/] */ customEventAttributes?: RegExp[]; /** * Use direct Unicode characters whenever possible. - * Default: false + * @default false */ decodeEntities?: Boolean; /** * Parse input according to HTML5 specifications. - * Default: true + * @default true */ html5?: Boolean; /** * Array of regex'es that allow to ignore certain comments, when matched. - * Default: [ /^!/, /^\s*#/ ] + * @default [/^!/, /^\s*#/] */ ignoreCustomComments?: RegExp[]; /** * Array of regex'es that allow to ignore certain fragments, when matched (e.g. ``, `{{ ... }}`, etc.). - * Default: [ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ] + * @default [/<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/] */ ignoreCustomFragments?: RegExp[]; /** * Insert tags generated by HTML parser. - * Default: true + * @default true */ includeAutoGeneratedTags?: Boolean; /** * Keep the trailing slash on singleton elements. - * Default: false + * @default false */ keepClosingSlash?: Boolean; /** * Specify a maximum line length. * Compressed output will be split by newlines at valid HTML split-points. - * Default: null + * @default null */ maxLineLength?: Number | null; /** * Minify CSS in style elements and style attributes (uses clean-css). - * Default: false (could be `true`, `Object`, `Function(text, type)` + * @default false (could be `true`, `Object`, `Function(text, type)` */ minifyCSS?: | Boolean @@ -114,7 +116,7 @@ export default interface HTML { /** * Minify JavaScript in script elements and event attributes (uses Terser). - * Default: false (could be `true`, `Object`, `Function(text, inline)` + * @default false (could be `true`, `Object`, `Function(text, inline)` */ minifyJS?: | Boolean @@ -123,62 +125,62 @@ export default interface HTML { /** * Minify URLs in various attributes (uses relateurl). - * Default: false (could be `String`, `Object`, `Function(text)` + * @default false (could be `String`, `Object`, `Function(text)` */ minifyURLs?: Boolean | String | Object | (({}: { text: string }) => void); /** * Never add a newline before a tag that closes an element. - * Default: false + * @default false */ noNewlinesBeforeTagClose?: Boolean; /** * Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. * Must be used in conjunction with `collapseWhitespace=true` - * Default: false + * @default false */ preserveLineBreaks?: Boolean; /** * Prevents the escaping of the values of attributes. - * Default: false + * @default false */ preventAttributesEscaping?: Boolean; /** * Process contents of conditional comments through minifier. - * Default: false + * @default false */ processConditionalComments?: Boolean; /** * Array of strings corresponding to types of script elements to process through minifier (e.g. `text/ng-template`, `text/x-handlebars-template`, etc.). - * Default: [ ] + * @default [] */ processScripts?: String[]; /** * Type of quote to use for attribute values (' or "). - * Default: "" + * @default "" */ quoteCharacter?: String; /** * Remove quotes around attributes when possible. - * Default: false + * @default false */ removeAttributeQuotes?: Boolean; /** * Strip HTML comments. - * Default: false + * @default false */ removeComments?: Boolean; /** * Remove all attributes with whitespace-only values. - * Default: false (could be `true`, `Function(attrName, tag)` + * @default false (could be `true`, `Function(attrName, tag)` */ removeEmptyAttributes?: | Boolean @@ -186,64 +188,64 @@ export default interface HTML { /** * Remove all elements with empty contents. - * Default: false + * @default false */ removeEmptyElements?: Boolean; /** * Remove optional tags. - * Default: false + * @default false */ removeOptionalTags?: Boolean; /** * Remove attributes when value matches default. - * Default: false + * @default false */ removeRedundantAttributes?: Boolean; /** * Remove `type="text/javascript"` from `script` tags. * Other `type` attribute values are left intact - * Default: false + * @default false */ removeScriptTypeAttributes?: Boolean; /** * Remove `type="text/css"` from `style` and `link` tags. * Other `type` attribute values are left intact - * Default: false + * @default false */ removeStyleLinkTypeAttributes?: Boolean; /** * Remove space between attributes whenever possible. * Note that this will result in invalid HTML! - * Default: false + * @default false */ removeTagWhitespace?: Boolean; /** * Sort attributes by frequency. - * Default: false + * @default false */ sortAttributes?: Boolean; /** * Sort style classes by frequency. - * Default: false + * @default false */ sortClassName?: Boolean; /** * Trim white space around `ignoreCustomFragments` - * Default: false + * @default false */ trimCustomFragments?: Boolean; /** * Replaces the `doctype` with the short (HTML5) doctype. - * Default: false + * @default false */ useShortDoctype?: Boolean; } diff --git a/src/options/img.ts b/src/options/img.ts new file mode 100644 index 00000000..f879dc71 --- /dev/null +++ b/src/options/img.ts @@ -0,0 +1,407 @@ +export default interface IMG { + [key: string]: any; + + /** + * [avif] + */ + avif?: { + /** + * Quality, integer 1-100 + * @default 50 + */ + quality?: number; + + /** + * Use lossless compression + * @default false + */ + lossless?: boolean; + + /** + * CPU effort, between 0 (fastest) and 9 (slowest) + * @default 4 + */ + effort?: number; + + /** + * Set to '4:2:0' to use chroma subsampling + * @default '4:4:4' + */ + chromaSubsampling?: string; + }; + + /** + * [gif] + */ + gif?: { + /** + * Maximum number of palette entries, including transparency, between 2 and 256 + * @default 256 + */ + colours?: number; + + /** + * Alternative spelling of options.colours + * @default 256 + */ + colors?: number; + + /** + * CPU effort, between 1 (fastest) and 10 (slowest) + * @default 7 + */ + effort?: number; + + /** + * Level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) + * @default 1.0 + */ + dither?: number; + + /** + * Number of animation iterations, use 0 for infinite animation + * @default 0 + */ + loop?: number; + + /** + * Delay(s) between animation frames (in milliseconds) + */ + delay?: number | Array; + + /** + * Force GIF output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + + /** + * [heif] + */ + heif?: { + /** + * Quality, integer 1-100 + * @default 50 + */ + quality?: number; + + /** + * Compression format: av1, hevc + * @default 'av1' + */ + compression?: string; + + /** + * Use lossless compression + * @default false + */ + lossless?: boolean; + + /** + * CPU effort, between 0 (fastest) and 9 (slowest) + * @default 4 + */ + effort?: number; + + /** + * Set to '4:2:0' to use chroma subsampling + * @default '4:4:4' + */ + chromaSubsampling?: string; + }; + + /** + * [jpeg] + */ + jpeg?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + + /** + * Use progressive (interlace) scan + * @default false + */ + progressive?: boolean; + + /** + * Set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling/** + * @default '4:2:0' + */ + chromaSubsampling?: string; + + /** + * Optimise Huffman coding tables + * @default true + */ + optimiseCoding?: boolean; + + /** + * Alternative spelling of optimiseCoding + * @default true + */ + optimizeCoding?: boolean; + + /** + * Use mozjpeg defaults, equivalent to { trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 } + * @default false + */ + mozjpeg?: boolean; + + /** + * Apply trellis quantisation + * @default false + */ + trellisQuantisation?: boolean; + + /** + * Apply overshoot deringing + * @default false + */ + overshootDeringing?: boolean; + + /** + * Optimise progressive scans, forces progressive + * @default false + */ + optimiseScans?: boolean; + + /** + * Alternative spelling of optimiseScans + * @default false + */ + optimizeScans?: boolean; + + /** + * Quantization table to use, integer 0-8 + * @default 0 + */ + quantisationTable?: number; + + /** + * Alternative spelling of quantisationTable + * @default 0 + */ + quantizationTable?: number; + + /** + * Force JPEG output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + + /** + * [png] + */ + png?: { + /** + * Use progressive (interlace) scan + * @default false + */ + progressive?: boolean; + + /** + * Zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) + * @default 6 + */ + compressionLevel?: number; + + /** + * Use adaptive row filtering + * @default false + */ + adaptiveFiltering?: boolean; + + /** + * Quantise to a palette-based image with alpha transparency support + * @default false + */ + palette?: boolean; + + /** + * Use the lowest number of colours needed to achieve given quality, sets palette to true + * @default 100 + */ + quality?: number; + + /** + * CPU effort, between 1 (fastest) and 10 (slowest), sets palette to true + * @default 7 + */ + effort?: number; + + /** + * Maximum number of palette entries, sets palette to true + * @default 256 + */ + colours?: number; + + /** + * Alternative spelling of options.colours, sets palette to true + * @default 256 + */ + colors?: number; + + /** + * Level of Floyd-Steinberg error diffusion, sets palette to true + * @default 1.0 + */ + dither?: number; + + /** + * Force PNG output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; + + /** + * [raw] + */ + raw?: { + /** + * Bit depth, one of: char, uchar, short, ushort, int, uint, float, complex, double, dpcomplex + * @default 'uchar' + */ + depth?: string; + }; + + /** + * [tiff] + */ + tiff?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + + /** + * Force TIFF output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + + /** + * Compression options: lzw, deflate, jpeg, ccittfax4 + * @default 'jpeg' + */ + compression?: string; + + /** + * Compression predictor options: none, horizontal, float + * @default 'horizontal' + */ + predictor?: string; + + /** + * Write an image pyramid + * @default false + */ + pyramid?: boolean; + + /** + * Write a tiled tiff + * @default false + */ + tile?: boolean; + + /** + * Horizontal tile size + * @default 256 + */ + tileWidth?: number; + + /** + * Vertical tile size + * @default 256 + */ + tileHeight?: number; + + /** + * Horizontal resolution in pixels/mm + * @default 1.0 + */ + xres?: number; + + /** + * Vertical resolution in pixels/mm + * @default 1.0 + */ + yres?: number; + + /** + * Resolution unit options: inch, cm + * @default 'inch' + */ + resolutionUnit?: string; + + /** + * Reduce bitdepth to 1, 2 or 4 bit + * @default 8 + */ + bitdepth?: number; + }; + + /** + * [webp] + */ + webp?: { + /** + * Quality, integer 1-100 + * @default 80 + */ + quality?: number; + + /** + * Quality of alpha layer, integer 0-100 + * @default 100 + */ + alphaQuality?: number; + + /** + * Use lossless compression mode + * @default false + */ + lossless?: boolean; + + /** + * Use near_lossless compression mode + * @default false + */ + nearLossless?: boolean; + + /** + * Use high quality chroma subsampling + * @default false + */ + smartSubsample?: boolean; + + /** + * CPU effort, between 0 (fastest) and 6 (slowest) + * @default 4 + */ + effort?: number; + + /** + * Number of animation iterations, use 0 for infinite animation + * @default 0 + */ + loop?: number; + + /** + * Delay(s) between animation frames (in milliseconds) + */ + delay?: number | Array; + + /** + * Force WebP output, otherwise attempt to use input format + * @default true + */ + force?: boolean; + }; +} diff --git a/dist/options/terser.d.ts b/src/options/js.ts similarity index 81% rename from dist/options/terser.d.ts rename to src/options/js.ts index c8fcc79b..7b5eb58c 100644 --- a/dist/options/terser.d.ts +++ b/src/options/js.ts @@ -1,3 +1,5 @@ import { MinifyOptions } from "terser"; + export default interface JS extends MinifyOptions { + [key: string]: any; } diff --git a/src/options/sharp.ts b/src/options/sharp.ts deleted file mode 100644 index 38559b83..00000000 --- a/src/options/sharp.ts +++ /dev/null @@ -1,353 +0,0 @@ -export default interface IMG { - [key: string]: any; - - fit?: { - width?: number; - height?: number; - }; - - /** - * [avif] - */ - avif?: { - /** - * quality, integer 1-100 (optional, default 50) - */ - quality?: number; - - /** - * use lossless compression (optional, default false) - */ - lossless?: boolean; - - /** - * CPU effort, between 0 (fastest) and 9 (slowest) (optional, default 4) - */ - effort?: number; - - /** - * set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') - */ - chromaSubsampling?: string; - }; - - /** - * [gif] - */ - gif?: { - /** - * maximum number of palette entries, including transparency, between 2 and 256 (optional, default 256) - */ - colours?: number; - - /** - * alternative spelling of options.colours (optional, default 256) - */ - colors?: number; - - /** - * CPU effort, between 1 (fastest) and 10 (slowest) (optional, default 7) - */ - effort?: number; - - /** - * level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default 1.0) - */ - dither?: number; - - /** - * number of animation iterations, use 0 for infinite animation (optional, default 0) - */ - loop?: number; - - /** - * delay(s) between animation frames (in milliseconds) - */ - delay?: number | Array; - - /** - * force GIF output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - - /** - * [heif] - */ - heif?: { - /** - * quality, integer 1-100 (optional, default 50) - */ - quality?: number; - - /** - * compression format: av1, hevc (optional, default 'av1') - */ - compression?: string; - - /** - * use lossless compression (optional, default false) - */ - lossless?: boolean; - - /** - * CPU effort, between 0 (fastest) and 9 (slowest) (optional, default 4) - */ - effort?: number; - - /** - * set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') - */ - chromaSubsampling?: string; - }; - - /** - * [jpeg] - */ - jpeg?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - - /** - * use progressive (interlace) scan (optional, default false) - */ - progressive?: boolean; - - /** - * set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling/** (optional, default '4:2:0') - */ - chromaSubsampling?: string; - - /** - * optimise Huffman coding tables (optional, default true) - */ - optimiseCoding?: boolean; - - /** - * alternative spelling of optimiseCoding (optional, default true) - */ - optimizeCoding?: boolean; - - /** - * use mozjpeg defaults, equivalent to { trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 } (optional, default false) - */ - mozjpeg?: boolean; - - /** - * apply trellis quantisation (optional, default false) - */ - trellisQuantisation?: boolean; - - /** - * apply overshoot deringing (optional, default false) - */ - overshootDeringing?: boolean; - - /** - * optimise progressive scans, forces progressive (optional, default false) - */ - optimiseScans?: boolean; - - /** - * alternative spelling of optimiseScans (optional, default false) - */ - optimizeScans?: boolean; - - /** - * quantization table to use, integer 0-8 (optional, default 0) - */ - quantisationTable?: number; - - /** - * alternative spelling of quantisationTable (optional, default 0) - */ - quantizationTable?: number; - - /** - * force JPEG output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - - /** - * [png] - */ - png?: { - /** - * use progressive (interlace) scan (optional, default false) - */ - progressive?: boolean; - - /** - * zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default 6) - */ - compressionLevel?: number; - - /** - * use adaptive row filtering (optional, default false) - */ - adaptiveFiltering?: boolean; - - /** - * quantise to a palette-based image with alpha transparency support (optional, default false) - */ - palette?: boolean; - - /** - * use the lowest number of colours needed to achieve given quality, sets palette to true (optional, default 100) - */ - quality?: number; - - /** - * CPU effort, between 1 (fastest) and 10 (slowest), sets palette to true (optional, default 7) - */ - effort?: number; - - /** - * maximum number of palette entries, sets palette to true (optional, default 256) - */ - colours?: number; - - /** - * alternative spelling of options.colours, sets palette to true (optional, default 256) - */ - colors?: number; - - /** - * level of Floyd-Steinberg error diffusion, sets palette to true (optional, default 1.0) - */ - dither?: number; - - /** - * force PNG output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; - - /** - * [raw] - */ - raw?: { - /** - * bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex (optional, default 'uchar') - */ - depth?: string; - }; - - /** - * [tiff] - */ - tiff?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - - /** - * force TIFF output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - - /** - * compression options: lzw, deflate, jpeg, ccittfax4 (optional, default 'jpeg') - */ - compression?: string; - - /** - * compression predictor options: none, horizontal, float (optional, default 'horizontal') - */ - predictor?: string; - - /** - * write an image pyramid (optional, default false) - */ - pyramid?: boolean; - - /** - * write a tiled tiff (optional, default false) - */ - tile?: boolean; - - /** - * horizontal tile size (optional, default 256) - */ - tileWidth?: number; - - /** - * vertical tile size (optional, default 256) - */ - tileHeight?: number; - - /** - * horizontal resolution in pixels/mm (optional, default 1.0) - */ - xres?: number; - - /** - * vertical resolution in pixels/mm (optional, default 1.0) - */ - yres?: number; - - /** - * resolution unit options: inch, cm (optional, default 'inch') - */ - resolutionUnit?: string; - - /** - * reduce bitdepth to 1, 2 or 4 bit (optional, default 8) - */ - bitdepth?: number; - }; - - /** - * [webp] - */ - webp?: { - /** - * quality, integer 1-100 (optional, default 80) - */ - quality?: number; - - /** - * quality of alpha layer, integer 0-100 (optional, default 100) - */ - alphaQuality?: number; - - /** - * use lossless compression mode (optional, default false) - */ - lossless?: boolean; - - /** - * use near_lossless compression mode (optional, default false) - */ - nearLossless?: boolean; - - /** - * use high quality chroma subsampling (optional, default false) - */ - smartSubsample?: boolean; - - /** - * CPU effort, between 0 (fastest) and 6 (slowest) (optional, default 4) - */ - effort?: number; - - /** - * number of animation iterations, use 0 for infinite animation (optional, default 0) - */ - loop?: number; - - /** - * delay(s) between animation frames (in milliseconds) - */ - delay?: number | Array; - - /** - * force WebP output, otherwise attempt to use input format (optional, default true) - */ - force?: boolean; - }; -} diff --git a/src/options/svg.ts b/src/options/svg.ts new file mode 100644 index 00000000..d066376c --- /dev/null +++ b/src/options/svg.ts @@ -0,0 +1,37 @@ +export interface js2svgOptions { + /** + * String with spaces or number of spaces + * @default 4 + */ + indent?: string | number; + + /** + * @default false + */ + pretty?: boolean; +} + +export default interface SVG { + [key: string]: any; + + /** + * @default false + */ + multipass?: boolean; + + /** + * 'enc' or 'unenc' + * @default "base64" + */ + datauri?: string; + + /** + * @default { indent: 4, pretty: false } + */ + js2svg?: js2svgOptions; + + /** + * @default ["preset-default"] + */ + plugins?: any; +} diff --git a/src/options/terser.ts b/src/options/terser.ts deleted file mode 100644 index f6389755..00000000 --- a/src/options/terser.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { MinifyOptions } from "terser"; - -export default interface JS extends MinifyOptions {}