Skip to content

Commit 46c5adf

Browse files
committed
wip: translate sourcemaps in PostCSS plugin
postcss
1 parent e68c4f5 commit 46c5adf

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

packages/@tailwindcss-postcss/src/ast.ts

+62-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,50 @@
11
import postcss, {
2+
Input,
23
type ChildNode as PostCssChildNode,
34
type Container as PostCssContainerNode,
45
type Root as PostCssRoot,
56
type Source as PostcssSource,
67
} from 'postcss'
78
import { atRule, comment, decl, rule, type AstNode } from '../../tailwindcss/src/ast'
9+
import { createLineTable, type LineTable } from '../../tailwindcss/src/source-maps/line-table'
10+
import type { Source, SourceLocation } from '../../tailwindcss/src/source-maps/source'
11+
import { DefaultMap } from '../../tailwindcss/src/utils/default-map'
812

913
const EXCLAMATION_MARK = 0x21
1014

1115
export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undefined): PostCssRoot {
16+
let inputMap = new DefaultMap<Source, Input>((src) => {
17+
return new Input(src.code, {
18+
map: source?.input.map,
19+
from: src.file ?? undefined,
20+
})
21+
})
22+
23+
let lineTables = new DefaultMap<Source, LineTable>((src) => createLineTable(src.code))
24+
1225
let root = postcss.root()
1326
root.source = source
1427

28+
function toSource(loc: SourceLocation | undefined): PostcssSource | undefined {
29+
// Use the fallback if this node has no location info in the AST
30+
if (!loc) return source
31+
if (!loc[0]) return source
32+
33+
let table = lineTables.get(loc[0])
34+
35+
return {
36+
input: inputMap.get(loc[0]),
37+
start: {
38+
...table.find(loc[1]),
39+
offset: loc[1],
40+
},
41+
end: {
42+
...table.find(loc[2]),
43+
offset: loc[2],
44+
},
45+
}
46+
}
47+
1548
function transform(node: AstNode, parent: PostCssContainerNode) {
1649
// Declaration
1750
if (node.kind === 'declaration') {
@@ -20,14 +53,14 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
2053
value: node.value ?? '',
2154
important: node.important,
2255
})
23-
astNode.source = source
56+
astNode.source = toSource(node.src)
2457
parent.append(astNode)
2558
}
2659

2760
// Rule
2861
else if (node.kind === 'rule') {
2962
let astNode = postcss.rule({ selector: node.selector })
30-
astNode.source = source
63+
astNode.source = toSource(node.src)
3164
astNode.raws.semicolon = true
3265
parent.append(astNode)
3366
for (let child of node.nodes) {
@@ -38,7 +71,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
3871
// AtRule
3972
else if (node.kind === 'at-rule') {
4073
let astNode = postcss.atRule({ name: node.name.slice(1), params: node.params })
41-
astNode.source = source
74+
astNode.source = toSource(node.src)
4275
astNode.raws.semicolon = true
4376
parent.append(astNode)
4477
for (let child of node.nodes) {
@@ -53,7 +86,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
5386
// spaces.
5487
astNode.raws.left = ''
5588
astNode.raws.right = ''
56-
astNode.source = source
89+
astNode.source = toSource(node.src)
5790
parent.append(astNode)
5891
}
5992

@@ -75,33 +108,56 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
75108
}
76109

77110
export function postCssAstToCssAst(root: PostCssRoot): AstNode[] {
111+
let inputMap = new DefaultMap<Input, Source>((input) => ({
112+
file: input.file ?? input.id ?? null,
113+
code: input.css,
114+
}))
115+
116+
function toSource(node: PostCssChildNode): SourceLocation | undefined {
117+
let source = node.source
118+
if (!source) return
119+
120+
let input = source.input
121+
if (!input) return
122+
if (source.start === undefined) return
123+
if (source.end === undefined) return
124+
125+
return [inputMap.get(input), source.start.offset, source.end.offset]
126+
}
127+
78128
function transform(
79129
node: PostCssChildNode,
80130
parent: Extract<AstNode, { nodes: AstNode[] }>['nodes'],
81131
) {
82132
// Declaration
83133
if (node.type === 'decl') {
84-
parent.push(decl(node.prop, node.value, node.important))
134+
let astNode = decl(node.prop, node.value, node.important)
135+
astNode.src = toSource(node)
136+
parent.push(astNode)
85137
}
86138

87139
// Rule
88140
else if (node.type === 'rule') {
89141
let astNode = rule(node.selector)
142+
astNode.src = toSource(node)
90143
node.each((child) => transform(child, astNode.nodes))
91144
parent.push(astNode)
92145
}
93146

94147
// AtRule
95148
else if (node.type === 'atrule') {
96149
let astNode = atRule(`@${node.name}`, node.params)
150+
astNode.src = toSource(node)
97151
node.each((child) => transform(child, astNode.nodes))
98152
parent.push(astNode)
99153
}
100154

101155
// Comment
102156
else if (node.type === 'comment') {
103157
if (node.text.charCodeAt(0) !== EXCLAMATION_MARK) return
104-
parent.push(comment(node.text))
158+
let astNode = comment(node.text)
159+
astNode.src = toSource(node)
160+
parent.push(astNode)
105161
}
106162

107163
// Unknown

packages/@tailwindcss-postcss/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,13 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
283283
DEBUG && I.end('AST -> CSS')
284284

285285
DEBUG && I.start('Lightning CSS')
286-
let ast = optimizeCss(css, {
286+
let optimized = optimizeCss(css, {
287287
minify: typeof optimize === 'object' ? optimize.minify : true,
288288
})
289289
DEBUG && I.end('Lightning CSS')
290290

291291
DEBUG && I.start('CSS -> PostCSS AST')
292-
context.optimizedPostCssAst = postcss.parse(ast, result.opts)
292+
context.optimizedPostCssAst = postcss.parse(optimized.code, result.opts)
293293
DEBUG && I.end('CSS -> PostCSS AST')
294294

295295
DEBUG && I.end('Optimization')

0 commit comments

Comments
 (0)