From 5b75a7041065db51a7641307b3bbb2c90a4c31da Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sun, 16 Apr 2023 22:26:23 +0200 Subject: [PATCH] gradient interpolation method : fix infinite loop --- .../postcss-gradients-interpolation-method/CHANGELOG.md | 4 ++++ .../postcss-gradients-interpolation-method/dist/index.cjs | 2 +- .../postcss-gradients-interpolation-method/dist/index.mjs | 2 +- .../src/is-gradient.ts | 2 +- .../src/modify-gradient-component-values.ts | 8 ++++++-- .../postcss-gradients-interpolation-method/test/basic.css | 5 +++++ .../test/basic.expect.css | 5 +++++ .../test/basic.preserve-false.expect.css | 5 +++++ .../test/basic.with-cloned-rules.expect.css | 5 +++++ 9 files changed, 33 insertions(+), 5 deletions(-) diff --git a/plugins/postcss-gradients-interpolation-method/CHANGELOG.md b/plugins/postcss-gradients-interpolation-method/CHANGELOG.md index e6b797d7a..6eeeaa544 100644 --- a/plugins/postcss-gradients-interpolation-method/CHANGELOG.md +++ b/plugins/postcss-gradients-interpolation-method/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Gradients Interpolation Method +### Unreleased (patch) + +- Fix infinite loop when parsing gradient functions. (see : https://github.com/csstools/postcss-plugins/issues/948) + ### 3.0.3 (April 10, 2023) - Updated `@csstools/css-tokenizer` to `2.1.1` (patch) diff --git a/plugins/postcss-gradients-interpolation-method/dist/index.cjs b/plugins/postcss-gradients-interpolation-method/dist/index.cjs index f77f94229..c3f807968 100644 --- a/plugins/postcss-gradients-interpolation-method/dist/index.cjs +++ b/plugins/postcss-gradients-interpolation-method/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("@csstools/postcss-progressive-custom-properties"),o=require("@csstools/css-parser-algorithms"),t=require("@csstools/css-tokenizer"),n=require("@csstools/css-color-parser");const i=/(repeating-)?(linear|radial|conic)-gradient\(/i,s=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(e){const o=e.parent;if(!o)return!1;const t=e.prop.toLowerCase(),n=o.index(e);for(let e=0;e0&&N.some((e=>!o.isCommentNode(e)))&&N.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]])),trim([...N,...trim(g)])}function trim(e){let t=0,n=e.length-1;for(let n=0;n=0;t--)if(!o.isWhitespaceNode(e[t])){n=t;break}return e.slice(t,n+1)}const basePlugin=e=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(n){if(!i.test(n.value))return;if(hasFallback(n))return;if(hasSupportsAtRuleAncestor(n))return;const s=t.tokenize({css:n.value}),r=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(s),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e);t&&(e.value=t)})));if(r===n.value)return;const a=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(t.cloneTokens(s)),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e,!0);t&&(e.value=t)})));n.cloneBefore({value:r}),r!==a&&n.cloneBefore({value:a}),null!=e&&e.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0,module.exports=postcssPlugin; +"use strict";var e=require("@csstools/postcss-progressive-custom-properties"),o=require("@csstools/css-parser-algorithms"),t=require("@csstools/css-tokenizer"),n=require("@csstools/css-color-parser");const i=/(repeating-)?(linear|radial|conic)-gradient\(.*?in/i,s=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(e){const o=e.parent;if(!o)return!1;const t=e.prop.toLowerCase(),n=o.index(e);for(let e=0;e0&&N.some((e=>!o.isCommentNode(e)))&&N.push(new o.TokenNode([t.TokenType.Comma,",",-1,-1,void 0]),new o.WhitespaceNode([[t.TokenType.Whitespace," ",-1,-1,void 0]])),trim([...N,...trim(g)])}function trim(e){let t=0,n=e.length-1;for(let n=0;n=0;t--)if(!o.isWhitespaceNode(e[t])){n=t;break}return e.slice(t,n+1)}const basePlugin=e=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(n){if(!i.test(n.value))return;if(hasFallback(n))return;if(hasSupportsAtRuleAncestor(n))return;const s=t.tokenize({css:n.value}),r=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(s),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e);t&&(e.value=t)})));if(r===n.value)return;const a=o.stringify(o.replaceComponentValues(o.parseCommaSeparatedListOfComponentValues(t.cloneTokens(s)),(e=>{if(!o.isFunctionNode(e))return;const t=modifyGradientFunctionComponentValues(e,!0);t&&(e.value=t)})));n.cloneBefore({value:r}),r!==a&&n.cloneBefore({value:a}),null!=e&&e.preserve||n.remove()}});basePlugin.postcss=!0;const postcssPlugin=o=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},o);return t.enableProgressiveCustomProperties?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[e(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0,module.exports=postcssPlugin; diff --git a/plugins/postcss-gradients-interpolation-method/dist/index.mjs b/plugins/postcss-gradients-interpolation-method/dist/index.mjs index 69081cde0..8378bdb44 100644 --- a/plugins/postcss-gradients-interpolation-method/dist/index.mjs +++ b/plugins/postcss-gradients-interpolation-method/dist/index.mjs @@ -1 +1 @@ -import o from"@csstools/postcss-progressive-custom-properties";import{WhitespaceNode as e,TokenNode as t,FunctionNode as r,isCommentNode as n,isWhitespaceNode as i,isTokenNode as s,stringify as l,replaceComponentValues as a,parseCommaSeparatedListOfComponentValues as c,isFunctionNode as u}from"@csstools/css-parser-algorithms";import{TokenType as p,tokenize as f,cloneTokens as v}from"@csstools/css-tokenizer";import{serializeP3 as h,color as d,colorDataFitsRGB_Gamut as m,serializeRGB as g}from"@csstools/css-color-parser";const w=/(repeating-)?(linear|radial|conic)-gradient\(/i,C=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(o){const e=o.parent;if(!e)return!1;const t=o.prop.toLowerCase(),r=e.index(o);for(let o=0;o0&&w.some((o=>!n(o)))&&w.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]])),trim([...w,...trim(g)])}function trim(o){let e=0,t=o.length-1;for(let t=0;t=0;e--)if(!i(o[e])){t=e;break}return o.slice(e,t+1)}const basePlugin=o=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(e){if(!w.test(e.value))return;if(hasFallback(e))return;if(hasSupportsAtRuleAncestor(e))return;const t=f({css:e.value}),r=l(a(c(t),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o);e&&(o.value=e)})));if(r===e.value)return;const n=l(a(c(v(t)),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o,!0);e&&(o.value=e)})));e.cloneBefore({value:r}),r!==n&&e.cloneBefore({value:n}),null!=o&&o.preserve||e.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},e);return t.enableProgressiveCustomProperties?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[o(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0;export{postcssPlugin as default}; +import o from"@csstools/postcss-progressive-custom-properties";import{WhitespaceNode as e,TokenNode as t,FunctionNode as r,isCommentNode as n,isWhitespaceNode as i,isTokenNode as s,stringify as l,replaceComponentValues as a,parseCommaSeparatedListOfComponentValues as c,isFunctionNode as u}from"@csstools/css-parser-algorithms";import{TokenType as p,tokenize as f,cloneTokens as v}from"@csstools/css-tokenizer";import{serializeP3 as h,color as d,colorDataFitsRGB_Gamut as m,serializeRGB as g}from"@csstools/css-color-parser";const w=/(repeating-)?(linear|radial|conic)-gradient\(.*?in/i,C=/^(repeating-)?(linear|radial|conic)-gradient$/i;function hasFallback(o){const e=o.parent;if(!e)return!1;const t=o.prop.toLowerCase(),r=e.index(o);for(let o=0;o0&&w.some((o=>!n(o)))&&w.push(new t([p.Comma,",",-1,-1,void 0]),new e([[p.Whitespace," ",-1,-1,void 0]])),trim([...w,...trim(g)])}function trim(o){let e=0,t=o.length-1;for(let t=0;t=0;e--)if(!i(o[e])){t=e;break}return o.slice(e,t+1)}const basePlugin=o=>({postcssPlugin:"postcss-gradients-interpolation-method",Declaration(e){if(!w.test(e.value))return;if(hasFallback(e))return;if(hasSupportsAtRuleAncestor(e))return;const t=f({css:e.value}),r=l(a(c(t),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o);e&&(o.value=e)})));if(r===e.value)return;const n=l(a(c(v(t)),(o=>{if(!u(o))return;const e=modifyGradientFunctionComponentValues(o,!0);e&&(o.value=e)})));e.cloneBefore({value:r}),r!==n&&e.cloneBefore({value:n}),null!=o&&o.preserve||e.remove()}});basePlugin.postcss=!0;const postcssPlugin=e=>{const t=Object.assign({enableProgressiveCustomProperties:!0,preserve:!0},e);return t.enableProgressiveCustomProperties?{postcssPlugin:"postcss-gradients-interpolation-method",plugins:[o(),basePlugin(t)]}:basePlugin(t)};postcssPlugin.postcss=!0;export{postcssPlugin as default}; diff --git a/plugins/postcss-gradients-interpolation-method/src/is-gradient.ts b/plugins/postcss-gradients-interpolation-method/src/is-gradient.ts index 587f8847b..2c98bede9 100644 --- a/plugins/postcss-gradients-interpolation-method/src/is-gradient.ts +++ b/plugins/postcss-gradients-interpolation-method/src/is-gradient.ts @@ -1,2 +1,2 @@ -export const gradientFunctionRegex = /(repeating-)?(linear|radial|conic)-gradient\(/i; +export const gradientFunctionRegex = /(repeating-)?(linear|radial|conic)-gradient\(.*?in/i; export const gradientNameRegex = /^(repeating-)?(linear|radial|conic)-gradient$/i; diff --git a/plugins/postcss-gradients-interpolation-method/src/modify-gradient-component-values.ts b/plugins/postcss-gradients-interpolation-method/src/modify-gradient-component-values.ts index 6033ee98d..7684a247d 100644 --- a/plugins/postcss-gradients-interpolation-method/src/modify-gradient-component-values.ts +++ b/plugins/postcss-gradients-interpolation-method/src/modify-gradient-component-values.ts @@ -33,7 +33,7 @@ export function modifyGradientFunctionComponentValues(gradientFunction: Function { // Advance to "in" keyword - while (!(isTokenNode(node) && node.value[0] === TokenType.Ident && inKeywordRegex.test(node.value[4].value))) { + while (node && !(isTokenNode(node) && node.value[0] === TokenType.Ident && inKeywordRegex.test(node.value[4].value))) { if (isTokenNode(node) && node.value[0] === TokenType.Comma) { // comma before "in" keyword return false; @@ -114,12 +114,16 @@ export function modifyGradientFunctionComponentValues(gradientFunction: Function } // Find first comma - while (!isTokenNode(node) || node.value[0] !== TokenType.Comma) { + while (node && (!isTokenNode(node) || node.value[0] !== TokenType.Comma)) { i++; node = gradientFunction.value[i]; } firstComma = node; + if (!firstComma) { + return false; + } + remainder = gradientFunction.value.slice(i + 1); } diff --git a/plugins/postcss-gradients-interpolation-method/test/basic.css b/plugins/postcss-gradients-interpolation-method/test/basic.css index c78305f7a..adaf3db49 100644 --- a/plugins/postcss-gradients-interpolation-method/test/basic.css +++ b/plugins/postcss-gradients-interpolation-method/test/basic.css @@ -309,3 +309,8 @@ to-clone { .color-with-longer-interpolation-method { background: conic-gradient(in oklch longer hue, rgb(255 150 150) 0deg, rgb(255 150 150) 360deg); } + +.issue-948 { + background-1: linear-gradient(#fff 0 0); + background-2: linear-gradient(in #fff 0 0); /* incorrect syntax, but omitting the comma to catch regressions */ +} diff --git a/plugins/postcss-gradients-interpolation-method/test/basic.expect.css b/plugins/postcss-gradients-interpolation-method/test/basic.expect.css index e9ba95d9a..bb4f30a8a 100644 --- a/plugins/postcss-gradients-interpolation-method/test/basic.expect.css +++ b/plugins/postcss-gradients-interpolation-method/test/basic.expect.css @@ -398,3 +398,8 @@ to-clone { background: conic-gradient(rgb(255, 150, 150) 0deg, rgb(246, 162, 100), rgb(213, 182, 78), rgb(160, 200, 109), rgb(93, 210, 163), rgb(41, 208, 217), rgb(93, 196, 254), color(display-p3 0.63036 0.6977 1.01537), rgb(207, 162, 246), rgb(241, 150, 204), rgb(255, 150, 150) 360deg); background: conic-gradient(in oklch longer hue, rgb(255 150 150) 0deg, rgb(255 150 150) 360deg); } + +.issue-948 { + background-1: linear-gradient(#fff 0 0); + background-2: linear-gradient(in #fff 0 0); /* incorrect syntax, but omitting the comma to catch regressions */ +} diff --git a/plugins/postcss-gradients-interpolation-method/test/basic.preserve-false.expect.css b/plugins/postcss-gradients-interpolation-method/test/basic.preserve-false.expect.css index df5a94173..886b843f9 100644 --- a/plugins/postcss-gradients-interpolation-method/test/basic.preserve-false.expect.css +++ b/plugins/postcss-gradients-interpolation-method/test/basic.preserve-false.expect.css @@ -324,3 +324,8 @@ to-clone { background: conic-gradient(rgb(255, 150, 150) 0deg, rgb(246, 162, 100), rgb(213, 182, 78), rgb(160, 200, 109), rgb(93, 210, 163), rgb(41, 208, 217), rgb(93, 196, 254), rgb(157, 179, 255), rgb(207, 162, 246), rgb(241, 150, 204), rgb(255, 150, 150) 360deg); background: conic-gradient(rgb(255, 150, 150) 0deg, rgb(246, 162, 100), rgb(213, 182, 78), rgb(160, 200, 109), rgb(93, 210, 163), rgb(41, 208, 217), rgb(93, 196, 254), color(display-p3 0.63036 0.6977 1.01537), rgb(207, 162, 246), rgb(241, 150, 204), rgb(255, 150, 150) 360deg); } + +.issue-948 { + background-1: linear-gradient(#fff 0 0); + background-2: linear-gradient(in #fff 0 0); /* incorrect syntax, but omitting the comma to catch regressions */ +} diff --git a/plugins/postcss-gradients-interpolation-method/test/basic.with-cloned-rules.expect.css b/plugins/postcss-gradients-interpolation-method/test/basic.with-cloned-rules.expect.css index 905317052..15bca36ec 100644 --- a/plugins/postcss-gradients-interpolation-method/test/basic.with-cloned-rules.expect.css +++ b/plugins/postcss-gradients-interpolation-method/test/basic.with-cloned-rules.expect.css @@ -403,3 +403,8 @@ to-clone { background: conic-gradient(rgb(255, 150, 150) 0deg, rgb(246, 162, 100), rgb(213, 182, 78), rgb(160, 200, 109), rgb(93, 210, 163), rgb(41, 208, 217), rgb(93, 196, 254), color(display-p3 0.63036 0.6977 1.01537), rgb(207, 162, 246), rgb(241, 150, 204), rgb(255, 150, 150) 360deg); background: conic-gradient(in oklch longer hue, rgb(255 150 150) 0deg, rgb(255 150 150) 360deg); } + +.issue-948 { + background-1: linear-gradient(#fff 0 0); + background-2: linear-gradient(in #fff 0 0); /* incorrect syntax, but omitting the comma to catch regressions */ +}