@@ -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 }
0 commit comments