Skip to content

Commit 7bc1330

Browse files
authored
Add support for maps with numeric keys (#81)
1 parent 97325a6 commit 7bc1330

6 files changed

Lines changed: 92 additions & 11 deletions

File tree

src/app/converter/converter.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,24 @@ describe('Converter class', () => {
213213
expect(map[ix].compiledValue).to.be.equal(compiled);
214214
});
215215
});
216+
217+
it('should allow maps with numeric keys', () => {
218+
let result = structured['levels'][0];
219+
expect(result).to.have.property('mapValue');
220+
let map = result.mapValue;
221+
222+
let expected = [
223+
{ name: '100', value: '80%', compiledValue: '80%' },
224+
{ name: '500', value: '0', compiledValue: '0' },
225+
{ name: '900', value: '80%', compiledValue: '80%' },
226+
];
227+
228+
expected.forEach(({name, value, compiledValue}, ix) => {
229+
expect(map[ix].name).to.be.equal(name);
230+
expect(map[ix].value).to.be.equal(value);
231+
expect(map[ix].compiledValue).to.be.equal(compiledValue);
232+
});
233+
});
216234
});
217235

218236
describe('mixins support', () => {

src/app/parser/parser.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ describe('Parser class', () => {
4747

4848
expect(parser.parse()).that.is.an('array');
4949
expect(parser.parse()).to.be.empty;
50+
51+
rawContent = '$0: test;';
52+
parser = new Parser(rawContent);
53+
54+
expect(parser.parse()).that.is.an('array');
55+
expect(parser.parse()).to.be.empty;
5056
});
5157

5258
describe('parseStructured Validations', () => {
@@ -228,6 +234,26 @@ describe('Parser class', () => {
228234
expect(parsedArray[0].mapValue[1].value).be.equal('$bp-medium');
229235
});
230236

237+
it('should parse map with numeric keys', () => {
238+
let content = `$levels: (
239+
100: 80%,
240+
500: 0,
241+
900: 80%
242+
);`;
243+
244+
let parser = new Parser(content);
245+
let parsedArray = parser.parse();
246+
247+
expect(parsedArray[0].mapValue[0].name).be.equal('100');
248+
expect(parsedArray[0].mapValue[0].value).be.equal('80%');
249+
250+
expect(parsedArray[0].mapValue[1].name).be.equal('500');
251+
expect(parsedArray[0].mapValue[1].value).be.equal('0');
252+
253+
expect(parsedArray[0].mapValue[2].name).be.equal('900');
254+
expect(parsedArray[0].mapValue[2].value).be.equal('80%');
255+
});
256+
231257
it('should ignore comments inline', () => {
232258
let content = `
233259
$font-size: (
@@ -398,5 +424,31 @@ describe('Parser class', () => {
398424

399425
expect(parsedArray[0].mapValue[2].mapValue[1].value).be.equal('$bp-xl');
400426
});
427+
428+
it('should parse map with numeric keys', () => {
429+
let content = `$content: (
430+
0: 0,
431+
1: 1,
432+
2: (
433+
0: 0,
434+
1: 1,
435+
2: 2
436+
)
437+
);`;
438+
439+
let parser = new Parser(content);
440+
let parsedArray = parser.parse();
441+
442+
expect(parsedArray[0].mapValue[2].name).be.equal('2');
443+
444+
expect(parsedArray[0].mapValue[2].mapValue[0].name).be.equal('0');
445+
expect(parsedArray[0].mapValue[2].mapValue[0].value).be.equal('0');
446+
447+
expect(parsedArray[0].mapValue[2].mapValue[1].name).be.equal('1');
448+
expect(parsedArray[0].mapValue[2].mapValue[1].value).be.equal('1');
449+
450+
expect(parsedArray[0].mapValue[2].mapValue[2].name).be.equal('2');
451+
expect(parsedArray[0].mapValue[2].mapValue[2].value).be.equal('2');
452+
});
401453
});
402454
});

src/app/parser/parser.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
const VARIABLE_PATERN = '(?!\\d)[\\w_-][\\w\\d_-]*';
2+
const MAP_KEY_PATTERN = '[\\w-]+';
23
const VALUE_PATERN = '[^;]+|"(?:[^"]+|(?:\\\\"|[^"])*)"';
3-
const DECLARATION_PATTERN =
4-
`\\$['"]?(${VARIABLE_PATERN})['"]?\\s*:\\s*(${VALUE_PATERN})(?:\\s*!(global|default)\\s*;|\\s*;(?![^\\{]*\\}))`;
54

6-
const MAP_DECLARATIOM_REGEX = /['"]?((?!\d)[\w_-][\w\d_-]*)['"]?\s*:\s*([a-z\-]+\([^\)]+\)|[^\)\(,\/]+|\([^\)]+\))/gi;
5+
const MAP_DECLARATIOM_REGEX = /['"]?([\w_-][\w\d_-]*)['"]?\s*:\s*([a-z\-]+\([^\)]+\)|[^\)\(,\/]+|\([^\)]+\))/gi;
76

87
const QUOTES_PATTERN = /^(['"]).*\1$/;
98
const QUOTES_REPLACE = /^(['"])|(['"])$/g;
@@ -77,7 +76,7 @@ export class Parser {
7776

7877

7978
private extractDeclarationsStructured(content: string): [any] {
80-
const matches = content.match(new RegExp(`${DECLARATION_PATTERN}|${SECTION_PATTERN}|${END_SECTION_PATTERN}`, 'g'));
79+
const matches = content.match(new RegExp(`${this.getDeclarationPattern()}|${SECTION_PATTERN}|${END_SECTION_PATTERN}`, 'g'));
8180

8281
if (!matches) {
8382
return [] as any;
@@ -88,7 +87,7 @@ export class Parser {
8887

8988

9089
private extractDeclarations(content: string): [any] {
91-
const matches = content.match(new RegExp(DECLARATION_PATTERN, 'g'));
90+
const matches = content.match(new RegExp(this.getDeclarationPattern(), 'g'));
9291

9392
if (!matches) {
9493
return [] as any;
@@ -108,10 +107,10 @@ export class Parser {
108107
}
109108

110109

111-
private parseSingleDeclaration(matchDeclaration: string): IDeclaration {
110+
private parseSingleDeclaration(matchDeclaration: string, isMap: boolean = false): IDeclaration {
112111
let matches = matchDeclaration
113112
.replace(/\s*!(default|global)\s*;/, ';')
114-
.match(new RegExp(DECLARATION_PATTERN));
113+
.match(new RegExp(this.getDeclarationPattern(isMap)));
115114

116115
if (!matches) {
117116
return;
@@ -132,7 +131,8 @@ export class Parser {
132131
if (map.length) {
133132
parsedDeclaration.mapValue = map.map((declaration) => {
134133
const singleDeclaration = this.parseSingleDeclaration(
135-
`$${declaration};`
134+
`$${declaration};`,
135+
true
136136
);
137137
this.parseMapDeclarations(singleDeclaration);
138138

@@ -148,4 +148,8 @@ export class Parser {
148148
private checkIsSectionEnd(content: string): boolean {
149149
return (new RegExp(END_SECTION_PATTERN, 'gi')).test(content);
150150
}
151+
152+
private getDeclarationPattern(isMap: boolean = false): string {
153+
return `\\$['"]?(${isMap ? MAP_KEY_PATTERN : VARIABLE_PATERN})['"]?\\s*:\\s*(${VALUE_PATERN})(?:\\s*!(global|default)\\s*;|\\s*;(?![^\\{]*\\}))`;
154+
}
151155
}

src/app/utils/utils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describe('Utils class', () => {
3333

3434
it('should wrap a variable', () => {
3535
let declaration = { name: 'var', value: '$the-value', compiledValue: '' };
36-
let expectedResult = '#sass-export-id.var{content:"#{$the-value}";}';
36+
let expectedResult = '#sass-export-id._var{content:"#{$the-value}";}';
3737
let wrapped = Utils.wrapCss(declaration, false);
3838

3939
expect(wrapped).to.be.equal(expectedResult);

src/app/utils/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ export class Utils {
1717

1818
public static wrapCss(cssDeclaration: IDeclaration, useInspect: boolean): string {
1919
if (useInspect) {
20-
return `${WRAPPER_CSS_ID}.${cssDeclaration.name}{content:"#{inspect(${cssDeclaration.value})}";}`;
20+
return `${WRAPPER_CSS_ID}._${cssDeclaration.name}{content:"#{inspect(${cssDeclaration.value})}";}`;
2121
}
22-
return `${WRAPPER_CSS_ID}.${cssDeclaration.name}{content:"#{${cssDeclaration.value}}";}`;
22+
return `${WRAPPER_CSS_ID}._${cssDeclaration.name}{content:"#{${cssDeclaration.value}}";}`;
2323
}
2424

2525
public static removeDoubleQuotes(wrappedContent: string): string {

test/scss/_maps.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ $funcs: (
3535
'rgba': rgba(255, 0, 0, .5),
3636
'darken': darken(#b37399, 20%)
3737
);
38+
39+
// @sass-export-section="levels"
40+
$levels: (
41+
100: 80%,
42+
500: 0,
43+
900: 80%
44+
);

0 commit comments

Comments
 (0)