Skip to content

Commit

Permalink
[scss] support interpolation in layer name. For #180631 (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli authored Nov 7, 2023
1 parent 0f2dfc4 commit 0443e38
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 127 deletions.
5 changes: 5 additions & 0 deletions src/parser/cssParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,11 @@ export class Parser {
return this.finish(node, ParseError.URIOrStringExpected);
}

return this._completeParseImport(node);
}


public _completeParseImport(node: nodes.Import): nodes.Node | null {
if (this.acceptIdent('layer')) {
if (this.accept(TokenType.ParenthesisL)) {
if (!node.addChild(this._parseLayerName())) {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/lessParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class LESSParser extends cssParser.Parser {
node.setMedialist(this._parseMediaQueryList());
}

return this.finish(node);
return this._completeParseImport(node);
}

public _parsePlugin(): nodes.Node | null {
Expand Down
6 changes: 1 addition & 5 deletions src/parser/scssParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ export class SCSSParser extends cssParser.Parser {
}
}

if (!this.peek(TokenType.SemiColon) && !this.peek(TokenType.EOF)) {
node.setMedialist(this._parseMediaQueryList());
}

return this.finish(node);
return this._completeParseImport(node);
}

// scss variables: $font-size: 12px;
Expand Down
99 changes: 52 additions & 47 deletions src/test/less/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { assertNode, assertNoNode, assertError } from '../css/parser.test';
suite('LESS - Parser', () => {

test('Variable', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('@color', parser, parser._parseVariable.bind(parser));
assertNode('$color', parser, parser._parseVariable.bind(parser));
assertNode('$$color', parser, parser._parseVariable.bind(parser));
Expand All @@ -36,7 +36,7 @@ suite('LESS - Parser', () => {
});

test('Media', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('@media @phone {}', parser, parser._parseMedia.bind(parser));
assertNode('@media(max-width: 767px) { .mixinRef() }', parser, parser._parseMedia.bind(parser));
assertNode('@media(max-width: 767px) { .mixinDec() {} }', parser, parser._parseMedia.bind(parser));
Expand All @@ -48,7 +48,7 @@ suite('LESS - Parser', () => {
});

test('VariableDeclaration', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('@color: #F5F5F5', parser, parser._parseVariableDeclaration.bind(parser));
assertNode('@color: 0', parser, parser._parseVariableDeclaration.bind(parser));
assertNode('@color: 255', parser, parser._parseVariableDeclaration.bind(parser));
Expand All @@ -67,7 +67,7 @@ suite('LESS - Parser', () => {
});

test('MixinDeclaration', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.color (@color: 25.5px) { }', parser, parser._tryParseMixinDeclaration.bind(parser));
assertNode('.color(@color: 25.5px) { }', parser, parser._tryParseMixinDeclaration.bind(parser));
assertNode('.color(@color) { }', parser, parser._tryParseMixinDeclaration.bind(parser));
Expand All @@ -90,7 +90,7 @@ suite('LESS - Parser', () => {
});

test('MixinReference', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.box-shadow(0 0 5px, 30%)', parser, parser._tryParseMixinReference.bind(parser));
assertNode('.box-shadow', parser, parser._tryParseMixinReference.bind(parser));
assertNode('.mixin(10) !important', parser, parser._tryParseMixinReference.bind(parser));
Expand All @@ -105,7 +105,7 @@ suite('LESS - Parser', () => {
});

test('DetachedRuleSet', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.foo { @greeting(); }', parser, parser._parseStylesheet.bind(parser));
assertNode('.media-switch(@styles) { @media(orientation:landscape){ @styles(); @foo: 9; } }', parser, parser._parseStylesheet.bind(parser));
assertNode('.media-switch({ flex-direction: row; });', parser, parser._parseStylesheet.bind(parser));
Expand All @@ -120,10 +120,10 @@ suite('LESS - Parser', () => {
});

test('MixinParameter', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('@_', parser, parser._parseMixinParameter.bind(parser));
assertNode('@let: value', parser, parser._parseMixinParameter.bind(parser));
assertNode('@let', parser, parser._parseMixinParameter.bind(parser));
assertNode('@const: value', parser, parser._parseMixinParameter.bind(parser));
assertNode('@const', parser, parser._parseMixinParameter.bind(parser));
assertNode('@rest...', parser, parser._parseMixinParameter.bind(parser));
assertNode('...', parser, parser._parseMixinParameter.bind(parser));
assertNode('value', parser, parser._parseMixinParameter.bind(parser));
Expand All @@ -132,7 +132,7 @@ suite('LESS - Parser', () => {
});

test('Function', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('%()', parser, parser._parseFunction.bind(parser));
assertNoNode('% ()', parser, parser._parseFunction.bind(parser));

Expand All @@ -145,25 +145,25 @@ suite('LESS - Parser', () => {
});

test('Expr', function () {
let parser = new LESSParser();
assertNode('(@let + 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@let - 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@let * 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@let / 20)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 - @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 * @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 / @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 / 20 + @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + 20 + @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + 20 + 20 + @let)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + @let + 20 + 20 + @let)', parser, parser._parseExpr.bind(parser));
const parser = new LESSParser();
assertNode('(@const + 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@const - 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@const * 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@const / 20)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 - @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 * @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 / @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 / 20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + 20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + 20 + 20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20 + @const + 20 + 20 + @const)', parser, parser._parseExpr.bind(parser));
assertNode('(20 + 20)', parser, parser._parseExpr.bind(parser));
assertNode('(@var1 + @var2)', parser, parser._parseExpr.bind(parser));
assertNode('((@let + 5) * 2)', parser, parser._parseExpr.bind(parser));
assertNode('((@let + (5 + 2)) * 2)', parser, parser._parseExpr.bind(parser));
assertNode('(@let + ((5 + 2) * 2))', parser, parser._parseExpr.bind(parser));
assertNode('((@const + 5) * 2)', parser, parser._parseExpr.bind(parser));
assertNode('((@const + (5 + 2)) * 2)', parser, parser._parseExpr.bind(parser));
assertNode('(@const + ((5 + 2) * 2))', parser, parser._parseExpr.bind(parser));
assertNode('@color', parser, parser._parseExpr.bind(parser));
assertNode('@color, @color', parser, parser._parseExpr.bind(parser));
assertNode('@color, 42%', parser, parser._parseExpr.bind(parser));
Expand All @@ -175,15 +175,15 @@ suite('LESS - Parser', () => {
});

test('LessOperator', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('>=', parser, parser._parseOperator.bind(parser));
assertNode('>', parser, parser._parseOperator.bind(parser));
assertNode('<', parser, parser._parseOperator.bind(parser));
assertNode('=<', parser, parser._parseOperator.bind(parser));
});

test('Extend', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('nav { &:extend(.inline); }', parser, parser._parseRuleset.bind(parser));
assertNode('nav { &:extend(.test all); }', parser, parser._parseRuleset.bind(parser));
assertNode('.big-bucket:extend(.bucket all) { }', parser, parser._parseRuleset.bind(parser));
Expand All @@ -193,12 +193,12 @@ suite('LESS - Parser', () => {
});

test('Declaration', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('border: thin solid 1px', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: @color', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: blue', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: (20 / @let)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: (20 / 20 + @let)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: (20 / @const)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: (20 / 20 + @const)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: func(@red)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: desaturate(@red, 10%)', parser, parser._parseDeclaration.bind(parser));
assertNode('dummy: desaturate(16, 10%)', parser, parser._parseDeclaration.bind(parser));
Expand All @@ -218,7 +218,7 @@ suite('LESS - Parser', () => {
});

test('Stylesheet', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.color (@radius: 5px){ -border-radius: #F5F5F5 }', parser, parser._parseStylesheet.bind(parser));
assertNode('.color (@radius: 5px){ -border-radius: @radius }', parser, parser._parseStylesheet.bind(parser));
assertNode('.color (@radius: 5px){ -border-radius: #F5F5F5 } .color (@radius: 5px) { -border-radius: #F5F5F5 }', parser, parser._parseStylesheet.bind(parser));
Expand All @@ -236,6 +236,11 @@ suite('LESS - Parser', () => {
assertNode('@color: #F5F5F5; @color: #F5F5F5;', parser, parser._parseStylesheet.bind(parser));
assertNode('@color: #F5F5F5; @color: #F5F5F5; @color: #F5F5F5;', parser, parser._parseStylesheet.bind(parser));
assertNode('@color: #F5F5F5; .color (@radius: 5px) { -border-radius: #F5F5F5 } @color: #F5F5F5;', parser, parser._parseStylesheet.bind(parser));
});

test('@iport', function () {
const parser = new LESSParser();
assertNode('@import url("override.css") layer;', parser, parser._parseStylesheet.bind(parser));
assertNode('@import-once "lib";', parser, parser._parseStylesheet.bind(parser));
assertNode('@import-once (css) "hello";', parser, parser._parseStylesheet.bind(parser));
assertError('@import-once () "hello";', parser, parser._parseStylesheet.bind(parser), ParseError.IdentifierExpected);
Expand All @@ -247,8 +252,8 @@ suite('LESS - Parser', () => {
});

test('Ruleset', function () {
let parser = new LESSParser();
assertNode('.selector { prop: erty @let 1px; }', parser, parser._parseRuleset.bind(parser));
const parser = new LESSParser();
assertNode('.selector { prop: erty @const 1px; }', parser, parser._parseRuleset.bind(parser));
assertNode('selector { .mixin; }', parser, parser._parseRuleset.bind(parser));
assertNode('selector { .mixin(1px); .mixin(blue, 1px, \'farboo\') }', parser, parser._parseRuleset.bind(parser));
assertNode('selector { .mixin(blue; 1px;\'farboo\') }', parser, parser._parseRuleset.bind(parser));
Expand All @@ -264,24 +269,24 @@ suite('LESS - Parser', () => {
});

test('term', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('%(\'repetitions: %S file: %S\', 1 + 2, "directory/file.less")', parser, parser._parseTerm.bind(parser));
assertNode('~"ms:alwaysHasItsOwnSyntax.For.Stuff()"', parser, parser._parseTerm.bind(parser)); // less syntax
assertNode('~`colorPalette("@{blue}", 1)`', parser, parser._parseTerm.bind(parser)); // less syntax
assertNode('~`colorPaconstte("@{blue}", 1)`', parser, parser._parseTerm.bind(parser)); // less syntax
});

test('Nested Ruleset', function () {
let parser = new LESSParser();
assertNode('.class1 { @let: 1; .class { @let: 2; three: @let; let: 3; } one: @let; }', parser, parser._parseRuleset.bind(parser));
assertNode('.class1 { @let: 1; > .class2 { display: none; } }', parser, parser._parseRuleset.bind(parser));
const parser = new LESSParser();
assertNode('.class1 { @const: 1; .class { @const: 2; three: @const; const: 3; } one: @const; }', parser, parser._parseRuleset.bind(parser));
assertNode('.class1 { @const: 1; > .class2 { display: none; } }', parser, parser._parseRuleset.bind(parser));
assertNode('.foo { @supports(display: grid) { .bar { display: none; }}}', parser, parser._parseRuleset.bind(parser));
assertNode('.foo { @supports(display: grid) { display: none; }}', parser, parser._parseRuleset.bind(parser));
assertNode('.parent { color:green; @document url-prefix() { .child { color:red; }}}', parser, parser._parseStylesheet.bind(parser));
assertNode('@supports (property: value) { .outOfMedia & { @media (max-size: 2px) { @supports (whatever: something) { property: value;}}}}', parser, parser._parseStylesheet.bind(parser));
});

test('Interpolation', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.@{name} { }', parser, parser._parseRuleset.bind(parser));
assertNode('.${name} { }', parser, parser._parseRuleset.bind(parser));
assertNode('.my-element:not(.prefix-@{sub-element}) { }', parser, parser._parseStylesheet.bind(parser));
Expand All @@ -294,7 +299,7 @@ suite('LESS - Parser', () => {
});

test('Selector Combinator', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('&:hover', parser, parser._parseSimpleSelector.bind(parser));
assertNode('&.float', parser, parser._parseSimpleSelector.bind(parser));
assertNode('&-foo', parser, parser._parseSimpleSelector.bind(parser));
Expand All @@ -307,20 +312,20 @@ suite('LESS - Parser', () => {
});

test('CSS Guards', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('button when (@my-option = true) { color: white; }', parser, parser._parseStylesheet.bind(parser));
assertNode('.something .other when (@my-option = true) { color: white; }', parser, parser._parseStylesheet.bind(parser));
assertNode('& when (@my-option = true) { button { color: white; } }', parser, parser._parseStylesheet.bind(parser));
});

test('Merge', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('.mixin() { transform+_: scale(2); }', parser, parser._parseStylesheet.bind(parser));
assertNode('.myclass { box-shadow+: inset 0 0 10px #555; }', parser, parser._parseStylesheet.bind(parser));
});

test('url', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('url(//yourdomain/yourpath.png)', parser, parser._parseURILiteral.bind(parser));
assertNode('url(\'http://msft.com\')', parser, parser._parseURILiteral.bind(parser));
assertNode('url("http://msft.com")', parser, parser._parseURILiteral.bind(parser));
Expand All @@ -339,7 +344,7 @@ suite('LESS - Parser', () => {
});

test('@plugin', function () {
let parser = new LESSParser();
const parser = new LESSParser();
assertNode('@plugin "my-plugin";', parser, parser._parseStylesheet.bind(parser));
});
});
});
Loading

0 comments on commit 0443e38

Please sign in to comment.