From f46e97ae6110d295e9319a068b1596f34be1f648 Mon Sep 17 00:00:00 2001 From: Thierry Bela Nanga Date: Mon, 13 Jul 2026 02:05:29 -0400 Subject: [PATCH] disable if syntax expansion #131 --- CHANGELOG.md | 4 + README.md | 1 - dist/index-umd-web.js | 45 +++++- dist/index.cjs | 45 +++++- dist/lib/ast/features/if.js | 45 +++++- dist/lib/parser/parse.js | 2 + jsr.json | 2 +- package.json | 2 +- src/lib/ast/features/if.ts | 70 ++++++++- src/lib/parser/parse.ts | 3 + test/specs/code/at-rules.js | 66 ++++---- test/specs/code/expand.js | 292 ++++++++++++++++++------------------ 12 files changed, 388 insertions(+), 189 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0d0cdc..f4964db0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v1.4.5 + +- [x] Temporarily disable if syntax expansion + ## v1.4.4 ### Validation changes diff --git a/README.md b/README.md index 6e9655d0..63a24c6f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ $ deno add @tbela99/css-parser ## Syntax lowering CSS-Parser can transform these modern CSS features into lower-level CSS syntax: * **Nested CSS transpilation** to legacy-compatible syntax. -* **`if()` function transpilation** for broader browser compatibility. * **`color-mix()` function conversion** convert new color-mix() syntax to any of the supported colors. ## Benchmark diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 8e9501bb..b5efc654 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -21543,7 +21543,48 @@ } } if (result.length > 0) { - replaceNodeOrValue(declarationNode.parent, declarationNode, result); + let invalidTokensTypes = new Set([ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.SemiColonTokenType, + exports.EnumToken.ColonTokenType + ]); + for (i = 0; i < result.length; i++) { + if (result[i].typ === exports.EnumToken.DeclarationNodeType) { + for (const { value } of walkValues(result[i].val, result[i])) { + if (value.typ === exports.EnumToken.ImageFunctionTokenType && value.val.includes('-gradient')) { + let valid = true; + let j; + for (j = 0; j < value.chi.length; j++) { + if (value.chi[j].typ === exports.EnumToken.IdenTokenType && 'else' == value.chi[j].val) { + valid = false; + break; + } + if (invalidTokensTypes.has(value.chi[j].typ) && + (value.chi[j + 1]?.typ === exports.EnumToken.CommaTokenType || j == value.chi.length - 1) && + (j == 0 || exports.EnumToken.CommaTokenType == value.chi[j - 1]?.typ)) { + valid = false; + break; + } + } + if (!valid) { + result.splice(i--, 1); + break; + } + // const res = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (value as FunctionToken).val + '()'), createValidationContext((value as FunctionToken).chi), { + // }); + // if (!res.success) { + // result.splice(i--, 1); + // break; + // } + } + } + } + } + // console.debug({result}); + // throw new Error("Not implemented"); + if (result.length > 0) { + replaceNodeOrValue(declarationNode.parent, declarationNode, result); + } } // else remove node? return result; @@ -30090,6 +30131,8 @@ if (options.resolveImport) { options.resolveUrls = true; } + // turn off expandIfSyntax for now + options.expandIfSyntax = false; const startTime = performance.now(); const errors = []; const src = options.src; diff --git a/dist/index.cjs b/dist/index.cjs index 9f40e165..9bdd000b 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -22785,7 +22785,48 @@ function processNode(declarationNode, cache) { } } if (result.length > 0) { - replaceNodeOrValue(declarationNode.parent, declarationNode, result); + let invalidTokensTypes = new Set([ + exports.EnumToken.WhitespaceTokenType, + exports.EnumToken.SemiColonTokenType, + exports.EnumToken.ColonTokenType + ]); + for (i = 0; i < result.length; i++) { + if (result[i].typ === exports.EnumToken.DeclarationNodeType) { + for (const { value } of walkValues(result[i].val, result[i])) { + if (value.typ === exports.EnumToken.ImageFunctionTokenType && value.val.includes('-gradient')) { + let valid = true; + let j; + for (j = 0; j < value.chi.length; j++) { + if (value.chi[j].typ === exports.EnumToken.IdenTokenType && 'else' == value.chi[j].val) { + valid = false; + break; + } + if (invalidTokensTypes.has(value.chi[j].typ) && + (value.chi[j + 1]?.typ === exports.EnumToken.CommaTokenType || j == value.chi.length - 1) && + (j == 0 || exports.EnumToken.CommaTokenType == value.chi[j - 1]?.typ)) { + valid = false; + break; + } + } + if (!valid) { + result.splice(i--, 1); + break; + } + // const res = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (value as FunctionToken).val + '()'), createValidationContext((value as FunctionToken).chi), { + // }); + // if (!res.success) { + // result.splice(i--, 1); + // break; + // } + } + } + } + } + // console.debug({result}); + // throw new Error("Not implemented"); + if (result.length > 0) { + replaceNodeOrValue(declarationNode.parent, declarationNode, result); + } } // else remove node? return result; @@ -29855,6 +29896,8 @@ async function doParse(iter, options = {}) { if (options.resolveImport) { options.resolveUrls = true; } + // turn off expandIfSyntax for now + options.expandIfSyntax = false; const startTime = performance.now(); const errors = []; const src = options.src; diff --git a/dist/lib/ast/features/if.js b/dist/lib/ast/features/if.js index 7d28497e..3744539b 100644 --- a/dist/lib/ast/features/if.js +++ b/dist/lib/ast/features/if.js @@ -7,7 +7,7 @@ import { replaceNodeOrValue } from '../../parser/utils/token.js'; import { cloneNode } from '../clone.js'; import { trimArray } from '../../validation/match.js'; import { findByValue } from '../find.js'; -import { walk } from '../walk.js'; +import { walk, walkValues } from '../walk.js'; import { eq } from '../../parser/utils/eq.js'; const nodeMatcher = (value) => value.typ === EnumToken.IfConditionTokenType || @@ -169,7 +169,48 @@ function processNode(declarationNode, cache) { } } if (result.length > 0) { - replaceNodeOrValue(declarationNode.parent, declarationNode, result); + let invalidTokensTypes = new Set([ + EnumToken.WhitespaceTokenType, + EnumToken.SemiColonTokenType, + EnumToken.ColonTokenType + ]); + for (i = 0; i < result.length; i++) { + if (result[i].typ === EnumToken.DeclarationNodeType) { + for (const { value } of walkValues(result[i].val, result[i])) { + if (value.typ === EnumToken.ImageFunctionTokenType && value.val.includes('-gradient')) { + let valid = true; + let j; + for (j = 0; j < value.chi.length; j++) { + if (value.chi[j].typ === EnumToken.IdenTokenType && 'else' == value.chi[j].val) { + valid = false; + break; + } + if (invalidTokensTypes.has(value.chi[j].typ) && + (value.chi[j + 1]?.typ === EnumToken.CommaTokenType || j == value.chi.length - 1) && + (j == 0 || EnumToken.CommaTokenType == value.chi[j - 1]?.typ)) { + valid = false; + break; + } + } + if (!valid) { + result.splice(i--, 1); + break; + } + // const res = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (value as FunctionToken).val + '()'), createValidationContext((value as FunctionToken).chi), { + // }); + // if (!res.success) { + // result.splice(i--, 1); + // break; + // } + } + } + } + } + // console.debug({result}); + // throw new Error("Not implemented"); + if (result.length > 0) { + replaceNodeOrValue(declarationNode.parent, declarationNode, result); + } } // else remove node? return result; diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 55f12277..d84dc5c2 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -244,6 +244,8 @@ async function doParse(iter, options = {}) { if (options.resolveImport) { options.resolveUrls = true; } + // turn off expandIfSyntax for now + options.expandIfSyntax = false; const startTime = performance.now(); const errors = []; const src = options.src; diff --git a/jsr.json b/jsr.json index bf381b16..1a21db4f 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@tbela99/css-parser", - "version": "1.4.4", + "version": "1.4.5", "publish": { "include": [ "src", diff --git a/package.json b/package.json index e5e0f78a..fa4c17a3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@tbela99/css-parser", "description": "CSS parser, minifier and validator for node and the browser", - "version": "1.4.4", + "version": "1.4.5", "exports": { ".": "./dist/node.js", "./node": "./dist/node.js", diff --git a/src/lib/ast/features/if.ts b/src/lib/ast/features/if.ts index 168b4273..56fddaa0 100644 --- a/src/lib/ast/features/if.ts +++ b/src/lib/ast/features/if.ts @@ -17,10 +17,12 @@ import { definedPropertySettings } from "../../syntax/constants.ts"; import { equalsIgnoreCase } from "../../parser/utils/text.ts"; import { replaceNodeOrValue } from "../../parser/utils/token.ts"; import { cloneNode } from "../../ast/clone.ts"; -import { trimArray } from "../../validation/match.ts"; +import { createValidationContext, matchAllSyntaxes, trimArray } from "../../validation/match.ts"; import { findByValue } from "../find.ts"; -import { walk } from "../walk.ts"; +import { walk, walkValues } from "../walk.ts"; import { eq } from "../../parser/utils/eq.ts"; +import { getParsedSyntax } from "../../validation/config.ts"; +import { ValidationSyntaxGroupEnum } from "../../validation/parser/typedef.ts"; const nodeMatcher = (value: Token) => value.typ === EnumToken.IfConditionTokenType || @@ -295,7 +297,69 @@ function processNode(declarationNode: AstDeclaration, cache: Set): AstN } if (result.length > 0) { - replaceNodeOrValue(declarationNode.parent, declarationNode, result); + + let invalidTokensTypes = new Set([ + EnumToken.WhitespaceTokenType, + EnumToken.SemiColonTokenType, + EnumToken.ColonTokenType + ]) + + for (i = 0; i < result.length; i++) { + if (result[i].typ === EnumToken.DeclarationNodeType) { + + for (const {value} of walkValues((result[i] as AstDeclaration).val, result[i])) { + + if (value.typ === EnumToken.ImageFunctionTokenType && (value as FunctionToken).val.includes('-gradient')) { + + let valid: boolean = true; + let j: number + + for (j = 0; j < (value as FunctionToken).chi.length; j++) { + + if ((value as FunctionToken).chi[j].typ === EnumToken.IdenTokenType && 'else' == ((value as FunctionToken).chi[j] as IdentToken).val) { + + valid = false; + break; + } + + if (invalidTokensTypes.has((value as FunctionToken).chi[j].typ) && + ((value as FunctionToken).chi[j + 1]?.typ === EnumToken.CommaTokenType || j == (value as FunctionToken).chi.length - 1) && + (j == 0 || EnumToken.CommaTokenType == (value as FunctionToken).chi[j - 1]?.typ)) { + + valid = false; + break; + } + } + + if (!valid) { + + result.splice(i--, 1); + break; + } + + + + // const res = matchAllSyntaxes(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (value as FunctionToken).val + '()'), createValidationContext((value as FunctionToken).chi), { + + // }); + + // if (!res.success) { + + // result.splice(i--, 1); + // break; + // } + } + } + } + } + + // console.debug({result}); + // throw new Error("Not implemented"); + + if (result.length > 0) { + + replaceNodeOrValue(declarationNode.parent, declarationNode, result); + } } // else remove node? return result; diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index 14b5ed4a..df0cf61f 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -351,6 +351,9 @@ export async function doParse( options.resolveUrls = true; } + // turn off expandIfSyntax for now + options.expandIfSyntax = false; + const startTime: number = performance.now(); const errors: ErrorDescription[] = []; const src: string = options.src as string; diff --git a/test/specs/code/at-rules.js b/test/specs/code/at-rules.js index 92440784..57e4217c 100644 --- a/test/specs/code/at-rules.js +++ b/test/specs/code/at-rules.js @@ -1333,40 +1333,40 @@ supports((selector(h2 > p)) and (font-tech(color-COLRv1))) { - it('do not minify supports() arguments #58', function () { - return transform(` - -body { - background-color: if( - supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232); - else: #00adf3; - ); +// it('do not minify supports() arguments #58', function () { +// return transform(` + +// body { +// background-color: if( +// supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232); +// else: #00adf3; +// ); - &::after { - content: if( - supports(color: oklch(0.7 0.185 232)): "Your browser supports OKLCH"; - else: "Your browser does not support OKLCH"; - ); - } -} - - `, { - beautify: true, - expandIfSyntax: true, - validation: true - }).then((result) => expect(result.code).equals(`body { - background-color: #00adf3; - @supports (color:oklch(.7 .185 232)) { - background-color: #00aefc - } - &:after { - content: "Your browser does not support OKLCH"; - @supports (color:oklch(.7 .185 232)) { - content: "Your browser supports OKLCH" - } - } -}`)); - }); +// &::after { +// content: if( +// supports(color: oklch(0.7 0.185 232)): "Your browser supports OKLCH"; +// else: "Your browser does not support OKLCH"; +// ); +// } +// } + +// `, { +// beautify: true, +// expandIfSyntax: true, +// validation: true +// }).then((result) => expect(result.code).equals(`body { +// background-color: #00adf3; +// @supports (color:oklch(.7 .185 232)) { +// background-color: #00aefc +// } +// &:after { +// content: "Your browser does not support OKLCH"; +// @supports (color:oklch(.7 .185 232)) { +// content: "Your browser supports OKLCH" +// } +// } +// }`)); +// }); diff --git a/test/specs/code/expand.js b/test/specs/code/expand.js index f3a9a170..d5d76b98 100644 --- a/test/specs/code/expand.js +++ b/test/specs/code/expand.js @@ -406,162 +406,162 @@ html { }`)); }); - it('if(media()) #18', function () { - const nesting1 = ` -button { - aspect-ratio: 1; - width: if(media(any-pointer: fine): 30px; else: 44px); -} -`; - return transform(nesting1, { - beautify: true, expandIfSyntax: true - }).then((result) => expect(result.code).equals(`button { - aspect-ratio: 1; - width: 44px; - @media (any-pointer:fine) { - width: 30px - } -}`)); - }); +// it('if(media()) #18', function () { +// const nesting1 = ` +// button { +// aspect-ratio: 1; +// width: if(media(any-pointer: fine): 30px; else: 44px); +// } +// `; +// return transform(nesting1, { +// beautify: true, expandIfSyntax: true +// }).then((result) => expect(result.code).equals(`button { +// aspect-ratio: 1; +// width: 44px; +// @media (any-pointer:fine) { +// width: 30px +// } +// }`)); +// }); - it('if(media()) #19', function () { - const nesting1 = ` - -div-1 { - background-image: if( - else: none; - ); -} -div-2 { - background-image: if( - style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8) ; - else: none ; -); - -div-3 { - background-image: if( - style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8); - style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971); - else: none; - ); -} -div-4 { - background-image: if( +// it('if(media()) #19', function () { +// const nesting1 = ` + +// div-1 { +// background-image: if( +// else: none; +// ); +// } +// div-2 { +// background-image: if( +// style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8) ; +// else: none ; +// ); + +// div-3 { +// background-image: if( +// style(--scheme: ice): linear-gradient(#caf0f8, white, #caf0f8); +// style(--scheme: fire): linear-gradient(#ffc971, white, #ffc971); +// else: none; +// ); +// } +// div-4 { +// background-image: if( - ); -} -`; - return transform(nesting1, { - beautify: true, expandIfSyntax: true - }).then((result) => expect(result.code).equals(`div-1,div-2 { - background-image: none -} -div-2 { - @container style(--scheme:ice) { - background-image: linear-gradient(#caf0f8,#fff,#caf0f8) - } - div-3 { - background-image: none; - @container style(--scheme:ice) { - background-image: linear-gradient(#caf0f8,#fff,#caf0f8) - } - @container style(--scheme:fire) { - background-image: linear-gradient(#ffc971,#fff,#ffc971) - } - } - div-4 { - background-image: if() - } -}`)); - }); +// ); +// } +// `; +// return transform(nesting1, { +// beautify: true, expandIfSyntax: true +// }).then((result) => expect(result.code).equals(`div-1,div-2 { +// background-image: none +// } +// div-2 { +// @container style(--scheme:ice) { +// background-image: linear-gradient(#caf0f8,#fff,#caf0f8) +// } +// div-3 { +// background-image: none; +// @container style(--scheme:ice) { +// background-image: linear-gradient(#caf0f8,#fff,#caf0f8) +// } +// @container style(--scheme:fire) { +// background-image: linear-gradient(#ffc971,#fff,#ffc971) +// } +// } +// div-4 { +// background-image: if() +// } +// }`)); +// }); - it('if(media(), style()) #20', function () { - const nesting1 = ` - -button { - background: linear-gradient( - if(media(min-width: 768px): to right; else: to bottom), - if(style(--dark-mode): #333; else: #fff), - if(style(--dark-mode): #000; else: #ccc) - ); -} -`; - return transform(nesting1, { - beautify: true, expandIfSyntax: true - }).then((result) => expect(result.code).equals(`button { - background: linear-gradient(#fff,#ccc); - @media (min-width:768px) { - background: linear-gradient(90deg,#fff,#ccc); - @container style(--dark-mode) { - background: linear-gradient(90deg,#333,#000) - } - } - @container style(--dark-mode) { - background: linear-gradient(#333,#000) - } -}`)); - }); +// it('if(media(), style()) #20', function () { +// const nesting1 = ` + +// button { +// background: linear-gradient( +// if(media(min-width: 768px): to right; else: to bottom), +// if(style(--dark-mode): #333; else: #fff), +// if(style(--dark-mode): #000; else: #ccc) +// ); +// } +// `; +// return transform(nesting1, { +// beautify: true, expandIfSyntax: true +// }).then((result) => expect(result.code).equals(`button { +// background: linear-gradient(#fff,#ccc); +// @media (min-width:768px) { +// background: linear-gradient(90deg,#fff,#ccc); +// @container style(--dark-mode) { +// background: linear-gradient(90deg,#333,#000) +// } +// } +// @container style(--dark-mode) { +// background: linear-gradient(#333,#000) +// } +// }`)); +// }); - it('if(media(), style()) #21', function () { - const nesting1 = ` +// it('if(media(), style()) #21', function () { +// const nesting1 = ` -div-4 { - color: if( +// div-4 { +// color: if( - ); - background: - light-dark(if(style(--dark-mode): #333; else: #fff), if(media(min-width: 768px): white; else: black) - ); -} -`; - return transform(nesting1, { - beautify: true, expandIfSyntax: true - }).then((result) => expect(result.code).equals(`div-4 { - color: if(); - background: light-dark(#fff,#000); - @container style(--dark-mode) { - background: light-dark(#333,#000); - @media (min-width:768px) { - background: light-dark(#333,#fff) - } - } - @media (min-width:768px) { - background: light-dark(#fff,#fff) - } -}`)); - }); +// ); +// background: +// light-dark(if(style(--dark-mode): #333; else: #fff), if(media(min-width: 768px): white; else: black) +// ); +// } +// `; +// return transform(nesting1, { +// beautify: true, expandIfSyntax: true +// }).then((result) => expect(result.code).equals(`div-4 { +// color: if(); +// background: light-dark(#fff,#000); +// @container style(--dark-mode) { +// background: light-dark(#333,#000); +// @media (min-width:768px) { +// background: light-dark(#333,#fff) +// } +// } +// @media (min-width:768px) { +// background: light-dark(#fff,#fff) +// } +// }`)); +// }); - it('if(supports()) #22', function () { - const nesting1 = ` - -body { - background-color: if( - supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232); - else: #00adf3; - ); +// it('if(supports()) #22', function () { +// const nesting1 = ` + +// body { +// background-color: if( +// supports(color: oklch(0.7 0.185 232)): oklch(0.7 0.185 232); +// else: #00adf3; +// ); - &::after { - content: if( - supports(color: oklch(0.7 0.185 232)): "Your browser supports OKLCH"; - else: "Your browser does not support OKLCH"; - ); - } -} - -`; - return transform(nesting1, { - beautify: true, - removePrefix: true, - validation: true - }).then((result) => expect(result.code).equals(`body { - background-color: if(supports(color:oklch(.7 .185 232)):#00aefc;else:#00adf3); - &:after { - content: if(supports(color:oklch(.7 .185 232)):"Your browser supports OKLCH";else:"Your browser does not support OKLCH") - } -}`)); - }); +// &::after { +// content: if( +// supports(color: oklch(0.7 0.185 232)): "Your browser supports OKLCH"; +// else: "Your browser does not support OKLCH"; +// ); +// } +// } + +// `; +// return transform(nesting1, { +// beautify: true, +// removePrefix: true, +// validation: true +// }).then((result) => expect(result.code).equals(`body { +// background-color: if(supports(color:oklch(.7 .185 232)):#00aefc;else:#00adf3); +// &:after { +// content: if(supports(color:oklch(.7 .185 232)):"Your browser supports OKLCH";else:"Your browser does not support OKLCH") +// } +// }`)); +// }); }); }