Skip to content

Commit 243fbbf

Browse files
committed
wip: translate sourcemaps in PostCSS plugin
1 parent 5fa1926 commit 243fbbf

2 files changed

Lines changed: 67 additions & 6 deletions

File tree

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

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,54 @@
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+
let start = table.find(loc[1])
35+
let end = table.find(loc[2])
36+
37+
return {
38+
input: inputMap.get(loc[0]),
39+
start: {
40+
line: start.line,
41+
column: start.column + 1,
42+
offset: loc[1],
43+
},
44+
end: {
45+
line: end.line,
46+
column: end.column + 1,
47+
offset: loc[2],
48+
},
49+
}
50+
}
51+
1552
function transform(node: AstNode, parent: PostCssContainerNode) {
1653
// Declaration
1754
if (node.kind === 'declaration') {
@@ -20,14 +57,14 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
2057
value: node.value ?? '',
2158
important: node.important,
2259
})
23-
astNode.source = source
60+
astNode.source = toSource(node.src)
2461
parent.append(astNode)
2562
}
2663

2764
// Rule
2865
else if (node.kind === 'rule') {
2966
let astNode = postcss.rule({ selector: node.selector })
30-
astNode.source = source
67+
astNode.source = toSource(node.src)
3168
astNode.raws.semicolon = true
3269
parent.append(astNode)
3370
for (let child of node.nodes) {
@@ -38,7 +75,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
3875
// AtRule
3976
else if (node.kind === 'at-rule') {
4077
let astNode = postcss.atRule({ name: node.name.slice(1), params: node.params })
41-
astNode.source = source
78+
astNode.source = toSource(node.src)
4279
astNode.raws.semicolon = true
4380
parent.append(astNode)
4481
for (let child of node.nodes) {
@@ -53,7 +90,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
5390
// spaces.
5491
astNode.raws.left = ''
5592
astNode.raws.right = ''
56-
astNode.source = source
93+
astNode.source = toSource(node.src)
5794
parent.append(astNode)
5895
}
5996

@@ -75,33 +112,56 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef
75112
}
76113

77114
export function postCssAstToCssAst(root: PostCssRoot): AstNode[] {
115+
let inputMap = new DefaultMap<Input, Source>((input) => ({
116+
file: input.file ?? input.id ?? null,
117+
code: input.css,
118+
}))
119+
120+
function toSource(node: PostCssChildNode): SourceLocation | undefined {
121+
let source = node.source
122+
if (!source) return
123+
124+
let input = source.input
125+
if (!input) return
126+
if (source.start === undefined) return
127+
if (source.end === undefined) return
128+
129+
return [inputMap.get(input), source.start.offset, source.end.offset]
130+
}
131+
78132
function transform(
79133
node: PostCssChildNode,
80134
parent: Extract<AstNode, { nodes: AstNode[] }>['nodes'],
81135
) {
82136
// Declaration
83137
if (node.type === 'decl') {
84-
parent.push(decl(node.prop, node.value, node.important))
138+
let astNode = decl(node.prop, node.value, node.important)
139+
astNode.src = toSource(node)
140+
parent.push(astNode)
85141
}
86142

87143
// Rule
88144
else if (node.type === 'rule') {
89145
let astNode = rule(node.selector)
146+
astNode.src = toSource(node)
90147
node.each((child) => transform(child, astNode.nodes))
91148
parent.push(astNode)
92149
}
93150

94151
// AtRule
95152
else if (node.type === 'atrule') {
96153
let astNode = atRule(`@${node.name}`, node.params)
154+
astNode.src = toSource(node)
97155
node.each((child) => transform(child, astNode.nodes))
98156
parent.push(astNode)
99157
}
100158

101159
// Comment
102160
else if (node.type === 'comment') {
103161
if (node.text.charCodeAt(0) !== EXCLAMATION_MARK) return
104-
parent.push(comment(node.text))
162+
let astNode = comment(node.text)
163+
astNode.src = toSource(node)
164+
parent.push(astNode)
105165
}
106166

107167
// Unknown

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
121121

122122
DEBUG && I.start('Create compiler')
123123
let compiler = await compileAst(ast, {
124+
from: result.opts.from,
124125
base: inputBasePath,
125126
shouldRewriteUrls: true,
126127
onDependency: (path) => context.fullRebuildPaths.push(path),

0 commit comments

Comments
 (0)