Skip to content

Commit 9322308

Browse files
committed
Fix source map normalization
1 parent 7d491ed commit 9322308

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

packages/less/test/less-test.js

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ module.exports = function(testFilter) {
8888
function validateSourcemapMappings(sourcemap, lessFile, compiledCSS) {
8989
// Validate sourcemap mappings using SourceMapConsumer
9090
var SourceMapConsumer = require('source-map').SourceMapConsumer;
91+
// sourcemap can be either a string or already parsed object
9192
var sourceMapObj = typeof sourcemap === 'string' ? JSON.parse(sourcemap) : sourcemap;
9293
var consumer = new SourceMapConsumer(sourceMapObj);
9394

@@ -128,9 +129,14 @@ module.exports = function(testFilter) {
128129
if (mapping.line && mapping.line > 0) {
129130
// If we can find the source file, validate the line exists
130131
var sourceIndex = sourceMapObj.sources.indexOf(mapping.source);
131-
if (sourceIndex >= 0 && sourceMapObj.sourcesContent && sourceMapObj.sourcesContent[sourceIndex]) {
132+
if (sourceIndex >= 0 && sourceMapObj.sourcesContent && sourceMapObj.sourcesContent[sourceIndex] !== undefined && sourceMapObj.sourcesContent[sourceIndex] !== null) {
132133
var sourceContent = sourceMapObj.sourcesContent[sourceIndex];
133-
var sourceLines = sourceContent.split('\n');
134+
// Ensure sourceContent is a string (it should be, but be defensive)
135+
if (typeof sourceContent !== 'string') {
136+
sourceContent = String(sourceContent);
137+
}
138+
// Split by newline - handle both \n and \r\n
139+
var sourceLines = sourceContent.split(/\r?\n/);
134140
if (mapping.line > sourceLines.length) {
135141
errors.push('Line ' + cssLine + ': mapped to line ' + mapping.line + ' in "' + mapping.source + '" but source only has ' + sourceLines.length + ' lines');
136142
}
@@ -209,15 +215,34 @@ module.exports = function(testFilter) {
209215

210216
// Apply doReplacements to the expected sourcemap to handle {path} placeholders
211217
// This normalizes absolute paths that differ between environments
212-
// For sourcemaps, we need to ensure paths use forward slashes (web-compatible)
213218
expectedSourcemap = doReplacements(expectedSourcemap, baseFolder, path.join(baseFolder, name) + '.less');
219+
214220
// Normalize paths in sourcemap JSON to use forward slashes (web-compatible)
215-
// Replace any backslashes that might have been introduced by path.join or path operations
216-
expectedSourcemap = expectedSourcemap.replace(/\\/g, '/');
217-
// Also normalize the actual sourcemap to ensure consistency
218-
var normalizedSourcemap = sourcemap.replace(/\\/g, '/');
221+
// We need to parse the JSON, normalize the file property, then stringify for comparison
222+
// This avoids breaking escape sequences like \n in the JSON string
223+
function normalizeSourcemapPaths(sm) {
224+
try {
225+
var parsed = typeof sm === 'string' ? JSON.parse(sm) : sm;
226+
if (parsed.file) {
227+
parsed.file = parsed.file.replace(/\\/g, '/');
228+
}
229+
// Also normalize paths in sources array
230+
if (parsed.sources && Array.isArray(parsed.sources)) {
231+
parsed.sources = parsed.sources.map(function(src) {
232+
return src.replace(/\\/g, '/');
233+
});
234+
}
235+
return JSON.stringify(parsed, null, 0);
236+
} catch (parseErr) {
237+
// If parsing fails, return original (shouldn't happen)
238+
return sm;
239+
}
240+
}
241+
242+
var normalizedSourcemap = normalizeSourcemapPaths(sourcemap);
243+
var normalizedExpected = normalizeSourcemapPaths(expectedSourcemap);
219244

220-
if (normalizedSourcemap === expectedSourcemap) {
245+
if (normalizedSourcemap === normalizedExpected) {
221246
// Validate the sourcemap mappings are correct
222247
// Find the actual LESS file - it might be in a subdirectory
223248
var nameParts = name.split('/');
@@ -228,8 +253,10 @@ module.exports = function(testFilter) {
228253
// Only validate if the LESS file exists
229254
if (fs.existsSync(lessFile)) {
230255
try {
231-
// Use normalized sourcemap for validation (forward slashes)
232-
var validation = validateSourcemapMappings(normalizedSourcemap, lessFile, compiledLess);
256+
// Parse the sourcemap once for validation (avoid re-parsing)
257+
// Use the original sourcemap string, not the normalized one
258+
var sourceMapObjForValidation = typeof sourcemap === 'string' ? JSON.parse(sourcemap) : sourcemap;
259+
var validation = validateSourcemapMappings(sourceMapObjForValidation, lessFile, compiledLess);
233260
if (!validation.valid) {
234261
fail('ERROR: Sourcemap validation failed:\n' + validation.errors.join('\n'));
235262
return;
@@ -253,7 +280,7 @@ module.exports = function(testFilter) {
253280
process.stdout.write(err.stack + '\n');
254281
}
255282
} else {
256-
difference('FAIL', expectedSourcemap, normalizedSourcemap);
283+
difference('FAIL', normalizedExpected, normalizedSourcemap);
257284
}
258285
});
259286
}

packages/test-data/tests-config/sourcemaps-include-source/sourcemaps-include-source.css.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)