@@ -59,217 +59,175 @@ function encodeRelativeURL(startPath, relative, callerPath) {
59
59
// may be undefined
60
60
return result ;
61
61
}
62
- } else {
63
- return ;
64
62
}
65
63
}
66
64
67
65
/**
68
- * @param {number } [bannerWidth] The width of banner comment, zero or omitted for none
66
+ * Use <code>node-sass</code> to compile the files of the input stream.
67
+ * Outputs a stream of compiled files and their source-maps, alternately.
68
+ * @see https://github.com/sass/node-sass#outputstyle
69
+ * @param {Array.<string> } [libraryPaths] Any number of library path strings
70
+ * @returns {stream.Through } A through stream that performs the operation of a gulp stream
69
71
*/
70
- module . exports = function ( bannerWidth ) {
72
+ module . exports = function ( bannerWidth , libraryPaths ) {
71
73
'use strict' ;
72
- var libList = [ ] ;
73
-
74
- /**
75
- * Add string values to the <code>libList</code> uniquely.
76
- * @param {string|Array } value An explicit library path string or array thereof
77
- */
78
- function addUnique ( value ) {
79
- if ( ( typeof value === 'object' ) && ( value instanceof Array ) ) {
80
- value . forEach ( addUnique ) ;
81
- } else if ( ( typeof value === 'string' ) && ( libList . indexOf ( value ) < 0 ) ) {
82
- libList . push ( value ) ;
83
- }
84
- }
85
-
86
- return {
74
+ var output = [ ] ;
75
+ var libList = ( libraryPaths || [ ] ) . filter ( function isString ( value ) {
76
+ return ( typeof value === 'string' ) ;
77
+ } ) ;
78
+ return through . obj ( function ( file , encoding , done ) {
79
+ var stream = this ;
80
+
81
+ // setup parameters
82
+ var sourcePath = file . path . replace ( path . basename ( file . path ) , '' ) ;
83
+ var sourceName = path . basename ( file . path , path . extname ( file . path ) ) ;
84
+ var mapName = sourceName + '.css.map' ;
85
+ var sourceMapConsumer ;
87
86
88
87
/**
89
- * Infer library paths from the <code>base</code> paths in the input stream in preparation for <code>compile</code>.
90
- * Any arguments given are treated as explicit paths and are added as-is to the library list.
91
- * Outputs a stream of the same files.
92
- * @param {...string|Array } Any number of explicit library path strings or arrays thereof
93
- * @returns {stream.Through } A through stream that performs the operation of a gulp stream
88
+ * Push file contents to the output stream.
89
+ * @param {string } ext The extention for the file, including dot
90
+ * @param {string|object? } contents The contents for the file or fields to assign to it
91
+ * @return {vinyl.File } The file that has been pushed to the stream
94
92
*/
95
- libraries : function ( ) {
96
- addUnique ( Array . prototype . slice . call ( arguments ) ) ;
97
- return through . obj ( function ( file , encoding , done ) {
98
- addUnique ( file . base ) ;
99
- this . push ( file ) ;
100
- done ( ) ;
93
+ function pushResult ( ext , contents ) {
94
+ var pending = new gutil . File ( {
95
+ cwd : file . cwd ,
96
+ base : file . base ,
97
+ path : sourcePath + sourceName + ext ,
98
+ contents : ( typeof contents === 'string' ) ? new Buffer ( contents ) : null
101
99
} ) ;
102
- } ,
100
+ if ( typeof contents === 'object' ) {
101
+ for ( var key in contents ) {
102
+ pending [ key ] = contents [ key ] ;
103
+ }
104
+ }
105
+ stream . push ( pending ) ;
106
+ return pending ;
107
+ }
103
108
104
109
/**
105
- * Use <code>node-sass</code> to compile the files of the input stream.
106
- * Uses any library paths defined in <code>libraries</code>. Does not utilise the file content in the input stream.
107
- * Outputs a stream of compiled files and their source-maps, alternately.
108
- * @see https://github.com/sass/node-sass#outputstyle
109
- * @param {string? } outputStyle One of the <code>libsass</code> supported output styles
110
- * @returns {stream.Through } A through stream that performs the operation of a gulp stream
110
+ * Plugin for css rework that follows SASS transpilation
111
+ * @param {object } stylesheet AST for the CSS output from SASS
111
112
*/
112
- compile : function ( outputStyle ) {
113
- var output = [ ] ;
114
- return through . obj ( function ( file , encoding , done ) {
115
- var stream = this ;
116
-
117
- // setup parameters
118
- var sourcePath = file . path . replace ( path . basename ( file . path ) , '' ) ;
119
- var sourceName = path . basename ( file . path , path . extname ( file . path ) ) ;
120
- var mapName = sourceName + '.css.map' ;
121
- var stats = { } ;
122
- var sourceMapConsumer ;
123
-
124
- /**
125
- * Push file contents to the output stream.
126
- * @param {string } ext The extention for the file, including dot
127
- * @param {string|object? } contents The contents for the file or fields to assign to it
128
- * @return {vinyl.File } The file that has been pushed to the stream
129
- */
130
- function pushResult ( ext , contents ) {
131
- var pending = new gutil . File ( {
132
- cwd : file . cwd ,
133
- base : file . base ,
134
- path : sourcePath + sourceName + ext ,
135
- contents : ( typeof contents === 'string' ) ? new Buffer ( contents ) : null
136
- } ) ;
137
- if ( typeof contents === 'object' ) {
138
- for ( var key in contents ) {
139
- pending [ key ] = contents [ key ] ;
140
- }
141
- }
142
- stream . push ( pending ) ;
143
- return pending ;
144
- }
145
-
146
- /**
147
- * Plugin for css rework that follows SASS transpilation
148
- * @param {object } stylesheet AST for the CSS output from SASS
149
- */
150
- function reworkPlugin ( stylesheet ) {
113
+ function reworkPlugin ( stylesheet ) {
151
114
152
- // visit each node (selector) in the stylesheet recursively using the official utility method
153
- visit ( stylesheet , function ( declarations , node ) {
115
+ // visit each node (selector) in the stylesheet recursively using the official utility method
116
+ visit ( stylesheet , function ( declarations , node ) {
154
117
155
- // each node may have multiple declarations
156
- declarations . forEach ( function ( declaration ) {
118
+ // each node may have multiple declarations
119
+ declarations . forEach ( function ( declaration ) {
157
120
158
- // reverse the original source-map to find the original sass file
159
- var cssStart = declaration . position . start ;
160
- var sassStart = sourceMapConsumer . originalPositionFor ( {
161
- line : cssStart . line ,
162
- column : cssStart . column
163
- } ) ;
164
- var sassDir = path . dirname ( sassStart . source ) ;
165
-
166
- // allow multiple url() values in the declaration
167
- // the url will be every second value (i % 2)
168
- declaration . value = declaration . value
169
- . split ( / u r l \s * \( \s * [ ' " ] ? ( [ ^ ' " ? # ] * ) [ ^ ) ] * \) / g) // split by url statements
170
- . map ( function ( token , i ) {
171
- return ( i % 2 ) ? ( encodeRelativeURL ( sassDir , token ) || ( 'url(' + token + ')' ) ) : token ;
172
- } ) . join ( '' ) ;
173
- } ) ;
174
- } ) ;
175
- }
176
-
177
- /**
178
- * Handler for successful transpilation using node-sass.
179
- * @param {string } css Compiled css
180
- * @param {string } map The source-map for the compiled css
181
- */
182
- function successHandler ( css , map ) {
183
-
184
- // adjust sourcemap
185
- var source = minimatch . makeRe ( file . cwd ) . source
186
- . replace ( / ^ \^ | \$ $ / g, '' ) // match text anywhere on the line by removing line start/end
187
- . replace ( / \\ \/ / g, '[\\\\\\/]' ) + // detect any platform path format
188
- '|\\.\\.\\/' ; // relative paths are an artifact and must be removed
189
- var parsable = slash ( map . replace ( new RegExp ( source , 'g' ) , '' ) ) ;
190
- var sourceMap = JSON . parse ( parsable ) ;
191
- sourceMap . sources . forEach ( function ( value , i , array ) {
192
- array [ i ] = path . resolve ( value . replace ( / ^ \/ / , '' ) . replace ( / \b \/ + \b / g, '/' ) ) ; // ensure single slash absolute
193
- } ) ;
194
- sourceMapConsumer = new SourceMapConsumer ( sourceMap ) ;
195
-
196
- // embed sourcemap in css
197
- var cssWithMap = css . replace (
198
- / \/ \* # \s * s o u r c e M a p p i n g U R L = [ ^ * ] * \* \/ / m,
199
- '/*# sourceMappingURL=data:application/json;base64,' + convert . fromObject ( sourceMap ) . toBase64 ( ) + ' */'
200
- ) ;
201
-
202
- // rework css
203
- var reworked = rework ( cssWithMap , '' )
204
- . use ( reworkPlugin )
205
- . toString ( {
206
- sourcemap : true ,
207
- sourcemapAsObject : true
208
- } ) ;
209
-
210
- // adjust overall sourcemap
211
- delete reworked . map . file ;
212
- delete reworked . map . sourcesContent ;
213
- reworked . map . sources . forEach ( function ( value , i , array ) {
214
- array [ i ] = '/' + path . relative ( process . cwd ( ) , value ) ; // ensure root relative
121
+ // reverse the original source-map to find the original sass file
122
+ var cssStart = declaration . position . start ;
123
+ var sassStart = sourceMapConsumer . originalPositionFor ( {
124
+ line : cssStart . line ,
125
+ column : cssStart . column
215
126
} ) ;
127
+ var sassDir = path . dirname ( sassStart . source ) ;
128
+
129
+ // allow multiple url() values in the declaration
130
+ // the url will be every second value (i % 2)
131
+ declaration . value = declaration . value
132
+ . split ( / u r l \s * \( \s * [ ' " ] ? ( [ ^ ' " ? # ] * ) [ ^ ) ] * \) / g) // split by url statements
133
+ . map ( function ( token , i ) {
134
+ return ( i % 2 ) ? ( encodeRelativeURL ( sassDir , token ) || ( 'url(' + token + ')' ) ) : token ;
135
+ } ) . join ( '' ) ;
136
+ } ) ;
137
+ } ) ;
138
+ }
216
139
217
- // write stream output
218
- pushResult ( '.css' , reworked . code + '\n/*# sourceMappingURL=' + mapName + ' */' ) ;
219
- pushResult ( '.css.map' , JSON . stringify ( reworked . map , null , ' ' ) ) ;
220
- done ( ) ;
221
- }
140
+ /**
141
+ * Handler for successful transpilation using node-sass.
142
+ * @param {string } css Compiled css
143
+ * @param {string } map The source-map for the compiled css
144
+ */
145
+ function successHandler ( css , map ) {
146
+
147
+ // adjust sourcemap
148
+ var source = minimatch . makeRe ( file . cwd ) . source
149
+ . replace ( / ^ \^ | \$ $ / g, '' ) // match text anywhere on the line by removing line start/end
150
+ . replace ( / \\ \/ / g, '[\\\\\\/]' ) + // detect any platform path format
151
+ '|\\.\\.\\/' ; // relative paths are an artifact and must be removed
152
+ var parsable = slash ( map . replace ( new RegExp ( source , 'g' ) , '' ) ) ;
153
+ var sourceMap = JSON . parse ( parsable ) ;
154
+ sourceMap . sources . forEach ( function ( value , i , array ) {
155
+ array [ i ] = path . resolve ( value . replace ( / ^ \/ / , '' ) . replace ( / \b \/ + \b / g, '/' ) ) ; // ensure single slash absolute
156
+ } ) ;
157
+ sourceMapConsumer = new SourceMapConsumer ( sourceMap ) ;
158
+
159
+ // embed sourcemap in css
160
+ var cssWithMap = css . replace (
161
+ / \/ \* # \s * s o u r c e M a p p i n g U R L = [ ^ * ] * \* \/ / m,
162
+ '/*# sourceMappingURL=data:application/json;base64,' + convert . fromObject ( sourceMap ) . toBase64 ( ) + ' */'
163
+ ) ;
164
+
165
+ // rework css
166
+ var reworked = rework ( cssWithMap , '' )
167
+ . use ( reworkPlugin )
168
+ . toString ( {
169
+ sourcemap : true ,
170
+ sourcemapAsObject : true
171
+ } ) ;
222
172
223
- /**
224
- * Handler for error in node-sass.
225
- * @param {string } error The error text from node-sass
226
- */
227
- function errorHandler ( error ) {
228
- var analysis = / ( .* ) \: ( \d + ) \: \s * e r r o r \: \s * ( .* ) / . exec ( error ) ;
229
- var message = analysis ?
230
- ( path . resolve ( analysis [ 1 ] ) + ':' + analysis [ 2 ] + ':0: ' + analysis [ 3 ] + '\n' ) :
231
- ( 'TODO parse this error\n' + error + '\n' ) ;
232
- if ( output . indexOf ( message ) < 0 ) {
233
- output . push ( message ) ;
234
- }
235
- done ( ) ;
236
- }
173
+ // adjust overall sourcemap
174
+ delete reworked . map . file ;
175
+ delete reworked . map . sourcesContent ;
176
+ reworked . map . sources . forEach ( function ( value , i , array ) {
177
+ array [ i ] = '/' + slash ( path . relative ( process . cwd ( ) , value ) ) ; // ensure root relative
178
+ } ) ;
237
179
238
- /**
239
- * Perform the sass render with the given <code>sourceMap</code>, <code>error</code>, and <code>success</code>
240
- * parameters.
241
- * @param {string|boolean } map The source map filename or <code>false</code> for none
242
- * @param {function ({string}) } error Handler for error
243
- * @param {function ({string}, {string}) } success Handler for success
244
- */
245
- function render ( map , error , success ) {
246
- sass . render ( {
247
- file : file . path ,
248
- success : success ,
249
- error : error ,
250
- includePaths : libList ,
251
- outputStyle : outputStyle || 'compressed' ,
252
- stats : stats ,
253
- sourceMap : map
254
- } ) ;
255
- }
180
+ // write stream output
181
+ pushResult ( '.css' , reworked . code + '\n/*# sourceMappingURL=' + mapName + ' */' ) ;
182
+ pushResult ( '.css.map' , JSON . stringify ( reworked . map , null , ' ' ) ) ;
183
+ done ( ) ;
184
+ }
256
185
257
- // run first without sourcemap as this can cause process exit where errors exist
258
- render ( false , errorHandler , function ( ) {
259
- render ( mapName , errorHandler , successHandler ) ;
260
- } ) ;
186
+ /**
187
+ * Handler for error in node-sass.
188
+ * @param {string } error The error text from node-sass
189
+ */
190
+ function errorHandler ( error ) {
191
+ var analysis = / ( .* ) \: ( \d + ) \: \s * e r r o r \: \s * ( .* ) / . exec ( error ) ;
192
+ var resolved = path . resolve ( analysis [ 1 ] ) ;
193
+ var filename = [ '.scss' , '.css' ]
194
+ . map ( function ( ext ) {
195
+ return resolved + ext ;
196
+ } )
197
+ . filter ( function ( fullname ) {
198
+ return fs . existsSync ( fullname ) ;
199
+ } )
200
+ . pop ( ) ;
201
+ var message = analysis ?
202
+ ( ( filename || resolved ) + ':' + analysis [ 2 ] + ':0: ' + analysis [ 3 ] + '\n' ) :
203
+ ( 'TODO parse this error\n' + error + '\n' ) ;
204
+ if ( output . indexOf ( message ) < 0 ) {
205
+ output . push ( message ) ;
206
+ }
207
+ done ( ) ;
208
+ }
261
209
262
- // display the output buffer with padding before and after and between each item
263
- } , function ( done ) {
264
- if ( output . length ) {
265
- var width = Number ( bannerWidth ) || 0 ;
266
- var hr = new Array ( width + 1 ) ; // this is a good trick to repeat a character N times
267
- var start = ( width > 0 ) ? ( hr . join ( '\u25BC' ) + '\n' ) : '' ;
268
- var stop = ( width > 0 ) ? ( hr . join ( '\u25B2' ) + '\n' ) : '' ;
269
- process . stdout . write ( start + '\n' + output . join ( '\n' ) + '\n' + stop ) ;
270
- }
271
- done ( ) ;
272
- } ) ;
210
+ // perform the sass render
211
+ sass . render ( {
212
+ file : file . path ,
213
+ data : file . contents . toString ( ) ,
214
+ success : successHandler ,
215
+ error : errorHandler ,
216
+ includePaths : libList ,
217
+ outputStyle : 'compressed' ,
218
+ stats : { } ,
219
+ sourceMap : mapName
220
+ } ) ;
221
+
222
+ // display the output buffer with padding before and after and between each item
223
+ } , function ( done ) {
224
+ if ( output . length ) {
225
+ var width = Number ( bannerWidth ) || 0 ;
226
+ var hr = new Array ( width + 1 ) ; // this is a good trick to repeat a character N times
227
+ var start = ( width > 0 ) ? ( hr . join ( '\u25BC' ) + '\n' ) : '' ;
228
+ var stop = ( width > 0 ) ? ( hr . join ( '\u25B2' ) + '\n' ) : '' ;
229
+ process . stdout . write ( start + '\n' + output . join ( '\n' ) + '\n' + stop ) ;
273
230
}
274
- } ;
275
- } ;
231
+ done ( ) ;
232
+ } ) ;
233
+ } ;
0 commit comments