Skip to content

fix faulty source map generation with variables in selectors #3761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 2, 2023
Merged
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
127 changes: 59 additions & 68 deletions packages/less/src/less/parser/parser.js

Large diffs are not rendered by default.

15 changes: 7 additions & 8 deletions packages/less/src/less/tree/ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 3 additions & 4 deletions packages/less/src/less/tree/selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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({
Expand Down
2 changes: 2 additions & 0 deletions packages/less/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
Expand Down
42 changes: 33 additions & 9 deletions packages/less/test/less-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this from another source map test:

fail('ERROR: ' + (err && err.message));
if (isVerbose) {
process.stdout.write('\n');
process.stdout.write(err.stack + '\n');
}

Although I think this actually refers to the wrong error object? I think this should refer to the variable e that comes from the fs.readFile callback, because err (passed as an arg to the function) is handled at the beginning of the function:

if (err) {
fail('ERROR: ' + (err && err.message));
return;
}

This happens in several of the tests, so I just followed the convention. If it's correct that this should, in fact, be e (and e.message/e.stack), I'm happy to follow up with a PR that fixes these references throughout this file 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I think this actually refers to the wrong error object?

@pgoldberg That's entirely possible. I'd like to see all of the source entirely type-checked with JSDoc annotations, because then anyone contributing would not have to guess what is what.

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));
Expand Down Expand Up @@ -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) {
Expand All @@ -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';
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -327,7 +350,7 @@ module.exports = function() {

function runTestSetInternal(baseFolder, opts, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
foldername = foldername || '';

var originalOptions = opts || {};

if (!doReplacements) {
Expand Down Expand Up @@ -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 || {};
Expand Down Expand Up @@ -577,7 +600,7 @@ module.exports = function() {
}
ok(stylize('OK\n', 'green'));
}
);
);
}

return {
Expand All @@ -588,6 +611,7 @@ module.exports = function() {
testTypeErrors: testTypeErrors,
testSourcemap: testSourcemap,
testSourcemapWithoutUrlAnnotation: testSourcemapWithoutUrlAnnotation,
testSourcemapWithVariableInSelector: testSourcemapWithVariableInSelector,
testImports: testImports,
testImportRedirect: testImportRedirect,
testEmptySourcemap: testEmptySourcemap,
Expand Down
1 change: 1 addition & 0 deletions packages/less/test/sourcemaps-variable-selector/basic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":3,"sources":["testweb/sourcemaps-variable-selector/basic.less"],"names":[],"mappings":"AAEC;EACG,eAAA","file":"sourcemaps-variable-selector/basic.css"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import (reference) "./vars.less";

.@{hello}-class {
font-size: @font-size;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@foo: bar;
@font-size: 12px;
@hello: world;