From 3f580bbe2d1fe8c2d768f012c8a6e115f57cf58a Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 27 Feb 2015 01:25:57 +0100 Subject: [PATCH] redone with objects --- .jshintignore | 1 + .jshintrc | 38 ++++ index.js | 397 ---------------------------------- lib/asset-assembler-config.js | 63 ++++++ lib/asset-assembler.js | 252 +++++++++++++++++++++ lib/assets-builder-config.js | 57 +++++ lib/assets-builder.js | 115 ++++++++++ lib/factory.js | 144 ++++++++++++ lib/node-ff-assets.js | 16 ++ package.json | 14 +- test/jshint.spec.js | 3 + test/test-node-ff-assets.js | 25 +++ 12 files changed, 725 insertions(+), 400 deletions(-) create mode 100644 .jshintignore create mode 100644 .jshintrc delete mode 100644 index.js create mode 100644 lib/asset-assembler-config.js create mode 100644 lib/asset-assembler.js create mode 100644 lib/assets-builder-config.js create mode 100644 lib/assets-builder.js create mode 100644 lib/factory.js create mode 100644 lib/node-ff-assets.js create mode 100644 test/jshint.spec.js create mode 100644 test/test-node-ff-assets.js diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.jshintignore @@ -0,0 +1 @@ +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..72cc948 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,38 @@ +{ + "predef": [ + "before", + "after", + "it", + "describe", + "beforeEach", + "afterEach" + ], + "asi" : false, + "bitwise" : true, + "boss" : false, + "curly" : true, + "debug": false, + "devel": false, + "eqeqeq": true, + "evil": false, + "expr": true, + "forin": false, + "immed": true, + "latedef" : false, + "laxbreak": false, + "multistr": true, + "newcap": true, + "noarg": true, + "node" : true, + "noempty": false, + "nonew": true, + "onevar": false, + "plusplus": false, + "proto": true, + "regexp": false, + "strict": false, + "sub": false, + "trailing" : true, + "undef": true, + "unused": true +} diff --git a/index.js b/index.js deleted file mode 100644 index cbd0aca..0000000 --- a/index.js +++ /dev/null @@ -1,397 +0,0 @@ -var NodeFsExtra = require('fs-extra'); -var NodeMinify = require('minify'); -var NodeEvents = require('events'); -var NodeFetch = require('fetch'); -var NodeFs = require('fs'); - -var DEFAULT_DOWNLOAD_MAX_SIZE = (1024*1024) * 10; // 10mb -var DEFAULT_DOWNLOAD_TIMEOUT = (1000*60) * 1; // 1min -var DEFAULT_OPTIONS = { - downloadStartDelay: null, - downloadForce: false, - downloadOptions: { - timeout: DEFAULT_DOWNLOAD_TIMEOUT, - maxResponseLength: DEFAULT_DOWNLOAD_MAX_SIZE, - agent: 'node-ff-assets asset fetcher' - }, - - linkTargetSingleResult: true, - linkTarget: null, - linkSources: [], - linkMapping: [], - - assetHeader: '\n/* node-ff-assets: begin */\n\n', - assetFooter: '\n/* node-ff-assets: end */\n\n', - assetSeperator: '\n/* node-ff-assets: next */\n', -} - -function ResourceBuilder(config, readFile) { - this.config = config; - this.startTime = 0; - this.readFile = readFile || FunctionFactory.Constructor.readFile(); - NodeEvents.EventEmitter.call(this); - - this.run = function(callback) { - configCheckSync(this.config); - configDefaultsSync(this.config); - runBuildSafe(this, this.readFile, callback); - }; -} - -ResourceBuilder.prototype.__proto__ = NodeEvents.EventEmitter.prototype; - -var FunctionFactory = { - Constructor: { - readFile: function() { - return function(file,callback) { - NodeFs.readFile(file, function(err, data) { - if (err) { - callback(err); - } else { - callback(null, data); - } - }); - }; - }, - readFileMinify: function(options) { - if (options == null) { - options = {}; - } - return function(file, callback) { - NodeMinify(file, options, function(err, data) { - if (err) { - callback(err); - } else { - callback(null, data); - } - }); - }; - }, - }, - Event: { - log: { - console: function(logType) { - if (logType == null) { - throw Error('no logType'); - } - return function(logLevel, logMessage) { - console.log(logLevel+': node-ff-assets-'+logType+' '+logMessage); - }; - }, - }, - result: { - objectFunction: function(object, functionName, resultKey) { - if (object == null) { - throw Error('no object'); - } - if (functionName == null) { - throw Error('no functionName'); - } - return function(resultValue) { - if (resultKey == null) { - object[functionName](resultValue); - } else { - object[functionName](resultKey, resultValue); - } - }; - }, - objectAdd: function(object,resultKey) { - return FunctionFactory.Event.result.objectFunction(object,'add',resultKey); - }, - objectSet: function(object,resultKey) { - return FunctionFactory.Event.result.objectFunction(object,'set',resultKey); - }, - objectPut: function(object,resultKey) { - return FunctionFactory.Event.result.objectFunction(object,'put',resultKey); - }, - arrayPush: function(object,resultKey) { - return FunctionFactory.Event.result.objectFunction(object,'push'); - }, - } - } -} - -function configCheckSync(config) { - if (!config) { - throw Error('no config'); - } - if (!config.linkTarget) { - throw Error('no config.linkTarget'); - } - if (!config.linkSources) { - throw Error('no config.linkSources'); - } - if (!config.linkMapping) { - throw Error('no config.linkMapping'); - } -} - -function configDefaultsSync(config) { - if (config.downloadOptions == null) { - config.downloadOptions = DEFAULT_OPTIONS.downloadOptions; - } - if (config.assetHeader == null) { - config.assetHeader = DEFAULT_OPTIONS.assetHeader; - } - if (config.assetFooter == null) { - config.assetFooter = DEFAULT_OPTIONS.assetFooter; - } - if (config.assetSeperator == null) { - config.assetSeperator = DEFAULT_OPTIONS.assetSeperator; - } -} - -function runBuildSafe(builder, readFile, callback) { - runBuild(builder, readFile, function(err) { - // send error - if (err) { - builder.emit('error',err); - } - // finish callback - if (callback != null) { - callback(err); - } - }); -} - -function runBuild(builder, readFile, callback) { - if (builder.config.downloadStartDelay) { - setTimeout(function() { - buildStart(builder, readFile, callback); - }, builder.config.downloadStartDelay); - } else { - buildStart(builder, readFile, callback); - } -} - -function buildStart(builder, readFile, callback) { - builder.startTime = new Date().getTime(); - builder.emit('begin'); - builder.emit('log','info','build begin for: '+builder.config.linkTarget); - if (builder.config.downloadForce) { - builder.emit('log','info','build using forced downloads.'); - } - var targetFile = mapLocalFileSync(builder, builder.config.linkTarget); - NodeFsExtra.ensureFile(targetFile, function (err) { - if (err) { - callback(err); - } else { - builder.emit('file-write-pre',targetFile); - NodeFsExtra.writeFile(targetFile, builder.config.assetHeader, function(err) { - if (err) { - callback(err); - } else { - buildAsset(builder, targetFile, readFile, callback); - } - }); - } - }); -} - -function buildAsset(builder, targetFile, readFile, callback) { - var uriList = builder.config.linkSources; - var localFileList = []; - var resultUriList = []; - var downloadList = []; - - for (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); - } - var localFile = mapLocalFileSync(builder, assetItem); - - // override force on all files - if (remoteForce==null && builder.config.downloadForce != null) { - remoteForce = builder.config.downloadForce; - } - - if (remoteUrl && (remoteForce || !NodeFs.existsSync(localFile))) { - downloadList.push({ - remoteUrl: remoteUrl, - localFile: localFile - }); - } - localFileList.push(localFile); - resultUriList.push(assetItem); - } - downloadList.reverse(); - localFileList.reverse(); - - downloadFileList(builder, downloadList, function(error) { - if (error) { - callback(err); - } else { - //for(.... - //if (!NodeFs.existsSync(localFile)) { - // Log.warn("illegal entry: "+localFile); - // continue; - //} - aggregateFileList(builder, targetFile, localFileList, readFile, function(err) { - if (err) { - callback(err); - } else { - buildEnd(builder, targetFile, resultUriList, callback); - } - }); - } - }); -} - -function mapLocalFileSync(builder, linkSource) { - var uriMapping = builder.config.linkMapping; - if (uriMapping) { - uriMappingKeys = Object.keys(uriMapping); - uriMappingKeys.sort(function(a, b) { - return a.length < b.length; // longest first as we break on first hit - }); - for (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; -} - -function downloadFileList(builder, downloadList, callback) { - if (downloadList.length == 0) { - callback(); - return; - } - var download = downloadList.pop(); - downloadFile(builder, download.remoteUrl, download.localFile, function(err) { - if (err) { - callback(err); - } else { - downloadFileList(builder, downloadList, callback); - } - }); -} - -function downloadFile(builder, remoteUrl, localFile, callback) { - builder.emit('file-download-pre',remoteUrl); - builder.emit('log','debug','downloadFile: '+remoteUrl); - NodeFsExtra.ensureFile(localFile, function(err) { - if (err) { - callback(err); - } else { - var stream = new NodeFetch.FetchStream(remoteUrl); - stream.on('error', function(err) { - callback(err); - }); - stream.on('end', function() { - builder.emit('file-download-post',remoteUrl); - callback(); - }); - stream.pipe(NodeFs.createWriteStream(localFile)); - } - }); -} - -function aggregateFileList(builder, targetFile, aggregateList, readFile, callback) { - if (aggregateList.length == 0) { - callback(); - return; - } - var aggregateFile = aggregateList.pop(); - builder.emit('file-read-pre',aggregateFile); - readFile(aggregateFile, function(err, data) { - if (err) { - callback(err); - } else { - builder.emit('file-read-post',aggregateFile); - builder.emit('log','debug','readFile: '+aggregateFile+' size: '+data.length); - if (aggregateList.length > 0) { - data = data + builder.config.assetSeperator; - } - NodeFs.appendFile(targetFile, data, function (err) { - if (err) { - callback(err); - } else { - aggregateFileList(builder, targetFile, aggregateList, readFile, callback); - } - }); - } - }); -} - -function buildEnd(builder, targetFile, resultUriList, callback) { - NodeFs.appendFile(targetFile, builder.config.assetFooter, function (err) { - if (err) { - callback(err); - } else { - builder.emit('file-write-post',targetFile); - var buildTime = new Date().getTime() - builder.startTime; - var buildResultSize = +resultUriList.length; - - if (builder.config.linkTargetSingleResult) { - resultUriList = [builder.config.linkTarget]; - - } - var targetStats = NodeFs.statSync(targetFile); - var targetSize = targetStats['size']; - - builder.emit('log','debug','target size: '+targetSize); - builder.emit('log','info','build result size: '+resultUriList.length+' from: '+buildResultSize); - builder.emit('log','info','build done in: '+buildTime+' ms.'); - builder.emit('result',resultUriList); - builder.emit('end'); - callback(); - } - }); -} - -function runBuilderList(builderList,callback) { - if (callback == null) { - callback = function(err) { - if(err) { - throw err; // mm - } - }; - } - if (builderList.length == 0) { - callback(); - return; - } - var builder = builderList.pop(); - builder.run(function(err) { - if (err) { - callback(err); - } else { - runBuilderList(builderList,callback); - } - }); -} - -function runAll(builderList,callback) { - builderList.reverse(); - runBuilderList(builderList,callback); -} - -function pushLinkSourcesSync(builder, linkPrefix, fileFolder) { - NodeFs.readdirSync(fileFolder).forEach(function (file) { - if (~file.indexOf('.js') || ~file.indexOf('.css')) { - builder.linkSources.push(linkPrefix+file) - } - }); -} - -module.exports = { - ResourceBuilder: ResourceBuilder, - FunctionFactory: FunctionFactory, - runAll: runAll, - pushLinkSourcesSync: pushLinkSourcesSync, -} diff --git a/lib/asset-assembler-config.js b/lib/asset-assembler-config.js new file mode 100644 index 0000000..bdc225e --- /dev/null +++ b/lib/asset-assembler-config.js @@ -0,0 +1,63 @@ +'use strict'; + +var name = 'node-ff-assets'; +var downloadTimeout = (1000*10) * 6; // 1min +var downloadMaxSize = (1024*1024) * 10; // 10mb + +var template = { + downloadStartDelay: null, + downloadForce: false, + downloadOptions: { + timeout: downloadTimeout, + maxResponseLength: downloadMaxSize, + agent: name+' remote fetcher', + }, + + linkTargetSingleResult: true, + linkTarget: null, + linkSources: [], + linkMapping: [], + + assetType: null, + assetHeader: '\n/* '+name+': begin */\n\n', + assetFooter: '\n/* '+name+': end */\n\n', + assetSeperator: '\n/* '+name+': next */\n', +}; + +function checkConfig(config,callback) { + if (!config) { + callback(new Error('no config')); + } + if (!config.linkTarget) { + callback(new Error('no config.linkTarget')); + } + if (!config.linkSources) { + callback(new Error('no config.linkSources')); + } + if (!config.linkMapping) { + callback(new Error('no config.linkMapping')); + } + callback(); +} + +function fillDefaults(config,callback) { + if (config.downloadOptions === undefined) { + config.downloadOptions = template.downloadOptions; + } + if (config.assetHeader === undefined) { + config.assetHeader = template.assetHeader; + } + if (config.assetFooter === undefined) { + config.assetFooter = template.assetFooter; + } + if (config.assetSeperator === undefined) { + config.assetSeperator = template.assetSeperator; + } + callback(null,config); +} + +module.exports = { + template: template, + checkConfig: checkConfig, + fillDefaults: fillDefaults, +}; diff --git a/lib/asset-assembler.js b/lib/asset-assembler.js new file mode 100644 index 0000000..1d11b98 --- /dev/null +++ b/lib/asset-assembler.js @@ -0,0 +1,252 @@ +'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'); + + +function buildEnd(builder, targetFile, resultUriList, callback) { + fs.appendFile(targetFile, builder.config.assetFooter, function (err) { + if (err) { + callback(err); + } else { + var buildTime = new Date().getTime() - builder.startTime, + buildResultSize = resultUriList.length, + targetStats = fs.statSync(targetFile), + targetSize = targetStats.size; + + if (builder.config.linkTargetSingleResult) { + resultUriList = [builder.config.linkTarget]; + } + builder.emit('file-write-post',targetFile); + builder.emit('log','debug','target size: '+targetSize); + builder.emit('log','info','build result size: '+resultUriList.length+' from: '+buildResultSize); + builder.emit('log','info','build done in: '+buildTime+' ms.'); + builder.emit('result',resultUriList); + builder.emit('end'); + callback(); + } + }); +} + + +function downloadFile(builder, remoteUrl, localFile, callback) { + builder.emit('file-download-pre',remoteUrl); + builder.emit('log','debug','downloadFile: '+remoteUrl); + fs.ensureFile(localFile, function(err) { + if (err) { + callback(err); + } else { + var stream = new fetch.FetchStream(remoteUrl); + stream.on('error', function(err) { + callback(err); + }); + stream.on('end', function() { + builder.emit('file-download-post',remoteUrl); + callback(); + }); + stream.pipe(fs.createWriteStream(localFile)); + } + }); +} + + +function downloadFileList(builder, downloadList, callback) { + if (downloadList.length === 0) { + callback(); + return; + } + var download = downloadList.pop(); + downloadFile(builder, download.remoteUrl, download.localFile, function(err) { + if (err) { + callback(err); + } else { + downloadFileList(builder, downloadList, callback); + } + }); +} + +function aggregateFileList(builder, targetFile, aggregateList, readFile, callback) { + if (aggregateList.length === 0) { + callback(); + return; + } + var aggregateFile = aggregateList.pop(); + builder.emit('file-read-pre',aggregateFile); + readFile(aggregateFile, function(err, data) { + if (err) { + callback(err); + } else { + builder.emit('file-read-post',aggregateFile); + builder.emit('log','debug','readFile: '+aggregateFile+' size: '+data.length); + if (aggregateList.length > 0) { + data = data + builder.config.assetSeperator; + } + fs.appendFile(targetFile, data, function (err) { + if (err) { + callback(err); + } else { + aggregateFileList(builder, targetFile, aggregateList, readFile, callback); + } + }); + } + }); +} + +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; +} + +function buildAsset(builder, targetFile, readFile, callback) { + var uriList = builder.config.linkSources; + var localFileList = []; + 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); + } + var localFile = mapLocalFileSync(builder, assetItem); + + // override force on all files + if (remoteForce===undefined && builder.config.downloadForce !== undefined) { + remoteForce = builder.config.downloadForce; + } + + if (remoteUrl && (remoteForce || !fs.existsSync(localFile))) { + downloadList.push({ + remoteUrl: remoteUrl, + localFile: localFile + }); + } + localFileList.push(localFile); + resultUriList.push(assetItem); + } + downloadList.reverse(); + localFileList.reverse(); + + downloadFileList(builder, downloadList, function(err) { + if (err) { + callback(err); + } else { + //for(.... + //if (!fs.existsSync(localFile)) { + // Log.warn("illegal entry: "+localFile); + // continue; + //} + aggregateFileList(builder, targetFile, localFileList, readFile, function(err) { + if (err) { + callback(err); + } else { + buildEnd(builder, targetFile, resultUriList, callback); + } + }); + } + }); +} + +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 runAssemblerSafe(assembler, readFile, callback) { + runAssembler(assembler, readFile, function(err) { + // send error + if (err) { + assembler.emit('error',err); + } + // finish callback + if (callback !== undefined) { + callback(err); + } + }); +} + +function AssetAssembler(config, readFile) { + 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 { + runAssemblerSafe(self, self.readFile, callback); + } + }); + } + }); +}; + +module.exports = AssetAssembler; diff --git a/lib/assets-builder-config.js b/lib/assets-builder-config.js new file mode 100644 index 0000000..f5ee059 --- /dev/null +++ b/lib/assets-builder-config.js @@ -0,0 +1,57 @@ +'use strict'; + +var factory = require('./factory'); + +var template = { + buildOrder: ['css','js'], + + configCreate: null, + configFill: factory.builder.configCreate.nop(), + assemblerCreate: factory.builder.assemblerCreate.readFile(), + assemblerFill: factory.builder.configCreate.nop(), + + assets: { + js: { + configCreate: null, + configFill: null, + assemblerCreate: null, + assemblerFill: null, + }, + css: { + configCreate: null, + configFill: null, + assemblerCreate: null, + assemblerFill: null, + }, + }, +}; + +function checkConfig(config,callback) { + if (!config) { + callback(new Error('no config')); + } + // TODO: add more + callback(); +} + +function fillDefaults(config,callback) { + if (config.buildOrder === undefined) { + config.buildOrder = Object.keys(config.assets); + } + if (config.configFill === undefined) { + config.configFill = template.configFill; + } + if (config.assemblerCreate === undefined) { + config.assemblerCreate = template.assemblerCreate; + } + if (config.assemblerFill === undefined) { + config.assemblerFill = template.assemblerFill; + } + callback(); +} + +module.exports = { + template: template, + checkConfig: checkConfig, + fillDefaults: fillDefaults, +}; diff --git a/lib/assets-builder.js b/lib/assets-builder.js new file mode 100644 index 0000000..4452934 --- /dev/null +++ b/lib/assets-builder.js @@ -0,0 +1,115 @@ +'use strict'; + +var events = require('events'); +var async = require('async'); +var config = require('./assets-builder-config'); + +function buildStepAssemblerRun(builder, buildConfig, assembler, callback) { + assembler.run(callback); +} + +function buildStepAssemblerFill(builder, buildConfig, assembler, callback) { + var assemblerFill = buildConfig.assemblerFill || builder.config.assemblerFill; + assemblerFill(assembler, function(err) { + if (err) { + callback(err); + } else { + buildStepAssemblerRun(builder, buildConfig, assembler, callback); + } + }); +} + +function buildStepAssemblerCreate(builder, buildConfig, assemblerConfig, callback) { + var assemblerCreate = buildConfig.assemblerCreate || builder.config.assemblerCreate; + assemblerCreate(assemblerConfig, function(err, assembler) { + if (err) { + callback(err); + } else { + buildStepAssemblerFill(builder, buildConfig, assembler, callback); + } + }); +} + +function buildStepConfigFill(builder, buildConfig, assemblerConfig, callback) { + var configFill = buildConfig.configFill || builder.config.configFill; + + if (assemblerConfig.assetType === undefined) { + assemblerConfig.assetType = builder.currentAssetType; + } + + configFill(assemblerConfig, function(err) { + if (err) { + callback(err); + } else { + buildStepAssemblerCreate(builder, buildConfig, assemblerConfig, callback); + } + }); +} + +function buildStepConfigCreate(builder, buildConfig, callback) { + var configCreate = buildConfig.configCreate || builder.config.configCreate; + configCreate(function(err, assemblerConfig) { + if (err) { + callback(err); + } else { + buildStepConfigFill(builder, buildConfig, assemblerConfig, callback); + } + }); +} + +function AssetsBuilder(config) { + this.config = config; + this.startTime = 0; + this.currentAssetType = ''; + events.EventEmitter.call(this); +} + +AssetsBuilder.prototype.__proto__ = events.EventEmitter.prototype; + +AssetsBuilder.prototype.runAll = function(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 { + var runners = []; + for (var i = 0; i < self.config.buildOrder.length; i++) { + var assetKey = self.config.buildOrder[i]; + runners.push(self.runAsset(assetKey)); + } + async.series(runners,self.config.buildDone); + } + }); + } + }); +}; + +AssetsBuilder.prototype.runAsset = function(name) { + var self = this; + return function(callback) { + if (name === undefined) { + throw new Error('no name'); + } + if (callback === undefined) { + throw new Error('no callback'); + } + config.checkConfig(self.config,function(err) { + if (err) { + callback(err); + } else { + var buildConfig = self.config.assets[name]; + if (buildConfig === undefined) { + throw new Error('no asset config found'); + } + self.currentAssetType = name; + buildStepConfigCreate(self,buildConfig,callback); + } + }); + }; +}; + +module.exports = AssetsBuilder; diff --git a/lib/factory.js b/lib/factory.js new file mode 100644 index 0000000..b5ee974 --- /dev/null +++ b/lib/factory.js @@ -0,0 +1,144 @@ +'use strict'; + +var fs = require('fs'); + +var assemblerConstructors = { + readFile : function() { + return function(file, callback) { + fs.readFile(file, function(err, data) { + if (err) { + callback(err); + } else { + callback(null, data); + } + }); + }; + }, + readFileMinify : function(options) { + if (options === undefined) { + options = {}; + } + var minify = require('minify'); + return function(file, callback) { + minify(file, options, function(err, data) { + if (err) { + callback(err); + } else { + callback(null, data); + } + }); + }; + }, +}; + +var assemblerEventLog = { + console : function(logType) { + if (logType === undefined) { + throw new Error('no logType'); + } + return function(logLevel, logMessage) { + console.log(logLevel + ': node-ff-assets-' + logType + ' ' + logMessage); + }; + }, + winston: function(logger, logType) { + if (logger === undefined) { + throw new Error('no logger'); + } + if (logType === undefined) { + throw new Error('no logType'); + } + return function(logLevel, logMessage) { + logger.log(logLevel, 'node-ff-assets-'+logType+' '+logMessage); + }; + }, +}; + +var assemblerEventResult = { + objectFunction : function(object, functionName, resultKey) { + if (object === undefined) { + throw new Error('no object'); + } + if (functionName === undefined) { + throw new Error('no functionName'); + } + return function(resultValue) { + if (resultKey === undefined) { + object[functionName](resultValue); + } else { + object[functionName](resultKey, resultValue); + } + }; + }, + objectAdd : function(object, resultKey) { + return assemblerEventResult.objectFunction(object, 'add', resultKey); + }, + objectSet : function(object, resultKey) { + return assemblerEventResult.objectFunction(object, 'set', resultKey); + }, + objectPut : function(object, resultKey) { + return assemblerEventResult.objectFunction(object, 'put', resultKey); + }, + arrayPush : function(object) { + return assemblerEventResult.objectFunction(object, 'push'); + }, +}; + +var libAsync = { + pushLinkSources: function (builder, linkPrefix, fileFolder) { + return function(callback) { + fs.readdir(fileFolder, function (err, fileList) { + if (err) { + callback(err); + } else { + fileList.forEach(function (file) { + if (file.indexOf('.js') > 0 || file.indexOf('.css') > 0) { + builder.linkSources.push(linkPrefix+file); + } + }); + callback(null,null); + } + }); + }; + }, + runBuilder: function (builder) { + return function(callback) { + builder.run(callback); + }; + }, +}; + +var builderAssemblerCreate = { + readFile: function () { + return function (assemblerConfig, callback) { + var AS = require('./asset-assembler'); + callback(null,new AS(assemblerConfig,assemblerConstructors.readFile())); + }; + }, + readFileMinify: function(options) { + return function (assemblerConfig, callback) { + var AS = require('./asset-assembler'); + callback(null,new AS(assemblerConfig,assemblerConstructors.readFileMinify(options))); + }; + }, +}; + +var builderEmptyCallback = function () {}; + +module.exports = { + assembler: { + constructor : assemblerConstructors, + event : { + log : assemblerEventLog, + result : assemblerEventResult, + }, + }, + builder: { + configCreate: { nop: builderEmptyCallback }, + configFill: { nop: builderEmptyCallback }, + assemblerCreate: builderAssemblerCreate, + assemblerFill: { nop: builderEmptyCallback }, + }, + lib: { + async: libAsync, + }, +}; diff --git a/lib/node-ff-assets.js b/lib/node-ff-assets.js new file mode 100644 index 0000000..2fa452c --- /dev/null +++ b/lib/node-ff-assets.js @@ -0,0 +1,16 @@ +'use strict'; + +var assets = { + factory: require('./factory'), + AssetAssembler: require('./asset-assembler'), + checkAssemblerConfig: require('./asset-assembler-config').checkConfig, + AssetsBuilder: require('./assets-builder'), + checkBuilderConfig: require('./assets-builder-config').checkConfig, +}; + +assets.build = function (config,callback) { + var builder = new assets.AssetsBuilder(config); + builder.runAll(callback); +}; + +module.exports = assets; diff --git a/package.json b/package.json index 2f4a8f7..5ab269f 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,24 @@ { "name": "node-ff-assets", - "version": "0.1.2", + "version": "0.2.0", "description": "Site assets local/remote aggregation with minify.", - "main": "index.js", + "main": "lib/node-ff-assets.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node_modules/mocha/bin/mocha" }, "author": "Willem (http://forwardfire.net/)", "license": "BSD-2-Clause", + "repository": { + "type": "git", + "url": "https://bitbucket.org/im_ik/node-ff-assets.git" + }, "dependencies": { "fetch": "^0.3.6", "fs-extra": "^0.16.3", "minify": "^1.4.8" + }, + "devDependencies": { + "mocha": "^2.1.0", + "mocha-jshint": "0.0.9" } } diff --git a/test/jshint.spec.js b/test/jshint.spec.js new file mode 100644 index 0000000..67cb299 --- /dev/null +++ b/test/jshint.spec.js @@ -0,0 +1,3 @@ +'use strict'; + +require('mocha-jshint')(['lib']); diff --git a/test/test-node-ff-assets.js b/test/test-node-ff-assets.js new file mode 100644 index 0000000..c4522a0 --- /dev/null +++ b/test/test-node-ff-assets.js @@ -0,0 +1,25 @@ +'use strict'; + +var assert = require("assert"); +var asserts = require("../lib/node-ff-assets"); + +describe('node-ff-assets', function() { + describe('export.* check undefined', function() { + it('asserts.factory should not be undefined', function() { + assert.equal(false, asserts.factory === undefined); + }); + it('asserts.AssetAssembler should not be undefined', function() { + assert.equal(false, asserts.AssetAssembler === undefined); + }); + it('asserts.checkAssemblerConfig should not be undefined', function() { + assert.equal(false, asserts.checkAssemblerConfig === undefined); + }); + it('asserts.AssetsBuilder should not be undefined', function() { + assert.equal(false, asserts.AssetsBuilder === undefined); + }); + it('asserts.checkBuilderConfig should not be undefined', function() { + assert.equal(false, asserts.checkBuilderConfig === undefined); + }); + }); +}); +