2015-02-27 00:25:57 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var events = require('events');
|
|
|
|
var fetch = require('fetch');
|
|
|
|
var fs = require('fs-extra');
|
|
|
|
var factory = require('./factory');
|
|
|
|
var config = require('./asset-assembler-config');
|
|
|
|
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
function buildEnd(assembler, targetFile, resultUriList, callback) {
|
|
|
|
fs.appendFile(targetFile, assembler.config.assetFooter, function (err) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
var buildTime = new Date().getTime() - assembler.startTime,
|
2015-02-27 00:25:57 +00:00
|
|
|
buildResultSize = resultUriList.length,
|
|
|
|
targetStats = fs.statSync(targetFile),
|
|
|
|
targetSize = targetStats.size;
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
if (assembler.config.linkTargetSingleResult) {
|
|
|
|
resultUriList = [assembler.config.linkTarget];
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
2015-02-27 20:50:19 +00:00
|
|
|
assembler.emit('file-write-post',targetFile);
|
|
|
|
assembler.emit('log','debug','target file: '+targetFile);
|
|
|
|
assembler.emit('log','debug','target size: '+targetSize);
|
|
|
|
assembler.emit('log','info','build result size: '+resultUriList.length+' from: '+buildResultSize);
|
|
|
|
assembler.emit('log','info','build done in: '+buildTime+' ms.');
|
|
|
|
assembler.emit('result',resultUriList);
|
|
|
|
assembler.emit('end');
|
2015-02-27 00:25:57 +00:00
|
|
|
callback();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
function downloadFile(assembler, remoteUrl, localFile, callback) {
|
|
|
|
assembler.emit('file-download-pre',remoteUrl);
|
|
|
|
assembler.emit('log','debug','downloadFile: '+remoteUrl);
|
2015-02-27 00:25:57 +00:00
|
|
|
fs.ensureFile(localFile, function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
var stream = new fetch.FetchStream(remoteUrl);
|
2015-02-27 20:50:19 +00:00
|
|
|
var streamCallbackErrorOnEnd = null;
|
|
|
|
var streamCallback = function(err) {
|
|
|
|
if (err) {
|
|
|
|
streamCallbackErrorOnEnd = err;
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
callback(streamCallbackErrorOnEnd);
|
|
|
|
}
|
|
|
|
};
|
2015-02-27 00:25:57 +00:00
|
|
|
stream.on('error', function(err) {
|
2015-02-27 20:50:19 +00:00
|
|
|
callback(err); // check !!
|
|
|
|
//streamCallback(err);
|
|
|
|
});
|
|
|
|
stream.on('meta', function(meta) {
|
|
|
|
if (meta.status !== 200) {
|
|
|
|
streamCallback(new Error('Got status: '+meta.status));
|
|
|
|
}
|
2015-02-27 00:25:57 +00:00
|
|
|
});
|
|
|
|
stream.on('end', function() {
|
2015-02-27 20:50:19 +00:00
|
|
|
assembler.emit('file-download-post',remoteUrl);
|
|
|
|
streamCallback();
|
2015-02-27 00:25:57 +00:00
|
|
|
});
|
|
|
|
stream.pipe(fs.createWriteStream(localFile));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
function downloadFileList(assembler, downloadList, callback) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (downloadList.length === 0) {
|
|
|
|
callback();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var download = downloadList.pop();
|
2015-02-27 20:50:19 +00:00
|
|
|
downloadFile(assembler, download.remoteUrl, download.localFile, function(err) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
downloadFileList(assembler, downloadList, callback);
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
function aggregateFileList(assembler, targetFile, aggregateList, readFile, callback) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (aggregateList.length === 0) {
|
|
|
|
callback();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var aggregateFile = aggregateList.pop();
|
2015-02-27 20:50:19 +00:00
|
|
|
assembler.emit('file-read-pre',aggregateFile);
|
2015-02-27 00:25:57 +00:00
|
|
|
readFile(aggregateFile, function(err, data) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
assembler.emit('file-read-post',aggregateFile);
|
|
|
|
assembler.emit('log','debug','readFile: '+aggregateFile+' size: '+data.length);
|
2015-02-27 00:25:57 +00:00
|
|
|
if (aggregateList.length > 0) {
|
2015-02-27 20:50:19 +00:00
|
|
|
data = data + assembler.config.assetSeperator;
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
fs.appendFile(targetFile, data, function (err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
aggregateFileList(assembler, targetFile, aggregateList, readFile, callback);
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function mapLocalFileSync(assembler, linkSource) {
|
|
|
|
var uriMapping = assembler.config.linkMapping;
|
|
|
|
if (uriMapping) {
|
|
|
|
var uriMappingKeys = Object.keys(uriMapping);
|
|
|
|
uriMappingKeys.sort(function(a, b) {
|
|
|
|
return a.length < b.length; // longest first as we break on first hit
|
|
|
|
});
|
|
|
|
for (var ii = 0; ii < uriMappingKeys.length; ii++) {
|
|
|
|
var uriKey = uriMappingKeys[ii];
|
|
|
|
var localPath = uriMapping[uriKey];
|
|
|
|
var mapIndex = linkSource.indexOf(uriKey);
|
|
|
|
if (mapIndex === 0) {
|
|
|
|
return localPath+linkSource.substring(uriKey.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return linkSource;
|
|
|
|
}
|
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
function buildAsset(assembler, targetFile, readFile, callback) {
|
|
|
|
var uriList = assembler.config.linkSources;
|
|
|
|
var readFileList = [];
|
2015-02-27 00:25:57 +00:00
|
|
|
var resultUriList = [];
|
|
|
|
var downloadList = [];
|
|
|
|
|
|
|
|
for (var i = 0; i < uriList.length; i++) {
|
|
|
|
var assetItem = uriList[i];
|
|
|
|
|
|
|
|
var remoteUrl = null;
|
|
|
|
var remoteForce = null;
|
|
|
|
var remoteIndex = assetItem.indexOf('@');
|
|
|
|
if (remoteIndex > 0) {
|
|
|
|
remoteUrl = assetItem.substring(remoteIndex + 1);
|
|
|
|
if (remoteUrl.indexOf('@') === 0) {
|
|
|
|
remoteUrl = assetItem.substring(remoteIndex + 2);
|
|
|
|
remoteForce = true;
|
|
|
|
}
|
|
|
|
assetItem = assetItem.substring(0,remoteIndex);
|
|
|
|
}
|
2015-02-27 20:50:19 +00:00
|
|
|
var localFile = mapLocalFileSync(assembler, assetItem);
|
2015-02-27 00:25:57 +00:00
|
|
|
|
|
|
|
// override force on all files
|
2015-02-27 20:50:19 +00:00
|
|
|
if (remoteForce===undefined && assembler.config.downloadForce !== undefined) {
|
|
|
|
remoteForce = assembler.config.downloadForce;
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (remoteUrl && (remoteForce || !fs.existsSync(localFile))) {
|
|
|
|
downloadList.push({
|
|
|
|
remoteUrl: remoteUrl,
|
|
|
|
localFile: localFile
|
|
|
|
});
|
|
|
|
}
|
2015-02-27 20:50:19 +00:00
|
|
|
readFileList.push(localFile);
|
2015-02-27 00:25:57 +00:00
|
|
|
resultUriList.push(assetItem);
|
|
|
|
}
|
|
|
|
downloadList.reverse();
|
2015-02-27 20:50:19 +00:00
|
|
|
readFileList.reverse();
|
|
|
|
|
|
|
|
assembler.emit('log','debug','downloadList size: '+downloadList.length);
|
|
|
|
assembler.emit('log','debug','readFileList size: '+readFileList.length);
|
2015-02-27 00:25:57 +00:00
|
|
|
|
2015-02-27 20:50:19 +00:00
|
|
|
downloadFileList(assembler, downloadList, function(err) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
//for(....
|
|
|
|
//if (!fs.existsSync(localFile)) {
|
|
|
|
// Log.warn("illegal entry: "+localFile);
|
|
|
|
// continue;
|
|
|
|
//}
|
2015-02-27 20:50:19 +00:00
|
|
|
aggregateFileList(assembler, targetFile, readFileList, readFile, function(err) {
|
2015-02-27 00:25:57 +00:00
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
buildEnd(assembler, targetFile, resultUriList, callback);
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildStart(assembler, readFile, callback) {
|
|
|
|
assembler.startTime = new Date().getTime();
|
|
|
|
assembler.emit('begin');
|
|
|
|
assembler.emit('log','info','build begin for: '+assembler.config.linkTarget);
|
|
|
|
if (assembler.config.downloadForce) {
|
|
|
|
assembler.emit('log','info','build using forced downloads.');
|
|
|
|
}
|
|
|
|
var targetFile = mapLocalFileSync(assembler, assembler.config.linkTarget);
|
|
|
|
fs.ensureFile(targetFile, function (err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
assembler.emit('file-write-pre',targetFile);
|
|
|
|
fs.writeFile(targetFile, assembler.config.assetHeader, function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
buildAsset(assembler, targetFile, readFile, callback);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function runAssembler(assembler, readFile, callback) {
|
|
|
|
if (assembler.config.downloadStartDelay) {
|
|
|
|
setTimeout(function() {
|
|
|
|
buildStart(assembler, readFile, callback);
|
|
|
|
}, assembler.config.downloadStartDelay);
|
|
|
|
} else {
|
|
|
|
buildStart(assembler, readFile, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function AssetAssembler(config, readFile) {
|
2015-02-27 20:50:19 +00:00
|
|
|
if (config === undefined) {
|
|
|
|
throw new Error('no config');
|
|
|
|
}
|
2015-02-27 00:25:57 +00:00
|
|
|
this.config = config;
|
|
|
|
this.startTime = 0;
|
|
|
|
this.readFile = readFile || factory.assembler.constructor.readFile();
|
|
|
|
events.EventEmitter.call(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetAssembler.prototype.__proto__ = events.EventEmitter.prototype;
|
|
|
|
|
|
|
|
AssetAssembler.prototype.run = function(callback) {
|
|
|
|
if (callback === undefined) {
|
|
|
|
throw new Error('no callback');
|
|
|
|
}
|
|
|
|
var self = this;
|
|
|
|
config.checkConfig(self.config,function(err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
config.fillDefaults(self.config, function (err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
2015-02-27 20:50:19 +00:00
|
|
|
runAssembler(self, self.readFile, callback);
|
2015-02-27 00:25:57 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = AssetAssembler;
|