tailwindcss/tests/util/source-maps.js
Robin Malfait 352c563d99
Fix source maps issue resulting in a crash (#11319)
* only try to pretty print `err.source` if it exists

* fix small typo

* drop unnecessary path resolving

* add `from` and `to` to `run` test helper

* enable all source map tests

* ensure source maps are generated properly

* update annotation values in tests

* handle `map: true`

* add test for `map: { inline: true }`

* force Lightning CSS to handle an inline source map

* base64 encode the test name

This will get rid of special characters that can cause issues /
confusion when debugging tests.

* ensure utilities and components with variants are pointing to the correct source

Co-authored-by: Jordan Pittman <jordan@cryptica.me>

* update changelog

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2023-05-31 18:28:11 +02:00

88 lines
2.5 KiB
JavaScript

import { SourceMapConsumer } from 'source-map-js'
/**
* Parse the source maps from a PostCSS result
*
* @param {import('postcss').Result} result
*/
export function parseSourceMaps(result) {
let map = result.map
? result.map.toJSON()
: (() => {
let css = result.toString()
let sourceMappingURL = css.match(/\/\*# sourceMappingURL=(.*) \*\//)?.[1]
let raw = sourceMappingURL
? Buffer.from(sourceMappingURL.split(',')[1], 'base64').toString()
: null
return JSON.parse(raw ?? '{}')
})()
return {
sources: map.sources,
annotations: annotatedMappings(map),
}
}
/**
* A string annotation that represents a source map
*
* It's not meant to be exhaustive just enough to
* verify that the source map is working and that
* lines are mapped back to the original source
*
* Including when using @apply with multiple classes
*
* @param {import('source-map-js').RawSourceMap} map
*/
function annotatedMappings(map) {
const smc = new SourceMapConsumer(map)
const annotations = {}
smc.eachMapping((mapping) => {
let annotation = (annotations[mapping.generatedLine] = annotations[mapping.generatedLine] || {
...mapping,
original: {
start: [mapping.originalLine, mapping.originalColumn],
end: [mapping.originalLine, mapping.originalColumn],
},
generated: {
start: [mapping.generatedLine, mapping.generatedColumn],
end: [mapping.generatedLine, mapping.generatedColumn],
},
})
annotation.generated.end[0] = mapping.generatedLine
annotation.generated.end[1] = mapping.generatedColumn
annotation.original.end[0] = mapping.originalLine
annotation.original.end[1] = mapping.originalColumn
})
return Object.values(annotations).map((annotation) => {
return `${formatRange(annotation.original)} -> ${formatRange(annotation.generated)}`
})
}
/**
* @param {object} range
* @param {[number, number]} range.start
* @param {[number, number]} range.end
*/
function formatRange(range) {
if (range.start[0] === range.end[0]) {
// This range is on the same line
// and the columns are the same
if (range.start[1] === range.end[1]) {
return `${range.start[0]}:${range.start[1]}`
}
// This range is on the same line
// but the columns are different
return `${range.start[0]}:${range.start[1]}-${range.end[1]}`
}
// This range spans multiple lines
return `${range.start[0]}:${range.start[1]}-${range.end[0]}:${range.end[1]}`
}