1+ import {
2+ parse
3+ } from "css-tree" ;
4+
5+ function mapSelectors ( selector : string ) : string [ ] {
6+ if ( ! selector ) {
7+ return [ ] ;
8+ }
9+
10+ return selector . split ( / \s * (? ! [ ^ ( ] * \) ) , \s * / ) . map ( s => s . replace ( / \u200C / g, "," ) ) ;
11+ }
12+
13+ function mapPosition ( node , css ) {
14+ let res : any = {
15+ start : {
16+ line : node . loc . start . line ,
17+ column : node . loc . start . column
18+ } ,
19+ end : {
20+ line : node . loc . end . line ,
21+ column : node . loc . end . column
22+ } ,
23+ content : css
24+ } ;
25+
26+ if ( node . loc . source && node . loc . source !== "<unknown>" ) {
27+ res . source = node . loc . source ;
28+ }
29+
30+ return res ;
31+ }
32+
33+ function transformAst ( node , css , type = null ) {
34+ if ( ! node ) {
35+ return ;
36+ }
37+
38+ if ( node . type === "StyleSheet" ) {
39+ return {
40+ type : "stylesheet" ,
41+ stylesheet : {
42+ rules : node . children . map ( child => transformAst ( child , css ) ) . toArray ( ) ,
43+ parsingErrors : [ ]
44+ }
45+ } ;
46+ }
47+
48+ if ( node . type === "Atrule" ) {
49+ let atrule : any = {
50+ type : node . name ,
51+ } ;
52+
53+ if ( node . name === "supports" || node . name === "media" ) {
54+ atrule [ node . name ] = node . prelude . value ;
55+ atrule . rules = transformAst ( node . block , css ) ;
56+ } else if ( node . name === "page" ) {
57+ atrule . selectors = node . prelude ? mapSelectors ( node . prelude . value ) : [ ] ;
58+ atrule . declarations = transformAst ( node . block , css ) ;
59+ } else if ( node . name === "document" ) {
60+ atrule . document = node . prelude ? node . prelude . value : "" ;
61+ atrule . vendor = "" ;
62+ atrule . rules = transformAst ( node . block , css ) ;
63+ } else if ( node . name === "font-face" ) {
64+ atrule . declarations = transformAst ( node . block , css ) ;
65+ } else if ( node . name === "import" || node . name === "charset" || node . name === "namespace" ) {
66+ atrule [ node . name ] = node . prelude ? node . prelude . value : "" ;
67+ } else if ( node . name === "keyframes" ) {
68+ atrule . name = node . prelude ? node . prelude . value : "" ;
69+ atrule . keyframes = transformAst ( node . block , css , "keyframe" ) ;
70+ atrule . vendor = undefined ;
71+ } else {
72+ atrule . rules = transformAst ( node . block , css ) ;
73+ }
74+
75+ atrule . position = mapPosition ( node , css ) ;
76+
77+ return atrule ;
78+ }
79+
80+ if ( node . type === "Block" ) {
81+ return node . children . map ( child => transformAst ( child , css , type ) ) . toArray ( ) ;
82+ }
83+
84+ if ( node . type === "Rule" ) {
85+ let value = node . prelude . value ;
86+
87+ let res : any = {
88+ type : type != null ? type : "rule" ,
89+ declarations : transformAst ( node . block , css ) ,
90+ position : mapPosition ( node , css )
91+ } ;
92+
93+ if ( type === "keyframe" ) {
94+ res . values = mapSelectors ( value ) ;
95+ } else {
96+ res . selectors = mapSelectors ( value ) ;
97+ }
98+
99+ return res ;
100+ }
101+
102+ if ( node . type === "Comment" ) {
103+ return {
104+ type : "comment" ,
105+ comment : node . value ,
106+ position : mapPosition ( node , css )
107+ } ;
108+ }
109+
110+ if ( node . type === "Declaration" ) {
111+ return {
112+ type : "declaration" ,
113+ property : node . property ,
114+ value : node . value . value ? node . value . value . trim ( ) : "" ,
115+ position : mapPosition ( node , css )
116+ } ;
117+ }
118+
119+ throw Error ( `Unknown node type ${ node . type } ` ) ;
120+ }
121+
122+ export function cssTreeParse ( css , source ) : any {
123+ let errors = [ ] ;
124+ let ast = parse ( css , {
125+ parseValue : false ,
126+ parseAtrulePrelude : false ,
127+ parseRulePrelude : false ,
128+ positions : true ,
129+ filename : source ,
130+ onParseError : error => {
131+ errors . push ( `${ source } :${ error . line } :${ error . column } : ${ error . formattedMessage } ` ) ;
132+ }
133+ } ) ;
134+
135+ if ( errors . length > 0 ) {
136+ throw new Error ( errors [ 0 ] ) ;
137+ }
138+
139+ return transformAst ( ast , css ) ;
140+ }
0 commit comments