Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions packages/less/lib/less/parser/parser-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,16 @@ export default () => {
* group array (or null). `[...]`/`{...}` do NOT shield a reference — only
* `(...)` (a declaration-value group) does.
*/
parserInput.$parseUntil = (tok, detectBareVar) => {
parserInput.$parseUntil = (tok, detectBareVar, stripLineComments) => {
let quote = '';
let returnVal = null;
let inComment = false;
let blockDepth = 0;
let parenDepth = 0;
let bareVarIndex = null;
// Open `(` groups, true for those that hold a URL: `url(`, `url-prefix(`, `domain(`.
const urlParens = [];
let urlDepth = 0;
const blockStack = [];
const parseGroups = [];
const length = input.length;
Expand Down Expand Up @@ -278,6 +281,27 @@ export default () => {
i++;
inComment = true;
blockDepth++;
} else if (stripLineComments &&
input.charAt(i + 1) === '/' &&
urlDepth === 0 &&
input.charAt(i - 1) !== ':') {
// A `//` comment, which is not CSS and must not
// reach the output. Two `//` are not comments: one
// inside a URL function, where the value parser
// turns comment absorption off too, so
// `url(//host/x.png)` and `url( //host/x.png)` keep
// their address; and one after a `:`, which is a
// scheme, as in a bare `http://host`.
const before = input.slice(lastPos, i);
if (before) {
parseGroups.push(before);
}
let nextNewLine = input.indexOf('\n', i + 2);
if (nextNewLine < 0) {
nextNewLine = length;
}
lastPos = nextNewLine;
i = nextNewLine - 1;
}
break;
case '\'':
Expand All @@ -298,11 +322,16 @@ export default () => {
blockStack.push('}');
blockDepth++;
break;
case '(':
case '(': {
blockStack.push(')');
blockDepth++;
parenDepth++;
const isUrl = /(?:^|[^\w-])(?:url|url-prefix|domain)$/i.test(
input.slice(Math.max(0, i - 11), i));
urlParens.push(isUrl);
if (isUrl) { urlDepth++; }
break;
}
case '[':
blockStack.push(']');
blockDepth++;
Expand All @@ -314,6 +343,7 @@ export default () => {
if (nextChar === expected) {
blockDepth--;
if (nextChar === ')' && parenDepth > 0) { parenDepth--; }
if (nextChar === ')' && urlParens.pop()) { urlDepth--; }
} else {
// move the parser to the error and return expected
skipWhitespace(i - startPos);
Expand Down
6 changes: 3 additions & 3 deletions packages/less/lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1874,7 +1874,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
* prelude (non-value position); accept `@{var}` interpolation and warn
* on a bare `@var` reference (which resolves today but is deprecated).
*/
permissiveValue: function (untilTokens, deprecateVariables) {
permissiveValue: function (untilTokens, deprecateVariables, stripLineComments) {
const entities = this.entities;
let i;
let e;
Expand Down Expand Up @@ -1942,7 +1942,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
}
parserInput.save();

value = parserInput.$parseUntil(tok, deprecateVariables);
value = parserInput.$parseUntil(tok, deprecateVariables, stripLineComments);

if (value) {
if (typeof value === 'string') {
Expand Down Expand Up @@ -2350,7 +2350,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
return e;
},
atruleUnknown: function (value, name, hasBlock) {
value = this.permissiveValue(/^[{;]/, true);
value = this.permissiveValue(/^[{;]/, true, true);
hasBlock = (parserInput.currentChar() === '{');
if (!value) {
if (!hasBlock && parserInput.currentChar() !== ';') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@supports selector(
:focus-visible ) {
a {
color: red;
}
}
@supports (display: grid) {
.grid {
display: grid;
}
}
@supports (display: flex) {
.flex {
display: flex;
}
}
@supports (background: url(http://example.com/a.png)) {
.absolute-url {
color: blue;
}
}
@document url-prefix(http://example.com/) {
.doc {
color: green;
}
}
@supports (background: url(//cdn.example.com/x.png)) {
.protocol-relative {
background: url(//cdn.example.com/x.png);
}
}
@supports (background: url( //cdn.example.com/y.png)) {
.protocol-relative-spaced {
color: navy;
}
}
@supports (a: b) /* keep me */ {
.block-comment {
color: teal;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// A `//` comment inside an unknown at-rule prelude is scanned as raw text by
// $parseUntil, so it used to survive into the CSS and swallow the rest of the
// line with it.

@supports selector(
:focus-visible // a line comment
) {
a {
color: red;
}
}

@supports (display: grid) // trailing comment
{
.grid {
display: grid;
}
}

@supports (display: flex)// no space before it
{
.flex {
display: flex;
}
}

// A `//` inside a URL function, or after a scheme's `:`, is part of an
// address. These must be left alone.
@supports (background: url(http://example.com/a.png)) {
.absolute-url {
color: blue;
}
}

@document url-prefix(http://example.com/) {
.doc {
color: green;
}
}

@supports (background: url(//cdn.example.com/x.png)) {
.protocol-relative {
background: url(//cdn.example.com/x.png);
}
}

@supports (background: url( //cdn.example.com/y.png)) {
.protocol-relative-spaced {
color: navy;
}
}

// Block comments are valid CSS and stay.
@supports (a: b) /* keep me */ {
.block-comment {
color: teal;
}
}
Loading