@@ -11,176 +11,183 @@ var combined = require('combined-stream'),
1111 fs = require ( 'fs' ) ;
1212
1313/**
14- * Retrieve version details of bower files as a stream
15- * @param {boolean } useComment Determines whether the output is wrapped in a comment
16- * @param {boolean } includeDev Determines whether dev dependencies are included
17- * @returns {stream.Readable } A readable stream containing only the manifest file
14+ * Create a gulp.src() method that emits the bower main files and optional version manifest.
15+ * @param {{pattern:string, base:boolean, manifest:boolean, dev:boolean} } Options
1816 */
19- function json ( includeDev ) {
20- function read ( filename ) {
21- try {
22- return fs . existsSync ( filename ) && require ( path . resolve ( filename ) ) ;
23- } catch ( error ) {
24- gutil . log ( 'Error parsing bower.json at ' + filename ) ;
25- }
26- }
27- var bower = read ( 'bower.json' ) ;
28- var deps = bower ? defaults ( bower . dependencies , ( includeDev && bower . devDependencies ) ) : { } ;
29- var result = { } ;
30- for ( var key in deps ) {
31- result [ key ] = read ( path . join ( 'bower_components' , key , 'bower.json' ) ) ;
32- }
33- return result ;
34- }
35-
36- /**
37- * Retrieve version details of bower files as a stream
38- * @param {boolean } useComment Determines whether the output is wrapped in a comment
39- * @param {boolean } includeDev Determines whether dev dependencies are included
40- * @returns {stream.Readable } A readable stream containing only the manifest file
41- */
42- function manifest ( useComment , includeDev ) {
43- var bower = json ( includeDev ) ;
44- var versions = { } ;
45- for ( var key in bower ) {
46- versions [ key ] = bower [ key ] . version ;
47- }
48- var text = JSON . stringify ( versions , null , 2 ) ;
49- var commented = ( useComment ) ? [ '/*' , text , '*/' ] . join ( '\n' ) : text ;
50- var cwd = process . cwd ( ) ;
51- return spigot ( { objectMode : true } , [
52- new gutil . File ( {
53- cwd : cwd ,
54- base : cwd ,
55- path : path . join ( cwd , 'manifest.json' ) ,
56- contents : new Buffer ( commented )
57- } )
58- ] ) ;
59- }
60-
61- module . exports = function ( ) {
62- var before = [ ] ;
63- var after = [ ] ;
17+ module . exports = function create ( options ) {
18+ defaults ( options , {
19+ pattern : '*' ,
20+ base : false ,
21+ manifest : false ,
22+ dev : false
23+ } ) ;
6424
6525 /**
66- * @param { string|RegExp } pattern Matches the extension of bower files
67- * @param {object? } options Options for <code>gulp.src()</code> stream
26+ * Get a gulp.src() stream for the bower main files.
27+ * @param {object } options Options for <code>gulp.src()</code> stream
6828 */
69- function src ( pattern , options ) {
70- options = options || { } ;
71-
72- function testExtension ( filename ) {
73- var ext = path . extname ( filename ) . slice ( 1 ) ;
74- return (
75- ( ( typeof pattern === 'string' ) && ( pattern . split ( '|' ) . indexOf ( ext ) >= 0 ) ) ||
76- ( ( typeof pattern === 'object' ) && ( 'test' in pattern ) && pattern . test ( ext ) ) ||
77- ( pattern === '*' )
78- ) ;
79- }
80-
81- function toAbsolute ( relative ) {
82- return path . resolve ( path . join ( 'bower_components' , packageName , '' ) , relative ) ;
83- }
84-
85- function greatestCommonBase ( absolute ) {
86- if ( base ) {
87- var i = 0 ;
88- while ( base [ i ] === absolute [ i ] ) {
89- i ++ ;
90- }
91- base = base . slice ( 0 , i ) ;
92- } else if ( options . base === true ) {
93- base = path . dirname ( absolute ) ;
94- }
95- }
29+ return function src ( gulpSrcOptions ) {
9630
9731 // create result list
98- // the parsed content will be sorted by extension
99- var parsed = json ( options . dev ) ;
100- var bowerPackages = [ ] ;
32+ var parsed = json ( options . dev , options . context ) ,
33+ manifestFile = ! ! options . manifest && manifest ( ( options . manifest === 'comment' ) , options . dev ) ,
34+ bowerPackages = [ ] ;
10135 for ( var packageName in parsed ) {
102- var config = parsed [ packageName ] ;
103- var bowerPackage = [ ]
104- . concat ( config . main )
105- . filter ( Boolean )
106- . map ( toAbsolute )
107- . filter ( testExtension ) ;
108- if ( bowerPackage . length ) {
109- var base = null ;
110- bowerPackage . forEach ( greatestCommonBase ) ;
111- bowerPackage . options = defaults ( { base : base || options . base } , options ) ; // use our base over that given
112- bowerPackage . name = packageName ;
113- bowerPackages . push ( bowerPackage ) ;
36+ var config = parsed [ packageName ] ,
37+ list = [ ]
38+ . concat ( config . main )
39+ . filter ( Boolean )
40+ . map ( toAbsoluteForPackage ( packageName ) )
41+ . filter ( testExtension ) ;
42+ if ( list . length ) {
43+ var base = list . reduce ( greatestCommonBase , null ) ;
44+ bowerPackages . push ( {
45+ name : packageName ,
46+ options : defaults ( { base : base || options . base } , gulpSrcOptions ) , // use our base over that given
47+ list : list
48+ } ) ;
11449 }
11550 }
116- var value = before . concat ( bowerPackages ) . concat ( after ) ;
117- var prepend = options . manifest && manifest ( options . manifest === 'comment' , options . dev ) ;
11851
119- // represent list as a readable stream
52+ // represent list as a readable stream, even if empty
12053 var stream ;
121- if ( ! value . length ) {
12254
123- // degenerate stream
124- stream = spigot ( { objectMode : true } , [ ] ) ;
125- stream . _read = function ( ) { } ;
55+ // degenerate stream
56+ if ( ! bowerPackages . length ) {
57+ stream = spigot ( { objectMode : true } , [ ] ) ;
58+ stream . _read = function ( ) {
59+ } ;
12660 stream . push ( null ) ;
127-
128- } else {
129-
130- // combination of source streams with specific base path for each package
61+ }
62+ // combination of source streams with specific base path for each package
63+ else {
13164 stream = combined . create ( ) ;
132- if ( prepend ) {
133- stream . append ( prepend ) ;
65+ if ( manifestFile ) {
66+ stream . append ( manifestFile ) ;
13467 }
135- value . forEach ( function ( bowerPackage ) {
136- var src = gulp . src ( bowerPackage . concat ( ) , bowerPackage . options )
137- . pipe ( through . obj ( function ( file , encoding , done ) {
138- if ( ( options . base === true ) && ( bowerPackage . name ) ) {
139- file . path = path . join ( file . base , bowerPackage . name , file . relative ) ;
140- }
141- this . push ( file ) ;
142- done ( ) ;
143- } ) ) ;
144- stream . append ( src ) ;
145- } ) ;
68+ bowerPackages . reduce ( eachPackage , stream ) ;
14669 }
14770
14871 // add the list to the stream as a sidecar
149- stream . list = flatten ( value ) ;
72+ stream . list = flatten ( bowerPackages ) ;
15073
15174 // complete
15275 return stream ;
153- }
76+ } ;
15477
155- function add ( array ) {
156- return function ( ) {
157- Array . prototype . slice . call ( arguments ) . forEach ( function ( arg ) {
158- if ( ( typeof arg === 'string' ) && fs . existsSync ( arg ) ) {
159- array . push ( arg ) ;
160- }
161- } ) ;
162- return self ;
163- } ;
78+ function greatestCommonBase ( candidate , absolute ) {
79+ if ( candidate ) {
80+ var i = 0 ;
81+ while ( candidate [ i ] === absolute [ i ] ) {
82+ i ++ ;
83+ }
84+ return candidate . slice ( 0 , i ) ;
85+ } else if ( options . base === true ) {
86+ return path . dirname ( absolute ) ;
87+ } else {
88+ return null ;
89+ }
16490 }
16591
166- // complete
167- var self = {
92+ function eachPackage ( stream , bowerPackage ) {
93+ return stream . append (
94+ gulp . src ( bowerPackage . list , bowerPackage . options )
95+ . pipe ( through . obj ( transform ) )
96+ ) ;
16897
169- /**
170- * Append any number of additional file paths to be used in any file lists that follow
171- */
172- prepend : add ( before ) ,
98+ function transform ( file , encoding , done ) {
99+ /* jshint validthis:true */
100+ if ( ( options . base === true ) && ( bowerPackage . name ) ) {
101+ file . path = path . join ( file . base , bowerPackage . name , file . relative ) ;
102+ }
103+ this . push ( file ) ;
104+ done ( ) ;
105+ }
106+ }
173107
174- /**
175- * Append any number of additional file paths to be used in any file lists that follow
176- */
177- append : add ( after ) ,
108+ function testExtension ( filename ) {
109+ var pattern = options . pattern ,
110+ ext = path . extname ( filename ) . slice ( 1 ) ;
111+ return (
112+ ( ( typeof pattern === 'string' ) && ( pattern . split ( '|' ) . indexOf ( ext ) >= 0 ) ) ||
113+ ( ( typeof pattern === 'object' ) && ( 'test' in pattern ) && pattern . test ( ext ) ) ||
114+ ( pattern === '*' )
115+ ) ;
116+ }
117+ } ;
178118
179- /**
180- * Retrieve all or some bower files as listed in the package <code>main</code> parameter
181- * @param {object } Options for the stream
182- */
183- src : src
119+ function toAbsoluteForPackage ( packageName ) {
120+ return function toAbsolute ( relative ) {
121+ return path . resolve ( path . join ( 'bower_components' , packageName , '' ) , relative ) ;
184122 } ;
185- return self ;
186- } ;
123+ }
124+
125+ /**
126+ * Retrieve version details of bower files as a stream.
127+ * @param {boolean } includeDev Determines whether dev dependencies are included
128+ * @param {string } context Key used to determine overrides
129+ * @returns {stream.Readable } A readable stream containing only the manifest file
130+ */
131+ function json ( includeDev , context ) {
132+ var root = read ( 'bower.json' ) ,
133+ packages = reduceJsonToPackages ( root , includeDev ) ;
134+ return processOverrides ( packages , root . overrides ) ;
135+
136+ function read ( filename ) {
137+ try {
138+ return fs . existsSync ( filename ) && require ( path . resolve ( filename ) ) ;
139+ } catch ( error ) {
140+ gutil . log ( 'Error parsing bower.json at ' + filename ) ;
141+ }
142+ }
143+
144+ function reduceJsonToPackages ( json , includeDev ) {
145+ var isValid = ( json && ( typeof json === 'object' ) ) ,
146+ dependencies = isValid && defaults ( json . dependencies , includeDev && json . devDependencies ) || { } ;
147+ return Object . keys ( dependencies )
148+ . reduce ( reduceDependencies , { } ) ;
149+
150+ function reduceDependencies ( result , packageName ) {
151+ var contents = read ( path . join ( 'bower_components' , packageName , 'bower.json' ) ) ;
152+ defaults ( result , reduceJsonToPackages ( contents , false ) ) ; // current items' dependencies precede the current item
153+ result [ packageName ] = contents ;
154+ return result ;
155+ }
156+ }
157+
158+ function processOverrides ( packages , overrides ) {
159+ return overrides ? Object . keys ( packages ) . reduce ( reducePackages , { } ) : packages ;
160+
161+ function reducePackages ( result , packageName ) {
162+ var item = packages [ packageName ] ,
163+ override = context && overrides [ packageName + '.' + context ] || overrides [ packageName ] ;
164+ result [ packageName ] = defaults ( { } , override , item ) ;
165+ return result ;
166+ }
167+ }
168+ }
169+
170+ /**
171+ * Retrieve version details of bower files as a stream.
172+ * @param {boolean } useComment Determines whether the output is wrapped in a comment
173+ * @param {boolean } includeDev Determines whether dev dependencies are included
174+ * @returns {stream.Readable } A readable stream containing only the manifest file
175+ */
176+ function manifest ( useComment , includeDev ) {
177+ var bower = json ( includeDev ) ,
178+ versions = { } ;
179+ for ( var key in bower ) {
180+ versions [ key ] = bower [ key ] . version ;
181+ }
182+ var text = JSON . stringify ( versions , null , 2 ) ,
183+ commented = ( useComment ) ? [ '/*' , text , '*/' ] . join ( '\n' ) : text ,
184+ cwd = process . cwd ( ) ;
185+ return spigot ( { objectMode : true } , [
186+ new gutil . File ( {
187+ cwd : cwd ,
188+ base : cwd ,
189+ path : path . join ( cwd , 'manifest.json' ) ,
190+ contents : new Buffer ( commented )
191+ } )
192+ ] ) ;
193+ }
0 commit comments