Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parsing of srcset without whitespaces #11

Merged
merged 4 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const integerRegex = /^-?\d+$/;
const srcsetRegex = /\s*([^,]\S*[^,](?:\s+[^,]+)?)\s*(?:,|$)/;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why such a complicated regex? If it really has to be like this, I would like to see a code comment that explains it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the regex to imageCandidateRegex to indicate that it matches loosely to an "image candidate string". It's the simplest way I can think of for now to solve the issue where there's no whitespace between two image candidate strings. I've included some comments above. Let me know if I can make it clearer!


function deepUnique(array) {
return array.sort().filter((element, index) => {
Expand All @@ -10,46 +10,46 @@ function deepUnique(array) {

exports.parse = string => {
return deepUnique(
string.split(/,\s+/).map(part => {
const result = {};

part
.trim()
.split(/\s+/)
.forEach((element, index) => {
if (index === 0) {
result.url = element;
return;
}
string.split(srcsetRegex)
.filter((part, index) => index % 2 === 1)
.map(part => {
const [url, ...descriptors] = part.trim().split(/\s+/);

const result = {url};

const value = element.slice(0, -1);
const postfix = element[element.length - 1];
const integerValue = Number.parseInt(value, 10);
const floatValue = Number.parseFloat(value);
(descriptors.length > 0 ? descriptors : ['1x']).forEach(descriptor => {
const postfix = descriptor[descriptor.length - 1];
const value = Number.parseFloat(descriptor.slice(0, -1));

if (postfix === 'w' && integerRegex.test(value)) {
if (integerValue <= 0) {
if (Number.isNaN(value)) {
throw new TypeError(`${descriptor.slice(0, -1)} is not a valid number`);
}

if (postfix === 'w') {
if (value <= 0) {
throw new Error('Width descriptor must be greater than zero');
} else if (!Number.isInteger(value)) {
throw new TypeError('Width descriptor must be an integer');
}

result.width = integerValue;
} else if (postfix === 'x' && !Number.isNaN(floatValue)) {
if (floatValue <= 0) {
result.width = value;
} else if (postfix === 'x') {
if (value <= 0) {
throw new Error('Pixel density descriptor must be greater than zero');
}

result.density = floatValue;
result.density = value;
} else {
throw new Error(`Invalid srcset descriptor: ${element}`);
throw new Error(`Invalid srcset descriptor: ${descriptor}`);
}

if (result.width && result.density) {
throw new Error('Image candidate string cannot have both width descriptor and pixel density descriptor');
}
});

return result;
})
return result;
})
);
};

Expand Down
23 changes: 21 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import test from 'ava';
import srcset from '.';

test('.parse() should parse srcset', t => {
const fixture = ' banner-HD.jpeg 2x, banner-HD.jpeg 2x, banner-HD.jpeg 2x, banner-phone.jpeg 100w, http://site.com/image.jpg?foo=bar,lorem 1x ';
const fixture = ' banner-HD.jpeg 2x, banner-HD.jpeg 2x, banner-HD.jpeg 2x, banner-phone.jpeg 100w, http://site.com/image.jpg?foo=bar,lorem 3x ,banner.jpeg ';

t.deepEqual(srcset.parse(fixture), [
{url: 'banner-HD.jpeg', density: 2},
{url: 'banner-phone.jpeg', width: 100},
{url: 'http://site.com/image.jpg?foo=bar,lorem', density: 1}
{url: 'http://site.com/image.jpg?foo=bar,lorem', density: 3},
{url: 'banner.jpeg', density: 1}
]);
});

Expand Down Expand Up @@ -42,6 +43,24 @@ test('.parse() should not parse invalid srcset', t => {
t.throws(() => {
srcset.parse('banner-phone-HD.jpeg -2x');
});

t.throws(() => {
srcset.parse('banner-phone-HD.jpeg 100.5w');
});

t.throws(() => {
srcset.parse('banner-phone-HD.jpeg xxx');
});
});

test('.parse() should parse srcset separated without white spaces', t => {
const fixture = 'banner-HD.jpeg 2x,banner-HD.jpeg 2x,banner-HD.jpeg 2x,banner-phone.jpeg 100w,http://site.com/image.jpg?foo=100w,lorem 1x';

t.deepEqual(srcset.parse(fixture), [
{url: 'banner-HD.jpeg', density: 2},
{url: 'banner-phone.jpeg', width: 100},
{url: 'http://site.com/image.jpg?foo=100w,lorem', density: 1}
]);
});

test('.stringify() should stringify srcset', t => {
Expand Down