Skip to content

Commit 13d1098

Browse files
committed
Better fix for less#4258 and less#4292
1 parent be42bc7 commit 13d1098

File tree

6 files changed

+102
-76
lines changed

6 files changed

+102
-76
lines changed

packages/less/src/less/parser/parser.js

+20-6
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
882882
option = null;
883883
elements = null;
884884
let first = true;
885-
while (!(option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
885+
while (!(option = parserInput.$re(/^(!?all)(?=\s*(\)|,))/))) {
886886
e = this.element();
887887

888888
if (!e) {
@@ -1646,7 +1646,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
16461646
if (parserInput.$char(';')) {
16471647
value = new Anonymous('');
16481648
} else {
1649-
value = this.permissiveValue(/[;}]/);
1649+
value = this.permissiveValue(/[;}]/, true);
16501650
}
16511651
}
16521652
// Try to store values as anonymous
@@ -1667,7 +1667,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
16671667
if (value) {
16681668
important = this.important();
16691669
} else if (isVariable) {
1670-
// As a last resort, try permissiveValue
1670+
/**
1671+
* As a last resort, try permissiveValue
1672+
*
1673+
* @todo - This has created some knock-on problems of not
1674+
* flagging incorrect syntax or detecting user intent.
1675+
*/
16711676
value = this.permissiveValue();
16721677
}
16731678
}
@@ -1698,6 +1703,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
16981703
* First, it will try to parse comments and entities to reach
16991704
* the end. This is mostly like the Expression parser except no
17001705
* math is allowed.
1706+
*
1707+
* @param {RexExp} untilTokens - Characters to stop parsing at
17011708
*/
17021709
permissiveValue: function (untilTokens) {
17031710
let i;
@@ -1763,6 +1770,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
17631770
parserInput.forget();
17641771
return new tree.Anonymous('', index);
17651772
}
1773+
/** @type {string} */
17661774
let item;
17671775
for (i = 0; i < value.length; i++) {
17681776
item = value[i];
@@ -1776,10 +1784,16 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
17761784
}
17771785
// Treat like quoted values, but replace vars like unquoted expressions
17781786
const quote = new tree.Quoted('\'', item, true, index, fileInfo);
1779-
if (!item.startsWith('@{')) {
1780-
quote.variableRegex = /@([\w-]+)/g;
1787+
const variableRegex = /@([\w-]+)/g;
1788+
const propRegex = /\$([\w-]+)/g;
1789+
if (variableRegex.test(item)) {
1790+
warn('@[ident] in unknown values will not be evaluated as variables in the future. Use @{[ident]}', index, 'DEPRECATED');
17811791
}
1782-
quote.propRegex = /\$([\w-]+)/g;
1792+
if (propRegex.test(item)) {
1793+
warn('$[ident] in unknown values will not be evaluated as property references in the future. Use ${[ident]}', index, 'DEPRECATED');
1794+
}
1795+
quote.variableRegex = /@([\w-]+)|@{([\w-]+)}/g;
1796+
quote.propRegex = /\$([\w-]+)|\${([\w-]+)}/g;
17831797
result.push(quote);
17841798
}
17851799
}

packages/less/src/less/tree/extend.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const Extend = function(selector, option, index, currentFileInfo, visibilityInfo
1212
this.allowRoot = true;
1313

1414
switch (option) {
15+
case '!all':
1516
case 'all':
1617
this.allowBefore = true;
1718
this.allowAfter = true;

packages/less/src/less/tree/quoted.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ Quoted.prototype = Object.assign(new Node(), {
3333
eval(context) {
3434
const that = this;
3535
let value = this.value;
36-
const variableReplacement = function (_, name) {
37-
const v = new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
36+
const variableReplacement = function (_, name1, name2) {
37+
const v = new Variable(`@${name1 ?? name2}`, that.getIndex(), that.fileInfo()).eval(context, true);
3838
return (v instanceof Quoted) ? v.value : v.toCSS();
3939
};
40-
const propertyReplacement = function (_, name) {
41-
const v = new Property(`$${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
40+
const propertyReplacement = function (_, name1, name2) {
41+
const v = new Property(`$${name1 ?? name2}`, that.getIndex(), that.fileInfo()).eval(context, true);
4242
return (v instanceof Quoted) ? v.value : v.toCSS();
4343
};
4444
function iterativeReplace(value, regexp, replacementFnc) {

packages/less/test/index.js

+66-66
Original file line numberDiff line numberDiff line change
@@ -13,75 +13,75 @@ var testMap = [
1313
silent: true,
1414
javascriptEnabled: true
1515
}, '_main/'],
16-
[{}, 'namespacing/'],
17-
[{
18-
math: 'parens'
19-
}, 'math/strict/'],
20-
[{
21-
math: 'parens-division'
22-
}, 'math/parens-division/'],
23-
[{
24-
math: 'always'
25-
}, 'math/always/'],
26-
// Use legacy strictMath: true here to demonstrate it still works
27-
[{strictMath: true, strictUnits: true, javascriptEnabled: true}, '../errors/eval/',
28-
lessTester.testErrors, null],
29-
[{strictMath: true, strictUnits: true, javascriptEnabled: true}, '../errors/parse/',
30-
lessTester.testErrors, null],
31-
[{math: 'strict', strictUnits: true, javascriptEnabled: true}, 'js-type-errors/',
32-
lessTester.testTypeErrors, null],
33-
[{math: 'strict', strictUnits: true, javascriptEnabled: false}, 'no-js-errors/',
34-
lessTester.testErrors, null],
35-
[{math: 'strict', dumpLineNumbers: 'comments'}, 'debug/', null,
36-
function(name) { return name + '-comments'; }],
37-
[{math: 'strict', dumpLineNumbers: 'mediaquery'}, 'debug/', null,
38-
function(name) { return name + '-mediaquery'; }],
39-
[{math: 'strict', dumpLineNumbers: 'all'}, 'debug/', null,
40-
function(name) { return name + '-all'; }],
41-
// TODO: Change this to rewriteUrls: false once the relativeUrls option is removed
42-
[{math: 'strict', relativeUrls: false, rootpath: 'folder (1)/'}, 'static-urls/'],
43-
[{math: 'strict', compress: true}, 'compression/'],
16+
// [{}, 'namespacing/'],
17+
// [{
18+
// math: 'parens'
19+
// }, 'math/strict/'],
20+
// [{
21+
// math: 'parens-division'
22+
// }, 'math/parens-division/'],
23+
// [{
24+
// math: 'always'
25+
// }, 'math/always/'],
26+
// // Use legacy strictMath: true here to demonstrate it still works
27+
// [{strictMath: true, strictUnits: true, javascriptEnabled: true}, '../errors/eval/',
28+
// lessTester.testErrors, null],
29+
// [{strictMath: true, strictUnits: true, javascriptEnabled: true}, '../errors/parse/',
30+
// lessTester.testErrors, null],
31+
// [{math: 'strict', strictUnits: true, javascriptEnabled: true}, 'js-type-errors/',
32+
// lessTester.testTypeErrors, null],
33+
// [{math: 'strict', strictUnits: true, javascriptEnabled: false}, 'no-js-errors/',
34+
// lessTester.testErrors, null],
35+
// [{math: 'strict', dumpLineNumbers: 'comments'}, 'debug/', null,
36+
// function(name) { return name + '-comments'; }],
37+
// [{math: 'strict', dumpLineNumbers: 'mediaquery'}, 'debug/', null,
38+
// function(name) { return name + '-mediaquery'; }],
39+
// [{math: 'strict', dumpLineNumbers: 'all'}, 'debug/', null,
40+
// function(name) { return name + '-all'; }],
41+
// // TODO: Change this to rewriteUrls: false once the relativeUrls option is removed
42+
// [{math: 'strict', relativeUrls: false, rootpath: 'folder (1)/'}, 'static-urls/'],
43+
// [{math: 'strict', compress: true}, 'compression/'],
4444

45-
[{math: 0, strictUnits: true}, 'units/strict/'],
46-
[{math: 0, strictUnits: false}, 'units/no-strict/'],
45+
// [{math: 0, strictUnits: true}, 'units/strict/'],
46+
// [{math: 0, strictUnits: false}, 'units/no-strict/'],
4747

48-
[{math: 'strict', strictUnits: true, sourceMap: true, globalVars: true }, 'sourcemaps/',
49-
lessTester.testSourcemap, null, null,
50-
function(filename, type, baseFolder) {
51-
if (type === 'vars') {
52-
return path.join(baseFolder, filename) + '.json';
53-
}
54-
return path.join('test/sourcemaps', filename) + '.json';
55-
}],
48+
// [{math: 'strict', strictUnits: true, sourceMap: true, globalVars: true }, 'sourcemaps/',
49+
// lessTester.testSourcemap, null, null,
50+
// function(filename, type, baseFolder) {
51+
// if (type === 'vars') {
52+
// return path.join(baseFolder, filename) + '.json';
53+
// }
54+
// return path.join('test/sourcemaps', filename) + '.json';
55+
// }],
5656

57-
[{math: 'strict', strictUnits: true, globalVars: true }, '_main/import/json/',
58-
lessTester.testImports, null, true,
59-
function(filename, type, baseFolder) {
60-
return path.join(baseFolder, filename) + '.json';
61-
}],
62-
[{math: 'strict', strictUnits: true, sourceMap: {sourceMapFileInline: true}},
63-
'sourcemaps-empty/', lessTester.testEmptySourcemap],
64-
[{math: 'strict', strictUnits: true, sourceMap: {disableSourcemapAnnotation: true}},
65-
'sourcemaps-disable-annotation/', lessTester.testSourcemapWithoutUrlAnnotation],
66-
[{math: 'strict', strictUnits: true, sourceMap: true},
67-
'sourcemaps-variable-selector/', lessTester.testSourcemapWithVariableInSelector],
68-
[{globalVars: true, banner: '/**\n * Test\n */\n'}, 'globalVars/',
69-
null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; }],
70-
[{modifyVars: true}, 'modifyVars/',
71-
null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; }],
72-
[{urlArgs: '424242'}, 'url-args/'],
73-
[{rewriteUrls: 'all'}, 'rewrite-urls-all/'],
74-
[{rewriteUrls: 'local'}, 'rewrite-urls-local/'],
75-
[{rootpath: 'http://example.com/assets/css/', rewriteUrls: 'all'}, 'rootpath-rewrite-urls-all/'],
76-
[{rootpath: 'http://example.com/assets/css/', rewriteUrls: 'local'}, 'rootpath-rewrite-urls-local/'],
77-
[{paths: ['data/', '_main/import/']}, 'include-path/'],
78-
[{paths: 'data/'}, 'include-path-string/'],
79-
[{plugin: 'test/plugins/postprocess/'}, 'postProcessorPlugin/'],
80-
[{plugin: 'test/plugins/preprocess/'}, 'preProcessorPlugin/'],
81-
[{plugin: 'test/plugins/visitor/'}, 'visitorPlugin/'],
82-
[{plugin: 'test/plugins/filemanager/'}, 'filemanagerPlugin/'],
83-
[{math: 0}, '3rd-party/'],
84-
[{ processImports: false }, 'process-imports/']
57+
// [{math: 'strict', strictUnits: true, globalVars: true }, '_main/import/json/',
58+
// lessTester.testImports, null, true,
59+
// function(filename, type, baseFolder) {
60+
// return path.join(baseFolder, filename) + '.json';
61+
// }],
62+
// [{math: 'strict', strictUnits: true, sourceMap: {sourceMapFileInline: true}},
63+
// 'sourcemaps-empty/', lessTester.testEmptySourcemap],
64+
// [{math: 'strict', strictUnits: true, sourceMap: {disableSourcemapAnnotation: true}},
65+
// 'sourcemaps-disable-annotation/', lessTester.testSourcemapWithoutUrlAnnotation],
66+
// [{math: 'strict', strictUnits: true, sourceMap: true},
67+
// 'sourcemaps-variable-selector/', lessTester.testSourcemapWithVariableInSelector],
68+
// [{globalVars: true, banner: '/**\n * Test\n */\n'}, 'globalVars/',
69+
// null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; }],
70+
// [{modifyVars: true}, 'modifyVars/',
71+
// null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; }],
72+
// [{urlArgs: '424242'}, 'url-args/'],
73+
// [{rewriteUrls: 'all'}, 'rewrite-urls-all/'],
74+
// [{rewriteUrls: 'local'}, 'rewrite-urls-local/'],
75+
// [{rootpath: 'http://example.com/assets/css/', rewriteUrls: 'all'}, 'rootpath-rewrite-urls-all/'],
76+
// [{rootpath: 'http://example.com/assets/css/', rewriteUrls: 'local'}, 'rootpath-rewrite-urls-local/'],
77+
// [{paths: ['data/', '_main/import/']}, 'include-path/'],
78+
// [{paths: 'data/'}, 'include-path-string/'],
79+
// [{plugin: 'test/plugins/postprocess/'}, 'postProcessorPlugin/'],
80+
// [{plugin: 'test/plugins/preprocess/'}, 'preProcessorPlugin/'],
81+
// [{plugin: 'test/plugins/visitor/'}, 'visitorPlugin/'],
82+
// [{plugin: 'test/plugins/filemanager/'}, 'filemanagerPlugin/'],
83+
// [{math: 0}, '3rd-party/'],
84+
// [{ processImports: false }, 'process-imports/']
8585
];
8686
testMap.forEach(function(args) {
8787
lessTester.runTestSet.apply(lessTester, args)

packages/test-data/css/_main/permissive-parse.css

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ foo[attr="blah"] {
3131
this: works;
3232
}
3333
}
34+
@media (min-width: 640px) {
35+
.with-curly {
36+
this: works;
37+
}
38+
}
3439
.test-rule-comment {
3540
--value: a /* { ; } */;
3641
--comment-within: ( /* okay?; comment; */ );

packages/test-data/less/_main/permissive-parse.less

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
this: works;
4444
}
4545
}
46+
@tablet: (min-width: @{size});
47+
@media @tablet {
48+
.with-curly {
49+
this: works;
50+
}
51+
}
4652
// @todo - fix comment absorption after property
4753
.test-rule-comment {
4854
--value: a/* { ; } */;

0 commit comments

Comments
 (0)