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 ) {
14+ return {
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+ } ;
24+ }
25+
26+ function transformAst ( node ) {
27+ if ( ! node ) {
28+ return ;
29+ }
30+
31+ if ( node . type === "StyleSheet" ) {
32+ return {
33+ type : "stylesheet" ,
34+ stylesheet : {
35+ source : node . loc . source ,
36+ rules : node . children . toArray ( ) . map ( child => transformAst ( child ) ) ,
37+ parsingErrors : [ ]
38+ }
39+ } ;
40+ }
41+
42+ if ( node . type === "Atrule" ) {
43+ let atrule : any = {
44+ type : node . name ,
45+ } ;
46+
47+ if ( node . name === "supports" || node . name === "media" ) {
48+ atrule [ node . name ] = node . prelude . value ;
49+ atrule . rules = transformAst ( node . block ) ;
50+ } else if ( node . name === "page" ) {
51+ atrule . selectors = node . prelude ? mapSelectors ( node . prelude . value ) : [ ] ;
52+ atrule . declarations = transformAst ( node . block ) ;
53+ } else if ( node . name === "document" ) {
54+ atrule . document = node . prelude ? node . prelude . value : "" ;
55+ atrule . vendor = "" ;
56+ atrule . rules = transformAst ( node . block ) ;
57+ } else if ( node . name === "font-face" ) {
58+ atrule . declarations = transformAst ( node . block ) ;
59+ } else if ( node . name === "import" || node . name === "charset" || node . name === "namespace" ) {
60+ atrule [ node . name ] = node . prelude ? node . prelude . value : "" ;
61+ } else {
62+ atrule . rules = transformAst ( node . block ) ;
63+ }
64+
65+ return atrule ;
66+ }
67+
68+ if ( node . type === "Block" ) {
69+ return node . children . toArray ( ) . map ( child => transformAst ( child ) ) ;
70+ }
71+
72+ if ( node . type === "Rule" ) {
73+ let value = node . prelude . value ;
74+
75+ return {
76+ type : "rule" ,
77+ selectors : mapSelectors ( value ) ,
78+ declarations : transformAst ( node . block ) ,
79+ position : mapPosition ( node )
80+ } ;
81+ }
82+
83+ if ( node . type === "Comment" ) {
84+ return {
85+ type : "comment" ,
86+ comment : node . value ,
87+ position : mapPosition ( node )
88+ } ;
89+ }
90+
91+ if ( node . type === "Declaration" ) {
92+ return {
93+ type : "declaration" ,
94+ property : node . property ,
95+ value : node . value . value ,
96+ position : mapPosition ( node )
97+ } ;
98+ }
99+
100+ throw Error ( `Unknown node type ${ node . type } ` ) ;
101+ }
102+
103+ export function cssTreeParse ( css , source ) : any {
104+ let errors = [ ] ;
105+ let ast = parse ( css , {
106+ parseValue : false ,
107+ parseAtrulePrelude : false ,
108+ parseRulePrelude : false ,
109+ positions : true ,
110+ filename : source ,
111+ onParseError : error => {
112+ errors . push ( `${ source } :${ error . line } :${ error . column } : ${ error . formattedMessage } ` ) ;
113+ }
114+ } ) ;
115+
116+ if ( errors . length > 0 ) {
117+ throw new Error ( errors [ 0 ] ) ;
118+ }
119+
120+ return transformAst ( ast ) ;
121+ }
0 commit comments