Skip to content

Commit 86353fc

Browse files
committed
Merge branch 'dev'
2 parents 7a3c522 + d5c6760 commit 86353fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+2482
-492
lines changed

.npmignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
/node_modules/
2-
__*
32
~*

chunk.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
function Chunk(data) {
2+
if (data.vinyl) {
3+
this.vinyl = data.vinyl;
4+
return;
5+
}
6+
if (data.directory) this.directory = data.directory;
7+
if (data.contents) this.contents = data.contents;
8+
}
9+
10+
Object.defineProperty(Chunk.prototype, "isVinyl", {
11+
get: function () {
12+
return Boolean(this.vinyl);
13+
}
14+
});
15+
16+
Chunk.create = function (data, options) {
17+
options = options || {};
18+
var result = null;
19+
if (data == null) {
20+
throw "Passed null or undefined argument.";
21+
}
22+
if (data.isBuffer && data.isBuffer()) {
23+
result = new Chunk({
24+
vinyl: data
25+
});
26+
} else if (data instanceof Buffer) {
27+
result = new Chunk({
28+
contents: data.toString()
29+
});
30+
} else if (typeof data === "string") {
31+
result = new Chunk({ contents: data });
32+
} else if (typeof data === "object" && data.constructor === Object) {
33+
result = new Chunk(data);
34+
} else if (data instanceof Chunk) {
35+
result = data;
36+
} else {
37+
throw "Passed unknown object.";
38+
}
39+
// Add additional properties.
40+
if (options.directory && result.getDirectory() == null) result.directory = options.directory;
41+
return result;
42+
};
43+
44+
Chunk.prototype.getDirectory = function () {
45+
if (this.vinyl) {
46+
return this.vinyl.base;
47+
}
48+
return this.directory;
49+
};
50+
51+
Chunk.prototype.getContents = function () {
52+
if (this.contents) {
53+
return this.contents;
54+
}
55+
if (this.vinyl) {
56+
return this.vinyl.contents.toString();
57+
}
58+
throw "Uknown type.";
59+
};
60+
61+
module.exports = Chunk;

gulp-cssimport.js

Lines changed: 118 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,145 @@
1+
/// <reference path="typings/node/node.d.ts" />
12
"use strict";
23
var gutil = require("gulp-util");
3-
var fs = require("fs");
4-
var path = require("path");
5-
var File = gutil.File;
6-
var PluginError = gutil.PluginError;
74
var through = require("through2");
85
var 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 = /(ftp|http|https):\/\/(\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

2714
var 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 = /@import\s+(?:url\()?(.+(?=['"\)]))(?:\))?.*/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(/@import\s+(?:url\()?(.+(?=['"\)]))(?:\))?.*/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

Comments
 (0)