diff --git a/README.md b/README.md index d23ee39..8149c64 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,178 @@ node-ff-assets ========= -A Node.js library providing automatic site assets aggregation and content modifiers. +A Node.js library providing automatic site assets aggregation. ## Installation npm install node-ff-assets --save +## Usage + + // note1: www_static/[js/css]/lib is version ignored. + // note2: server routes; + // app.use('/static', express.static(path.join(__dirname,'www_static'))); + // app.use('/static/module/flot', express.static(path.join(__dirname,'node_modules/flot/dist'))); + // app.use('/static/module/bootstrap', express.static(path.join(__dirname,'node_modules/bootstrap/dist'))); + + var assets = require('node-ff-assets'); + + var assetsConfig = { + // Map from uri paths to file systems paths. + linkMapping : { + '/static/module/bootstrap/': 'node_modules/bootstrap/dist/', + '/static/module/flot/': 'node_modules/flot/', + '/static/': 'www_static/', + }, + // Site sources in html page. + linkSourcesCss : [ + '/static/module/bootstrap/css/bootstrap.css', + ], + linkSourcesJs : [ + '/static/js/lib/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js', + '/static/module/bootstrap/js/bootstrap.js', + '/static/module/flot/jquery.flot.js', + '/static/module/flot/jquery.flot.resize.js', + '/static/module/flot/jquery.flot.pie.js', + '/static/js/lib/angularjs-1.4.0-b4/angular.js@https://code.angularjs.org/1.4.0-beta.4/angular.js', + '/static/js/lib/angularjs-1.4.0-b4/angular-route.js@https://code.angularjs.org/1.4.0-beta.4/angular-route.js', + '/static/js/lib/angularjs-1.4.0-b4/angular-resource.js@https://code.angularjs.org/1.4.0-beta.4/angular-resource.js', + '/static/js/lib/angularjs-1.4.0-b4/angular-touch.js@https://code.angularjs.org/1.4.0-beta.4/angular-touch.js', + ], + }; + + // callback helper to return an assembler config per asset type + function createBuildConfig(type) { + return function(callback) { + // Override rendered result from command line + var singleResult = 'false' != process.env.DEV_ASSETS_SINGLE_RESULT; + if (type == 'css') { + callback(null,{ + linkMapping: assetsConfig.linkMapping, + linkTargetSingleResult: singleResult, + linkTarget: '/static/css/lib/assets.css', + linkSources: assetsConfig.linkSourcesCss, + assetType: type, + }); + } else { + callback(null, { + linkTargetSingleResult: singleResult, + linkMapping: assetsConfig.linkMapping, + linkTarget: '/static/js/lib/assets.js', + linkSources: assetsConfig.linkSourcesJs, + assetType: type, + }); + } + } + } + + function buildAssets(server,callbackDone) { + assets.build({ + assets: { + js: { + configCreate: createBuildConfig('js'), + configFill: function (config, callback) { + async.series([ + assets.factory.lib.async.pushLinkSources(config, '/static/js/', 'www_static/js/'), + assets.factory.lib.async.pushLinkSources(config, '/static/js/controller/', 'www_static/js/controller/'), + ],callback); + }, + }, + css: { + configCreate: createBuildConfig('css'), + configFill: function (config, callback) { + async.series([ + assets.factory.lib.async.pushLinkSources(config, '/static/css/', 'www_static/css/'), + ],callback); + + }, + assemblerCreate: function (assemblerConfig, callback) { + callback(null,new assets.AssetAssembler(assemblerConfig,assets.factory.assembler.constructor.readFileMinify())); + }, + }, + }, + assemblerFill: function (assembler, callback) { + var serverResultKey = 'ff_assets_'+assembler.config.assetType; + assembler.on ('log', assets.factory.assembler.event.log.console(assembler.config.assetType)); + assembler.on ('result', assets.factory.assembler.event.result.objectSet(server,serverResultKey)); + callback(); + }, + },callbackDone); + } + + exports.build = function(server) { + buildAssets(server,function(err) { + if (err) { + throw err; + } + console.log('Server init done.'); + // init other stuff... + }); + } + + +## Config options + + The config option templates can be found in code; + todo: add link + +### Builder Config + + Only configCreate if required to set in root and/or in asset. + + 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, + }, + }, + }; + +### Assembler Config + + required: assetType,linkTarget,linkSources and linkMapping. + + 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', + }; + + ## Target/Source locations The asset target file location is a derived value. @@ -18,110 +184,102 @@ A Node.js library providing automatic site assets aggregation and content modifi linkTarget: '/static/js/ffa/assets.js', linkMapping: ['/static/':'www_static/',], linkSources: ['/static/js/site.js','/static/js/ffa/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js'], - Gives; - public js file: http://localhost:8080/static/js/site.js - public js file: http://localhost:8080/static/js/ffa/assets.js - public js file: http://localhost:8080/static/js/ffa/jquery-2.1.3/jquery.js - filesystem file: www_static/js/site.js - filesystem file: www_static/js/ffa/assets.js - filesystem file: www_static/js/ffa/jquery-2.1.3/jquery.js - .gitignore line: www_static/js/ffa + + Result; - if buildConfig.linkTargetSingleResult = true render - static/js/ffa/assets.js - else - static/js/site.js - static/js/ffa/jquery-2.1.3/jquery.js + public js file: http://localhost:8080/static/js/site.js + public js file: http://localhost:8080/static/js/ffa/assets.js + public js file: http://localhost:8080/static/js/ffa/jquery-2.1.3/jquery.js + filesystem file: www_static/js/site.js + filesystem file: www_static/js/ffa/assets.js + filesystem file: www_static/js/ffa/jquery-2.1.3/jquery.js + .gitignore line: www_static/js/ffa + + if buildConfig.linkTargetSingleResult = true render + static/js/ffa/assets.js + else + static/js/site.js + static/js/ffa/jquery-2.1.3/jquery.js -## Usage +## Objects - // Create config object - var buildConfig = { - downloadStartDelay: null, - downloadForce: false, - downloadOptions: { - timeout: 10000, - maxResponseLength: 10000000, - agent: 'node-ff-assets asset fetcher' - }, - - linkTargetSingleResult: true, - linkTarget: '/static-uri/js/assets.js', - linkSources: [ - '/static/js/lib/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js', - '/static/js/lib/foobar/dyna.js@@http://localhost/force-download-on-every-build-for-this-file.js', - '/static/js/site.js' - ], - linkMapping: ['/static-uri/':'www_static/',], - - assetHeader: '\n/* node-ff-assets: begin */\n\n', - assetFooter: '\n/* node-ff-assets: end */\n\n', - assetSeperator: '\n/* node-ff-assets: next */\n', - } + For some use cases the assets.build(config,callback) is flexable enough. + Then there are two objects which may provide the needed options. + +### Object AssetsBuilder + + var assets = require('node-ff-assets'); + var builderConfig = {...}; + + // Create builder + var assetsBuilderA = new assets.AssetsBuilder(builderConfig); - // Development override, targets still gets build but result is full list of assets. - buildConfig.linkTargetSingleResult = false; - - // Push local js/css files as uri sources into config - NodeFFAssets.pushLinkSourcesSync(buildConfig, '/static-uri/js/', 'www_public/js/'); - NodeFFAssets.pushLinkSourcesSync(buildConfig, '/static-uri/js/model/', 'www_public/js/model/'); - NodeFFAssets.pushLinkSourcesSync(buildConfig, '/static-uri/js/model/meta', 'www_public/js/model/meta'); - - // Require library - var NodeFFAssets = require('node-ff-assets'); - - // Create simpel builder - var assetBuilderA = new NodeFFAssets.ResourceBuilder(buildConfig); - - // Same as default, plain inclusion of content. - var assetBuilderB = new NodeFFAssets.ResourceBuilder(buildConfig,NodeFFAssets.FunctionFactory.Constructor.readFile()); - - // Minify content, with optional Minify config options. - var assetBuilderC = new NodeFFAssets.ResourceBuilder(buildConfig,NodeFFAssets.FunctionFactory.Constructor.readFileMinify()); - var assetBuilderD = new NodeFFAssets.ResourceBuilder(buildConfig,NodeFFAssets.FunctionFactory.Constructor.readFileMinify(minifyOptions)); - - // Third party content modifiers, with readFile function - var assetBuilderE = new NodeFFAssets.ResourceBuilder(buildConfig,function(file,callback) { - // should return file data to callback(err,data); - ); - - // Hook events - assetBuilderA.on('begin', function() { }); - assetBuilderA.on('end', function() { }); - assetBuilderA.on('log', function(logLevel, logMessage) { }); - assetBuilderA.on('error', function(err) { }); - assetBuilderA.on('result', function(resultValue) { }); - assetBuilderA.on('file-download-pre', function(remoteUrl) { }); - assetBuilderA.on('file-download-post', function(localFile) { }); - assetBuilderA.on('file-read-pre', function(localFile) { }); - assetBuilderA.on('file-read-post', function(localFile) { }); - assetBuilderA.on('file-write-pre', function(targetFile) { }); - assetBuilderA.on('file-write-post', function(targetFile) { }); - - // Use build in event functions - assetBuilderB.on('log', NodeFFAssets.FunctionFactory.Event.log.console('css')); - assetBuilderB.on('result', NodeFFAssets.FunctionFactory.Event.result.objectSet(server,'includeCssFiles')); - - // Run builder - assetBuilderC.run(function(err) { + // Run all + assetsBuilderB.runAll(function(err) { //done }); - // Run all builders - NodeFFAssets.runAll([ - assetBuilderA, - assetBuilderB, - assetBuilderC, - ], function(err) { - // all done - }); + // Run singles + async.series([ + assetsBuilderC.runAsset('css'), + assetsBuilderD.runAsset('js'), + ],function(err) { + //done + }); - // Render result + +### Object AssetAssembler + + var assets = require('node-ff-assets'); + var assemblerConfig = {...}; + + // Create default assembler. + var assetAssemblerA = new assets.AssetAssembler(assemblerConfig); + + // Same as default, plain inclusion of content. + var assetAssemblerB = new assets.AssetAssembler(assemblerConfig,assets.factory.assembler.constructor.readFile()); + + // Minify content, with optional Minify config options. + var assetAssemblerC = new assets.AssetAssembler(assemblerConfig,assets.factory.assembler.constructor.readFileMinify()); + var assetAssemblerD = new assets.AssetAssembler(assemblerConfig,assets.factory.assembler.constructor.readFileMinify(minifyOptions)); + + // Third party content modifiers, with readFile function + var assetAssemblerE = new assets.AssetAssembler(buildConfig,function(file,callback) { + // should return file data to callback(err,data); + ); + + // Registrate events (note: may change a bit) + assetAssemblerA.on('begin', function() { }); + assetAssemblerA.on('end', function() { }); + assetAssemblerA.on('log', function(logLevel, logMessage) { }); + assetAssemblerA.on('error', function(err) { }); + assetAssemblerA.on('result', function(resultValue) { }); + assetAssemblerA.on('file-download-pre', function(remoteUrl) { }); + assetAssemblerA.on('file-download-post', function(localFile) { }); + assetAssemblerA.on('file-read-pre', function(localFile) { }); + assetAssemblerA.on('file-read-post', function(localFile) { }); + assetAssemblerA.on('file-write-pre', function(targetFile) { }); + assetAssemblerA.on('file-write-post', function(targetFile) { }); + + // Use build in event functions + assetAssemblerB.on('log', assets.factory.assembler.event.log.console('css')); + assetAssemblerB.on('result', assets.factory.assembler.event.result.objectSet(server,'includeCssFiles')); + + // Run builder + assetAssemblerC.run(function(err) { + //done + }); + +## Rendering + + In these examples I use the server (app/express) object to store the results in for displaying. + Which the index page renderer will get and inject in page context. + function renderIndex(app) { return function (req, res, next) { res.render('index', { - includeCssFiles: app.get('includeCssFiles'), - includeJsFiles: app.get('includeJsFiles'), + includeCssFiles: server.get('ff_assets_css'), + includeJsFiles: server.get('ff_assets_js'), }); } }; @@ -133,11 +291,11 @@ A Node.js library providing automatic site assets aggregation and content modifi html(lang='en',ng-app='ffUI') head base(href='/ui') - title=title + title='Index' meta(charset='UTF-8') meta(http-equiv='X-UA-Compatible' content='IE=edge') meta(name='viewport' content='width=device-width, initial-scale=1') - meta(name='keywords' content=keywords) + meta(name='keywords' content='keywords') each cssFile in includeCssFiles link(rel='stylesheet' href='#{cssFile}') body @@ -150,132 +308,8 @@ A Node.js library providing automatic site assets aggregation and content modifi each jsFile in includeJsFiles script(src='#{jsFile}') +## Example log output -## Example mapping node_modules - - Some js libraries can be directly served from node_packages and thus also be - included the node-ff-assets output, but these required there own reverse mapping; - - '/static/module/bootstrap/': 'node_modules/bootstrap/dist/', - - This will let the parse find the /static/module/bootstrap/css/bootstrap.css resource and - maps it the local file; node_modules/bootstrap/dist/css/bootstrap.css - - // note1: www_static/[js/css]/lib is versioned ingored. - // note2: server routes; - // app.use('/static',express.static(path.join(__dirname,'www_static'))); - // app.use('/static/module/bootstrap',express.static(path.join(__dirname,'node_modules/bootstrap/dist'))); - - var NodeFFAssets = require('node-ff-assets'); - var buildCss = new NodeFFAssets.ResourceBuilder({ - linkMapping: { - '/static/module/bootstrap/': 'node_modules/bootstrap/dist/', - '/static/': 'www_static/', - }, - linkTarget: '/static/css/lib/assets.css', - linkSources: [ - '/static/module/bootstrap/css/bootstrap.css', - '/static/css/site.css', - ], - }); - buildCss.on('log', NodeFFAssets.FunctionFactory.Event.log.console('css')); - buildCss.on('result', NodeFFAssets.FunctionFactory.Event.result.objectSet(server,'includeCssFiles')); - buildCss.run(function(err) { - // done - }) - - // Render result as in usage example. - - -## Example complex - - var LogWinston = require('winston'); - var Log = LogWinston.loggers.get('main'); - var Mongoose = require('mongoose'); - var NodeFFAssets = require('node-ff-assets'); - - function createBuildConfig(type) { - var singleResult = 'false' != process.env.DEV_ASSETS_SINGLE_RESULT; - var linkMapping = { - '/static/module/bootstrap/': 'node_modules/bootstrap/dist/', - '/static/module/flot/': 'node_modules/flot/', - '/static/': 'www_static/', - } - if (type == 'css') { - return { - linkMapping: linkMapping, - linkTargetSingleResult: singleResult, - linkTarget: '/static/css/lib/assets.css', - linkSources: [ - '/static/module/bootstrap/css/bootstrap.css', - ], - }; - } else { - return { - downloadStartDelay: 200, - linkTargetSingleResult: singleResult, - linkMapping: linkMapping, - linkTarget: '/static/js/lib/assets.js', - linkSources: [ - '/static/js/lib/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js', - '/static/module/bootstrap/js/bootstrap.js', - '/static/module/flot/jquery.flot.js', - '/static/module/flot/jquery.flot.resize.js', - '/static/module/flot/jquery.flot.pie.js', - '/static/js/lib/angularjs-1.4.0-b4/angular.js@https://code.angularjs.org/1.4.0-beta.4/angular.js', - '/static/js/lib/angularjs-1.4.0-b4/angular-route.js@https://code.angularjs.org/1.4.0-beta.4/angular-route.js', - '/static/js/lib/angularjs-1.4.0-b4/angular-resource.js@https://code.angularjs.org/1.4.0-beta.4/angular-resource.js', - '/static/js/lib/angularjs-1.4.0-b4/angular-touch.js@https://code.angularjs.org/1.4.0-beta.4/angular-touch.js', - ], - }; - } - } - - function createBuildLogger(logType) { - return function(logLevel, logMessage) { - Log.log(logLevel, 'node-ff-assets-'+logType+' '+logMessage); - } - }; - - function buildAssets(server,callback) { - // Create asset config - var buildJsConfig = createBuildConfig('js'); - var buildCssConfig = createBuildConfig('css'); - - // Extends js/css lists with local folders - NodeFFAssets.pushLinkSourcesSync(buildJsConfig, '/static/js/', 'www_static/js/'); - NodeFFAssets.pushLinkSourcesSync(buildJsConfig, '/static/js/controller/', 'www_static/js/controller/'); - NodeFFAssets.pushLinkSourcesSync(buildCssConfig, '/static/css/', 'www_static/css/'); - - // Extend js list with crud entries - var localPort = server.get('server config').options.http.port; - var modelNames = Mongoose.connection.modelNames(); - for (i = 0; i < modelNames.length; i++) { - var modelName = modelNames[i]; - var assetLink = '/static/js/lib/xcrud/'+modelName+'.js@@http://localhost:'+localPort+'/ui/include/js/controller/'+modelName; - buildJsConfig.linkSources.push(assetLink); - } - - // Create builders - var buildJs = new NodeFFAssets.ResourceBuilder(buildJsConfig); // note: Minify fails here on some js. - var buildCss = new NodeFFAssets.ResourceBuilder(buildCssConfig,NodeFFAssets.FunctionFactory.Constructor.readFileMinify()); - - // Config build events - buildJs.on ('log', createBuildLogger('js')); - buildCss.on('log', createBuildLogger('css')); - buildJs.on ('result', NodeFFAssets.FunctionFactory.Event.result.objectSet(server,'includeJsFiles')); - buildCss.on('result', NodeFFAssets.FunctionFactory.Event.result.objectSet(server,'includeCssFiles')); - - // Run builders - NodeFFAssets.runAll([buildCss,buildJs],callback); - } - - exports.build = function(server) { - buildAssets(server,function() { - Log.info('Server init done.'); - }); - } - // Result First run info: node-ff-assets-css build begin for: /static/css/lib/assets.css debug: node-ff-assets-css readFile: node_modules/bootstrap/dist/css/bootstrap.css size: 285496 @@ -376,6 +410,11 @@ Add unit tests for any new or changed functionality. Lint and test your code. ## Release History +* 0.2.0 + redone objs + added tests + made jslint pass + * 0.1.2 added objectAdd * 0.1.1 Doc updates