mirror of
https://github.com/less/less.js.git
synced 2026-01-08 23:28:04 -05:00
fix faulty source map generation with variables in selectors (#3761)
* fix faulty source map generation * alternative solution * add test
This commit is contained in:
@@ -38,7 +38,8 @@ import functionRegistry from '../functions/function-registry';
|
||||
// It also takes care of moving all the indices forwards.
|
||||
//
|
||||
|
||||
const Parser = function Parser(context, imports, fileInfo) {
|
||||
const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
||||
currentIndex = currentIndex || 0;
|
||||
let parsers;
|
||||
const parserInput = getParserInput();
|
||||
|
||||
@@ -60,7 +61,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
error(msg || (typeof arg === 'string'
|
||||
? `expected '${arg}' got '${parserInput.currentChar()}'`
|
||||
: 'unexpected token'));
|
||||
@@ -85,13 +86,13 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
/**
|
||||
* Used after initial parsing to create nodes on the fly
|
||||
*
|
||||
* @param {String} str - string to parse
|
||||
*
|
||||
* @param {String} str - string to parse
|
||||
* @param {Array} parseList - array of parsers to run input through e.g. ["value", "important"]
|
||||
* @param {Number} currentIndex - start number to begin indexing
|
||||
* @param {Object} fileInfo - fileInfo to attach to created nodes
|
||||
*/
|
||||
function parseNode(str, parseList, currentIndex, fileInfo, callback) {
|
||||
function parseNode(str, parseList, callback) {
|
||||
let result;
|
||||
const returnNodes = [];
|
||||
const parser = parserInput;
|
||||
@@ -103,19 +104,9 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
index: index + currentIndex
|
||||
});
|
||||
});
|
||||
for (let x = 0, p, i; (p = parseList[x]); x++) {
|
||||
i = parser.i;
|
||||
for (let x = 0, p; (p = parseList[x]); x++) {
|
||||
result = parsers[p]();
|
||||
if (result) {
|
||||
try {
|
||||
result._index = i + currentIndex;
|
||||
result._fileInfo = fileInfo;
|
||||
} catch (e) {}
|
||||
returnNodes.push(result);
|
||||
}
|
||||
else {
|
||||
returnNodes.push(null);
|
||||
}
|
||||
returnNodes.push(result || null);
|
||||
}
|
||||
|
||||
const endInfo = parser.end();
|
||||
@@ -207,7 +198,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
root.root = true;
|
||||
root.firstRoot = true;
|
||||
root.functionRegistry = functionRegistry.inherit();
|
||||
|
||||
|
||||
} catch (e) {
|
||||
return callback(new LessError(e, imports, fileInfo.filename));
|
||||
}
|
||||
@@ -337,7 +328,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node = mixin.definition() || this.declaration() || mixin.call(false, false) ||
|
||||
node = mixin.definition() || this.declaration() || mixin.call(false, false) ||
|
||||
this.ruleset() || this.variableCall() || this.entities.call() || this.atrule();
|
||||
if (node) {
|
||||
root.push(node);
|
||||
@@ -360,7 +351,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
comment: function () {
|
||||
if (parserInput.commentStore.length) {
|
||||
const comment = parserInput.commentStore.shift();
|
||||
return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
|
||||
return new(tree.Comment)(comment.text, comment.isLineComment, comment.index + currentIndex, fileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -396,7 +387,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
parserInput.forget();
|
||||
|
||||
return new(tree.Quoted)(str.charAt(0), str.substr(1, str.length - 2), isEscaped, index, fileInfo);
|
||||
return new(tree.Quoted)(str.charAt(0), str.substr(1, str.length - 2), isEscaped, index + currentIndex, fileInfo);
|
||||
},
|
||||
|
||||
//
|
||||
@@ -433,7 +424,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
name = parserInput.$re(/^([\w-]+|%|~|progid:[\w\.]+)\(/);
|
||||
if (!name) {
|
||||
parserInput.forget();
|
||||
parserInput.forget();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -456,9 +447,9 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
parserInput.forget();
|
||||
|
||||
return new(tree.Call)(name, args, index, fileInfo);
|
||||
return new(tree.Call)(name, args, index + currentIndex, fileInfo);
|
||||
},
|
||||
|
||||
|
||||
//
|
||||
// Parsing rules for functions with non-standard args, e.g.:
|
||||
//
|
||||
@@ -481,11 +472,11 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
function f(parse, stop) {
|
||||
return {
|
||||
parse, // parsing function
|
||||
stop // when true - stop after parse() and return its result,
|
||||
stop // when true - stop after parse() and return its result,
|
||||
// otherwise continue for plain args
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function condition() {
|
||||
return [expect(parsers.condition, 'expected condition')];
|
||||
}
|
||||
@@ -591,10 +582,10 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
expectChar(')');
|
||||
|
||||
return new(tree.URL)((value.value !== undefined ||
|
||||
value instanceof tree.Variable ||
|
||||
return new(tree.URL)((value.value !== undefined ||
|
||||
value instanceof tree.Variable ||
|
||||
value instanceof tree.Property) ?
|
||||
value : new(tree.Anonymous)(value, index), index, fileInfo);
|
||||
value : new(tree.Anonymous)(value, index), index + currentIndex, fileInfo);
|
||||
},
|
||||
|
||||
//
|
||||
@@ -622,7 +613,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
}
|
||||
parserInput.forget();
|
||||
return new(tree.Variable)(name, index, fileInfo);
|
||||
return new(tree.Variable)(name, index + currentIndex, fileInfo);
|
||||
}
|
||||
parserInput.restore();
|
||||
},
|
||||
@@ -633,7 +624,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
const index = parserInput.i;
|
||||
|
||||
if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
|
||||
return new(tree.Variable)(`@${curly[1]}`, index, fileInfo);
|
||||
return new(tree.Variable)(`@${curly[1]}`, index + currentIndex, fileInfo);
|
||||
}
|
||||
},
|
||||
//
|
||||
@@ -646,7 +637,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
const index = parserInput.i;
|
||||
|
||||
if (parserInput.currentChar() === '$' && (name = parserInput.$re(/^\$[\w-]+/))) {
|
||||
return new(tree.Property)(name, index, fileInfo);
|
||||
return new(tree.Property)(name, index + currentIndex, fileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -656,7 +647,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
const index = parserInput.i;
|
||||
|
||||
if (parserInput.currentChar() === '$' && (curly = parserInput.$re(/^\$\{([\w-]+)\}/))) {
|
||||
return new(tree.Property)(`$${curly[1]}`, index, fileInfo);
|
||||
return new(tree.Property)(`$${curly[1]}`, index + currentIndex, fileInfo);
|
||||
}
|
||||
},
|
||||
//
|
||||
@@ -674,7 +665,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (!rgb[2]) {
|
||||
parserInput.forget();
|
||||
return new(tree.Color)(rgb[1], undefined, rgb[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
parserInput.restore();
|
||||
},
|
||||
@@ -749,7 +740,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
js = parserInput.$re(/^[^`]*`/);
|
||||
if (js) {
|
||||
parserInput.forget();
|
||||
return new(tree.JavaScript)(js.substr(0, js.length - 1), Boolean(escape), index, fileInfo);
|
||||
return new(tree.JavaScript)(js.substr(0, js.length - 1), Boolean(escape), index + currentIndex, fileInfo);
|
||||
}
|
||||
parserInput.restore('invalid javascript definition');
|
||||
}
|
||||
@@ -844,7 +835,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (!elements) {
|
||||
error('Missing target selector for :extend().');
|
||||
}
|
||||
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index, fileInfo);
|
||||
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index + currentIndex, fileInfo);
|
||||
if (extendList) {
|
||||
extendList.push(extend);
|
||||
} else {
|
||||
@@ -930,7 +921,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (inValue || parsers.end()) {
|
||||
parserInput.forget();
|
||||
const mixin = new(tree.mixin.Call)(elements, args, index, fileInfo, !lookups && important);
|
||||
const mixin = new(tree.mixin.Call)(elements, args, index + currentIndex, fileInfo, !lookups && important);
|
||||
if (lookups) {
|
||||
return new tree.NamespaceValue(mixin, lookups);
|
||||
}
|
||||
@@ -956,11 +947,11 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
while (true) {
|
||||
elemIndex = parserInput.i;
|
||||
e = parserInput.$re(re);
|
||||
|
||||
|
||||
if (!e) {
|
||||
break;
|
||||
}
|
||||
elem = new(tree.Element)(c, e, false, elemIndex, fileInfo);
|
||||
elem = new(tree.Element)(c, e, false, elemIndex + currentIndex, fileInfo);
|
||||
if (elements) {
|
||||
elements.push(elem);
|
||||
} else {
|
||||
@@ -1167,13 +1158,13 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.restore();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ruleLookups: function() {
|
||||
let rule;
|
||||
let args;
|
||||
const lookups = [];
|
||||
|
||||
if (parserInput.currentChar() !== '[') {
|
||||
if (parserInput.currentChar() !== '[') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1192,27 +1183,27 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
return lookups;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
lookupValue: function() {
|
||||
parserInput.save();
|
||||
|
||||
if (!parserInput.$char('[')) {
|
||||
|
||||
if (!parserInput.$char('[')) {
|
||||
parserInput.restore();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const name = parserInput.$re(/^(?:[@$]{0,2})[_a-zA-Z0-9-]*/);
|
||||
|
||||
|
||||
if (!parserInput.$char(']')) {
|
||||
parserInput.restore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (name || name === '') {
|
||||
parserInput.forget();
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
parserInput.restore();
|
||||
}
|
||||
},
|
||||
@@ -1296,7 +1287,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
if (e) { return new(tree.Element)(c, e, e instanceof tree.Variable, index, fileInfo); }
|
||||
if (e) { return new(tree.Element)(c, e, e instanceof tree.Variable, index + currentIndex, fileInfo); }
|
||||
},
|
||||
|
||||
//
|
||||
@@ -1380,7 +1371,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
if (elements) { return new(tree.Selector)(elements, allExtends, condition, index, fileInfo); }
|
||||
if (elements) { return new(tree.Selector)(elements, allExtends, condition, index + currentIndex, fileInfo); }
|
||||
if (allExtends) { error('Extend must be used to extend a selector, it cannot be used on its own'); }
|
||||
},
|
||||
selectors: function () {
|
||||
@@ -1466,7 +1457,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.save();
|
||||
if (parserInput.$re(/^[.#]\(/)) {
|
||||
/**
|
||||
* DR args currently only implemented for each() function, and not
|
||||
* DR args currently only implemented for each() function, and not
|
||||
* yet settable as `@dr: #(@arg) {}`
|
||||
* This should be done when DRs are merged with mixins.
|
||||
* See: https://github.com/less/less-meta/issues/16
|
||||
@@ -1561,7 +1552,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (value) {
|
||||
parserInput.forget();
|
||||
// anonymous values absorb the end ';' which is required for them to work
|
||||
return new(tree.Declaration)(name, value, false, merge, index, fileInfo);
|
||||
return new(tree.Declaration)(name, value, false, merge, index + currentIndex, fileInfo);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
@@ -1578,7 +1569,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (value && (this.end() || hasDR)) {
|
||||
parserInput.forget();
|
||||
return new(tree.Declaration)(name, value, important, merge, index, fileInfo);
|
||||
return new(tree.Declaration)(name, value, important, merge, index + currentIndex, fileInfo);
|
||||
}
|
||||
else {
|
||||
parserInput.restore();
|
||||
@@ -1591,14 +1582,14 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
const index = parserInput.i;
|
||||
const match = parserInput.$re(/^([^.#@\$+\/'"*`(;{}-]*);/);
|
||||
if (match) {
|
||||
return new(tree.Anonymous)(match[1], index);
|
||||
return new(tree.Anonymous)(match[1], index + currentIndex);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Used for custom properties, at-rules, and variables (as fallback)
|
||||
* Parses almost anything inside of {} [] () "" blocks
|
||||
* until it reaches outer-most tokens.
|
||||
*
|
||||
*
|
||||
* First, it will try to parse comments and entities to reach
|
||||
* the end. This is mostly like the Expression parser except no
|
||||
* math is allowed.
|
||||
@@ -1715,7 +1706,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
error('missing semi-colon or unrecognised media features on import');
|
||||
}
|
||||
features = features && new(tree.Value)(features);
|
||||
return new(tree.Import)(path, features, options, index, fileInfo);
|
||||
return new(tree.Import)(path, features, options, index + currentIndex, fileInfo);
|
||||
}
|
||||
else {
|
||||
parserInput.i = index;
|
||||
@@ -1777,7 +1768,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
e = this.value();
|
||||
if (parserInput.$char(')')) {
|
||||
if (p && e) {
|
||||
nodes.push(new(tree.Paren)(new(tree.Declaration)(p, e, null, null, parserInput.i, fileInfo, true)));
|
||||
nodes.push(new(tree.Paren)(new(tree.Declaration)(p, e, null, null, parserInput.i + currentIndex, fileInfo, true)));
|
||||
} else if (e) {
|
||||
nodes.push(new(tree.Paren)(e));
|
||||
} else {
|
||||
@@ -1840,7 +1831,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
parserInput.forget();
|
||||
|
||||
media = new(tree.Media)(rules, features, index, fileInfo);
|
||||
media = new(tree.Media)(rules, features, index + currentIndex, fileInfo);
|
||||
if (context.dumpLineNumbers) {
|
||||
media.debugInfo = debugInfo;
|
||||
}
|
||||
@@ -1883,7 +1874,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.i = index;
|
||||
error('missing semi-colon on @plugin');
|
||||
}
|
||||
return new(tree.Import)(path, null, options, index, fileInfo);
|
||||
return new(tree.Import)(path, null, options, index + currentIndex, fileInfo);
|
||||
}
|
||||
else {
|
||||
parserInput.i = index;
|
||||
@@ -1904,7 +1895,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.forget();
|
||||
return args[1].trim();
|
||||
}
|
||||
else {
|
||||
else {
|
||||
parserInput.restore();
|
||||
return null;
|
||||
}
|
||||
@@ -1999,7 +1990,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (rules || (!hasBlock && value && parserInput.$char(';'))) {
|
||||
parserInput.forget();
|
||||
return new(tree.AtRule)(name, value, rules, index, fileInfo,
|
||||
return new(tree.AtRule)(name, value, rules, index + currentIndex, fileInfo,
|
||||
context.dumpLineNumbers ? getDebugInfo(index) : null,
|
||||
isRooted
|
||||
);
|
||||
@@ -2030,7 +2021,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
} while (e);
|
||||
|
||||
if (expressions.length > 0) {
|
||||
return new(tree.Value)(expressions, index);
|
||||
return new(tree.Value)(expressions, index + currentIndex);
|
||||
}
|
||||
},
|
||||
important: function () {
|
||||
@@ -2132,7 +2123,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (!b) {
|
||||
break;
|
||||
}
|
||||
condition = new(tree.Condition)('or', condition || a, b, index);
|
||||
condition = new(tree.Condition)('or', condition || a, b, index + currentIndex);
|
||||
}
|
||||
return condition || a;
|
||||
}
|
||||
@@ -2282,12 +2273,12 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (op) {
|
||||
b = cond();
|
||||
if (b) {
|
||||
c = new(tree.Condition)(op, a, b, index, false);
|
||||
c = new(tree.Condition)(op, a, b, index + currentIndex, false);
|
||||
} else {
|
||||
error('expected expression');
|
||||
}
|
||||
} else {
|
||||
c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, false);
|
||||
c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index + currentIndex, false);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -2350,7 +2341,7 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
if (!parserInput.peek(/^\/[\/*]/)) {
|
||||
delim = parserInput.$char('/');
|
||||
if (delim) {
|
||||
entities.push(new(tree.Anonymous)(delim, index));
|
||||
entities.push(new(tree.Anonymous)(delim, index + currentIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2410,8 +2401,8 @@ const Parser = function Parser(context, imports, fileInfo) {
|
||||
name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
|
||||
new(tree.Keyword)(s) :
|
||||
(s.charAt(0) === '@' ?
|
||||
new(tree.Variable)(`@${s.slice(2, -1)}`, index[k], fileInfo) :
|
||||
new(tree.Property)(`$${s.slice(2, -1)}`, index[k], fileInfo));
|
||||
new(tree.Variable)(`@${s.slice(2, -1)}`, index[k] + currentIndex, fileInfo) :
|
||||
new(tree.Property)(`$${s.slice(2, -1)}`, index[k] + currentIndex, fileInfo));
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import globalFunctionRegistry from '../functions/function-registry';
|
||||
import defaultFunc from '../functions/default';
|
||||
import getDebugInfo from './debug-info';
|
||||
import * as utils from '../utils';
|
||||
import Parser from '../parser/parser';
|
||||
|
||||
const Ruleset = function(selectors, rules, strictImports, visibilityInfo) {
|
||||
this.selectors = selectors;
|
||||
@@ -79,11 +80,11 @@ Ruleset.prototype = Object.assign(new Node(), {
|
||||
selector = selectors[i];
|
||||
toParseSelectors[i] = selector.toCSS(context);
|
||||
}
|
||||
this.parse.parseNode(
|
||||
const startingIndex = selectors[0].getIndex();
|
||||
const selectorFileInfo = selectors[0].fileInfo();
|
||||
new Parser(context, this.parse.importManager, selectorFileInfo, startingIndex).parseNode(
|
||||
toParseSelectors.join(','),
|
||||
["selectors"],
|
||||
selectors[0].getIndex(),
|
||||
selectors[0].fileInfo(),
|
||||
["selectors"],
|
||||
function(err, result) {
|
||||
if (result) {
|
||||
selectors = utils.flattenArray(result);
|
||||
@@ -354,11 +355,9 @@ Ruleset.prototype = Object.assign(new Node(), {
|
||||
function transformDeclaration(decl) {
|
||||
if (decl.value instanceof Anonymous && !decl.parsed) {
|
||||
if (typeof decl.value.value === 'string') {
|
||||
this.parse.parseNode(
|
||||
new Parser(this.parse.context, this.parse.importManager, decl.fileInfo(), decl.value.getIndex()).parseNode(
|
||||
decl.value.value,
|
||||
['value', 'important'],
|
||||
decl.value.getIndex(),
|
||||
decl.fileInfo(),
|
||||
['value', 'important'],
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
decl.parsed = true;
|
||||
|
||||
@@ -2,6 +2,7 @@ import Node from './node';
|
||||
import Element from './element';
|
||||
import LessError from '../less-error';
|
||||
import * as utils from '../utils';
|
||||
import Parser from '../parser/parser';
|
||||
|
||||
const Selector = function(elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
|
||||
this.extendList = extendList;
|
||||
@@ -44,11 +45,9 @@ Selector.prototype = Object.assign(new Node(), {
|
||||
return [new Element('', '&', false, this._index, this._fileInfo)];
|
||||
}
|
||||
if (typeof els === 'string') {
|
||||
this.parse.parseNode(
|
||||
els,
|
||||
new Parser(this.parse.context, this.parse.importManager, this._fileInfo, this._index).parseNode(
|
||||
els,
|
||||
['selector'],
|
||||
this._index,
|
||||
this._fileInfo,
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
throw new LessError({
|
||||
|
||||
@@ -63,6 +63,8 @@ var testMap = [
|
||||
'sourcemaps-empty/', lessTester.testEmptySourcemap],
|
||||
[{math: 'strict', strictUnits: true, sourceMap: {disableSourcemapAnnotation: true}},
|
||||
'sourcemaps-disable-annotation/', lessTester.testSourcemapWithoutUrlAnnotation],
|
||||
[{math: 'strict', strictUnits: true, sourceMap: true},
|
||||
'sourcemaps-variable-selector/', lessTester.testSourcemapWithVariableInSelector],
|
||||
[{globalVars: true, banner: '/**\n * Test\n */\n'}, 'globalVars/',
|
||||
null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; }],
|
||||
[{modifyVars: true}, 'modifyVars/',
|
||||
|
||||
@@ -169,6 +169,29 @@ module.exports = function() {
|
||||
}
|
||||
}
|
||||
|
||||
function testSourcemapWithVariableInSelector(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
|
||||
if (err) {
|
||||
fail('ERROR: ' + (err && err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
// Even if annotation is not necessary, the map file should be there.
|
||||
fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
|
||||
process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
|
||||
if (sourcemap === expectedSourcemap) {
|
||||
ok('OK');
|
||||
} else if (err) {
|
||||
fail('ERROR: ' + (err && err.message));
|
||||
if (isVerbose) {
|
||||
process.stdout.write('\n');
|
||||
process.stdout.write(err.stack + '\n');
|
||||
}
|
||||
} else {
|
||||
difference('FAIL', expectedSourcemap, sourcemap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testImports(name, err, compiledLess, doReplacements, sourcemap, baseFolder, imports) {
|
||||
if (err) {
|
||||
fail('ERROR: ' + (err && err.message));
|
||||
@@ -228,7 +251,7 @@ module.exports = function() {
|
||||
|
||||
// To fix ci fail about error format change in upstream v8 project
|
||||
// https://github.com/v8/v8/commit/c0fd89c3c089e888c4f4e8582e56db7066fa779b
|
||||
// Node 16.9.0+ include this change via https://github.com/nodejs/node/pull/39947
|
||||
// Node 16.9.0+ include this change via https://github.com/nodejs/node/pull/39947
|
||||
function testTypeErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
|
||||
const fileSuffix = semver.gte(process.version, 'v16.9.0') ? '-2.txt' : '.txt';
|
||||
fs.readFile(path.join(baseFolder, name) + fileSuffix, 'utf8', function (e, expectedErr) {
|
||||
@@ -254,7 +277,7 @@ module.exports = function() {
|
||||
// https://github.com/less/less.js/issues/3112
|
||||
function testJSImport() {
|
||||
process.stdout.write('- Testing root function registry');
|
||||
less.functions.functionRegistry.add('ext', function() {
|
||||
less.functions.functionRegistry.add('ext', function() {
|
||||
return new less.tree.Anonymous('file');
|
||||
});
|
||||
var expected = '@charset "utf-8";\n';
|
||||
@@ -282,7 +305,7 @@ module.exports = function() {
|
||||
.replace(/\{pathhref\}/g, '')
|
||||
.replace(/\{404status\}/g, '')
|
||||
.replace(/\{nodepath\}/g, path.join(process.cwd(), 'node_modules', '/'))
|
||||
.replace(/\{pathrel\}/g, path.join(path.relative(lessFolder, p), '/'))
|
||||
.replace(/\{pathrel\}/g, path.join(path.relative(lessFolder, p), '/'))
|
||||
.replace(/\{pathesc\}/g, pathesc)
|
||||
.replace(/\{pathimport\}/g, pathimport)
|
||||
.replace(/\{pathimportesc\}/g, pathimportesc)
|
||||
@@ -327,7 +350,7 @@ module.exports = function() {
|
||||
|
||||
function runTestSetInternal(baseFolder, opts, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
|
||||
foldername = foldername || '';
|
||||
|
||||
|
||||
var originalOptions = opts || {};
|
||||
|
||||
if (!doReplacements) {
|
||||
@@ -497,10 +520,10 @@ module.exports = function() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} filePath
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} filePath
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function toCSS(options, filePath, callback) {
|
||||
options = options || {};
|
||||
@@ -577,7 +600,7 @@ module.exports = function() {
|
||||
}
|
||||
ok(stylize('OK\n', 'green'));
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -588,6 +611,7 @@ module.exports = function() {
|
||||
testTypeErrors: testTypeErrors,
|
||||
testSourcemap: testSourcemap,
|
||||
testSourcemapWithoutUrlAnnotation: testSourcemapWithoutUrlAnnotation,
|
||||
testSourcemapWithVariableInSelector: testSourcemapWithVariableInSelector,
|
||||
testImports: testImports,
|
||||
testImportRedirect: testImportRedirect,
|
||||
testEmptySourcemap: testEmptySourcemap,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["testweb/sourcemaps-variable-selector/basic.less"],"names":[],"mappings":"AAEC;EACG,eAAA","file":"sourcemaps-variable-selector/basic.css"}
|
||||
@@ -0,0 +1,5 @@
|
||||
@import (reference) "./vars.less";
|
||||
|
||||
.@{hello}-class {
|
||||
font-size: @font-size;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@foo: bar;
|
||||
@font-size: 12px;
|
||||
@hello: world;
|
||||
Reference in New Issue
Block a user