Update source maps when optimizing output CSS

This commit is contained in:
Jordan Pittman 2025-04-29 19:13:42 -04:00
parent 2302668b48
commit acf5f3182e
2 changed files with 40 additions and 6 deletions

View file

@ -37,9 +37,11 @@
}
},
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"enhanced-resolve": "^5.18.1",
"jiti": "^2.4.2",
"lightningcss": "catalog:",
"magic-string": "^0.30.17",
"source-map-js": "^1.2.1",
"tailwindcss": "workspace:*"
}

View file

@ -1,4 +1,6 @@
import remapping from '@ampproject/remapping'
import { Features, transform } from 'lightningcss'
import MagicString from 'magic-string'
export interface OptimizeOptions {
/**
@ -10,22 +12,31 @@ export interface OptimizeOptions {
* Enabled minified output
*/
minify?: boolean
/**
* The output source map before optimization
*
* If omitted an resulting source map will not be available
*/
map?: string
}
export interface TransformResult {
code: string
map: string | undefined
}
export function optimize(
input: string,
{ file = 'input.css', minify = false }: OptimizeOptions = {},
{ file = 'input.css', minify = false, map }: OptimizeOptions = {},
): TransformResult {
function optimize(code: Buffer | Uint8Array) {
function optimize(code: Buffer | Uint8Array, map: string | undefined) {
return transform({
filename: file,
code,
minify,
sourceMap: false,
sourceMap: typeof map !== 'undefined',
inputSourceMap: map,
drafts: {
customMedia: true,
},
@ -46,13 +57,34 @@ export function optimize(
// Running Lightning CSS twice to ensure that adjacent rules are merged after
// nesting is applied. This creates a more optimized output.
let result = optimize(Buffer.from(input))
result = optimize(result.code)
let result = optimize(Buffer.from(input), map)
map = result.map?.toString()
result = optimize(result.code, map)
map = result.map?.toString()
let code = result.code.toString()
code = code.replaceAll('@media not (', '@media not all and (')
// Work around an issue where the media query range syntax transpilation
// generates code that is invalid with `@media` queries level 3.
let magic = new MagicString(code)
magic.replaceAll('@media not (', '@media not all and (')
// We have to use a source-map-preserving method of replacing the content
// which requires the use of Magic String + remapping(…) to make sure
// the resulting map is correct
if (map !== undefined && magic.hasChanged()) {
let magicMap = magic.generateMap({ source: 'original', hires: 'boundary' }).toString()
let remapped = remapping([magicMap, map], () => null)
map = remapped.toString()
}
code = magic.toString()
return {
code,
map,
}
}