From e8dc908f803f22e4ccda609d1f2669c1154579e8 Mon Sep 17 00:00:00 2001 From: Thierry Bela Date: Sun, 13 Aug 2023 03:38:22 -0400 Subject: [PATCH] incorrect shorthand parsing #6 --- dist/index-umd-web.js | 71 ++++++++++++++++----------- dist/index.cjs | 71 ++++++++++++++++----------- dist/lib/parser/declaration/map.js | 67 +++++++++++++++----------- dist/lib/renderer/render.js | 4 ++ src/lib/parser/declaration/map.ts | 77 ++++++++++++++++++------------ src/lib/renderer/render.ts | 6 +++ 6 files changed, 181 insertions(+), 115 deletions(-) diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 15e17ea..062dd78 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -1639,6 +1639,10 @@ str = options.removeComments ? '' : node.val; } else if (node.typ == 'Declaration') { + if (node.val.length == 0) { + console.error(`invalid declaration`, node); + return ''; + } str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`; } else if (node.typ == 'AtRule' && !('chi' in node)) { @@ -2031,6 +2035,9 @@ this.pattern = config.pattern.split(/\s/); } add(declaration) { + for (const val of declaration.val) { + Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam }); + } if (declaration.nam == this.config.shorthand) { this.declarations = new Map; this.declarations.set(declaration.nam, declaration); @@ -2064,7 +2071,7 @@ i--; continue; } - if (matchType(acc[i], props)) { + if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) { if ('prefix' in props && props.previous != null && !(props.previous in tokens)) { return acc; } @@ -2198,10 +2205,12 @@ } else { let count = 0; + let match; const separator = this.config.separator; const tokens = {}; // @ts-ignore - /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => { + /* const valid: string[] =*/ + Object.entries(this.config.properties).reduce((acc, curr) => { if (!this.declarations.has(curr[0])) { if (curr[1].required) { acc.push(curr[0]); @@ -2210,33 +2219,39 @@ } let current = 0; const props = this.config.properties[curr[0]]; - const declaration = this.declarations.get(curr[0]); - // @ts-ignore - for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) { - if (separator != null && separator.typ == val.typ && eq(separator, val)) { - current++; - if (tokens[curr[0]].length == current) { - tokens[curr[0]].push([]); + const properties = this.declarations.get(curr[0]); + for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) { + // @ts-ignore + for (const val of declaration.val) { + if (separator != null && separator.typ == val.typ && eq(separator, val)) { + current++; + if (tokens[curr[0]].length == current) { + tokens[curr[0]].push([]); + } + continue; } - continue; - } - if (val.typ == 'Whitespace' || val.typ == 'Comment') { - continue; - } - if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { - continue; - } - if (matchType(val, curr[1])) { - if (!(curr[0] in tokens)) { - tokens[curr[0]] = [[]]; + if (val.typ == 'Whitespace' || val.typ == 'Comment') { + continue; + } + if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { + continue; + } + match = matchType(val, curr[1]); + if (isShorthand) { + isShorthand = match; + } + if (('propertyName' in val && val.propertyName == property) || match) { + if (!(curr[0] in tokens)) { + tokens[curr[0]] = [[]]; + } + // is default value + tokens[curr[0]][current].push(val); + // continue; + } + else { + acc.push(curr[0]); + break; } - // is default value - tokens[curr[0]][current].push(val); - // continue; - } - else { - acc.push(curr[0]); - break; } } if (count == 0) { @@ -2245,7 +2260,7 @@ return acc; }, []); count++; - if (Object.entries(this.config.properties).some(entry => { + if (!isShorthand || Object.entries(this.config.properties).some(entry => { // missing required property return entry[1].required && !(entry[0] in tokens); }) || !Object.values(tokens).every(v => v.length == count)) { diff --git a/dist/index.cjs b/dist/index.cjs index 888abf0..e0f5e27 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1637,6 +1637,10 @@ function doRender(data, options, reducer, level = 0, indents = []) { str = options.removeComments ? '' : node.val; } else if (node.typ == 'Declaration') { + if (node.val.length == 0) { + console.error(`invalid declaration`, node); + return ''; + } str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`; } else if (node.typ == 'AtRule' && !('chi' in node)) { @@ -2029,6 +2033,9 @@ class PropertyMap { this.pattern = config.pattern.split(/\s/); } add(declaration) { + for (const val of declaration.val) { + Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam }); + } if (declaration.nam == this.config.shorthand) { this.declarations = new Map; this.declarations.set(declaration.nam, declaration); @@ -2062,7 +2069,7 @@ class PropertyMap { i--; continue; } - if (matchType(acc[i], props)) { + if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) { if ('prefix' in props && props.previous != null && !(props.previous in tokens)) { return acc; } @@ -2196,10 +2203,12 @@ class PropertyMap { } else { let count = 0; + let match; const separator = this.config.separator; const tokens = {}; // @ts-ignore - /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => { + /* const valid: string[] =*/ + Object.entries(this.config.properties).reduce((acc, curr) => { if (!this.declarations.has(curr[0])) { if (curr[1].required) { acc.push(curr[0]); @@ -2208,33 +2217,39 @@ class PropertyMap { } let current = 0; const props = this.config.properties[curr[0]]; - const declaration = this.declarations.get(curr[0]); - // @ts-ignore - for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) { - if (separator != null && separator.typ == val.typ && eq(separator, val)) { - current++; - if (tokens[curr[0]].length == current) { - tokens[curr[0]].push([]); + const properties = this.declarations.get(curr[0]); + for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) { + // @ts-ignore + for (const val of declaration.val) { + if (separator != null && separator.typ == val.typ && eq(separator, val)) { + current++; + if (tokens[curr[0]].length == current) { + tokens[curr[0]].push([]); + } + continue; } - continue; - } - if (val.typ == 'Whitespace' || val.typ == 'Comment') { - continue; - } - if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { - continue; - } - if (matchType(val, curr[1])) { - if (!(curr[0] in tokens)) { - tokens[curr[0]] = [[]]; + if (val.typ == 'Whitespace' || val.typ == 'Comment') { + continue; + } + if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { + continue; + } + match = matchType(val, curr[1]); + if (isShorthand) { + isShorthand = match; + } + if (('propertyName' in val && val.propertyName == property) || match) { + if (!(curr[0] in tokens)) { + tokens[curr[0]] = [[]]; + } + // is default value + tokens[curr[0]][current].push(val); + // continue; + } + else { + acc.push(curr[0]); + break; } - // is default value - tokens[curr[0]][current].push(val); - // continue; - } - else { - acc.push(curr[0]); - break; } } if (count == 0) { @@ -2243,7 +2258,7 @@ class PropertyMap { return acc; }, []); count++; - if (Object.entries(this.config.properties).some(entry => { + if (!isShorthand || Object.entries(this.config.properties).some(entry => { // missing required property return entry[1].required && !(entry[0] in tokens); }) || !Object.values(tokens).every(v => v.length == count)) { diff --git a/dist/lib/parser/declaration/map.js b/dist/lib/parser/declaration/map.js index 527955c..07bef83 100644 --- a/dist/lib/parser/declaration/map.js +++ b/dist/lib/parser/declaration/map.js @@ -19,6 +19,9 @@ class PropertyMap { this.pattern = config.pattern.split(/\s/); } add(declaration) { + for (const val of declaration.val) { + Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam }); + } if (declaration.nam == this.config.shorthand) { this.declarations = new Map; this.declarations.set(declaration.nam, declaration); @@ -52,7 +55,7 @@ class PropertyMap { i--; continue; } - if (matchType(acc[i], props)) { + if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) { if ('prefix' in props && props.previous != null && !(props.previous in tokens)) { return acc; } @@ -186,10 +189,12 @@ class PropertyMap { } else { let count = 0; + let match; const separator = this.config.separator; const tokens = {}; // @ts-ignore - /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => { + /* const valid: string[] =*/ + Object.entries(this.config.properties).reduce((acc, curr) => { if (!this.declarations.has(curr[0])) { if (curr[1].required) { acc.push(curr[0]); @@ -198,33 +203,39 @@ class PropertyMap { } let current = 0; const props = this.config.properties[curr[0]]; - const declaration = this.declarations.get(curr[0]); - // @ts-ignore - for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) { - if (separator != null && separator.typ == val.typ && eq(separator, val)) { - current++; - if (tokens[curr[0]].length == current) { - tokens[curr[0]].push([]); + const properties = this.declarations.get(curr[0]); + for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) { + // @ts-ignore + for (const val of declaration.val) { + if (separator != null && separator.typ == val.typ && eq(separator, val)) { + current++; + if (tokens[curr[0]].length == current) { + tokens[curr[0]].push([]); + } + continue; } - continue; - } - if (val.typ == 'Whitespace' || val.typ == 'Comment') { - continue; - } - if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { - continue; - } - if (matchType(val, curr[1])) { - if (!(curr[0] in tokens)) { - tokens[curr[0]] = [[]]; + if (val.typ == 'Whitespace' || val.typ == 'Comment') { + continue; + } + if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { + continue; + } + match = matchType(val, curr[1]); + if (isShorthand) { + isShorthand = match; + } + if (('propertyName' in val && val.propertyName == property) || match) { + if (!(curr[0] in tokens)) { + tokens[curr[0]] = [[]]; + } + // is default value + tokens[curr[0]][current].push(val); + // continue; + } + else { + acc.push(curr[0]); + break; } - // is default value - tokens[curr[0]][current].push(val); - // continue; - } - else { - acc.push(curr[0]); - break; } } if (count == 0) { @@ -233,7 +244,7 @@ class PropertyMap { return acc; }, []); count++; - if (Object.entries(this.config.properties).some(entry => { + if (!isShorthand || Object.entries(this.config.properties).some(entry => { // missing required property return entry[1].required && !(entry[0] in tokens); }) || !Object.values(tokens).every(v => v.length == count)) { diff --git a/dist/lib/renderer/render.js b/dist/lib/renderer/render.js index 06544fe..5bb510f 100644 --- a/dist/lib/renderer/render.js +++ b/dist/lib/renderer/render.js @@ -62,6 +62,10 @@ function doRender(data, options, reducer, level = 0, indents = []) { str = options.removeComments ? '' : node.val; } else if (node.typ == 'Declaration') { + if (node.val.length == 0) { + console.error(`invalid declaration`, node); + return ''; + } str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`; } else if (node.typ == 'AtRule' && !('chi' in node)) { diff --git a/src/lib/parser/declaration/map.ts b/src/lib/parser/declaration/map.ts index 7759170..50404a2 100644 --- a/src/lib/parser/declaration/map.ts +++ b/src/lib/parser/declaration/map.ts @@ -1,6 +1,5 @@ import { - AstDeclaration, IdentToken, - PropertyMapType, ShorthandPropertyType, + AstDeclaration, PropertyMapType, ShorthandPropertyType, Token } from "../../../@types"; import {ShorthandMapType} from "../../../@types"; @@ -30,6 +29,11 @@ export class PropertyMap { add(declaration: AstDeclaration) { + for (const val of declaration.val) { + + Object.defineProperty(val, 'propertyName', {enumerable: false, writable: true, value: declaration.nam}); + } + if (declaration.nam == this.config.shorthand) { this.declarations = new Map; @@ -78,7 +82,7 @@ export class PropertyMap { continue; } - if (matchType(acc[i], props)) { + if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) { if ('prefix' in props && props.previous != null && !(props.previous in tokens)) { @@ -267,11 +271,13 @@ export class PropertyMap { } else { let count = 0; + let match: boolean; const separator = this.config.separator; const tokens = <{ [key: string]: Token[][] }>{}; // @ts-ignore - /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => { + /* const valid: string[] =*/ + Object.entries(this.config.properties).reduce((acc, curr) => { if (!this.declarations.has(curr[0])) { @@ -286,48 +292,56 @@ export class PropertyMap { let current = 0; const props = this.config.properties[curr[0]]; - const declaration = this.declarations.get(curr[0]); + const properties = this.declarations.get(curr[0]); - // @ts-ignore - for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) { + for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) { - if (separator != null && separator.typ == val.typ && eq(separator, val)) { + // @ts-ignore + for (const val of declaration.val) { - current++; + if (separator != null && separator.typ == val.typ && eq(separator, val)) { - if (tokens[curr[0]].length == current) { + current++; - tokens[curr[0]].push([]); - } + if (tokens[curr[0]].length == current) { - continue; - } + tokens[curr[0]].push([]); + } - if (val.typ == 'Whitespace' || val.typ == 'Comment') { + continue; + } - continue; - } + if (val.typ == 'Whitespace' || val.typ == 'Comment') { - if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { + continue; + } - continue; - } + if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { - if (matchType(val, curr[1])) { + continue; + } - if (!(curr[0] in tokens)) { + match = matchType(val, curr[1]); - tokens[curr[0]] = [[]]; + if (isShorthand) { + + isShorthand = match; } - // is default value + if (('propertyName' in val && val.propertyName == property) || match) { - tokens[curr[0]][current].push(val); - // continue; - } else { + if (!(curr[0] in tokens)) { - acc.push(curr[0]); - break; + tokens[curr[0]] = [[]]; + } + + // is default value + tokens[curr[0]][current].push(val); + } else { + + acc.push(curr[0]); + break; + } } } @@ -341,7 +355,7 @@ export class PropertyMap { count++; - if (Object.entries(this.config.properties).some(entry => { + if (!isShorthand || Object.entries(this.config.properties).some(entry => { // missing required property return entry[1].required && !(entry[0] in tokens); @@ -350,7 +364,8 @@ export class PropertyMap { // @ts-ignore iterable = this.declarations.values(); - } else { + } + else { const values: Token[] = Object.entries(tokens).reduce((acc, curr) => { diff --git a/src/lib/renderer/render.ts b/src/lib/renderer/render.ts index acc1f23..a76bedf 100644 --- a/src/lib/renderer/render.ts +++ b/src/lib/renderer/render.ts @@ -109,6 +109,12 @@ function doRender(data: AstNode, options: RenderOptions, reducer: Function, leve str = options.removeComments ? '' : (node).val; } else if (node.typ == 'Declaration') { + if ((node).val.length == 0) { + + console.error(`invalid declaration`, node); + return ''; + } + str = `${(node).nam}:${options.indent}${(node).val.reduce(<() => string>reducer, '').trimEnd()};`; } else if (node.typ == 'AtRule' && !('chi' in node)) {