diff --git a/index.d.ts b/index.d.ts index 98c2236..32a5f70 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,78 +1,70 @@ -/* eslint-disable no-redeclare */ - -declare namespace srcset { - interface SrcSetDefinition { - url: string; - width?: number; - density?: number; - } - - interface Options { - /** - When strict mode is enabled, errors will be thrown on invalid input. - - When disabled, a best effort will be made to handle invalid input and no errors will be thrown. The output may be invalid. - - @default false - */ - strict?: boolean; - } +export interface SrcSetDefinition { + readonly url: string; + readonly width?: number; + readonly density?: number; } -declare const srcset: { +export interface Options { /** - Parse the HTML `` [srcset](http://mobile.smashingmagazine.com/2013/08/21/webkit-implements-srcset-and-why-its-a-good-thing/) attribute. - - Accepts a “srcset” string and returns an array of objects with the possible properties: `url` (always), `width`, `density`, and `height`. + When strict mode is enabled, errors will be thrown on invalid input. - @param srcset - A “srcset” string. + When disabled, a best effort will be made to handle invalid input and no errors will be thrown. The output may be invalid. - @example - ``` - import srcset = require('srcset'); - - console.log(srcset.parse('banner-HD.jpg 2x, banner-phone.jpg 100w')); - // [ - // { - // url: 'banner-HD.jpg', - // density: 2 - // }, - // { - // url: 'banner-phone.jpg', - // width: 100 - // } - // ] - ``` + @default false */ - parse: (srcset: string, options?: srcset.Options) => srcset.SrcSetDefinition[]; - - /** - Stringify `SrcSetDefinition`s. - - @param SrcSetDefinitions - Each object should have a `url` field and may have either `width` or `density`. When the `strict` option is `true`, only `width` or `density` is accepted. - - @returns A “srcset” string. - - @example - ``` - import srcset = require('srcset'); - - const stringified = srcset.stringify([ - { - url: 'banner-HD.jpg', - density: 2 - }, - { - url: 'banner-phone.jpg', - width: 100 - } - ]); + readonly strict?: boolean; +} - console.log(stringified); - // banner-HD.jpg 2x, banner-phone.jpg 100w - ``` - */ - stringify: (srcSetDefinitions: readonly srcset.SrcSetDefinition[], options?: srcset.Options) => string; -}; +/** +Parse the HTML `` [srcset](http://mobile.smashingmagazine.com/2013/08/21/webkit-implements-srcset-and-why-its-a-good-thing/) attribute. + +Accepts a “srcset” string and returns an array of objects with the possible properties: `url` (always), `width`, `density`, and `height`. + +@param srcset - A “srcset” string. + +@example +``` +import {parseSrcset} from 'srcset'; + +console.log(parseSrcset('banner-HD.jpg 2x, banner-phone.jpg 100w')); +// [ +// { +// url: 'banner-HD.jpg', +// density: 2 +// }, +// { +// url: 'banner-phone.jpg', +// width: 100 +// } +// ] +``` +*/ +export function parseSrcset(srcset: string, options?: Options): SrcSetDefinition[]; + +/** +Stringify `SrcSetDefinition`s. + +@param SrcSetDefinitions - Each object should have a `url` field and may have either `width` or `density`. When the `strict` option is `true`, only `width` or `density` is accepted. + +@returns A “srcset” string. + +@example +``` +import {stringifySrcset} from 'srcset'; + +const stringified = stringifySrcset([ + { + url: 'banner-HD.jpg', + density: 2 + }, + { + url: 'banner-phone.jpg', + width: 100 + } +]); -export = srcset; +console.log(stringified); +// banner-HD.jpg 2x, banner-phone.jpg 100w +``` +*/ +export function stringifySrcset(srcSetDefinitions: readonly SrcSetDefinition[], options?: Options): string; diff --git a/index.js b/index.js index 2b0a40e..cb6bd69 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,3 @@ -'use strict'; - /** This regex represents a loose rule of an “image candidate string”. @@ -80,8 +78,9 @@ const validDescriptorCheck = (value, postfix, descriptor) => { } }; -exports.parse = (string, {strict = false} = {}) => { +export function parseSrcset(string, {strict = false} = {}) { const allDescriptors = strict ? {} : undefined; + return string.split(imageCandidateRegex) .filter((part, index) => index % 2 === 1) .map(part => { @@ -124,12 +123,13 @@ exports.parse = (string, {strict = false} = {}) => { return result; }); -}; +} const knownDescriptors = new Set(['width', 'height', 'density']); -exports.stringify = (array, {strict = false} = {}) => { - const allDescriptors = strict ? {} : null; +export function stringifySrcset(array, {strict = false} = {}) { + const allDescriptors = strict ? {} : undefined; + return array.map(element => { if (!element.url) { if (strict) { @@ -183,4 +183,4 @@ exports.stringify = (array, {strict = false} = {}) => { return result.join(' '); }).join(', '); -}; +} diff --git a/index.test-d.ts b/index.test-d.ts index c105ee4..ff1f29e 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,10 +1,10 @@ import {expectType, expectError} from 'tsd'; -import srcset = require('./index.js'); +import {parseSrcset, stringifySrcset, SrcSetDefinition} from './index.js'; -const parsed = srcset.parse('banner-HD.jpg 2x, banner-phone.jpg 100w'); -expectType(parsed); +const parsed = parseSrcset('banner-HD.jpg 2x, banner-phone.jpg 100w'); +expectType(parsed); parsed.push({url: 'banner-phone-HD.jpg'}, {url: 'banner-phone-HD.jpg', width: 100}, {url: 'banner-phone-HD.jpg', density: 2}); expectError(parsed.push({})); -expectType(srcset.stringify(parsed)); +expectType(stringifySrcset(parsed)); diff --git a/package.json b/package.json index a52a327..151eb1d 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": "./index.js", "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "scripts": { "test": "xo && ava && tsd" @@ -34,8 +36,8 @@ "element" ], "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.13.1", - "xo": "^0.39.0" + "ava": "^3.15.0", + "tsd": "^0.19.0", + "xo": "^0.46.4" } } diff --git a/readme.md b/readme.md index a419105..c86a06a 100644 --- a/readme.md +++ b/readme.md @@ -6,8 +6,8 @@ Can be useful if you're creating a build-tool. ## Install -``` -$ npm install srcset +```sh +npm install srcset ``` ## Usage @@ -25,9 +25,9 @@ How an image with `srcset` might look like: Then have some fun with it: ```js -const srcset = require('srcset'); +import {parseSrcset, stringifySrcset} from 'srcset'; -const parsed = srcset.parse('banner-HD.jpg 2x, banner-phone.jpg 100w'); +const parsed = parseSrcset('banner-HD.jpg 2x, banner-phone.jpg 100w'); console.log(parsed); /* [ @@ -47,7 +47,7 @@ parsed.push({ density: 3 }); -const stringified = srcset.stringify(parsed); +const stringified = stringifySrcset(parsed); console.log(stringified); /* banner-HD.jpg 2x, banner-phone.jpg 100w, banner-super-HD.jpg 3x @@ -56,7 +56,7 @@ banner-HD.jpg 2x, banner-phone.jpg 100w, banner-super-HD.jpg 3x ## API -### .parse(string, options?) +### parseSrcset(string, options?) Parse the HTML `` [srcset](http://mobile.smashingmagazine.com/2013/08/21/webkit-implements-srcset-and-why-its-a-good-thing/) attribute. @@ -79,7 +79,7 @@ Default: `false` When enabled, an invalid “srcset” string will cause an error to be thrown. When disabled, a best effort will be made to parse the string, potentially resulting in invalid or nonsensical output. -### .stringify(SrcSetDefinitions, options?) +### stringifySrcset(SrcSetDefinitions, options?) Stringify `SrcSetDefinition`s. Accepts an array of `SrcSetDefinition` objects and returns a “srcset” string. @@ -95,11 +95,10 @@ Type: `object` ##### strict -Type: `boolean` - +Type: `boolean`\ Default: `false` -Enable or disable validation of the SrcSetDefinitions. When true, invalid input will cause an error to be thrown. When false, a best effort will be made to stringify invalid input, likely resulting in invalid srcset value. +Enable or disable validation of the `SrcSetDefinition`'s. When true, invalid input will cause an error to be thrown. When false, a best effort will be made to stringify invalid input, likely resulting in invalid srcset value. --- diff --git a/test.js b/test.js index 3fa0fb5..3cd1852 100644 --- a/test.js +++ b/test.js @@ -1,55 +1,55 @@ import test from 'ava'; -import srcset from './index.js'; +import {parseSrcset, stringifySrcset} from './index.js'; -test('.parse() should parse srcset', t => { +test('parseSrcset() should parse srcset', t => { const fixture = ' banner-HD.jpeg 2x, banner-phone.jpeg 100w, http://site.com/image.jpg?foo=bar,lorem 3x ,banner.jpeg '; - t.deepEqual(srcset.parse(fixture), [ + t.deepEqual(parseSrcset(fixture), [ {url: 'banner-HD.jpeg', density: 2}, {url: 'banner-phone.jpeg', width: 100}, {url: 'http://site.com/image.jpg?foo=bar,lorem', density: 3}, - {url: 'banner.jpeg'} + {url: 'banner.jpeg'}, ]); }); -test('.parse() should parse URLs with commas', t => { +test('parseSrcset() should parse URLs with commas', t => { const fixture = 'https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_180,q_80,w_320/rbx48jwtuvpwum29aarr.jpg 320w, https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_264,q_80,w_470/rbx48jwtuvpwum29aarr.jpg 470w, https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_80,q_80,w_80/rbx48jwtuvpwum29aarr.jpg 80w'; - t.deepEqual(srcset.parse(fixture), [ + t.deepEqual(parseSrcset(fixture), [ { url: 'https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_180,q_80,w_320/rbx48jwtuvpwum29aarr.jpg', - width: 320 + width: 320, }, { url: 'https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_264,q_80,w_470/rbx48jwtuvpwum29aarr.jpg', - width: 470 + width: 470, }, { url: 'https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_80,q_80,w_80/rbx48jwtuvpwum29aarr.jpg', - width: 80 - } + width: 80, + }, ]); }); -test('.parse() should parse srcset separated without whitespaces', t => { +test('parseSrcset() should parse srcset separated without whitespaces', t => { const fixture = 'banner-HD.jpeg 2x,banner-phone.jpeg 100w,http://site.com/image.jpg?foo=100w,lorem 1x'; - t.deepEqual(srcset.parse(fixture), [ + t.deepEqual(parseSrcset(fixture), [ {url: 'banner-HD.jpeg', density: 2}, {url: 'banner-phone.jpeg', width: 100}, - {url: 'http://site.com/image.jpg?foo=100w,lorem', density: 1} + {url: 'http://site.com/image.jpg?foo=100w,lorem', density: 1}, ]); }); -test('.stringify() should stringify srcset', t => { +test('stringifySrcset() should stringify srcset', t => { const fixture = [ {url: 'banner-HD.jpeg', density: 2}, - {url: 'banner-phone.jpeg', width: 100} + {url: 'banner-phone.jpeg', width: 100}, ]; t.is( - srcset.stringify(fixture), - 'banner-HD.jpeg 2x, banner-phone.jpeg 100w' + stringifySrcset(fixture), + 'banner-HD.jpeg 2x, banner-phone.jpeg 100w', ); }); @@ -64,20 +64,20 @@ const invalidStrings = [ 'banner.jpeg 3q', // Invalid descriptor 'banner.jpeg xxx', // Nonsense descriptor 'banner.jpg 1x, fallback.jpg', // Duplicate descriptor because the fallback is equivalent to 1x - 'banner.jpg 2x, other.jpg 2.0x' // Duplicate descriptors after normalizing + 'banner.jpg 2x, other.jpg 2.0x', // Duplicate descriptors after normalizing ]; for (const invalidSrcset of invalidStrings) { - test(`.parse() should throw on invalid input when strict mode is enabled: "${invalidSrcset}"`, t => { + test(`parseSrcset() should throw on invalid input when strict mode is enabled: "${invalidSrcset}"`, t => { t.throws(() => { - srcset.parse(invalidSrcset, {strict: true}); + parseSrcset(invalidSrcset, {strict: true}); }); }); } for (const invalidSrcset of invalidStrings) { - test(`.parse() should not throw on invalid input when strict mode is disabled: "${invalidSrcset}"`, t => { - srcset.parse(invalidSrcset, {strict: false}); + test(`parseSrcset() should not throw on invalid input when strict mode is disabled: "${invalidSrcset}"`, t => { + parseSrcset(invalidSrcset, {strict: false}); t.pass(); }); } @@ -93,21 +93,21 @@ const invalidArrays = [ [{url: 'banner.jpeg', width: Number.NaN}], // Invalid descriptor [{url: 'banner.jpeg', width: 'xxx'}], // Nonsense descriptor [{url: 'banner.jpg', density: 1}, {url: 'fallback.jpg'}], // Duplicate descriptor because the fallback is equivalent to 1x - [{url: 'banner-hd.jpg', density: 2}, {url: 'other-hd.jpg', density: 2}] // Duplicate descriptors after normalizing + [{url: 'banner-hd.jpg', density: 2}, {url: 'other-hd.jpg', density: 2}], // Duplicate descriptors after normalizing ]; for (const invalidSrcset of invalidArrays) { - test(`.stringify() should throw on invalid input when strict mode is enabled: ${JSON.stringify(invalidSrcset)}`, t => { + test(`stringifySrcset() should throw on invalid input when strict mode is enabled: ${JSON.stringify(invalidSrcset)}`, t => { t.throws(() => { - srcset.stringify(invalidSrcset, {strict: true}); + stringifySrcset(invalidSrcset, {strict: true}); }); }); } for (const invalidSrcset of invalidArrays) { - test(`.stringify() should not throw on invalid input when strict mode is disabled: ${JSON.stringify(invalidSrcset)}`, t => { + test(`stringifySrcset() should not throw on invalid input when strict mode is disabled: ${JSON.stringify(invalidSrcset)}`, t => { t.notThrows(() => { - srcset.stringify(invalidSrcset, {strict: false}); + stringifySrcset(invalidSrcset, {strict: false}); }); }); }