1+ /// <reference path="typings/node/node.d.ts" />
12"use strict" ;
23var gutil = require ( "gulp-util" ) ;
3- var fs = require ( "fs" ) ;
4- var path = require ( "path" ) ;
5- var File = gutil . File ;
6- var PluginError = gutil . PluginError ;
74var through = require ( "through2" ) ;
85var format = require ( "util" ) . format ;
9- var trim = require ( "useful-functions.js" ) . trim ;
10- var getExtension = require ( "useful-functions.js" ) . getExtension ;
11- var url = require ( "url" ) ;
12- var EventEmitter = require ( "events" ) . EventEmitter ;
13-
14- var PLUGIN_NAME = "gulp-cssimport" ;
15-
16- function fail ( ) {
17- var args = Array . prototype . slice . call ( arguments ) ;
18- var message = format . apply ( null , args ) ;
19- gutil . log ( PLUGIN_NAME , gutil . colors . red ( "✘ " + message ) ) ;
20- }
21-
22- function isUrl ( s ) {
23- var regexp = / ( f t p | h t t p | h t t p s ) : \/ \/ ( \w + : { 0 , 1 } \w * @ ) ? ( \S + ) ( : [ 0 - 9 ] + ) ? ( \/ | \/ ( [ \w # ! : . ? + = & % @ ! \- \/ ] ) ) ? / ;
24- return regexp . test ( s ) ;
25- }
6+ var path = require ( "path" ) ;
7+ var deepExtend = require ( 'deep-extend' ) ;
8+ var isMatch = require ( "./helper" ) . isMatch ;
9+ var resolvePath = require ( "./helper" ) . resolvePath ;
10+ var trim = require ( "./helper" ) . trim ;
11+ var PathObject = require ( "./pathObject" ) ;
12+ var Chunk = require ( "./chunk" ) ;
2613
2714var defaults = {
2815 extensions : null ,
29- filter : null
16+ filter : null ,
17+ matchPattern : null ,
18+ limit : 5000
3019} ;
20+ Object . defineProperty ( defaults , "directory" , {
21+ enumerable : true ,
22+ get : function ( ) {
23+ return process . cwd ( ) ;
24+ }
25+ } ) ;
3126
32- module . exports = function ( options ) {
27+ module . exports = function cssImport ( options ) {
3328
34- options = options || { } ;
35- var parsedFiles = { } ;
36- var buffer = [ ] ;
29+ options = deepExtend ( { } , defaults , options || { } ) ;
3730
38- var extensions = options . extensions ;
39- if ( extensions ) {
40- if ( ! Array . isArray ( extensions ) ) {
41- extensions = extensions . toString ( ) . split ( "," ) . map ( function ( x ) {
42- return x . trim ( ) ;
31+ if ( options . extensions && ! Array . isArray ( options . extensions ) ) {
32+ options . extensions = options . extensions . toString ( ) . split ( "," ) . map ( function ( x ) {
33+ return x . trim ( ) ;
34+ } ) ;
35+ }
36+
37+ var stream ;
38+ var cssCount = 0 ;
39+
40+ function fileContents ( data , encoding , callback ) {
41+ if ( ! stream ) {
42+ stream = this ;
43+ }
44+ var chunk = Chunk . create ( data , { directory : options . directory } ) ;
45+ //console.log("chunk.isVinyl", chunk.isVinyl);
46+ // https://github.com/kevva/import-regex/
47+ var regex = '(?:@import)(?:\\s)(?:url)?(?:(?:(?:\\()(["\'])?(?:[^"\')]+)\\1(?:\\))|(["\'])(?:.+)\\2)(?:[A-Z\\s])*)+(?:;)' ;
48+ var importRe = new RegExp ( regex , "gi" ) ;
49+ var match ;
50+ var fileArray = [ ] ;
51+ var lastPos = 0 ;
52+ var count = 0 ;
53+ var contents = chunk . getContents ( ) ;
54+ while ( ( match = importRe . exec ( contents ) ) !== null ) {
55+ var match2 = / @ i m p o r t \s + (?: u r l \( ) ? ( .+ (? = [ ' " \) ] ) ) (?: \) ) ? .* / ig. exec ( match [ 0 ] ) ;
56+ var filePath = trim ( match2 [ 1 ] , "'\"" ) ;
57+ //console.log(filePath, isMatch(filePath, options));
58+ if ( ! isMatch ( filePath , options ) ) {
59+ continue ;
60+ }
61+ fileArray [ fileArray . length ] = contents . slice ( lastPos , match . index ) ;
62+ var index = fileArray . length ;
63+ var pathObject = new PathObject ( {
64+ index : index ,
65+ path : filePath ,
66+ directory : chunk . getDirectory ( )
4367 } ) ;
68+ fileArray [ index ] = format ( "importing file %j" , pathObject ) ;
69+ lastPos = importRe . lastIndex ;
70+ // Start resolving.
71+ // console.log("Start resolving", cssCount++, pathObject);
72+ if ( ++ cssCount > options . limit ) {
73+ stream . emit ( "error" , new Error ( "Exceed limit. Recursive include?" ) ) ;
74+ return ;
75+ }
76+ count ++ ;
77+ resolvePath ( pathObject , onResolvePath ) ;
4478 }
45- }
46-
47- function parseLineFactory ( filePath , callback ) {
48- var fileDirectory = path . dirname ( filePath ) ;
49- return function parseLine ( line ) {
5079
51- var args = Array . prototype . slice . call ( arguments ) ;
52- line = args . shift ( ) ;
53-
54- var match = line . match ( / @ i m p o r t \s + (?: u r l \( ) ? ( .+ (? = [ ' " \) ] ) ) (?: \) ) ? .* / i) ;
55- var importFile = match && trim ( match [ 1 ] , "'\"" ) ;
56-
57- start:
58- if ( importFile ) {
59- // Check extensions.
60- if ( extensions ) {
61- for ( var k = 0 ; k < extensions . length ; k ++ ) {
62- var extension = extensions [ k ] ;
63- var isInverse = extension . charAt ( 0 ) === "!" ;
64- if ( isInverse ) {
65- extension = extension . slice ( 1 ) ;
66- }
67- var fileExt = getExtension ( importFile ) ;
68- if ( isInverse && fileExt === extension ) { // !sass , sass === css
69- break start;
70- } else if ( ! isInverse && fileExt !== extension ) {
71- break start;
72- }
73- }
74- }
75-
76- if ( options . filter instanceof RegExp ) {
77- var result = options . filter . test ( importFile ) ;
78- if ( ! result ) {
79- break start;
80- }
81- }
82-
83- if ( isUrl ( importFile ) ) {
84-
85- var components = url . parse ( importFile ) || { } ;
86- var protocol = trim ( components . protocol , ":" ) ;
87- if ( [ "http" , "https" ] . indexOf ( protocol ) === - 1 ) {
88- fail ( "Cannot process file %j, unknown protocol %j." , importFile , protocol ) ;
89- break start;
90- }
91- var http = require ( protocol ) ;
92- http . get ( importFile , function ( response ) {
93- var body = "" ;
94- response . on ( "data" , function ( chunk ) {
95- body += chunk ;
96- } ) ;
97- response . on ( "end" , function ( ) {
98- callback . apply ( null , [ null , body ] . concat ( args ) ) ;
99- } ) ;
100- response . on ( "error" , function ( error ) {
101- callback . apply ( null , [ error ] ) ;
102- } ) ;
103- } ) ;
104- return ;
80+ function onResolvePath ( err , data , pathObject ) {
81+ if ( err ) {
82+ console . trace ( err ) ;
83+ throw err ;
84+ // todo: Make it more realiable.
85+ // callback(err);
86+ // return;
87+ }
88+ fileArray [ pathObject . index ] = data ;
89+ count -- ;
90+ if ( count === 0 ) {
91+ var state = { directory : pathObject . directory } ;
92+ if ( ! pathObject . isUrl ( ) ) {
93+ state . directory = pathObject . getPathDirectory ( ) ;
10594 }
106-
107- var importFilePath = path . normalize ( path . join ( fileDirectory , importFile ) ) ;
108- fs . readFile ( importFilePath , function readFileEnd ( error , buffer ) {
109- if ( error ) {
110- callback . apply ( null , [ error ] ) ;
111- return ;
112- }
113- line = buffer . toString ( ) ;
114- parsedFiles [ importFilePath ] = true ;
115- callback . apply ( null , [ null , line ] . concat ( args ) ) ;
116- } ) ;
117- return ;
95+ fileReady ( state ) ;
11896 }
119-
120- callback . apply ( null , [ null , line ] . concat ( args ) ) ;
121- } ;
122- }
123-
124- function fileContents ( file , encoding , callback ) {
125-
126- if ( file . path === null ) {
127- throw new Error ( "File.path is null." ) ;
12897 }
129-
130- if ( ! file . isBuffer ( ) ) {
131- throw new PluginError ( PLUGIN_NAME , "Only buffer is supported." ) ;
98+ // No import statements.
99+ if ( count === 0 ) {
100+ fileReady ( { done : true } ) ;
101+ return ;
132102 }
133-
134- // TODO: Bug... Cannot process minified files.
135- var contents = file . contents . toString ( ) ;
136- var lines = contents . split ( "\n" ) ;
137- var linesCount = lines . length ;
138- var fileParse = new EventEmitter ( ) ;
139-
140- fileParse . on ( "ready" , function ( newLines ) {
141- var newContents = newLines . join ( "\n" ) ;
142- if ( contents !== newContents ) {
143- file = new File ( {
144- cwd : file . cwd ,
145- base : file . base ,
146- path : file . path ,
147- contents : new Buffer ( newContents )
148- } ) ;
103+ // Adding trailing piece.
104+ fileArray [ fileArray . length ] = contents . slice ( lastPos ) ;
105+
106+ // todo: optimize do not scan all contents.
107+ function fileReady ( state ) {
108+ //console.log("fileReady.state", state);
109+ state = state || { } ;
110+ if ( fileArray . length > 0 ) {
111+ contents = fileArray . join ( "" ) ;
149112 }
150- buffer . push ( file ) ;
151- callback ( ) ;
152- } ) ;
153-
154- // lines.forEach(parseLineFactory(file.path, parseLineEnd));
155- lines . forEach ( function ( ) {
156- var args = Array . prototype . slice . call ( arguments ) ;
157- parseLineFactory ( file . path , parseFileEnd ) . apply ( this , args ) ;
158- } ) ;
159-
160- function parseFileEnd ( error , data , index , array ) {
161- if ( error ) {
162- fileParse . emit ( "error" , error ) ;
113+ if ( ! state . done ) {
114+ //console.log("chunk.isVinyl", chunk.isVinyl);
115+ var nextChunk ;
116+ if ( chunk . isVinyl ) {
117+ chunk . vinyl . contents = new Buffer ( contents ) ;
118+ chunk . vinyl . base = state . directory ;
119+ nextChunk = chunk . vinyl ;
120+ } else {
121+ nextChunk = Chunk . create ( {
122+ contents : contents ,
123+ directory : state . directory
124+ } ) ;
125+ }
126+ //console.log("state", state);
127+ fileContents ( nextChunk , null , callback ) ;
163128 return ;
164129 }
165- array [ index ] = data ;
166- if ( -- linesCount === 0 ) {
167- fileParse . emit ( "ready" , array ) ;
130+ //console.log("chunk.isVinyl", chunk.isVinyl);
131+ if ( chunk . isVinyl ) {
132+ contents = new gutil . File ( {
133+ cwd : data . cwd ,
134+ base : data . base ,
135+ path : data . path ,
136+ contents : new Buffer ( contents )
137+ } ) ;
168138 }
139+ callback ( null , contents ) ;
169140 }
170- }
171141
172- function endStream ( ) {
173- for ( var i = 0 , count = buffer . length ; i < count ; i ++ ) {
174- var file = buffer [ i ] ;
175- var filePath = path . normalize ( file . path ) ;
176- if ( parsedFiles [ filePath ] === true ) {
177- continue ;
178- }
179- this . push ( file ) ;
180- }
181- this . emit ( "end" ) ;
182142 }
183143
184- return through . obj ( fileContents , endStream ) ;
144+ return through . obj ( fileContents ) ;
185145} ;
0 commit comments