11"use strict" ;
22const Literal = require ( "./literal" ) ;
3+ const postcssParse = require ( "postcss/lib/parse" ) ;
4+ const Input = require ( "postcss/lib/input" ) ;
5+ const reNewLine = / (?: \r ? \n | \r ) / gm;
36const isLiteral = token => token [ 0 ] === "word" && / ^ \$ \{ [ \s \S ] * \} $ / . test ( token [ 1 ] ) ;
47function literal ( start ) {
58 if ( ! isLiteral ( start ) ) {
@@ -33,6 +36,28 @@ function literal (start) {
3336
3437 this . init ( node , start [ 2 ] , start [ 3 ] ) ;
3538
39+ const input = this . input ;
40+ if ( input . templateLiteralStyles ) {
41+ const offset = input . quasis [ 0 ] . start ;
42+ const nodeIndex = getNodeIndex ( node , input ) ;
43+ const start = offset + nodeIndex ;
44+ const end = start + node . text . length ;
45+ const templateLiteralStyles = input . templateLiteralStyles . filter ( style =>
46+ style . startIndex <= end && start < style . endIndex
47+ ) ;
48+ if ( templateLiteralStyles . length ) {
49+ const nodes = parseTemplateLiteralStyles (
50+ templateLiteralStyles ,
51+ input ,
52+ [ nodeIndex , nodeIndex + node . text . length ]
53+ ) ;
54+ if ( nodes . length ) {
55+ node . nodes = nodes ;
56+ nodes . forEach ( n => n . parent = node ) ;
57+ }
58+ }
59+ }
60+
3661 return node ;
3762}
3863
@@ -50,3 +75,138 @@ module.exports = {
5075 freeSemicolon : freeSemicolon ,
5176 literal : literal ,
5277} ;
78+
79+ function parseTemplateLiteralStyles ( styles , input , range ) {
80+ const offset = input . quasis [ 0 ] . start ;
81+ const source = input . css ;
82+ const parseStyle = docFixer ( offset , source , input . parseOptions ) ;
83+
84+ const nodes = [ ] ;
85+ let index = range [ 0 ] ;
86+ styles . sort ( ( a , b ) => (
87+ a . startIndex - b . startIndex
88+ ) ) . forEach ( style => {
89+ const root = parseStyle ( style ) ;
90+ if ( ! root || ! root . nodes . length ) {
91+ return
92+ }
93+ root . raws . beforeStart = source . slice ( index , style . startIndex - offset ) ;
94+ root . raws . afterEnd = '' ;
95+ if ( style . endIndex ) {
96+ index = style . endIndex - offset ;
97+ } else {
98+ index = style . startIndex - offset + ( style . content || root . source . input . css ) . length ;
99+ }
100+ nodes . push ( root ) ;
101+ } )
102+ if ( nodes . length ) {
103+ nodes [ nodes . length - 1 ] . raws . afterEnd = source . slice ( index , range [ 1 ] ) ;
104+ }
105+ return nodes ;
106+ }
107+
108+ class LocalFixer {
109+ constructor ( offset , lines , style , templateParse ) {
110+ const startIndex = style . startIndex - offset
111+ let line = 0 ;
112+ let column = startIndex ;
113+ lines . some ( ( lineEndIndex , lineNumber ) => {
114+ if ( lineEndIndex >= startIndex ) {
115+ line = lineNumber -- ;
116+ if ( lineNumber in lines ) {
117+ column = startIndex - lines [ lineNumber ] - 1 ;
118+ }
119+ return true ;
120+ }
121+ } ) ;
122+
123+ this . line = line ;
124+ this . column = column ;
125+ this . style = style ;
126+ this . templateParse = templateParse
127+ }
128+ object ( object ) {
129+ if ( object ) {
130+ if ( object . line === 1 ) {
131+ object . column += this . column ;
132+ }
133+ object . line += this . line ;
134+ }
135+ }
136+ node ( node ) {
137+ this . object ( node . source . start ) ;
138+ this . object ( node . source . end ) ;
139+ }
140+ root ( root ) {
141+ this . node ( root ) ;
142+ root . walk ( node => {
143+ this . node ( node ) ;
144+ } ) ;
145+ }
146+ error ( error ) {
147+ if ( error && error . name === "CssSyntaxError" ) {
148+ this . object ( error ) ;
149+ this . object ( error . input ) ;
150+ error . message = error . message . replace ( / : \d + : \d + : / , ":" + error . line + ":" + error . column + ":" ) ;
151+ }
152+ return error ;
153+ }
154+ parse ( opts ) {
155+ const style = this . style ;
156+ const syntax = style . syntax ;
157+ let root = style . root ;
158+ try {
159+ root = this . templateParse ( style . content , Object . assign ( { } , opts , {
160+ map : false ,
161+ } , style . opts ) ) ;
162+ } catch ( error ) {
163+ if ( style . ignoreErrors ) {
164+ return ;
165+ } else if ( ! style . skipConvert ) {
166+ this . error ( error ) ;
167+ }
168+ throw error ;
169+ }
170+ if ( ! style . skipConvert ) {
171+ this . root ( root ) ;
172+ }
173+
174+ root . source . inline = Boolean ( style . inline ) ;
175+ root . source . lang = style . lang ;
176+ root . source . syntax = syntax ;
177+ return root ;
178+ }
179+ }
180+
181+ function docFixer ( offset , source , opts ) {
182+ let match ;
183+ const lines = [ ] ;
184+ reNewLine . lastIndex = 0 ;
185+ while ( ( match = reNewLine . exec ( source ) ) ) {
186+ lines . push ( match . index ) ;
187+ }
188+ lines . push ( source . length ) ;
189+ return function parseStyle ( style ) {
190+ const parse = style . syntax ? style . syntax . parse : postcssParse
191+ return new LocalFixer ( offset , lines , style , parse ) . parse ( opts ) ;
192+ } ;
193+ }
194+
195+ function getNodeIndex ( node , input ) {
196+ const source = input . css
197+ let match ;
198+ let line = 1
199+ let lastIndex = - 1
200+ reNewLine . lastIndex = 0 ;
201+ while ( ( match = reNewLine . exec ( source ) ) ) {
202+ if ( line === node . source . start . line ) {
203+ return lastIndex + node . source . start . column
204+ }
205+ lastIndex = match . index
206+ line ++
207+ }
208+ if ( line === node . source . start . line ) {
209+ return lastIndex + node . source . start . column
210+ }
211+ return source . length
212+ }
0 commit comments