Skip to content

Commit

Permalink
Fix open issues : postcss-image-set-function (#58)
Browse files Browse the repository at this point in the history
* fix postcss-image-set-function with image lists

* fix no url case
  • Loading branch information
romainmenke authored Dec 2, 2021
1 parent 046e252 commit 551f846
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 47 deletions.
19 changes: 13 additions & 6 deletions plugins/postcss-image-set-function/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const creator: PluginCreator<{ preserve: boolean, oninvalid: string }> = (opts?:
}

// process every image-set() function
const imageSetFunctions = [];

valueAST.walk((node) => {
if (node.type !== 'function') {
return;
Expand Down Expand Up @@ -68,14 +70,19 @@ const creator: PluginCreator<{ preserve: boolean, oninvalid: string }> = (opts?:
return x.type !== 'comment' && x.type !== 'space';
});

processImageSet(relevantNodes, decl, {
decl,
oninvalid,
preserve,
result: result,
postcss: postcss,
imageSetFunctions.push({
imageSetFunction: node,
imageSetOptionNodes: relevantNodes,
});
});

processImageSet(imageSetFunctions, decl, {
decl,
oninvalid,
preserve,
result: result,
postcss: postcss,
});
},
};
};
Expand Down
2 changes: 1 addition & 1 deletion plugins/postcss-image-set-function/src/lib/get-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function getImage(node) {
}

if (node.type === 'string') {
return valueParser.stringify(node);
return 'url('+valueParser.stringify(node)+')';
}

if (
Expand Down
90 changes: 60 additions & 30 deletions plugins/postcss-image-set-function/src/lib/process-image-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,78 @@ import { getImage } from './get-image';
import { getMedia, getMediaDPI } from './get-media';
import { handleInvalidation } from './handle-invalidation';
import type { AtRule, Container, Declaration, Result, Postcss } from 'postcss';
import type { Node } from 'postcss-value-parser';

export const processImageSet = (imageSetOptionNodes, decl: Declaration, opts: { decl: Declaration, oninvalid: string, preserve: boolean, result: Result, postcss: Postcss }) => {
type imageSetFunction = {
imageSetFunction: Node;
imageSetOptionNodes: Array<Node>;
}

type mediaByDpr = {
atRule: AtRule;
value: string;
}

export const processImageSet = (imageSetFunctions: Array<imageSetFunction>, decl: Declaration, opts: { decl: Declaration, oninvalid: string, preserve: boolean, result: Result, postcss: Postcss }) => {
const parent = decl.parent;
const mediasByDpr: Map<number, AtRule> = new Map();

const length = imageSetOptionNodes.length;
let index = -1;

while (index < length) {
const comma = index < 0 ? true : getComma(imageSetOptionNodes[index]);
const value = getImage(imageSetOptionNodes[index + 1]);
const mediaDPI = getMediaDPI(imageSetOptionNodes[index + 2]);

const media = getMedia(mediaDPI, opts.postcss);

// handle invalidations
if (!comma) {
handleInvalidation(opts, 'expected a comma', valueParser.stringify(imageSetOptionNodes));
return;
} else if (!value) {
handleInvalidation(opts, 'unexpected image', valueParser.stringify(imageSetOptionNodes));
return;
} else if (!media || !mediaDPI || mediasByDpr.has(mediaDPI)) {
handleInvalidation(opts, 'unexpected resolution', valueParser.stringify(imageSetOptionNodes));
return;
const mediasByDpr: Map<number, mediaByDpr> = new Map();
const declValue = decl.value;

for (let i = 0; i < imageSetFunctions.length; i++) {
const { imageSetFunction, imageSetOptionNodes } = imageSetFunctions[i];
const mediasByDprPerItem: Map<number, AtRule> = new Map();

const length = imageSetOptionNodes.length;
let index = -1;

while (index < length) {
const comma = index < 0 ? true : getComma(imageSetOptionNodes[index]);
const value = getImage(imageSetOptionNodes[index + 1]);
const mediaDPI = getMediaDPI(imageSetOptionNodes[index + 2]);

const media = getMedia(mediaDPI, opts.postcss);

// handle invalidations
if (!comma) {
handleInvalidation(opts, 'expected a comma', valueParser.stringify(imageSetOptionNodes));
return;
} else if (!value) {
handleInvalidation(opts, 'unexpected image', valueParser.stringify(imageSetOptionNodes));
return;
} else if (!media || !mediaDPI || mediasByDprPerItem.has(mediaDPI)) {
handleInvalidation(opts, 'unexpected resolution', valueParser.stringify(imageSetOptionNodes));
return;
}

mediasByDprPerItem.set(mediaDPI, media);

if (mediasByDpr.has(mediaDPI)) {
const m = mediasByDpr.get(mediaDPI);
m.value = m.value.replace(valueParser.stringify(imageSetFunction), value.trim());
mediasByDpr.set(mediaDPI, m);
} else {
mediasByDpr.set(mediaDPI, {
atRule: media,
value: declValue.replace(valueParser.stringify(imageSetFunction), value.trim()),
});
}

index += 3;
}
}

mediasByDpr.set(mediaDPI, media);

for (const { atRule, value } of mediasByDpr.values()) {
// prepare @media { decl: <image> }
const parentClone = parent.clone().removeAll();
const declClone = decl.clone({ value: value.trim() });
const declClone = decl.clone({ value: value });

parentClone.append(declClone);
media.append(parentClone);

index += 3;
atRule.append(parentClone);
}

const medias = Array.from(mediasByDpr.keys())
.sort((a, b) => a - b)
.map(params => mediasByDpr.get(params));
.map(params => mediasByDpr.get(params).atRule);

if (!medias.length) {
return;
Expand Down
8 changes: 8 additions & 0 deletions plugins/postcss-image-set-function/test/basic.css
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@
}
}

.list-1 {
background-image: linear-gradient(#4444, #8888), image-set(url(img.png) 1x, url([email protected]) 2x);
}

.list-2 {
background-image: image-set(url(img-a.png) 1x, url([email protected]) 2x), image-set(url(img-b.png) 1x, url([email protected]) 2x);
}

.test-valid-data-url {
background-image: image-set(url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==) 1x, url(img/test-2x.png) 2x);
}
Expand Down
34 changes: 29 additions & 5 deletions plugins/postcss-image-set-function/test/basic.expect.css
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@

.test-no-url {
order: 1;
background-image: "img/test.png";
background-image: url("img/test.png");
background-image: image-set(
"img/test.png" 1x,
"img/test-2x.png" 2x
);
order: 2;
background-image: "img/test.png";
background-image: url("img/test.png");
background-image: image-set(
"img/test.png" 1x,
"img/test-2x.png" 2x,
Expand All @@ -174,21 +174,21 @@
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.test-no-url {
background-image: "img/test-2x.png";
background-image: url("img/test-2x.png");
}
}

@media (-webkit-min-device-pixel-ratio: 6.25), (min-resolution: 600dpi) {

.test-no-url {
background-image: "my-img-print.png";
background-image: url("my-img-print.png");
}
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.test-no-url {
background-image: "img/test-2x.png";
background-image: url("img/test-2x.png");
}
}

Expand Down Expand Up @@ -259,6 +259,30 @@
}
}

.list-1 {
background-image: linear-gradient(#4444, #8888), url(img.png);
background-image: linear-gradient(#4444, #8888), image-set(url(img.png) 1x, url([email protected]) 2x);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.list-1 {
background-image: linear-gradient(#4444, #8888), url([email protected]);
}
}

.list-2 {
background-image: url(img-a.png), url(img-b.png);
background-image: image-set(url(img-a.png) 1x, url([email protected]) 2x), image-set(url(img-b.png) 1x, url([email protected]) 2x);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.list-2 {
background-image: url([email protected]), url([email protected]);
}
}

.test-valid-data-url {
background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
background-image: image-set(url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==) 1x, url(img/test-2x.png) 2x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,30 +118,30 @@

.test-no-url {
order: 1;
background-image: "img/test.png";
background-image: url("img/test.png");
order: 2;
background-image: "img/test.png";
background-image: url("img/test.png");
order: 3;
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.test-no-url {
background-image: "img/test-2x.png";
background-image: url("img/test-2x.png");
}
}

@media (-webkit-min-device-pixel-ratio: 6.25), (min-resolution: 600dpi) {

.test-no-url {
background-image: "my-img-print.png";
background-image: url("my-img-print.png");
}
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.test-no-url {
background-image: "img/test-2x.png";
background-image: url("img/test-2x.png");
}
}

Expand Down Expand Up @@ -197,6 +197,28 @@
}
}

.list-1 {
background-image: linear-gradient(#4444, #8888), url(img.png);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.list-1 {
background-image: linear-gradient(#4444, #8888), url([email protected]);
}
}

.list-2 {
background-image: url(img-a.png), url(img-b.png);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {

.list-2 {
background-image: url([email protected]), url([email protected]);
}
}

.test-valid-data-url {
background-image: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
}
Expand Down

0 comments on commit 551f846

Please sign in to comment.