diff --git a/.size-limit b/.size-limit index 094df47..4dcbf07 100644 --- a/.size-limit +++ b/.size-limit @@ -1,7 +1,7 @@ [ { "path": "lib/index.js", - "limit": "3 KB", + "limit": "3.1 KB", "webpack": false }, { diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ba23f..7993366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,13 @@ DO NOT TOUCH. SAVE IT ON TOP. --> +## [3.0.0] - 2020-06-23 +### Breaking +- Naming change: srcset -> srcSet, Srcset -> SrcSet + +### Added +- `originMultiplier` image metadata + ## [2.0.0] - 2020-05-26 ### Breaking - Requires `Node 10+` diff --git a/README.md b/README.md index c32ec29..9de5204 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ yarn exec -- srcset [...sources] [...options] | Option | Description | Default | |--------|-------------|---------| -| sources | Source image(s) glob patterns. | | +| sources | Source image(s) glob patterns. | required | | ‑‑help, -h | Print this message. | | | ‑‑verbose, -v | Print additional info about progress. | | | ‑‑match, -m | Glob patern(s) or media query(ies) to match image(s) by name or size. | all images | @@ -67,7 +67,7 @@ yarn exec -- srcset [...sources] [...options] | ‑‑format, -f | Output image(s) formats to convert. | no convert | | ‑‑skipOptimization | Do not optimize output images. | `false` | | ‑‑noScalingUp | Do not generate images with higher resolution than they's sources are. | `false` -| ‑‑dest, -d | Destination directory. | | +| ‑‑dest, -d | Destination directory. | required | #### Example @@ -75,96 +75,64 @@ yarn exec -- srcset [...sources] [...options] srcset "src/images/*.jpg" --match "(min-width: 1920px)" --width 1920,1280,1024,860,540,320 --format jpg,webp -d static/images ``` -#### Configuration +### Configuration -Configuration file is optional. If needed, can be defined through `.srcsetrc` (JSON file) or `.srcsetrc.js` in the root directory of the project. +#### Common options -Supported options: - -```ts -interface ICommonConfig { - /** - * Object with Sharp configs for each supported format. - */ - processing?: Partial; - /** - * Object with imagemin plugins for each format. - */ - optimization?: Partial; - /** - * Do not optimize output images. - */ - skipOptimization?: boolean; - /** - * Generate images with higher resolution than they's sources are. - */ - scalingUp?: boolean; - /** - * Postfix string or function to generate postfix for image. - */ - postfix?: Postfix; -} +| Option | Type | Description | Default | +|--------|------|-------------|---------| +| processing | Partial\<[IProcessingConfig]\> | Object with Sharp configs for each supported format. | see [defaults.ts](src/defaults.ts) | +| optimization | Partial\<[IOptimizationConfig]\> | Object with imagemin plugins for each format. | see [defaults.ts](src/defaults.ts) | +| skipOptimization | boolean | Do not optimize output images. | `false` | +| scalingUp | boolean | Generate images with higher resolution than they's sources are. | `true` | +| postfix | [Postfix] | Output image(s) widths to resize, value less than or equal to 1 will be detected as multiplier. | see [defaults.ts](src/defaults.ts) | -interface IRule extends ICommonConfig { - /** - * There is support of 3 types of matchers: - * 1. Glob pattern of file path; - * 2. Media query to match image by size; - * 3. `(path: string, size: ISize, source: Vinyl) => boolean` function. - */ - match?: Matcher; - /** - * Output image(s) formats to convert. - */ - format?: SupportedExtension|SupportedExtension[]; - /** - * Output image(s) widths to resize, value less than or equal to 1 will be detected as multiplier. - */ - width?: number|number[]; -} +#### Rule options -/** - * RC file: - */ -interface IConfig extends ICommonConfig { - /** - * Source image(s) glob patterns. - */ - src?: string|string[]; - /** - * Rules. - */ - rules?: IRule[]; - /** - * Print additional info about progress. - */ - verbose?: boolean; - /** - * Destination directory. - */ - dest?: string; -} -``` +Extends [common options](#common-options). + +| Option | Type | Description | Default | +|--------|------|-------------|---------| +| match | [Matcher] | There is support of 3 types of matchers:
1. Glob pattern of file path;
2. Media query to match image by size;
3. `(path: string, size: ISize, source: Vinyl) => boolean` function. | all images | +| format | [SupportedExtension]\|[SupportedExtension]\[\] | Output image(s) formats to convert. | no convert | +| width | number\|number[] | Output image(s) widths to resize, value less than or equal to 1 will be detected as multiplier. | `[1]` | + +#### Configuration file + +Configuration file is optional. If needed, can be defined through `.srcsetrc` (JSON file) or `.srcsetrc.js` in the root directory of the project. -- [`IProcessingConfig`](https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.iprocessingconfig.html) -- [`IOptimizationConfig`](https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.ioptimizationconfig.html) -- [`Postfix`](https://trigensoftware.github.io/flexis-srcset/modules/_types_.html#postfix) -- [`Matcher`](https://trigensoftware.github.io/flexis-srcset/modules/_helpers_.html#matcher) -- [`SupportedExtension`](https://trigensoftware.github.io/flexis-srcset/modules/_extensions_.html#supportedextension) +Supported options, extends [common options](#common-options): + +| Option | Type | Description | Default | +|--------|------|-------------|---------| +| src | string\|string[] | Source image(s) glob patterns. | required | +| rules | [IRule](#rule-options)\[\] | Rules. | `[]` | +| verbose | boolean | Print additional info about progress. | `false` | +| dest | string | Destination directory. | required | + +[IProcessingConfig]: https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.iprocessingconfig.html +[IOptimizationConfig]: https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.ioptimizationconfig.html +[Postfix]: https://trigensoftware.github.io/flexis-srcset/modules/_types_.html#postfix +[Matcher]: https://trigensoftware.github.io/flexis-srcset/modules/_helpers_.html#matcher +[SupportedExtension]: https://trigensoftware.github.io/flexis-srcset/modules/_extensions_.html#supportedextension ### Gulp You can use `@flexis/srcset` with [Gulp](https://github.com/gulpjs/gulp): ```js -import srcset from '@flexis/srcset/lib/stream'; +import srcSet from '@flexis/srcset/lib/stream'; gulp.task('images', () => gulp.src('src/*.{jpg,png}') - .pipe(srcset([{ + .pipe(srcSet([{ match: '(min-width: 3000px)', width: [1920, 1280, 1024, 860, 540, 320], format: ['jpg', 'webp'] + }, { + match: '(max-width: 3000px)', + width: [1, .5], + format: ['jpg', 'webp'] }], { skipOptimization: true })) @@ -176,78 +144,22 @@ gulp.task('images', () => Plugin options: -```ts -interface ICommonConfig { - /** - * Object with Sharp configs for each supported format. - */ - processing?: Partial; - /** - * Object with imagemin plugins for each format. - */ - optimization?: Partial; - /** - * Do not optimize output images. - */ - skipOptimization?: boolean; - /** - * Generate images with higher resolution than they's sources are. - */ - scalingUp?: boolean; - /** - * Postfix string or function to generate postfix for image. - */ - postfix?: Postfix; -} - -/** - * First argument: IPluginRule[] - */ -interface IPluginRule extends ICommonConfig { - /** - * There is support of 3 types of matchers: - * 1. Glob pattern of file path; - * 2. Media query to match image by size; - * 3. `(path: string, size: ISize, source: Vinyl) => boolean` function. - */ - match?: Matcher; - /** - * Output image(s) formats to convert. - */ - format?: SupportedExtension|SupportedExtension[]; - /** - * Output image(s) widths to resize, value less than or equal to 1 will be detected as multiplier. - */ - width?: number|number[]; -} - -/** - * Second argument: - */ -interface IPluginConfig extends ICommonConfig { - /** - * Print additional info about progress. - */ - verbose?: boolean; -} -``` +First argument is [IRule](#rule-options)\[\], second argument extends [common options](#common-options): -- [`IProcessingConfig`](https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.iprocessingconfig.html) -- [`IOptimizationConfig`](https://trigensoftware.github.io/flexis-srcset/interfaces/_types_.ioptimizationconfig.html) -- [`Postfix`](https://trigensoftware.github.io/flexis-srcset/modules/_types_.html#postfix) -- [`Matcher`](https://trigensoftware.github.io/flexis-srcset/modules/_helpers_.html#matcher) -- [`SupportedExtension`](https://trigensoftware.github.io/flexis-srcset/modules/_extensions_.html#supportedextension) +| Option | Type | Description | Default | +|--------|------|-------------|---------| +| verbose | boolean | Print additional info about progress. | `false` | ### JS API Module exposes next API: ```js -export default SrcsetGenerator; +export default SrcSetGenerator; export { IProcessingConfig, IOptimizationConfig, - ISrsetVinyl, + ISrcSetVinyl, ISize, IMatcherFunction, SupportedExtension, @@ -269,7 +181,7 @@ export { import { promises as fs } from 'fs'; -import SrcsetGenerator from '@flexis/favicons'; +import SrcSetGenerator from '@flexis/favicons'; import Vinyl from 'vinyl'; async function generate() { @@ -280,8 +192,8 @@ async function generate() { path, contents }); - const srcset = new SrcsetGenerator(); - const images = srcset.generate(source, { + const srcSet = new SrcSetGenerator(); + const images = srcSet.generate(source, { width: [1920, 1280, 1024, 860, 540, 320], format: ['jpg', 'webp'] }); diff --git a/package.json b/package.json index ee717ee..9d26c63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flexis/srcset", - "version": "2.0.0", + "version": "3.0.0", "description": "Highly customizable tool for generating responsive images.", "author": "dangreen", "license": "MIT", diff --git a/src/ISrcsetVinyl.ts b/src/ISrcsetVinyl.ts deleted file mode 100644 index 0d819c7..0000000 --- a/src/ISrcsetVinyl.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Vinyl from 'vinyl'; -import { - Metadata -} from 'sharp'; - -export default interface ISrsetVinyl extends Vinyl { - metadata?: Metadata & { - originMultiplier?: number; - }; - postfix?: string; -} diff --git a/src/helpers.ts b/src/helpers.ts index c86542b..76cefe5 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -2,7 +2,9 @@ import Vinyl from 'vinyl'; import minimatch from 'minimatch'; import mediaQuery from 'css-mediaquery'; import Sharp from 'sharp'; -import ISrsetVinyl from './ISrcsetVinyl'; +import { + ISrcSetVinyl +} from './types'; import { isSupportedType } from './extensions'; @@ -41,7 +43,7 @@ export function isVinylBuffer(source: Vinyl) { * @param force - Force refetch metadata. * @return Source image file with attached metadata. */ -export async function attachMetadata(source: Vinyl, force = false): Promise { +export async function attachMetadata(source: Vinyl, force = false): Promise { if (!force && typeof source.metadata === 'object') { return source; @@ -53,6 +55,7 @@ export async function attachMetadata(source: Vinyl, force = false): Promise) { - const vinyls: ISrsetVinyl[] = []; + const vinyls: ISrcSetVinyl[] = []; for await (const vinyl of iterator) { vinyls.push(vinyl); @@ -21,45 +24,45 @@ async function vinylsFromAsyncIterator(iterator: AsyncIterableIterator) { return vinyls; } -describe('SrcsetGenerator', () => { +describe('SrcSetGenerator', () => { jest.setTimeout(30000); it('should create correct instance', () => { - const srcset = new SrcsetGenerator(); + const srcSet = new SrcSetGenerator(); - expect(typeof srcset.generate).toBe('function'); + expect(typeof srcSet.generate).toBe('function'); }); it('should skip optimization', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true, optimization }); - const [notOptimizedImage] = await vinylsFromAsyncIterator(srcset.generate(image)); + const [notOptimizedImage] = await vinylsFromAsyncIterator(srcSet.generate(image)); expect((notOptimizedImage.contents as Buffer).length).toBe(image.contents.length); }); it('should optimize', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ optimization }); - const [optimizedImage] = await vinylsFromAsyncIterator(srcset.generate(image)); + const [optimizedImage] = await vinylsFromAsyncIterator(srcSet.generate(image)); expect((optimizedImage.contents as Buffer).length).toBeLessThan(image.contents.length); }); it('should skip scaling up', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true, scalingUp: false }); - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { width: [1, 5000] })); @@ -68,10 +71,10 @@ describe('SrcsetGenerator', () => { it('should scaling up', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true }); - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { width: [1, 5000] })); @@ -80,10 +83,10 @@ describe('SrcsetGenerator', () => { it('should add default postfix', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true }); - const [imageWithPostfix] = await vinylsFromAsyncIterator(srcset.generate(image, { + const [imageWithPostfix] = await vinylsFromAsyncIterator(srcSet.generate(image, { width: [320] })); @@ -93,11 +96,11 @@ describe('SrcsetGenerator', () => { it('should add custom postfix', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true, postfix: '@postfix' }); - const [imageWithPostfix] = await vinylsFromAsyncIterator(srcset.generate(image)); + const [imageWithPostfix] = await vinylsFromAsyncIterator(srcSet.generate(image)); expect(imageWithPostfix.stem).toBe('image@postfix'); expect(imageWithPostfix.postfix).toBe('@postfix'); @@ -105,7 +108,7 @@ describe('SrcsetGenerator', () => { describe('#generate', () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ skipOptimization: true }); @@ -116,16 +119,16 @@ describe('SrcsetGenerator', () => { bmp.extname = '.bmp'; expect( - vinylsFromAsyncIterator(srcset.generate(bmp)) + vinylsFromAsyncIterator(srcSet.generate(bmp)) ).rejects.toThrow(); }); it('should skip optimization', async () => { - const srcset = new SrcsetGenerator({ + const srcSet = new SrcSetGenerator({ optimization }); - const [notOptimizedImage] = await vinylsFromAsyncIterator(srcset.generate(image, { + const [notOptimizedImage] = await vinylsFromAsyncIterator(srcSet.generate(image, { skipOptimization: true })); @@ -134,7 +137,7 @@ describe('SrcsetGenerator', () => { it('should skip scaling up', async () => { - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { scalingUp: false, width: [1, 5000] })); @@ -148,7 +151,7 @@ describe('SrcsetGenerator', () => { gif.extname = '.gif'; - const images = await vinylsFromAsyncIterator(srcset.generate(gif, { + const images = await vinylsFromAsyncIterator(srcSet.generate(gif, { format: ['jpg', 'webp'] })); @@ -161,7 +164,7 @@ describe('SrcsetGenerator', () => { gif.extname = '.gif'; - const images = await vinylsFromAsyncIterator(srcset.generate(gif, { + const images = await vinylsFromAsyncIterator(srcSet.generate(gif, { format: ['jpg', 'webp', 'gif'] })); @@ -170,7 +173,7 @@ describe('SrcsetGenerator', () => { it('should add custom postfix', async () => { - const [imageWithPostfix] = await vinylsFromAsyncIterator(srcset.generate(image, { + const [imageWithPostfix] = await vinylsFromAsyncIterator(srcSet.generate(image, { postfix: '@postfix' })); @@ -180,7 +183,7 @@ describe('SrcsetGenerator', () => { it('should generate desired widths', async () => { - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { width: [1, 1280, 320] })); @@ -192,7 +195,7 @@ describe('SrcsetGenerator', () => { it('should generate desired scaled widths', async () => { - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { width: [0.33, 0.66, 1] })); @@ -204,7 +207,7 @@ describe('SrcsetGenerator', () => { it('should generate desired formats', async () => { - const images = await vinylsFromAsyncIterator(srcset.generate(image, { + const images = await vinylsFromAsyncIterator(srcSet.generate(image, { format: ['jpg', 'webp', 'png'] })); @@ -217,7 +220,7 @@ describe('SrcsetGenerator', () => { it('should throw error if desired format is not supported', async () => { expect( - vinylsFromAsyncIterator(srcset.generate(image, { + vinylsFromAsyncIterator(srcSet.generate(image, { format: 'bmp' } as any)) ).rejects.toThrow(); @@ -226,15 +229,19 @@ describe('SrcsetGenerator', () => { it('should add originMultiplier to metadate', async () => { const [ + w64, x2, - x1 - ] = await vinylsFromAsyncIterator(srcset.generate(image, { + x1, + w320 + ] = await vinylsFromAsyncIterator(srcSet.generate(image, { scalingUp: false, - width: [1, .5] + width: [64, 1, .5, 320] })); + expect(w64.metadata.originMultiplier).toBeFalsy(); expect(x2.metadata.originMultiplier).toBe(1); expect(x1.metadata.originMultiplier).toBe(.5); + expect(w320.metadata.originMultiplier).toBeFalsy(); }); }); }); diff --git a/test/stream.spec.ts b/test/stream.spec.ts index a1a2e20..e1d050b 100644 --- a/test/stream.spec.ts +++ b/test/stream.spec.ts @@ -1,6 +1,6 @@ import path from 'path'; import vfs from 'vinyl-fs'; -import srcset from '../src/stream'; +import srcSet from '../src/stream'; jest.setTimeout(50000); @@ -13,7 +13,7 @@ describe('stream', () => { vfs.src( path.join(__dirname, 'images/*.{jpg,png,gif,ico}') ) - .pipe(srcset([{ + .pipe(srcSet([{ match: '(min-width: 3000px)', width: [1, 3200, 1920, 1280, 720, 560, 320], format: ['jpg', 'webp'] @@ -35,7 +35,7 @@ describe('stream', () => { vfs.src( path.join(__dirname, 'images/*.{jpg,png,gif,ico}') ) - .pipe(srcset([{ + .pipe(srcSet([{ match: '**/*.png', width: [1, .5] }]))