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,137 @@ 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 ( 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 ( lines , style , templateParse ) {
110+ let line = 0 ;
111+ let column = style . startIndex ;
112+ lines . some ( ( lineEndIndex , lineNumber ) => {
113+ if ( lineEndIndex >= style . startIndex ) {
114+ line = lineNumber -- ;
115+ if ( lineNumber in lines ) {
116+ column = style . startIndex - lines [ lineNumber ] - 1 ;
117+ }
118+ return true ;
119+ }
120+ } ) ;
121+
122+ this . line = line ;
123+ this . column = column ;
124+ this . style = style ;
125+ this . templateParse = templateParse
126+ }
127+ object ( object ) {
128+ if ( object ) {
129+ if ( object . line === 1 ) {
130+ object . column += this . column ;
131+ }
132+ object . line += this . line ;
133+ }
134+ }
135+ node ( node ) {
136+ this . object ( node . source . start ) ;
137+ this . object ( node . source . end ) ;
138+ }
139+ root ( root ) {
140+ this . node ( root ) ;
141+ root . walk ( node => {
142+ this . node ( node ) ;
143+ } ) ;
144+ }
145+ error ( error ) {
146+ if ( error && error . name === "CssSyntaxError" ) {
147+ this . object ( error ) ;
148+ this . object ( error . input ) ;
149+ error . message = error . message . replace ( / : \d + : \d + : / , ":" + error . line + ":" + error . column + ":" ) ;
150+ }
151+ return error ;
152+ }
153+ parse ( opts ) {
154+ const style = this . style ;
155+ const syntax = style . syntax ;
156+ let root = style . root ;
157+ try {
158+ root = this . templateParse ( style . content , Object . assign ( { } , opts , {
159+ map : false ,
160+ } , style . opts ) ) ;
161+ } catch ( error ) {
162+ if ( style . ignoreErrors ) {
163+ return ;
164+ } else if ( ! style . skipConvert ) {
165+ this . error ( error ) ;
166+ }
167+ throw error ;
168+ }
169+ if ( ! style . skipConvert ) {
170+ this . root ( root ) ;
171+ }
172+
173+ root . source . inline = Boolean ( style . inline ) ;
174+ root . source . lang = style . lang ;
175+ root . source . syntax = syntax ;
176+ return root ;
177+ }
178+ }
179+
180+ function docFixer ( source , opts ) {
181+ let match ;
182+ const lines = [ ] ;
183+ reNewLine . lastIndex = 0 ;
184+ while ( ( match = reNewLine . exec ( source ) ) ) {
185+ lines . push ( match . index ) ;
186+ }
187+ lines . push ( source . length ) ;
188+ return function parseStyle ( style ) {
189+ const parse = style . syntax ? style . syntax . parse : postcssParse
190+ return new LocalFixer ( lines , style , parse ) . parse ( opts ) ;
191+ } ;
192+ }
193+
194+ function getNodeIndex ( node , input ) {
195+ const source = input . css
196+ let match ;
197+ let line = 1
198+ let lastIndex = - 1
199+ reNewLine . lastIndex = 0 ;
200+ while ( ( match = reNewLine . exec ( source ) ) ) {
201+ if ( line === node . source . start . line ) {
202+ return lastIndex + node . source . start . column
203+ }
204+ lastIndex = match . index
205+ line ++
206+ }
207+ if ( line === node . source . start . line ) {
208+ return lastIndex + node . source . start . column
209+ }
210+ return source . length
211+ }
0 commit comments