From 34c940bea71fbe711d7f9dd0f26e46f31d2b46df Mon Sep 17 00:00:00 2001 From: Jakob Warrer Thygesen Date: Mon, 17 May 2021 14:03:09 +0200 Subject: [PATCH] Added suport for nested maps --- src/app/parser/parser.test.ts | 107 ++++++++++++++++++++++++++++++++++ src/app/parser/parser.ts | 34 +++++------ 2 files changed, 124 insertions(+), 17 deletions(-) diff --git a/src/app/parser/parser.test.ts b/src/app/parser/parser.test.ts index 4c31dfe..9fc3003 100644 --- a/src/app/parser/parser.test.ts +++ b/src/app/parser/parser.test.ts @@ -282,4 +282,111 @@ describe('Parser class', () => { expect(map[3].value).be.equal('darken(#b37399, 20%)'); }); }); + + describe('nested maps support', () => { + it('should parse a map into an array', () => { + let content = `$breakpoints: ( + small: 767px, + medium: 992px, + large: ( + lg: 1200px, + xl: 1400px + ) + );`; + + let parser = new Parser(content); + let structured = parser.parseStructured(); + + expect(structured.variables[0].mapValue[2].mapValue).that.is.an('array'); + }); + + it('should have a structured result', () => { + let content = `$breakpoints: ( + small: 767px, + medium: $bp-medium, + large: ( + lg: 1200px, + xl: $bp-xl + ) + );`; + + let parser = new Parser(content); + let structured = parser.parseStructured(); + expect(structured.variables[0].mapValue[2].name).be.equal('large'); + + expect(structured.variables[0].mapValue[2].mapValue[0].name).be.equal( + 'lg' + ); + expect(structured.variables[0].mapValue[2].mapValue[0].value).be.equal( + '1200px' + ); + + expect(structured.variables[0].mapValue[2].mapValue[1].value).be.equal( + '$bp-xl' + ); + }); + + it('should have a structured result for array type', () => { + let content = `$breakpoints: ( + small: 767px, + medium: $bp-medium, + large: ( + lg: 1200px, + xl: $bp-xl + ) + );`; + + let parser = new Parser(content); + let parsedArray = parser.parse(); + + expect(parsedArray[0].mapValue[2].name).be.equal('large'); + + expect(parsedArray[0].mapValue[2].mapValue[0].name).be.equal('lg'); + expect(parsedArray[0].mapValue[2].mapValue[0].value).be.equal('1200px'); + + expect(parsedArray[0].mapValue[2].mapValue[1].value).be.equal('$bp-xl'); + }); + + it('should parse map with single quote keys', () => { + let content = `$breakpoints: ( + 'small': 767px, + 'medium': $bp-medium, + 'large': ( + 'lg': 1200px, + 'xl': $bp-xl + ) + );`; + + let parser = new Parser(content); + let parsedArray = parser.parse(); + + expect(parsedArray[0].mapValue[2].name).be.equal('large'); + + expect(parsedArray[0].mapValue[2].mapValue[0].name).be.equal('lg'); + expect(parsedArray[0].mapValue[2].mapValue[0].value).be.equal('1200px'); + + expect(parsedArray[0].mapValue[2].mapValue[1].value).be.equal('$bp-xl'); + }); + + it('should parse map with double quote keys', () => { + let content = `$breakpoints: ( + "small": 767px, + "medium": $bp-medium, + "large": ( + "lg": 1200px, + "xl": $bp-xl + ) + );`; + + let parser = new Parser(content); + let parsedArray = parser.parse(); + + expect(parsedArray[0].mapValue[2].name).be.equal('large'); + + expect(parsedArray[0].mapValue[2].mapValue[0].name).be.equal('lg'); + expect(parsedArray[0].mapValue[2].mapValue[0].value).be.equal('1200px'); + + expect(parsedArray[0].mapValue[2].mapValue[1].value).be.equal('$bp-xl'); + }); + }); }); diff --git a/src/app/parser/parser.ts b/src/app/parser/parser.ts index a2e35c1..eb93d1f 100644 --- a/src/app/parser/parser.ts +++ b/src/app/parser/parser.ts @@ -3,7 +3,7 @@ const VALUE_PATERN = '[^;]+|"(?:[^"]+|(?:\\\\"|[^"])*)"'; const DECLARATION_PATTERN = `\\$['"]?(${VARIABLE_PATERN})['"]?\\s*:\\s*(${VALUE_PATERN})(?:\\s*!(global|default)\\s*;|\\s*;(?![^\\{]*\\}))`; -const MAP_DECLARATIOM_REGEX = /['"]?((?!\d)[\w_-][\w\d_-]*)['"]?\s*:\s*([a-z\-]+\([^\)]+\)|[^\),\/]+)/gi; +const MAP_DECLARATIOM_REGEX = /['"]?((?!\d)[\w_-][\w\d_-]*)['"]?\s*:\s*([a-z\-]+\([^\)]+\)|[^\)\(,\/]+|\([^\)]+\))/gi; const QUOTES_PATTERN = /^(['"]).*\1$/; const QUOTES_REPLACE = /^(['"])|(['"])$/g; @@ -31,13 +31,7 @@ export class Parser { let parsed = this.parseSingleDeclaration(match); if (parsed) { - let map = this.extractMapDeclarations(parsed.value); - - // in case the variable is a sass map - if (map.length) { - parsed.mapValue = map.map((declaration) => this.parseSingleDeclaration(`$${declaration};`)); - } - + this.parseMapDeclarations(parsed); declarations.push(parsed); } } @@ -72,13 +66,7 @@ export class Parser { let parsed = this.parseSingleDeclaration(match); if (parsed) { - let map = this.extractMapDeclarations(parsed.value); - - // in case the variable is a sass map - if (map.length) { - parsed.mapValue = map.map((declaration) => this.parseSingleDeclaration(`$${declaration};`)); - } - + this.parseMapDeclarations(parsed); declarations[currentSection].push(parsed); } } @@ -138,8 +126,20 @@ export class Parser { return { name, value } as IDeclaration; } - - + private parseMapDeclarations(parsedDeclaration: IDeclaration) { + let map = this.extractMapDeclarations(parsedDeclaration.value); + + if (map.length) { + parsedDeclaration.mapValue = map.map((declaration) => { + const singleDeclaration = this.parseSingleDeclaration( + `$${declaration};` + ); + this.parseMapDeclarations(singleDeclaration); + + return singleDeclaration; + }); + } + } private checkIsSectionStart(content: string): boolean { return (new RegExp(SECTION_PATTERN, 'gi')).test(content); }