From ca1d944dd30eb62797b41b4da042c744713c88b1 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 6 Nov 2014 23:02:54 +0100 Subject: [PATCH 01/59] expose server in API using it for CLI building of resources --- lib/websocket/transports/engineio/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/websocket/transports/engineio/index.js b/lib/websocket/transports/engineio/index.js index 6b76c29d..cfb3501a 100644 --- a/lib/websocket/transports/engineio/index.js +++ b/lib/websocket/transports/engineio/index.js @@ -38,6 +38,9 @@ module.exports = function(ss, messageEmitter, httpServer, config){ // Tell the SocketStream client to use this transport, passing any client-side config along to the wrapper ss.client.send('code', 'transport', "require('socketstream').assignTransport(" + JSON.stringify(config.client) + ");"); + // don't set up server for CLI and test + if (httpServer == null) return; + // Create a new Engine.IO server and bind to /ws ws = engine.attach(httpServer, config.server); // ws.installHandlers(httpServer, {prefix: '/ws'}); From ac1a788c8493ad0bacc246b2c3c91125e32cf453 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 18 Nov 2014 23:49:27 +0100 Subject: [PATCH 02/59] pass client name in dev --- lib/client/serve/dev.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index d00d0876..56ff4e5c 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -28,12 +28,15 @@ module.exports = function (ss, router, options) { params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); return asset.js(path, { + client: params.client, + clientId: params.ts, pathPrefix: params.pathPrefix }, function(output) { return utils.serve.js(output, response); }); }); router.on('/_serveDev/start?*', function(request, response) { + //TODO initCode for a client with ts=.. return utils.serve.js(system.serve.initCode(), response); }); @@ -42,8 +45,12 @@ module.exports = function (ss, router, options) { // Listen for requests for CSS files return router.on('/_serveDev/css?*', function(request, response) { var path; + params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); - return asset.css(path, {}, function(output) { + return asset.css(path, { + client: params.client, + clientId: params.ts + }, function(output) { return utils.serve.css(output, response); }); }); From 06dc8c3553abb8f146b03150599064c05aae9a75 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 18 Nov 2014 23:51:27 +0100 Subject: [PATCH 03/59] bundler will handle the asset combining Allow changing the include logic for html/css/code --- lib/client/bundler/index.js | 56 ++++++++++++++++++++++++++++++ lib/client/bundler/pack.js | 68 +++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 lib/client/bundler/index.js create mode 100644 lib/client/bundler/pack.js diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js new file mode 100644 index 00000000..4967025d --- /dev/null +++ b/lib/client/bundler/index.js @@ -0,0 +1,56 @@ +function includeFlags(overrides) { + var includes = { + css: true, + html: true, + system: true, + initCode: true + }; + if (overrides) { + for(var n in overrides) { includes[n] = overrides[n]; } + } + return includes; +} + + +module.exports = function bundler(ss,options){ + var pack = require('./pack')(ss,options); + + return { + + define: function define(client, paths) { + + if (typeof paths.view !== 'string') { + throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); + } + if (paths.view.indexOf('.') === -1) { + throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); + } + + // Alias 'templates' to 'tmpl' + if (paths.templates) { + paths.tmpl = paths.templates; + } + + // Force each into an array + ['css', 'code', 'tmpl'].forEach(function(assetType) { + if (!(paths[assetType] instanceof Array)) { + paths[assetType] = [paths[assetType]]; + return paths[assetType]; + } + }); + + // Define new client object + client.paths = paths; + client.includes = includeFlags(paths.includes); + + return client; + }, + + packCSS: pack.css, + packJS: pack.js, + + load: function load() { + //TODO something + } + }; +}; \ No newline at end of file diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js new file mode 100644 index 00000000..02c49b23 --- /dev/null +++ b/lib/client/bundler/pack.js @@ -0,0 +1,68 @@ +//TODO ss.root.lenth, ss.root +// clientDir + +module.exports = function(ss, options) { + +function packAssetSet(assetType, dir, client, postProcess) { + var filePaths, prefix, + paths = client.paths[assetType], + description = ss.client.describeAssets(client.name); + + function writeFile(fileContents) { + var fileName = description.paths[assetType]; + fs.writeFileSync(fileName, postProcess(fileContents)); + return log.info('✓'.green, 'Packed ' + filePaths.length + ' files into ' + fileName.substr(ss.root.length)); + } + + function processFiles(fileContents, i) { + var file, path, _ref; + if (!fileContents) { + fileContents = []; + } + if (!i) { + i = 0; + } + _ref = filePaths[i], path = _ref.path, file = _ref.file; + return asset[assetType](file, { + pathPrefix: path, + compress: true + }, function(output) { + fileContents.push(output); + if (filePaths[++i]) { + return processFiles(fileContents, i); + } else { + return writeFile(fileContents); + } + }); + } + + // Expand any dirs into real files + if (paths && paths.length > 0) { + filePaths = []; + prefix = pathlib.join(ss.root, dir); + paths.forEach(function(path) { + return magicPath.files(prefix, path).forEach(function(file) { + return filePaths.push({ + path: path, + file: file + }); + }); + }); + return processFiles(); + } +} + + + return { + + js: function(client, postProcess) { + packAssetSet('js', options.dirs.code, client, postProcess); + }, + + css: function(client, postProcess) { + packAssetSet('css', options.dirs.css, client, postProcess); + } + + }; +}; + From ba030e25cb2bd2372164a7df16f740d1afe81200 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 18 Nov 2014 23:53:41 +0100 Subject: [PATCH 04/59] missing var --- lib/client/serve/dev.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index 56ff4e5c..e41ca798 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -44,7 +44,8 @@ module.exports = function (ss, router, options) { // Listen for requests for CSS files return router.on('/_serveDev/css?*', function(request, response) { - var path; + var params, path, thisUrl; + thisUrl = url.parse(request.url); params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); return asset.css(path, { From c1423497384a640140f25ceffb8931027e51accb Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Wed, 19 Nov 2014 19:06:23 +0100 Subject: [PATCH 05/59] reworked bundler client bundler with the current asset combination --- lib/client/{ => bundler}/asset.js | 20 ++++---- lib/client/bundler/default.js | 58 +++++++++++++++++++++++ lib/client/bundler/index.js | 77 +++++++++++++------------------ lib/client/index.js | 1 + lib/client/serve/ondemand.js | 2 + lib/client/view.js | 7 ++- 6 files changed, 103 insertions(+), 62 deletions(-) rename lib/client/{ => bundler}/asset.js (93%) create mode 100644 lib/client/bundler/default.js diff --git a/lib/client/asset.js b/lib/client/bundler/asset.js similarity index 93% rename from lib/client/asset.js rename to lib/client/bundler/asset.js index 5f2e7d45..a9c93079 100644 --- a/lib/client/asset.js +++ b/lib/client/bundler/asset.js @@ -1,6 +1,3 @@ -// Client Asset File -// ----------------- -// An asset is a Code (JS or CoffeeScript), CSS or HTML file 'use strict'; var formatKb, formatters, fs, jsp, log, minifyJSFile, pathlib, pro, uglifyjs, wrap, wrapCode, @@ -12,11 +9,11 @@ pathlib = require('path'); uglifyjs = require('uglify-js'); -formatters = require('./formatters'); +formatters = require('../formatters'); -log = require('../utils/log'); +log = require('../../utils/log'); -wrap = require('./wrap'); +wrap = require('../wrap'); jsp = uglifyjs.parser; @@ -25,8 +22,8 @@ pro = uglifyjs.uglify; // Load, compile and minify the following assets module.exports = function(ss, options) { - var loadFile; - loadFile = function(dir, fileName, type, options, cb) { + + function loadFile(dir, fileName, type, options, cb) { var extension, formatter, path; dir = pathlib.join(ss.root, dir); path = pathlib.join(dir, fileName); @@ -43,11 +40,10 @@ module.exports = function(ss, options) { throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); } return formatter.compile(path.replace(/\\/g, '/'), options, cb); - }; - return { - - // Public + } + + return { js: function(path, opts, cb) { return loadFile(options.dirs.code, path, 'js', opts, function(output) { output = wrapCode(output, path, opts.pathPrefix, options); diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js new file mode 100644 index 00000000..fb14d2c5 --- /dev/null +++ b/lib/client/bundler/default.js @@ -0,0 +1,58 @@ +// Default bundler implementation + +function includeFlags(overrides) { + var includes = { + css: true, + html: true, + system: true, + initCode: true + }; + if (overrides) { + for(var n in overrides) { includes[n] = overrides[n]; } + } + return includes; +} + + +module.exports = function bundler(ss,options){ + var pack = require('./pack')(ss,options); + var asset = require('./asset')(ss,options); + + return { + asset: asset, + + define: function define(client, paths) { + + if (typeof paths.view !== 'string') { + throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); + } + if (paths.view.indexOf('.') === -1) { + throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); + } + + // Alias 'templates' to 'tmpl' + if (paths.templates) { + paths.tmpl = paths.templates; + } + + // Force each into an array + ['css', 'code', 'tmpl'].forEach(function(assetType) { + if (!(paths[assetType] instanceof Array)) { + paths[assetType] = [paths[assetType]]; + return paths[assetType]; + } + }); + + // Define new client object + client.paths = paths; + client.includes = includeFlags(paths.includes); + + return client; + }, + + packCSS: pack.css, + packJS: pack.js, + + loadFile: asset.loadFile + }; +}; \ No newline at end of file diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 4967025d..23a83d0e 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -1,56 +1,41 @@ -function includeFlags(overrides) { - var includes = { - css: true, - html: true, - system: true, - initCode: true - }; - if (overrides) { - for(var n in overrides) { includes[n] = overrides[n]; } - } - return includes; -} - - -module.exports = function bundler(ss,options){ - var pack = require('./pack')(ss,options); - return { +var defaultBundler, + bundlers = {}; // get bundler by client name - define: function define(client, paths) { +/** + * Define the bundler for a client + * @param client object to store the definition in + * @param args arguments passed to define + */ +exports.define = function bundler(ss,client,args,options) { - if (typeof paths.view !== 'string') { - throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); - } - if (paths.view.indexOf('.') === -1) { - throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); - } + var name = args[0], + pathsOrFunc = args[1]; - // Alias 'templates' to 'tmpl' - if (paths.templates) { - paths.tmpl = paths.templates; - } + defaultBundler = defaultBundler || require('./default')(ss,options); - // Force each into an array - ['css', 'code', 'tmpl'].forEach(function(assetType) { - if (!(paths[assetType] instanceof Array)) { - paths[assetType] = [paths[assetType]]; - return paths[assetType]; - } - }); + if (typeof pathsOrFunc === "function") { + bundlers[name] = pathsOrFunc(ss,options); + bundlers[name].define(client, args[2], args[3], args[4], args[5]); + } else { + defaultBundler.define(client, args[1]); + } +}; - // Define new client object - client.paths = paths; - client.includes = includeFlags(paths.includes); +/** + * Determine the bundler for a client + * @param client Query params with client=name or an actual client object + */ +exports.get = function bundler(ss,client,options){ - return client; - }, + if (client.bundler) return client.bundler; - packCSS: pack.css, - packJS: pack.js, + if (typeof client.client === "string") { + return bundlers[client.client] || defaultBundler; + } + if (typeof client.name === "string") { + return bundlers[client.name] || defaultBundler; + } - load: function load() { - //TODO something - } - }; + return defaultBundler; }; \ No newline at end of file diff --git a/lib/client/index.js b/lib/client/index.js index baf68d24..397ea897 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -21,6 +21,7 @@ var options = { packedAssets: packAssets || false, liveReload: ['code', 'css', 'static', 'templates', 'views'], dirs: { + client: '/client', code: '/client/code', css: '/client/css', static: '/client/static', diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index 1a09de97..2b1eb5bc 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -44,9 +44,11 @@ module.exports = function(ss, router, options) { output = []; dir = pathlib.join(ss.root, options.dirs.code); files = magicPath.files(dir, [path]); + //TODO determine client and get bundler return files.forEach(function(file) { var description; try { + //TODO return bundler.js() return asset.js(file, { pathPrefix: options.globalModules? null : path, compress: options.packAssets diff --git a/lib/client/view.js b/lib/client/view.js index a9dffd85..113d50a7 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -8,9 +8,8 @@ var pathlib = require('path'), wrap = require('./wrap'); module.exports = function(ss, client, options, cb) { - var templateEngine = require('./template_engine')(ss); - - var asset = require('./asset')(ss, options); + var templateEngine = require('./template_engine')(ss), + bundler = require('./bundler/index').get(ss, client, options); // Add links to CSS and JS files var includes = headers().concat(templates()); @@ -21,7 +20,7 @@ module.exports = function(ss, client, options, cb) { compress: options.packedAssets, filename: client.paths.view }; - return asset.html(client.paths.view, htmlOptions, cb); + return bundler.asset.html(client.paths.client, htmlOptions, cb); // When packing assets the default path to the CSS or JS file can be overridden // either with a string or a function, typically pointing to an resource on a CDN From a8d55747343718a4b68ccc4defc42417c848933f Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 20 Nov 2014 18:00:01 +0100 Subject: [PATCH 06/59] no asset ref --- lib/client/serve/ondemand.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index 2b1eb5bc..51e26106 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -21,8 +21,8 @@ utils = require('./utils'); queryCache = {}; module.exports = function(ss, router, options) { - var asset, code, serve, worker; - asset = require('../asset')(ss, options); + var code, serve, worker; + serve = function(processor) { return function(request, response) { var path; From e9bed65937d769eef52d8632a730b94bfc4113bb Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 20 Nov 2014 18:01:29 +0100 Subject: [PATCH 07/59] pack with bundler --- lib/client/bundler/default.js | 116 ++++++++++++++++++++++++---------- lib/client/bundler/index.js | 79 +++++++++++++++++++---- lib/client/bundler/pack.js | 94 +++++++++++++-------------- 3 files changed, 200 insertions(+), 89 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index fb14d2c5..6d680d39 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -1,5 +1,10 @@ // Default bundler implementation +var fs = require('fs'), + path = require('path'), + log = require('../../utils/log'); + + function includeFlags(overrides) { var includes = { css: true, @@ -13,46 +18,93 @@ function includeFlags(overrides) { return includes; } +function deleteOldFiles(clientDir) { + var filesDeleted, numFilesDeleted; + numFilesDeleted = 0; + filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { + return fs.unlinkSync(path.join(clientDir, fileName)); + }); + return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted'); +} -module.exports = function bundler(ss,options){ - var pack = require('./pack')(ss,options); - var asset = require('./asset')(ss,options); +function mkdir(dir) { + if (!fs.existsSync(dir)) { + return fs.mkdirSync(dir); + } +} - return { - asset: asset, - define: function define(client, paths) { +module.exports = function bundler(ss,client,options){ + var bundler = { + define: define, + load: load, + ensureAssetFolder: ensureAssetFolder + }; - if (typeof paths.view !== 'string') { - throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); - } - if (paths.view.indexOf('.') === -1) { - throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); - } + bundler.pack = require('./pack')(ss,client,bundler,options); + bundler.asset = require('./asset')(ss,options); + + return bundler; - // Alias 'templates' to 'tmpl' - if (paths.templates) { - paths.tmpl = paths.templates; + function define(paths) { + + if (typeof paths.view !== 'string') { + throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); + } + if (paths.view.indexOf('.') === -1) { + throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); + } + + // Alias 'templates' to 'tmpl' + if (paths.templates) { + paths.tmpl = paths.templates; + } + + // Force each into an array + ['css', 'code', 'tmpl'].forEach(function(assetType) { + if (!(paths[assetType] instanceof Array)) { + paths[assetType] = [paths[assetType]]; + return paths[assetType]; } + }); - // Force each into an array - ['css', 'code', 'tmpl'].forEach(function(assetType) { - if (!(paths[assetType] instanceof Array)) { - paths[assetType] = [paths[assetType]]; - return paths[assetType]; - } - }); + // Define new client object + client.paths = paths; + client.includes = includeFlags(paths.includes); - // Define new client object - client.paths = paths; - client.includes = includeFlags(paths.includes); + return client; + } - return client; - }, + function load() { + var containerDir = path.join(ss.root, options.dirs.assets); + var clientDir = path.join(containerDir, client.name); - packCSS: pack.css, - packJS: pack.js, + this.description = { + + //TODO perhaps mixin the abs versions by SS + paths: { + html: path.join(clientDir, client.id + '.html'), + js: path.join(clientDir, client.id + '.js'), + css: path.join(clientDir, client.id + '.css') + }, + relPaths: { + html: path.join(options.dirs.assets, client.name, client.id + '.html'), + js: path.join(options.dirs.assets, client.name, client.id + '.js'), + css: path.join(options.dirs.assets, client.name, client.id + '.css') + }, + dir: clientDir, + containerDir: containerDir + }; + } + + function ensureAssetFolder() { + + // Prepare folder + mkdir(this.description.containerDir); + mkdir(this.description.dir); + if (!(options.packedAssets && options.packedAssets.keepOldFiles)) { + deleteOldFiles(this.description.dir); + } + } +}; - loadFile: asset.loadFile - }; -}; \ No newline at end of file diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 23a83d0e..439bb5ca 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -1,24 +1,27 @@ +var fs = require('fs'), + log = require('../../utils/log'), + cleanCSS = require('clean-css'), + system = require('../system'), + view = require('../view'); -var defaultBundler, - bundlers = {}; // get bundler by client name +var bundlers = {}; // get bundler by client name /** * Define the bundler for a client * @param client object to store the definition in * @param args arguments passed to define */ -exports.define = function bundler(ss,client,args,options) { +exports.define = function defineBundler(ss,client,args,options) { var name = args[0], pathsOrFunc = args[1]; - defaultBundler = defaultBundler || require('./default')(ss,options); - if (typeof pathsOrFunc === "function") { bundlers[name] = pathsOrFunc(ss,options); bundlers[name].define(client, args[2], args[3], args[4], args[5]); } else { - defaultBundler.define(client, args[1]); + bundlers[name] = require('./default')(ss,client,options); + bundlers[name].define(args[1]); } }; @@ -26,16 +29,70 @@ exports.define = function bundler(ss,client,args,options) { * Determine the bundler for a client * @param client Query params with client=name or an actual client object */ -exports.get = function bundler(ss,client,options){ +function getBundler(ss,client,options){ if (client.bundler) return client.bundler; if (typeof client.client === "string") { - return bundlers[client.client] || defaultBundler; + return bundlers[client.client]; } if (typeof client.name === "string") { - return bundlers[client.name] || defaultBundler; + return bundlers[client.name]; } - return defaultBundler; -}; \ No newline at end of file + throw new Error('Unknow client '+(client.name || client.client) ); +} + +exports.get = getBundler; + +exports.load = function() { + for(var n in bundlers) bundlers[n].load(); +}; + +exports.pack = function pack(ss, client, options) { + client.pack = true; + + // the concrete bundler for the client + var bundler = getBundler(ss, client, options); + + /* PACKER */ + + log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow); + + bundler.ensureAssetFolder(); + + // Output CSS + bundler.pack.css(client, function(files) { + var minified, original; + original = files.join('\n'); + minified = cleanCSS.process(original); + log.info((' Minified CSS from ' + (formatKb(original.length)) + ' to ' + (formatKb(minified.length))).grey); + return minified; + }); + + // Output JS + bundler.pack.js(client, function(files) { + var parts = []; + if (client.includes.system) { + parts.push( system.serve.js({ compress:true }) ); + } + parts = parts.concat(files); + if (client.includes.initCode) { + parts.push( system.serve.initCode() ); + } + + return parts.join(";"); + }); + + // Output HTML view + return view(ss, client, options, function(html) { + fs.writeFileSync(bundler.description.paths.html, html); + return log.info('✓'.green, 'Created and cached HTML file ' + bundler.description.relPaths.html); + }); +}; + +// PRIVATE + +function formatKb(size) { + return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; +} diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js index 02c49b23..843b2b8c 100644 --- a/lib/client/bundler/pack.js +++ b/lib/client/bundler/pack.js @@ -1,65 +1,67 @@ -//TODO ss.root.lenth, ss.root -// clientDir +var fs = require('fs'), + path = require('path'), + magicPath = require('../magic_path'), + view = require('../view'); -module.exports = function(ss, options) { +module.exports = function(ss, client, bundler, options) { -function packAssetSet(assetType, dir, client, postProcess) { - var filePaths, prefix, - paths = client.paths[assetType], - description = ss.client.describeAssets(client.name); + function packAssetSet(assetType, dir, client, postProcess) { + var filePaths, prefix, + paths = client.paths[assetType], + description = bundler.description; - function writeFile(fileContents) { - var fileName = description.paths[assetType]; - fs.writeFileSync(fileName, postProcess(fileContents)); - return log.info('✓'.green, 'Packed ' + filePaths.length + ' files into ' + fileName.substr(ss.root.length)); - } - - function processFiles(fileContents, i) { - var file, path, _ref; - if (!fileContents) { - fileContents = []; - } - if (!i) { - i = 0; + function writeFile(fileContents) { + var fileName = description.paths[assetType]; + fs.writeFileSync(fileName, postProcess(fileContents)); + return log.info('✓'.green, 'Packed ' + filePaths.length + ' files into ' + fileName.substr(ss.root.length)); } - _ref = filePaths[i], path = _ref.path, file = _ref.file; - return asset[assetType](file, { - pathPrefix: path, - compress: true - }, function(output) { - fileContents.push(output); - if (filePaths[++i]) { - return processFiles(fileContents, i); - } else { - return writeFile(fileContents); + + function processFiles(fileContents, i) { + var file, path, _ref; + if (!fileContents) { + fileContents = []; } - }); - } + if (!i) { + i = 0; + } + _ref = filePaths[i], path = _ref.path, file = _ref.file; + return bundler.asset[assetType](file, { + pathPrefix: path, + compress: true + }, function(output) { + fileContents.push(output); + if (filePaths[++i]) { + return processFiles(fileContents, i); + } else { + return writeFile(fileContents); + } + }); + } - // Expand any dirs into real files - if (paths && paths.length > 0) { - filePaths = []; - prefix = pathlib.join(ss.root, dir); - paths.forEach(function(path) { - return magicPath.files(prefix, path).forEach(function(file) { - return filePaths.push({ - path: path, - file: file + // Expand any dirs into real files + if (paths && paths.length > 0) { + filePaths = []; + prefix = path.join(ss.root, dir); + paths.forEach(function(path) { + return magicPath.files(prefix, path).forEach(function(file) { + return filePaths.push({ + path: path, + file: file + }); }); }); - }); - return processFiles(); + return processFiles(); + } } -} return { - js: function(client, postProcess) { + js: function(postProcess) { packAssetSet('js', options.dirs.code, client, postProcess); }, - css: function(client, postProcess) { + css: function(postProcess) { packAssetSet('css', options.dirs.css, client, postProcess); } From 5c629daa4f53ee75f11f1b4bb74e68d9813f3214 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 20 Nov 2014 23:51:15 +0100 Subject: [PATCH 08/59] pack fix log include pack param --- lib/client/bundler/index.js | 4 ++-- lib/client/bundler/pack.js | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 439bb5ca..d4e9a7cf 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -62,7 +62,7 @@ exports.pack = function pack(ss, client, options) { bundler.ensureAssetFolder(); // Output CSS - bundler.pack.css(client, function(files) { + bundler.pack.css(function(files) { var minified, original; original = files.join('\n'); minified = cleanCSS.process(original); @@ -71,7 +71,7 @@ exports.pack = function pack(ss, client, options) { }); // Output JS - bundler.pack.js(client, function(files) { + bundler.pack.js(function(files) { var parts = []; if (client.includes.system) { parts.push( system.serve.js({ compress:true }) ); diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js index 843b2b8c..447c78c0 100644 --- a/lib/client/bundler/pack.js +++ b/lib/client/bundler/pack.js @@ -1,19 +1,20 @@ var fs = require('fs'), path = require('path'), + log = require('../../utils/log'), magicPath = require('../magic_path'), view = require('../view'); module.exports = function(ss, client, bundler, options) { function packAssetSet(assetType, dir, client, postProcess) { - var filePaths, prefix, - paths = client.paths[assetType], - description = bundler.description; + var filePaths, + prefix, + paths = client.paths[assetType]; function writeFile(fileContents) { - var fileName = description.paths[assetType]; + var fileName = bundler.description.paths[assetType]; fs.writeFileSync(fileName, postProcess(fileContents)); - return log.info('✓'.green, 'Packed ' + filePaths.length + ' files into ' + fileName.substr(ss.root.length)); + return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.description.relPaths[assetType]); } function processFiles(fileContents, i) { From 52795b6dd61302cd907b782ab2b5daf626009a3d Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 4 Dec 2014 08:41:42 +0100 Subject: [PATCH 09/59] TODO --- lib/client/wrap.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/client/wrap.js b/lib/client/wrap.js index 508bbd03..c91e2046 100644 --- a/lib/client/wrap.js +++ b/lib/client/wrap.js @@ -1,6 +1,8 @@ // Simple wrapper for modules 'use strict'; +//TODO review if this should be isolated in bundler API + exports.module = function(modPath, code) { return 'require.define("' + modPath + '", function (require, module, exports, __dirname, __filename){\n' + code + '\n});'; }; From 17fc4270770f8c88e1a8d0dbb8c4b213cb4b1586 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Fri, 30 Jan 2015 20:17:35 +0100 Subject: [PATCH 10/59] asset is in bundler --- lib/client/serve/dev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index e41ca798..3c26307f 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -12,7 +12,7 @@ var url = require('url'), // module.exports = function (ss, router, options) { var asset; - asset = require('../asset')(ss, options); + asset = require('../bundler/asset')(ss, options); // JAVASCRIPT From 0d8cdbf72639b60d4d4da750bbb46f1076bacd53 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 17:47:54 +0100 Subject: [PATCH 11/59] doc(bundler): How to write a custom bundler --- docs/js/docs-setup.js | 9 +++ .../tutorials/client_side_xbundler.html | 54 ++++++++++++++ lib/client/bundler/default.js | 1 + lib/client/bundler/index.js | 6 +- lib/client/bundler/pack.js | 6 +- .../tutorials/en/client_side_xbundler.ngdoc | 71 +++++++++++++++++++ 6 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 docs/partials/tutorials/client_side_xbundler.html create mode 100644 src/docs/tutorials/en/client_side_xbundler.ngdoc diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 9d9a5710..46f31cfd 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -41,6 +41,15 @@ NG_DOCS={ "shortDescription": "Client-Side Templates", "keywords": "accessible alice allowing allows angular apart app append argument astronomy bad bandwidth biology bob browser building built-in bundled called client client-side clients code coffeekup compiled construct convert correct create css data default define directories directory display div don dramatically easy ember engine engines enter example exiting extension external favorite file find flexibility folder format formatter functions generate github good happy html including inline iphone jade jquery js languages larger layoutless length lib library libs limit live ll logic main major manually match md mix mixing mobile model model-person module modules mustache npm number optional organizing out-of-the-box outputs overview pass passing people perfect person practice prefer prefix project raw ready recommended reduces refactoring refresh render required requires scale scope second send serve server serving simple simply single-page small socketstream solution source ss ss-hogan string student studies styl subdirectory suitable supported supports tag template templateengine templates templating time tmpl tmpl- tutorials types var view ways websocket wrappers wraps" }, + { + "section": "tutorials", + "id": "client_side_xbundler", + "shortName": "Client-Side Bundler", + "type": "overview", + "moduleName": "Client-Side Bundler", + "shortDescription": "Client-Side Bundler", + "keywords": "action additional arguments asset assets aware based browserify bulk bundle bundler bundlers bundling callback called cb changes client client-side code complete completely config contents create css current custom default define definition described development directory discuss dropped early ensureassetfolder experimental features file files function future html implement implementation implemented implementing individual jade js jspm lacks load method methods minification move named newer next_arg object objective optimisations options opts overview pack pass passed production referenced responsibilities return saved scss separate served server ss starting step supported templates text tmpl tutorials types var view views webpack webpackbundler worker" + }, { "section": "tutorials", "id": "defining_multiple_clients", diff --git a/docs/partials/tutorials/client_side_xbundler.html b/docs/partials/tutorials/client_side_xbundler.html new file mode 100644 index 00000000..388b9773 --- /dev/null +++ b/docs/partials/tutorials/client_side_xbundler.html @@ -0,0 +1,54 @@ +

+
+
+

+

Client-Side Bundler

+

Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view. +The default bundler will create the bundle based on the client definition as described in Client-Side Code and Client-Side Templates.

+

You can implement your own bundler and use it for a client definition. The bundlers are named and referenced by name.

+

Be aware the API is experimental. The current bundler is based on an early Browserify implementation, so it lacks some +features. The objective is to be able to move to bundling based on newer ones such as WebPack or JSPM. It should be possible +to implement a bundler that completely changes how the client is implemented. Hence there will be additional responsibilities +for bundlers in the future.

+
Custom Bundler
+

You can define a custom bundler by implementing a bundler function that will be called for each client it is used on.

+
function webpackBundler(ss, options) {
+    var bundler = {};
+    bundler.define = function(client,config,next_arg...) {};
+    bundler.load = function() {};
+    bundler.ensureAssetFolder = function() {}; // likely to be dropped
+    bundler.asset = {
+        html: function(path, opts, cb) { cb(output) },
+        css: function(path, opts, cb) { cb(output) },
+        js: function(path, opts, cb) { cb(output) },
+        worker: function(path, opts, cb) { cb(output) }
+    };
+    bundler.pack = {
+        css: function(cb) { cb(output); },
+        js: function(cb) { cb(output); }
+    };
+
+    return bundler;
+}
+

You can use a custom bundler by for a client view by specifying it in the definition.

+
ss.client.define('discuss', webpackBundler, {
+  view: './views/discuss.jade',
+  css:  './css/discuss.scss',
+  code: './code/discuss',
+  tmpl: './templates/discuss'
+});
+

The define method of the bundler will be called to complete ss.client.define.

+
Bundler Define define(client,config,..)
+

The define method will be called with a client object containing id, name, +If you pass additional arguments to define they will be passed to bundler.define. This may be dropped in the future.

+
Bundler Load load()
+

The load method is called as the first step to load the client views. This is done as a bulk action as part of starting +the server.

+
Bundler asset methods
+

For each of the asset types supported individual files can be served during development. +A callback function is passed, and must be called with the text contents.

+
Bundler pack methods
+

Files are saved in the assets directory for production use. The HTML file is the same as the one used during development, +so the asset.html method will be called. For JS and CSS the pack methods are called to do additional minification and +other optimisations.

+
diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 6d680d39..52b2e830 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -97,6 +97,7 @@ module.exports = function bundler(ss,client,options){ }; } + //TODO move to index.js function ensureAssetFolder() { // Prepare folder diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index d4e9a7cf..68e2b190 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -4,7 +4,11 @@ var fs = require('fs'), system = require('../system'), view = require('../view'); -var bundlers = {}; // get bundler by client name +/** + * Bundler by client name + * @type {{}} + */ +var bundlers = {}; /** * Define the bundler for a client diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js index 447c78c0..8d9a233a 100644 --- a/lib/client/bundler/pack.js +++ b/lib/client/bundler/pack.js @@ -1,3 +1,5 @@ +'use strict'; + var fs = require('fs'), path = require('path'), log = require('../../utils/log'), @@ -7,8 +9,8 @@ var fs = require('fs'), module.exports = function(ss, client, bundler, options) { function packAssetSet(assetType, dir, client, postProcess) { - var filePaths, - prefix, + var filePaths, + prefix, paths = client.paths[assetType]; function writeFile(fileContents) { diff --git a/src/docs/tutorials/en/client_side_xbundler.ngdoc b/src/docs/tutorials/en/client_side_xbundler.ngdoc new file mode 100644 index 00000000..5a60e1aa --- /dev/null +++ b/src/docs/tutorials/en/client_side_xbundler.ngdoc @@ -0,0 +1,71 @@ +@ngdoc overview +@name Client-Side Bundler + +@description +# Client-Side Bundler + +Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view. +The default bundler will create the bundle based on the client definition as described in Client-Side Code and Client-Side Templates. + +You can implement your own bundler and use it for a client definition. The bundlers are named and referenced by name. + +Be aware the *API is experimental*. The current bundler is based on an early Browserify implementation, so it lacks some +features. The objective is to be able to move to bundling based on newer ones such as WebPack or JSPM. It should be possible +to implement a bundler that completely changes how the client is implemented. Hence there will be additional responsibilities +for bundlers in the future. + +#### Custom Bundler + +You can define a custom bundler by implementing a bundler function that will be called for each client it is used on. + + function webpackBundler(ss, options) { + var bundler = {}; + bundler.define = function(client,config,next_arg...) {}; + bundler.load = function() {}; + bundler.ensureAssetFolder = function() {}; // likely to be dropped + bundler.asset = { + html: function(path, opts, cb) { cb(output) }, + css: function(path, opts, cb) { cb(output) }, + js: function(path, opts, cb) { cb(output) }, + worker: function(path, opts, cb) { cb(output) } + }; + bundler.pack = { + css: function(cb) { cb(output); }, + js: function(cb) { cb(output); } + }; + + return bundler; + } + +You can use a custom bundler by for a client view by specifying it in the definition. + + ss.client.define('discuss', webpackBundler, { + view: './views/discuss.jade', + css: './css/discuss.scss', + code: './code/discuss', + tmpl: './templates/discuss' + }); + +The define method of the bundler will be called to complete `ss.client.define`. + +#### Bundler Define `define(client,config,..)` + +The define method will be called with a client object containing `id`, `name`, +If you pass additional arguments to define they will be passed to `bundler.define`. This may be dropped in the future. + +#### Bundler Load `load()` + +The load method is called as the first step to load the client views. This is done as a bulk action as part of starting +the server. + +#### Bundler asset methods + +For each of the asset types supported individual files can be served during development. +A callback function is passed, and must be called with the text contents. + + +#### Bundler pack methods + +Files are saved in the assets directory for production use. The HTML file is the same as the one used during development, +so the `asset.html` method will be called. For JS and CSS the pack methods are called to do additional minification and +other optimisations. From cc1dfa7f1b2c2e46c4841521e5a084242309a67f Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 17:50:01 +0100 Subject: [PATCH 12/59] chore(linting): use strict --- lib/client/bundler/default.js | 4 ++-- lib/client/bundler/index.js | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 52b2e830..a20623ca 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -1,10 +1,10 @@ // Default bundler implementation +'use strict'; -var fs = require('fs'), +var fs = require('fs'), path = require('path'), log = require('../../utils/log'); - function includeFlags(overrides) { var includes = { css: true, diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 68e2b190..f4297bf8 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -1,3 +1,6 @@ +// Client-Side Bundler of assets in development and production +'use strict'; + var fs = require('fs'), log = require('../../utils/log'), cleanCSS = require('clean-css'), @@ -17,7 +20,7 @@ var bundlers = {}; */ exports.define = function defineBundler(ss,client,args,options) { - var name = args[0], + var name = args[0], pathsOrFunc = args[1]; if (typeof pathsOrFunc === "function") { @@ -35,7 +38,7 @@ exports.define = function defineBundler(ss,client,args,options) { */ function getBundler(ss,client,options){ - if (client.bundler) return client.bundler; + if (client.bundler) { return client.bundler; } if (typeof client.client === "string") { return bundlers[client.client]; @@ -50,7 +53,9 @@ function getBundler(ss,client,options){ exports.get = getBundler; exports.load = function() { - for(var n in bundlers) bundlers[n].load(); + for(var n in bundlers) { + bundlers[n].load(); + } }; exports.pack = function pack(ss, client, options) { From cf7641c4ca62a1d34dc2bc8c1d143746f204ab3c Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 17:54:44 +0100 Subject: [PATCH 13/59] feat(bundler): Use default bundler in the client --- lib/client/index.js | 60 +++++++----------------------------- lib/client/pack.js | 68 ++++++----------------------------------- lib/client/serve/dev.js | 6 ++-- lib/client/view.js | 6 ++-- 4 files changed, 26 insertions(+), 114 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index 397ea897..b9500c7b 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -11,7 +11,9 @@ require('colors'); var fs = require('fs'), path = require('path'), log = require('../utils/log'), - systemAssets = require('./system'); + systemAssets = require('./system'), + + bundler = require('./bundler/index'); // Determine if assets should be (re)packed on startup var packAssets = process.env['SS_PACK']; @@ -35,23 +37,6 @@ var options = { // Store each client as an object var clients = {}; -function includeFlags(overrides) { - var includes = { - css: true, - html: true, - system: true, - initCode: true - }, n; - if (overrides) { - for (n in overrides) { - if (overrides.hasOwnProperty(n)) { - includes[n] = overrides[n]; - } - } - } - return includes; -} - module.exports = function(ss, router) { // Require sub modules @@ -148,40 +133,19 @@ module.exports = function(ss, router) { if (clients[name]) { throw new Error('Client name \'' + name + '\' has already been defined'); } - if (typeof paths.view !== 'string') { - throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); - } - if (paths.view.indexOf('.') === -1) { - throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); - } - - // Alias 'templates' to 'tmpl' - if (paths.templates) { - paths.tmpl = paths.templates; - } - - // Force each into an array - ['css', 'code', 'tmpl'].forEach(function(assetType) { - if (!(paths[assetType] instanceof Array)) { - paths[assetType] = [paths[assetType]]; - return paths[assetType]; - } - }); - - // Define new client object - clients[name] = { - id: Number(Date.now()), - name: name, - paths: paths, - includes: includeFlags(paths.includes) - }; - return clients[name]; + // if a function is used construct a bundler with it otherwise use default bundler + var client = clients[name] = { name: name }; + client.id = Number(Date.now()); + bundler.define(ss,client,arguments,options); + return client; }, // Listen and serve incoming asset requests load: function() { var client, id, name, pack, entryInit; + bundler.load(); + // Cache instances of code formatters and template engines here // This may change in the future as I don't like hanging system objects // on the 'ss' internal API object, but for now it solves a problem @@ -221,11 +185,9 @@ module.exports = function(ss, router) { // Pack Assets if (packAssets) { - pack = require('./pack'); for (name in clients) { if (clients.hasOwnProperty(name)) { - client = clients[name]; - pack(ss, client, options); + bundler.pack(ss, clients[name], options); } } } diff --git a/lib/client/pack.js b/lib/client/pack.js index 1d793a4c..18f9e107 100644 --- a/lib/client/pack.js +++ b/lib/client/pack.js @@ -15,81 +15,33 @@ var fs = require('fs'), log = require('../utils/log'); module.exports = function(ss, client, options) { - var asset, clientDir, containerDir, packAssetSet; - asset = require('./asset')(ss, options); + var bundler = require('./bundler/index').get(ss, client, options); + var clientDir, containerDir; //TODO client.pack = true; - containerDir = pathlib.join(ss.root, options.dirs.assets); - clientDir = pathlib.join(containerDir, client.name); - packAssetSet = function(assetType, paths, dir, postProcess) { - var filePaths, prefix, processFiles, writeFile; - writeFile = function(fileContents) { - var fileName; - fileName = clientDir + '/' + client.id + '.' + assetType; - fs.writeFileSync(fileName, postProcess(fileContents)); - return log.info('✓'.green, 'Packed ' + filePaths.length + ' files into ' + fileName.substr(ss.root.length)); - }; - processFiles = function(fileContents, i) { - var file, path, _ref; - if (!fileContents) { - fileContents = []; - } - if (!i) { - i = 0; - } - _ref = filePaths[i]; - path = _ref.path; - file = _ref.file; - return asset[assetType](file, { - pathPrefix: path, - compress: true - }, function(output) { - fileContents.push(output); - if (filePaths[++i]) { - return processFiles(fileContents, i); - } else { - return writeFile(fileContents); - } - }); - }; - - // Expand any dirs into real files - if (paths && paths.length > 0) { - filePaths = []; - prefix = pathlib.join(ss.root, dir); - paths.forEach(function(path) { - return magicPath.files(prefix, path).forEach(function(file) { - return filePaths.push({ - path: path, - file: file - }); - }); - }); - return processFiles(); - } - }; /* PACKER */ log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow); - + + var description = ss.client.describeAssets(client.name); + // Prepare folder - mkdir(containerDir); - mkdir(clientDir); + description.ensureDirs(); if (!(options.packedAssets && options.packedAssets.keepOldFiles)) { - deleteOldFiles(clientDir); + deleteOldFiles(description.dir); } // Output CSS - packAssetSet('css', client.paths.css, options.dirs.css, function(files) { + bundler.packCSS(client, function(files) { var minified, original; original = files.join('\n'); - minified = cleanCSS().minify(original); + minified = cleanCSS.process(original); log.info((' Minified CSS from ' + (formatKb(original.length)) + ' to ' + (formatKb(minified.length))).grey); return minified; }); // Output JS - packAssetSet('js', client.paths.code, options.dirs.code, function(files) { + bundler.packJS(client, function(files) { var parts = []; if (client.includes.system) { parts.push( system.serve.js({ compress:true }) ); diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index 3c26307f..84d8ba57 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -11,8 +11,6 @@ var url = require('url'), // Expose asset server as the public API // module.exports = function (ss, router, options) { - var asset; - asset = require('../bundler/asset')(ss, options); // JAVASCRIPT @@ -27,7 +25,7 @@ module.exports = function (ss, router, options) { thisUrl = url.parse(request.url); params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); - return asset.js(path, { + return bundler.get(ss,params,options).asset.js(path, { client: params.client, clientId: params.ts, pathPrefix: params.pathPrefix @@ -48,7 +46,7 @@ module.exports = function (ss, router, options) { thisUrl = url.parse(request.url); params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); - return asset.css(path, { + return bundler.get(ss,params,options).asset.css(path, { client: params.client, clientId: params.ts }, function(output) { diff --git a/lib/client/view.js b/lib/client/view.js index 113d50a7..e389856f 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -87,7 +87,7 @@ module.exports = function(ss, client, options, cb) { if (client.includes.css) { client.paths.css.forEach(function(path) { return magicPath.files(pathlib.join(ss.root, options.dirs.css), path).forEach(function(file) { - return output.push(wrap.htmlTag.css('/_serveDev/css/' + file + '?ts=' + client.id)); + return output.push(wrap.htmlTag.css('/_serveDev/css/' + file + '?ts=' + client.id + '&client=' + client.name)); }); }); } @@ -95,8 +95,8 @@ module.exports = function(ss, client, options, cb) { // Send Application Code client.paths.code.forEach(function(path) { return magicPath.files(pathlib.join(ss.root, options.dirs.code), path).forEach(function(file) { - var url = '/_serveDev/code/' + file + '?ts=' + client.id; - if (! options.globalModules) { url += '&pathPrefix=' + path; } + var url = '/_serveDev/code/' + file + '?ts=' + client.id + '&client=' + client.name; + if (! options.globalModules) url += '&pathPrefix=' + path; return output.push(wrap.htmlTag.js(url)); }); }); From 03c1b04ff704624ee51ab6fa60aae3a289a002bb Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 18:09:32 +0100 Subject: [PATCH 14/59] perf(array): Use map over forEach --- lib/client/view.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/client/view.js b/lib/client/view.js index e389856f..52aa790f 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -44,9 +44,8 @@ module.exports = function(ss, client, options, cb) { } else { return defaultPath; } - } - - function templates() { + }; + templates = function() { var dir, files, output; dir = pathlib.join(ss.root, options.dirs.templates); output = []; From aea18d25e8365532ef48680ba4ef5d5d4879f5af Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 23:53:45 +0100 Subject: [PATCH 15/59] feat(bundler): Bundlers have common implementation methods ss.bundler provides common functionality --- lib/client/bundler/asset.js | 133 ---------------------------- lib/client/bundler/default.js | 69 ++++++++++----- lib/client/bundler/index.js | 160 ++++++++++++++++++++++++++++++++-- lib/client/bundler/pack.js | 61 +------------ lib/client/index.js | 3 + 5 files changed, 205 insertions(+), 221 deletions(-) delete mode 100644 lib/client/bundler/asset.js diff --git a/lib/client/bundler/asset.js b/lib/client/bundler/asset.js deleted file mode 100644 index a9c93079..00000000 --- a/lib/client/bundler/asset.js +++ /dev/null @@ -1,133 +0,0 @@ -'use strict'; - -var formatKb, formatters, fs, jsp, log, minifyJSFile, pathlib, pro, uglifyjs, wrap, wrapCode, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) {return i; }} return -1; }; - -fs = require('fs'); - -pathlib = require('path'); - -uglifyjs = require('uglify-js'); - -formatters = require('../formatters'); - -log = require('../../utils/log'); - -wrap = require('../wrap'); - -jsp = uglifyjs.parser; - -pro = uglifyjs.uglify; - -// Load, compile and minify the following assets - -module.exports = function(ss, options) { - - function loadFile(dir, fileName, type, options, cb) { - var extension, formatter, path; - dir = pathlib.join(ss.root, dir); - path = pathlib.join(dir, fileName); - extension = pathlib.extname(path); - extension = extension && extension.substring(1); // argh! - formatter = ss.client.formatters[extension]; - if (path.substr(0, dir.length) !== dir) { - throw new Error('Invalid path. Request for ' + path + ' must not live outside ' + dir); - } - if (!formatter) { - throw new Error('Unsupported file extension \'.' + extension + '\' when we were expecting some type of ' + (type.toUpperCase()) + ' file. Please provide a formatter for ' + (path.substring(ss.root.length)) + ' or move it to /client/static'); - } - if (formatter.assetType !== type) { - throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); - } - return formatter.compile(path.replace(/\\/g, '/'), options, cb); - } - - - return { - js: function(path, opts, cb) { - return loadFile(options.dirs.code, path, 'js', opts, function(output) { - output = wrapCode(output, path, opts.pathPrefix, options); - if (opts.compress && path.indexOf('.min') === -1) { - output = minifyJSFile(output, path); - } - return cb(output); - }); - }, - worker: function(path, opts, cb) { - return loadFile(options.dirs.workers, path, 'js', opts, function(output) { - if (opts.compress) { - output = minifyJSFile(output, path); - } - return cb(output); - }); - }, - css: function(path, opts, cb) { - return loadFile(options.dirs.css, path, 'css', opts, cb); - }, - html: function(path, opts, cb) { - return loadFile(options.dirs.views, path, 'html', opts, cb); - } - }; -}; - -// PRIVATE - -formatKb = function(size) { - return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; -}; - -minifyJSFile = function(originalCode, fileName) { - var ast, minifiedCode; - ast = jsp.parse(originalCode); - ast = pro.ast_mangle(ast); - ast = pro.ast_squeeze(ast); - minifiedCode = pro.gen_code(ast); - log.info((' Minified ' + fileName + ' from ' + (formatKb(originalCode.length)) + ' to ' + (formatKb(minifiedCode.length))).grey); - return minifiedCode; -}; - -// Before client-side code is sent to the browser any file which is NOT a library (e.g. /client/code/libs) -// is wrapped in a module wrapper (to keep vars local and allow you to require() one file in another). -// The 'system' directory is a special case - any module placed in this dir will not have a leading slash -wrapCode = function(code, path, pathPrefix, options) { - var modPath, pathAry, sp, i; - pathAry = path.split('/'); - - // Don't touch the code if it's in a 'libs' directory - if (__indexOf.call(pathAry, 'libs') >= 0) { - return code; - } - - if (__indexOf.call(pathAry, 'entry.js') === -1 && options && options.browserifyExcludePaths) { - for(i in options.browserifyExcludePaths) { - if (options.browserifyExcludePaths.hasOwnProperty(i)) { - if ( path.split( options.browserifyExcludePaths[i] )[0] === '' ) { - return code; - } - } - } - } - - // Don't add a leading slash if this is a 'system' module - if (__indexOf.call(pathAry, 'system') >= 0) { - modPath = pathAry[pathAry.length - 1]; - return wrap.module(modPath, code); - } else { - - // Otherwise treat as a regular module - modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/'); - - // Work out namespace for module - if (pathPrefix) { - - // Ignore any filenames in the path - if (pathPrefix.indexOf('.') > 0) { - sp = pathPrefix.split('/'); - sp.pop(); - pathPrefix = sp.join('/'); - } - modPath = path.substr(pathPrefix.length + 1); - } - return wrap.module('/' + modPath, code); - } -}; diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index a20623ca..ff58afd3 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -34,16 +34,23 @@ function mkdir(dir) { } -module.exports = function bundler(ss,client,options){ +module.exports = function(ss,client,options){ var bundler = { define: define, load: load, + asset: { + js: assetJS, + worker: assetWorker, + css: assetCSS, + html: assetHTML + }, + pack: { + js: packJS, + css: packCSS + }, ensureAssetFolder: ensureAssetFolder }; - bundler.pack = require('./pack')(ss,client,bundler,options); - bundler.asset = require('./asset')(ss,options); - return bundler; function define(paths) { @@ -76,25 +83,41 @@ module.exports = function bundler(ss,client,options){ } function load() { - var containerDir = path.join(ss.root, options.dirs.assets); - var clientDir = path.join(containerDir, client.name); - - this.description = { - - //TODO perhaps mixin the abs versions by SS - paths: { - html: path.join(clientDir, client.id + '.html'), - js: path.join(clientDir, client.id + '.js'), - css: path.join(clientDir, client.id + '.css') - }, - relPaths: { - html: path.join(options.dirs.assets, client.name, client.id + '.html'), - js: path.join(options.dirs.assets, client.name, client.id + '.js'), - css: path.join(options.dirs.assets, client.name, client.id + '.css') - }, - dir: clientDir, - containerDir: containerDir - }; + this.description = ss.bundler.descriptionFor(ss,client,options); + } + + function assetJS(path, opts, cb) { + return ss.bundler.loadFile(ss, options.dirs.code, path, 'js', opts, function(output) { + output = ss.bundler.wrapCode(output, path, opts.pathPrefix, options); + if (opts.compress && path.indexOf('.min') === -1) { + output = ss.bundler.minifyJSFile(output, path); + } + return cb(output); + }); + } + function assetWorker(path, opts, cb) { + return ss.bundler.loadFile(ss, options.dirs.workers, path, 'js', opts, function(output) { + if (opts.compress) { + output = ss.bundler.minifyJSFile(output, path); + } + return cb(output); + }); + } + + function assetCSS(path, opts, cb) { + return ss.bundler.loadFile(ss, options.dirs.css, path, 'css', opts, cb); + } + + function assetHTML(path, opts, cb) { + return ss.bundler.loadFile(ss, options.dirs.views, path, 'html', opts, cb); + } + + function packJS(postProcess) { + ss.bundler.packAssetSet('js', options.dirs.code, client, bundler, postProcess); + } + + function packCSS(postProcess) { + ss.bundler.packAssetSet('css', options.dirs.css, client, bundler, postProcess); } //TODO move to index.js diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index f4297bf8..4809c996 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -2,10 +2,15 @@ 'use strict'; var fs = require('fs'), + path = require('path'), log = require('../../utils/log'), cleanCSS = require('clean-css'), system = require('../system'), - view = require('../view'); + view = require('../view'), + wrap = require('../wrap'), + uglifyjs = require('uglify-js'), + jsp = uglifyjs.parser, + pro = uglifyjs.uglify; /** * Bundler by client name @@ -75,7 +80,7 @@ exports.pack = function pack(ss, client, options) { var minified, original; original = files.join('\n'); minified = cleanCSS.process(original); - log.info((' Minified CSS from ' + (formatKb(original.length)) + ' to ' + (formatKb(minified.length))).grey); + log.info((' Minified CSS from ' + (exports.formatKb(original.length)) + ' to ' + (exports.formatKb(minified.length))).grey); return minified; }); @@ -100,8 +105,153 @@ exports.pack = function pack(ss, client, options) { }); }; -// PRIVATE +// API for implementing bundlers -function formatKb(size) { - return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; +exports.loadFile = function loadFile(ss, dir, fileName, type, options, cb) { + dir = path.join(ss.root, dir); + var p = path.join(dir, fileName); + var extension = path.extname(p); + extension = extension && extension.substring(1); // argh! + var formatter = ss.client.formatters[extension]; + if (p.substr(0, dir.length) !== dir) { + throw new Error('Invalid path. Request for ' + p + ' must not live outside ' + dir); + } + if (!formatter) { + throw new Error('Unsupported file extension \'.' + extension + '\' when we were expecting some type of ' + (type.toUpperCase()) + ' file. Please provide a formatter for ' + (p.substring(ss.root.length)) + ' or move it to /client/static'); + } + if (formatter.assetType !== type) { + throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); + } + return formatter.compile(p.replace(/\\/g, '/'), options, cb); +}; + +exports.minifyJSFile = function minifyJSFile(originalCode, fileName) { + var ast = jsp.parse(originalCode); + ast = pro.ast_mangle(ast); + ast = pro.ast_squeeze(ast); + var minifiedCode = pro.gen_code(ast); + log.info((' Minified ' + fileName + ' from ' + (exports.formatKb(originalCode.length)) + ' to ' + (exports.formatKb(minifiedCode.length))).grey); + return minifiedCode; +}; + +exports.descriptionFor = function(ss,client,options) { + var containerDir = path.join(ss.root, options.dirs.assets); + var clientDir = path.join(containerDir, client.name); + + return { + + //TODO perhaps mixin the abs versions by SS + paths: { + html: path.join(clientDir, client.id + '.html'), + js: path.join(clientDir, client.id + '.js'), + css: path.join(clientDir, client.id + '.css') + }, + relPaths: { + html: path.join(options.dirs.assets, client.name, client.id + '.html'), + js: path.join(options.dirs.assets, client.name, client.id + '.js'), + css: path.join(options.dirs.assets, client.name, client.id + '.css') + }, + dir: clientDir, + containerDir: containerDir + }; +} + +exports.packAssetSet = function packAssetSet(assetType, dir, client, bundler, postProcess) { + var filePaths, + prefix, + paths = client.paths[assetType]; + + function writeFile(fileContents) { + var fileName = bundler.description.paths[assetType]; + fs.writeFileSync(fileName, postProcess(fileContents)); + return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.description.relPaths[assetType]); + } + + function processFiles(fileContents, i) { + var file, path, _ref; + if (!fileContents) { + fileContents = []; + } + if (!i) { + i = 0; + } + _ref = filePaths[i], path = _ref.path, file = _ref.file; + return bundler.asset[assetType](file, { + pathPrefix: path, + compress: true + }, function(output) { + fileContents.push(output); + if (filePaths[++i]) { + return processFiles(fileContents, i); + } else { + return writeFile(fileContents); + } + }); + } + + // Expand any dirs into real files + if (paths && paths.length > 0) { + filePaths = []; + prefix = path.join(ss.root, dir); + paths.forEach(function(path) { + return magicPath.files(prefix, path).forEach(function(file) { + return filePaths.push({ + path: path, + file: file + }); + }); + }); + return processFiles(); + } } + + +exports.formatKb = function formatKb(size) { + return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; +}; + +// Before client-side code is sent to the browser any file which is NOT a library (e.g. /client/code/libs) +// is wrapped in a module wrapper (to keep vars local and allow you to require() one file in another). +// The 'system' directory is a special case - any module placed in this dir will not have a leading slash +exports.wrapCode = function wrapCode(code, path, pathPrefix, options) { + var modPath, sp, i; + var pathAry = path.split('/'); + + // Don't touch the code if it's in a 'libs' directory + if (pathAry.indexOf('libs') >= 0) { + return code; + } + + if (pathAry.indexOf('entry.js') === -1 && options && options.browserifyExcludePaths) { + for(i in options.browserifyExcludePaths) { + if (options.browserifyExcludePaths.hasOwnProperty(i)) { + if ( path.split( options.browserifyExcludePaths[i] )[0] === '' ) { + return code; + } + } + } + } + + // Don't add a leading slash if this is a 'system' module + if (pathAry.indexOf('system') >= 0) { + modPath = pathAry[pathAry.length - 1]; + return wrap.module(modPath, code); + } else { + + // Otherwise treat as a regular module + modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/'); + + // Work out namespace for module + if (pathPrefix) { + + // Ignore any filenames in the path + if (pathPrefix.indexOf('.') > 0) { + sp = pathPrefix.split('/'); + sp.pop(); + pathPrefix = sp.join('/'); + } + modPath = path.substr(pathPrefix.length + 1); + } + return wrap.module('/' + modPath, code); + } +}; diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js index 8d9a233a..2cfbf1cb 100644 --- a/lib/client/bundler/pack.js +++ b/lib/client/bundler/pack.js @@ -8,66 +8,7 @@ var fs = require('fs'), module.exports = function(ss, client, bundler, options) { - function packAssetSet(assetType, dir, client, postProcess) { - var filePaths, - prefix, - paths = client.paths[assetType]; - function writeFile(fileContents) { - var fileName = bundler.description.paths[assetType]; - fs.writeFileSync(fileName, postProcess(fileContents)); - return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.description.relPaths[assetType]); - } - - function processFiles(fileContents, i) { - var file, path, _ref; - if (!fileContents) { - fileContents = []; - } - if (!i) { - i = 0; - } - _ref = filePaths[i], path = _ref.path, file = _ref.file; - return bundler.asset[assetType](file, { - pathPrefix: path, - compress: true - }, function(output) { - fileContents.push(output); - if (filePaths[++i]) { - return processFiles(fileContents, i); - } else { - return writeFile(fileContents); - } - }); - } - - // Expand any dirs into real files - if (paths && paths.length > 0) { - filePaths = []; - prefix = path.join(ss.root, dir); - paths.forEach(function(path) { - return magicPath.files(prefix, path).forEach(function(file) { - return filePaths.push({ - path: path, - file: file - }); - }); - }); - return processFiles(); - } - } - - - return { - - js: function(postProcess) { - packAssetSet('js', options.dirs.code, client, postProcess); - }, - - css: function(postProcess) { - packAssetSet('css', options.dirs.css, client, postProcess); - } - - }; + return ; }; diff --git a/lib/client/index.js b/lib/client/index.js index b9500c7b..4ca1b709 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -39,6 +39,9 @@ var clients = {}; module.exports = function(ss, router) { + // make bundler methods available for default and other implementations + ss.bundler = bundler; + // Require sub modules var templateEngine = require('./template_engine')(ss), formatters = require('./formatters')(ss), From ef97274b250afd610807d087e6dbf6700afe2ced Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 31 Jan 2015 23:56:19 +0100 Subject: [PATCH 16/59] perf(array): indexOf direct call No need to support Node JS without Array.indexOf --- lib/client/live_reload.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/client/live_reload.js b/lib/client/live_reload.js index f57fcdb9..f0b64998 100644 --- a/lib/client/live_reload.js +++ b/lib/client/live_reload.js @@ -3,8 +3,7 @@ // Detects changes in client files and sends an event to connected browsers instructing them to refresh the page 'use strict'; -var chokidar, consoleMessage, cssExtensions, lastRun, pathlib, log, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) {return i;}} return -1; }; +var chokidar, consoleMessage, cssExtensions, lastRun, pathlib, log; require('colors'); @@ -60,7 +59,7 @@ module.exports = function(ss, options) { // onChange = function (path) { var action, _ref; - action = (_ref = pathlib.extname(path), __indexOf.call(cssExtensions, _ref) >= 0) ? 'updateCSS' : 'reload'; + action = (_ref = pathlib.extname(path), cssExtensions.indexOf(_ref) >= 0) ? 'updateCSS' : 'reload'; if ((Date.now() - lastRun[action]) > 1000) { // Reload browser max once per second log.info('✎'.green, consoleMessage[action].grey); ss.publish.all('__ss:' + action); From ca55b5daa4a5ca98718d6ce4df0119af29dc4478 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 01:02:30 +0100 Subject: [PATCH 17/59] feat(bundler): Manage saved asset files bundler.define returns the destination paths so pack can manage the output files. --- docs/js/docs-setup.js | 2 +- .../tutorials/client_side_xbundler.html | 6 ++- lib/client/bundler/default.js | 34 ++---------- lib/client/bundler/index.js | 52 +++++++++++++------ lib/client/bundler/pack.js | 14 ----- .../tutorials/en/client_side_xbundler.ngdoc | 6 ++- 6 files changed, 48 insertions(+), 66 deletions(-) delete mode 100644 lib/client/bundler/pack.js diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 46f31cfd..cef19541 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -48,7 +48,7 @@ NG_DOCS={ "type": "overview", "moduleName": "Client-Side Bundler", "shortDescription": "Client-Side Bundler", - "keywords": "action additional arguments asset assets aware based browserify bulk bundle bundler bundlers bundling callback called cb changes client client-side code complete completely config contents create css current custom default define definition described development directory discuss dropped early ensureassetfolder experimental features file files function future html implement implementation implemented implementing individual jade js jspm lacks load method methods minification move named newer next_arg object objective optimisations options opts overview pack pass passed production referenced responsibilities return saved scss separate served server ss starting step supported templates text tmpl tutorials types var view views webpack webpackbundler worker" + "keywords": "action additional arguments asset assets aware based browserify bulk bundle bundler bundlers bundling callback called cb changes client client-side code complete completely config contents create css current custom default define definition described destsfor development directory discuss dropped early experimental features file files function future html implement implementation implemented implementing individual jade js jspm lacks load method methods minification move named newer next_arg object objective optimisations options opts overview pack pass passed production referenced responsibilities return saved scss separate served server ss starting step supported templates text tmpl tutorials types var view views webpack webpackbundler worker" }, { "section": "tutorials", diff --git a/docs/partials/tutorials/client_side_xbundler.html b/docs/partials/tutorials/client_side_xbundler.html index 388b9773..2dda8b38 100644 --- a/docs/partials/tutorials/client_side_xbundler.html +++ b/docs/partials/tutorials/client_side_xbundler.html @@ -14,9 +14,11 @@
Custom Bundler

You can define a custom bundler by implementing a bundler function that will be called for each client it is used on.

function webpackBundler(ss, options) {
     var bundler = {};
-    bundler.define = function(client,config,next_arg...) {};
+    bundler.define = function(client,config,next_arg...) {
+        // ...
+        return ss.bundler.destsFor(ss,client,options);
+    };
     bundler.load = function() {};
-    bundler.ensureAssetFolder = function() {}; // likely to be dropped
     bundler.asset = {
         html: function(path, opts, cb) { cb(output) },
         css: function(path, opts, cb) { cb(output) },
diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js
index ff58afd3..3eec6918 100644
--- a/lib/client/bundler/default.js
+++ b/lib/client/bundler/default.js
@@ -18,22 +18,6 @@ function includeFlags(overrides) {
   return includes;
 }
 
-function deleteOldFiles(clientDir) {
-  var filesDeleted, numFilesDeleted;
-  numFilesDeleted = 0;
-  filesDeleted = fs.readdirSync(clientDir).map(function(fileName) {
-    return fs.unlinkSync(path.join(clientDir, fileName));
-  });
-  return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted');
-}
-
-function mkdir(dir) {
-  if (!fs.existsSync(dir)) {
-    return fs.mkdirSync(dir);
-  }
-}
-
-
 module.exports = function(ss,client,options){
   var bundler = {
     define: define,
@@ -47,8 +31,7 @@ module.exports = function(ss,client,options){
     pack: {
       js: packJS,
       css: packCSS
-    },
-    ensureAssetFolder: ensureAssetFolder
+    }
   };
 
   return bundler;
@@ -79,11 +62,11 @@ module.exports = function(ss,client,options){
     client.paths = paths;
     client.includes = includeFlags(paths.includes);
 
-    return client;
+    return ss.bundler.destsFor(ss,client,options);
 	}
 
   function load() {
-    this.description = ss.bundler.descriptionFor(ss,client,options);
+
   }
 
   function assetJS(path, opts, cb) {
@@ -119,16 +102,5 @@ module.exports = function(ss,client,options){
   function packCSS(postProcess) {
     ss.bundler.packAssetSet('css', options.dirs.css, client, bundler, postProcess);
   }
-
-  //TODO move to index.js
-  function ensureAssetFolder() {
-
-    // Prepare folder
-    mkdir(this.description.containerDir);
-    mkdir(this.description.dir);
-    if (!(options.packedAssets && options.packedAssets.keepOldFiles)) {
-      deleteOldFiles(this.description.dir);
-    }
-  }
 };
 
diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js
index 4809c996..279905be 100644
--- a/lib/client/bundler/index.js
+++ b/lib/client/bundler/index.js
@@ -8,6 +8,7 @@ var fs = require('fs'),
     system = require('../system'),
     view = require('../view'),
     wrap = require('../wrap'),
+    magicPath = require('../magic_path'),
     uglifyjs = require('uglify-js'),
     jsp = uglifyjs.parser,
     pro = uglifyjs.uglify;
@@ -30,10 +31,10 @@ exports.define = function defineBundler(ss,client,args,options) {
 
   if (typeof pathsOrFunc === "function") {
     bundlers[name] = pathsOrFunc(ss,options);
-    bundlers[name].define(client, args[2], args[3], args[4], args[5]);
+    bundlers[name].dests = bundlers[name].define(client, args[2], args[3], args[4], args[5]);
   } else {
     bundlers[name] = require('./default')(ss,client,options);
-    bundlers[name].define(args[1]);
+    bundlers[name].dests = bundlers[name].define(args[1]);
   }
 };
 
@@ -73,7 +74,12 @@ exports.pack = function pack(ss, client, options) {
 
   log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow);
   
-  bundler.ensureAssetFolder();
+  // Prepare folder
+  mkdir(bundler.dests.containerDir);
+  mkdir(bundler.dests.dir);
+  if (!(options.packedAssets && options.packedAssets.keepOldFiles)) {
+    deleteOldFiles(bundler.dests.dir);
+  }
 
   // Output CSS  
   bundler.pack.css(function(files) {
@@ -100,11 +106,26 @@ exports.pack = function pack(ss, client, options) {
 
   // Output HTML view  
   return view(ss, client, options, function(html) {
-    fs.writeFileSync(bundler.description.paths.html, html);
-    return log.info('✓'.green, 'Created and cached HTML file ' + bundler.description.relPaths.html);
+    fs.writeFileSync(bundler.dests.paths.html, html);
+    return log.info('✓'.green, 'Created and cached HTML file ' + bundler.dests.relPaths.html);
   });
 };
 
+
+function deleteOldFiles(clientDir) {
+  var numFilesDeleted = 0,
+      filesDeleted = fs.readdirSync(clientDir).map(function(fileName) {
+        return fs.unlinkSync(path.join(clientDir, fileName));
+      });
+  return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted');
+}
+
+function mkdir(dir) {
+  if (!fs.existsSync(dir)) {
+    return fs.mkdirSync(dir);
+  }
+}
+
 // API for implementing bundlers
 
 exports.loadFile = function loadFile(ss, dir, fileName, type, options, cb) {
@@ -134,7 +155,7 @@ exports.minifyJSFile = function minifyJSFile(originalCode, fileName) {
   return minifiedCode;
 };
 
-exports.descriptionFor = function(ss,client,options) {
+exports.destsFor = function(ss,client,options) {
   var containerDir = path.join(ss.root, options.dirs.assets);
   var clientDir = path.join(containerDir, client.name);
 
@@ -162,9 +183,9 @@ exports.packAssetSet = function packAssetSet(assetType, dir, client, bundler, po
     paths = client.paths[assetType];
 
   function writeFile(fileContents) {
-    var fileName = bundler.description.paths[assetType];
+    var fileName = bundler.dests.paths[assetType];
     fs.writeFileSync(fileName, postProcess(fileContents));
-    return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.description.relPaths[assetType]);
+    return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.dests.relPaths[assetType]);
   }
 
   function processFiles(fileContents, i) {
@@ -214,7 +235,6 @@ exports.formatKb = function formatKb(size) {
 // is wrapped in a module wrapper (to keep vars local and allow you to require() one file in another).
 // The 'system' directory is a special case - any module placed in this dir will not have a leading slash
 exports.wrapCode = function wrapCode(code, path, pathPrefix, options) {
-  var modPath, sp, i;
   var pathAry = path.split('/');
 
   // Don't touch the code if it's in a 'libs' directory
@@ -223,9 +243,10 @@ exports.wrapCode = function wrapCode(code, path, pathPrefix, options) {
   }
 
   if (pathAry.indexOf('entry.js') === -1 && options && options.browserifyExcludePaths) {
-    for(i in options.browserifyExcludePaths) {
-      if (options.browserifyExcludePaths.hasOwnProperty(i)) {
-        if ( path.split( options.browserifyExcludePaths[i] )[0] === '' ) {
+    //TODO is this an array? should be revised
+    for(var p in options.browserifyExcludePaths) {
+      if (options.browserifyExcludePaths.hasOwnProperty(p)) {
+        if ( path.split( options.browserifyExcludePaths[p] )[0] === '' ) {
           return code;
         }
       }
@@ -234,19 +255,18 @@ exports.wrapCode = function wrapCode(code, path, pathPrefix, options) {
 
   // Don't add a leading slash if this is a 'system' module
   if (pathAry.indexOf('system') >= 0) {
-    modPath = pathAry[pathAry.length - 1];
-    return wrap.module(modPath, code);
+    return wrap.module(pathAry[pathAry.length - 1], code);
   } else {
 
     // Otherwise treat as a regular module
-    modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/');
+    var modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/');
 
     // Work out namespace for module
     if (pathPrefix) {
 
       // Ignore any filenames in the path
       if (pathPrefix.indexOf('.') > 0) {
-        sp = pathPrefix.split('/');
+        var sp = pathPrefix.split('/');
         sp.pop();
         pathPrefix = sp.join('/');
       }
diff --git a/lib/client/bundler/pack.js b/lib/client/bundler/pack.js
deleted file mode 100644
index 2cfbf1cb..00000000
--- a/lib/client/bundler/pack.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict';
-
-var fs = require('fs'),
-    path = require('path'),
-    log = require('../../utils/log'),
-    magicPath = require('../magic_path'),
-    view = require('../view');
-
-module.exports = function(ss, client, bundler, options) {
-
-
-  return ;
-};
-
diff --git a/src/docs/tutorials/en/client_side_xbundler.ngdoc b/src/docs/tutorials/en/client_side_xbundler.ngdoc
index 5a60e1aa..20bcae87 100644
--- a/src/docs/tutorials/en/client_side_xbundler.ngdoc
+++ b/src/docs/tutorials/en/client_side_xbundler.ngdoc
@@ -20,9 +20,11 @@ You can define a custom bundler by implementing a bundler function that will be
 
     function webpackBundler(ss, options) {
         var bundler = {};
-        bundler.define = function(client,config,next_arg...) {};
+        bundler.define = function(client,config,next_arg...) {
+            // ...
+            return ss.bundler.destsFor(ss,client,options);
+        };
         bundler.load = function() {};
-        bundler.ensureAssetFolder = function() {}; // likely to be dropped
         bundler.asset = {
             html: function(path, opts, cb) { cb(output) },
             css: function(path, opts, cb) { cb(output) },

From f99eb5fead9d4e49dc61f7f532932d5c1542d0db Mon Sep 17 00:00:00 2001
From: Henrik Vendelbo 
Date: Sun, 1 Feb 2015 01:12:45 +0100
Subject: [PATCH 18/59] feat(bundler): Alternate bundler

webpack bundler for trying out
---
 lib/client/bundler/webpack.js | 92 +++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 lib/client/bundler/webpack.js

diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js
new file mode 100644
index 00000000..2a9f7ab5
--- /dev/null
+++ b/lib/client/bundler/webpack.js
@@ -0,0 +1,92 @@
+// Webpack bundler implementation
+'use strict';
+
+var fs = require('fs'),
+  path = require('path'),
+  log = require('../../utils/log');
+
+module.exports = function(webpack) {
+  return function(ss,options){
+    var bundler = {
+      define: define,
+      load: load,
+      pack: {
+        js: packJS,
+        css: packCSS
+      },
+      asset: {
+        html: assetHTML,
+        js: assetJS,
+        worker: assetWorker,
+        css: assetCSS
+      }
+    };
+    return bundler;
+
+    function define(client, paths) {
+
+      if (typeof paths.view !== 'string') {
+        throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array');
+      }
+      if (paths.view.indexOf('.') === -1) {
+        throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)');
+      }
+
+      bundler.client = client;
+
+      // Alias 'templates' to 'tmpl'
+      if (paths.templates) {
+        paths.tmpl = paths.templates;
+      }
+
+      // Force each into an array
+      ['css', 'code', 'tmpl'].forEach(function(assetType) {
+        if (!(paths[assetType] instanceof Array)) {
+          paths[assetType] = [paths[assetType]];
+          return paths[assetType];
+        }
+      });
+
+      // Define new client object
+      client.paths = paths;
+
+      return ss.bundler.destsFor(ss,client,options);
+    }
+
+    function load() {
+
+    }
+
+    function assetCSS(path, opts, cb) {
+      return ss.bundler.loadFile(ss, options.dirs.css, path, 'css', opts, cb);
+    }
+
+    function assetHTML(path, opts, cb) {
+      return ss.bundler.loadFile(ss, options.dirs.views, path, 'html', opts, cb);
+    }
+
+    function assetJS(path, opts, cb) {
+      webpack({}, function() {
+        cb('//');
+      });
+
+    }
+
+    function assetWorker(path, opts, cb) {
+      webpack({}, function() {
+        cb('//');
+      });
+
+    }
+
+    function packCSS(postProcess) {
+      ss.bundler.packAssetSet('css', options.dirs.css, client, bundler, postProcess);
+    }
+
+    function packJS() {
+
+    }
+  };
+
+};
+

From 5c37be20317e24d1b23417d489183581e575172b Mon Sep 17 00:00:00 2001
From: Henrik Vendelbo 
Date: Sun, 1 Feb 2015 15:28:22 +0100
Subject: [PATCH 19/59] doc(bundler): Bundling in development

---
 docs/js/docs-setup.js                         | 11 +++++++-
 .../tutorials/client_side_development.html    | 15 ++++++++++
 .../tutorials/client_side_xbundler.html       | 25 +++++++++++++----
 .../en/client_side_development.ngdoc          | 17 +++++++++++
 .../tutorials/en/client_side_xbundler.ngdoc   | 28 +++++++++++++++----
 5 files changed, 84 insertions(+), 12 deletions(-)
 create mode 100644 docs/partials/tutorials/client_side_development.html
 create mode 100644 src/docs/tutorials/en/client_side_development.ngdoc

diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js
index cef19541..8d556272 100644
--- a/docs/js/docs-setup.js
+++ b/docs/js/docs-setup.js
@@ -32,6 +32,15 @@ NG_DOCS={
       "shortDescription": "Client-Side Code",
       "keywords": "ability absolutely access accessed add allowing allows alphanumerically amd amount app apply approaches as-is automatically aware awesome backbone background bear better blank boiler-plate brought browser browserify browserifyexcludepaths cache called calls case cases catch change chaos choice clean client client-code client-side code coffee coming command common components connection console copy create created critical critically default define demand depend dependencies depends developer developers difference directores directory disable distinction doesn don easily ensure entry entrymodulename error established exactly example exceptions exclude excluded excluding execuring execute executed exist expect experience explicitly export file files folder forget form full functions future globalmodules goal going great handle head history hopes https inbuilt info instantly internally javascript jquery js killing leading legacy libraries library libs lightweight list live lives ll load loading long magic major making manage managing manually md mess mind modification modify module modules namespacing node null onus options order org overview packaging path paths performs point position problem problems project querystring reconnecting reference regular relative require required requires send serve served server server-side set share sharing side single slash small socketstream solution solutions solves special ss stack statement string structure subdirectories substack syntax system top track treated tricky true tutorials type typically underscore unique unstructured url values variable view views wade wanted web websocket window work works write writing"
     },
+    {
+      "section": "tutorials",
+      "id": "client_side_development",
+      "shortName": "Client-Side Development",
+      "type": "overview",
+      "moduleName": "Client-Side Development",
+      "shortDescription": "Client-Side Development",
+      "keywords": "asset assets break browser bundle bundler caching client client-side css determine development directory entry file fly html http injected js overview path pattern relative separate separately served timestamp tutorials type url view"
+    },
     {
       "section": "tutorials",
       "id": "client_side_templates",
@@ -48,7 +57,7 @@ NG_DOCS={
       "type": "overview",
       "moduleName": "Client-Side Bundler",
       "shortDescription": "Client-Side Bundler",
-      "keywords": "action additional arguments asset assets aware based browserify bulk bundle bundler bundlers bundling callback called cb changes client client-side code complete completely config contents create css current custom default define definition described destsfor development directory discuss dropped early experimental features file files function future html implement implementation implemented implementing individual jade js jspm lacks load method methods minification move named newer next_arg object objective optimisations options opts overview pack pass passed production referenced responsibilities return saved scss separate served server ss starting step supported templates text tmpl tutorials types var view views webpack webpackbundler worker"
+      "keywords": "action additional arguments array asset assets aware based behaviour browserify built-in bulk bundle bundler bundlers bundling callback called cb changes client client-side code complete completely config considered contents create css current custom default define definition described destsfor development directory dirs discuss dropped early entries existing experimental features file files forced function functionality future html implement implementation implemented implementing individual jade js jspm lacks load lot method methods minification move named newer next_arg object objective optimisations options opts overview pack pass passed path paths production referenced relative responsibilities return returns revised saved scss separate served server set shorthand shouldn sourcepaths ss starting starts step supported templates text tmpl tutorials types var view views webpack webpackbundler worker write"
     },
     {
       "section": "tutorials",
diff --git a/docs/partials/tutorials/client_side_development.html b/docs/partials/tutorials/client_side_development.html
new file mode 100644
index 00000000..dc64043b
--- /dev/null
+++ b/docs/partials/tutorials/client_side_development.html
@@ -0,0 +1,15 @@
+

+
+
+

+

Client-Side Development

+

Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view.

+

Each entry is served separately to the browser injected in the HTML on the fly.

+
    +
  • A relative path is given in the URL, relative to the "/client" directory.
  • +
  • The client is given in the URL to determine the bundler.
  • +
  • The asset type is specified in the URL to determine the bundler.
  • +
  • A timestamp is given in the URL to break any caching.
  • +
+

The URL pattern is http:///_serveDev/<type>/<relative path>?ts=<id>&client=<client>.

+
diff --git a/docs/partials/tutorials/client_side_xbundler.html b/docs/partials/tutorials/client_side_xbundler.html index 2dda8b38..4daf96d1 100644 --- a/docs/partials/tutorials/client_side_xbundler.html +++ b/docs/partials/tutorials/client_side_xbundler.html @@ -10,7 +10,7 @@

features. The objective is to be able to move to bundling based on newer ones such as WebPack or JSPM. It should be possible to implement a bundler that completely changes how the client is implemented. Hence there will be additional responsibilities for bundlers in the future.

-

Custom Bundler
+

Custom Bundler

You can define a custom bundler by implementing a bundler function that will be called for each client it is used on.

function webpackBundler(ss, options) {
     var bundler = {};
@@ -40,17 +40,30 @@ 
Custom Bundler
tmpl: './templates/discuss' });

The define method of the bundler will be called to complete ss.client.define.

-
Bundler Define define(client,config,..)
+

Bundler Define define(client,config,..)

The define method will be called with a client object containing id, name, If you pass additional arguments to define they will be passed to bundler.define. This may be dropped in the future.

-
Bundler Load load()
+

Bundler Load load()

The load method is called as the first step to load the client views. This is done as a bulk action as part of starting the server.

-
Bundler asset methods
+

Bundler asset methods

For each of the asset types supported individual files can be served during development. A callback function is passed, and must be called with the text contents.

-
Bundler pack methods
+

Bundler pack methods

Files are saved in the assets directory for production use. The HTML file is the same as the one used during development, so the asset.html method will be called. For JS and CSS the pack methods are called to do additional minification and other optimisations.

- +

Bundler shorthand

+

A lot of functionality is built-in. When you write your own bundler you shouldn't have to do it all over again. So most +of the existing behaviour can be called through ss.bundler.

+
ss.bundler.sourcePaths(ss,paths,options)
+

This returns a revised paths object. Paths should contain entries code, css, tmpl. They will be forced into an array. +If a path starts with "./", it is considered relative to "/client". Otherwise it is considered relative to "/client/code", +"/client/css", "/client/templates". These directory options can be set using ss.client.set.

+
ss.client.set({
+    dirs: {
+        client: '/client',
+        code: '/client/code'
+    }
+});
+
diff --git a/src/docs/tutorials/en/client_side_development.ngdoc b/src/docs/tutorials/en/client_side_development.ngdoc new file mode 100644 index 00000000..6eeac654 --- /dev/null +++ b/src/docs/tutorials/en/client_side_development.ngdoc @@ -0,0 +1,17 @@ +@ngdoc overview +@name Client-Side Development + +@description +# Client-Side Development + +Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view. + +Each entry is served separately to the browser injected in the HTML on the fly. + +* A relative path is given in the URL, relative to the "/client" directory. +* The client is given in the URL to determine the bundler. +* The asset type is specified in the URL to determine the bundler. +* A timestamp is given in the URL to break any caching. + +The URL pattern is `http:///_serveDev//?ts=&client=`. + diff --git a/src/docs/tutorials/en/client_side_xbundler.ngdoc b/src/docs/tutorials/en/client_side_xbundler.ngdoc index 20bcae87..78336444 100644 --- a/src/docs/tutorials/en/client_side_xbundler.ngdoc +++ b/src/docs/tutorials/en/client_side_xbundler.ngdoc @@ -14,7 +14,7 @@ features. The objective is to be able to move to bundling based on newer ones su to implement a bundler that completely changes how the client is implemented. Hence there will be additional responsibilities for bundlers in the future. -#### Custom Bundler +### Custom Bundler You can define a custom bundler by implementing a bundler function that will be called for each client it is used on. @@ -50,24 +50,42 @@ You can use a custom bundler by for a client view by specifying it in the defini The define method of the bundler will be called to complete `ss.client.define`. -#### Bundler Define `define(client,config,..)` +### Bundler Define `define(client,config,..)` The define method will be called with a client object containing `id`, `name`, If you pass additional arguments to define they will be passed to `bundler.define`. This may be dropped in the future. -#### Bundler Load `load()` +### Bundler Load `load()` The load method is called as the first step to load the client views. This is done as a bulk action as part of starting the server. -#### Bundler asset methods +### Bundler asset methods For each of the asset types supported individual files can be served during development. A callback function is passed, and must be called with the text contents. -#### Bundler pack methods +### Bundler pack methods Files are saved in the assets directory for production use. The HTML file is the same as the one used during development, so the `asset.html` method will be called. For JS and CSS the pack methods are called to do additional minification and other optimisations. + +### Bundler shorthand + +A lot of functionality is built-in. When you write your own bundler you shouldn't have to do it all over again. So most +of the existing behaviour can be called through `ss.bundler`. + +##### `ss.bundler.sourcePaths(ss,paths,options)` + +This returns a revised paths object. Paths should contain entries `code`, `css`, `tmpl`. They will be forced into an array. +If a path starts with "./", it is considered relative to "/client". Otherwise it is considered relative to "/client/code", +"/client/css", "/client/templates". These directory options can be set using `ss.client.set`. + + ss.client.set({ + dirs: { + client: '/client', + code: '/client/code' + } + }); From 0643377160faa3748d1ebf0040e55f407a63bee8 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 15:31:30 +0100 Subject: [PATCH 20/59] feat(bundler): Assets relative to /client sourcePaths common logic notes paths relative to /client rather than type specific. --- lib/client/bundler/default.js | 27 +++++++-------------------- lib/client/bundler/index.js | 25 +++++++++++++++++++++++++ lib/client/bundler/webpack.js | 21 ++++----------------- lib/client/serve/ondemand.js | 26 ++++++++++++-------------- lib/client/view.js | 6 +++--- 5 files changed, 51 insertions(+), 54 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 3eec6918..b3c78feb 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -45,21 +45,8 @@ module.exports = function(ss,client,options){ throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); } - // Alias 'templates' to 'tmpl' - if (paths.templates) { - paths.tmpl = paths.templates; - } - - // Force each into an array - ['css', 'code', 'tmpl'].forEach(function(assetType) { - if (!(paths[assetType] instanceof Array)) { - paths[assetType] = [paths[assetType]]; - return paths[assetType]; - } - }); - // Define new client object - client.paths = paths; + client.paths = ss.bundler.sourcePaths(ss,paths,options); client.includes = includeFlags(paths.includes); return ss.bundler.destsFor(ss,client,options); @@ -70,7 +57,7 @@ module.exports = function(ss,client,options){ } function assetJS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.code, path, 'js', opts, function(output) { + return ss.bundler.loadFile(ss, options.dirs.client, path, 'js', opts, function(output) { output = ss.bundler.wrapCode(output, path, opts.pathPrefix, options); if (opts.compress && path.indexOf('.min') === -1) { output = ss.bundler.minifyJSFile(output, path); @@ -79,7 +66,7 @@ module.exports = function(ss,client,options){ }); } function assetWorker(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.workers, path, 'js', opts, function(output) { + return ss.bundler.loadFile(ss, options.dirs.client, path, 'js', opts, function(output) { if (opts.compress) { output = ss.bundler.minifyJSFile(output, path); } @@ -88,19 +75,19 @@ module.exports = function(ss,client,options){ } function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.css, path, 'css', opts, cb); + return ss.bundler.loadFile(ss, options.dirs.client, path, 'css', opts, cb); } function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.views, path, 'html', opts, cb); + return ss.bundler.loadFile(ss, options.dirs.client, path, 'html', opts, cb); } function packJS(postProcess) { - ss.bundler.packAssetSet('js', options.dirs.code, client, bundler, postProcess); + ss.bundler.packAssetSet('js', options.dirs.client, client, bundler, postProcess); } function packCSS(postProcess) { - ss.bundler.packAssetSet('css', options.dirs.css, client, bundler, postProcess); + ss.bundler.packAssetSet('css', options.dirs.client, client, bundler, postProcess); } }; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 279905be..bcb7f972 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -155,6 +155,31 @@ exports.minifyJSFile = function minifyJSFile(originalCode, fileName) { return minifiedCode; }; +// input is decorated and returned +exports.sourcePaths = function(ss,paths,options) { + + function entries(from, dirType) { + if (from == null) { + return []; + } + var list = (from instanceof Array)? from : [from]; + + return list.map(function(value) { + var relClient = './' + path.relative(options.dirs.client, options.dirs[dirType]); + return value.substring(0,2) === './'? value : path.join(relClient, value); + }); + } + + paths.css = entries(paths.css, 'css'); + paths.code = entries(paths.code, 'code'); + paths.tmpl = entries(paths.tmpl || paths.templates, 'templates'); + + var relClient = './' + path.relative(options.dirs.client, options.dirs['views']); + paths.view = paths.view.substring(0,2) === './'? paths.view : path.join(relClient, paths.view); + + return paths; +}; + exports.destsFor = function(ss,client,options) { var containerDir = path.join(ss.root, options.dirs.assets); var clientDir = path.join(containerDir, client.name); diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 2a9f7ab5..375eff7b 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -34,21 +34,8 @@ module.exports = function(webpack) { bundler.client = client; - // Alias 'templates' to 'tmpl' - if (paths.templates) { - paths.tmpl = paths.templates; - } - - // Force each into an array - ['css', 'code', 'tmpl'].forEach(function(assetType) { - if (!(paths[assetType] instanceof Array)) { - paths[assetType] = [paths[assetType]]; - return paths[assetType]; - } - }); - // Define new client object - client.paths = paths; + client.paths = ss.bundler.sourcePaths(ss,paths,options); return ss.bundler.destsFor(ss,client,options); } @@ -58,11 +45,11 @@ module.exports = function(webpack) { } function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.css, path, 'css', opts, cb); + return ss.bundler.loadFile(ss, options.dirs.client, path, 'css', opts, cb); } function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.views, path, 'html', opts, cb); + return ss.bundler.loadFile(ss, options.dirs.client, path, 'html', opts, cb); } function assetJS(path, opts, cb) { @@ -80,7 +67,7 @@ module.exports = function(webpack) { } function packCSS(postProcess) { - ss.bundler.packAssetSet('css', options.dirs.css, client, bundler, postProcess); + ss.bundler.packAssetSet('css', options.dirs.client, client, bundler, postProcess); } function packJS() { diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index 51e26106..074f6ee1 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -21,12 +21,9 @@ utils = require('./utils'); queryCache = {}; module.exports = function(ss, router, options) { - var code, serve, worker; - - serve = function(processor) { + function serve(processor) { return function(request, response) { - var path; - path = utils.parseUrl(request.url); + var path = utils.parseUrl(request.url); if (options.packAssets && queryCache[path]) { return utils.serve.js(queryCache[path], response); } else { @@ -36,14 +33,14 @@ module.exports = function(ss, router, options) { }); } }; - }; + } // Async Code Loading - code = function(request, response, path, cb) { - var dir, files, output; - output = []; - dir = pathlib.join(ss.root, options.dirs.code); - files = magicPath.files(dir, [path]); + function code(request, response, path, cb) { + var output = [], + dir = pathlib.join(ss.root, options.dirs.client), + files = magicPath.files(dir, [path]); + //TODO determine client and get bundler return files.forEach(function(file) { var description; @@ -63,14 +60,15 @@ module.exports = function(ss, router, options) { return log.error(('! Unable to load ' + file + ' on demand:').red, description); } }); - }; + } // Web Workers - worker = function(request, response, path, cb) { + function worker(request, response, path, cb) { + //TODO return bundler.js() return asset.worker(path, { compress: options.packAssets }, cb); - }; + } // Bind to routes router.on('/_serve/code?*', serve(code)); diff --git a/lib/client/view.js b/lib/client/view.js index 52aa790f..6246e4a7 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -29,7 +29,7 @@ module.exports = function(ss, client, options, cb) { defaultPath = '/assets/' + client.name + '/' + client.id + '.' + type; if (link = (_ref = options.packedAssets) !== undefined ? (_ref1 = _ref.cdn) !== undefined ? _ref1[type] : void 0 : void 0) { if (typeof link === 'function') { - file = { + var file = { id: client.id, name: client.name, extension: type, @@ -85,7 +85,7 @@ module.exports = function(ss, client, options, cb) { // Send all CSS if (client.includes.css) { client.paths.css.forEach(function(path) { - return magicPath.files(pathlib.join(ss.root, options.dirs.css), path).forEach(function(file) { + return magicPath.files(pathlib.join(ss.root, options.dirs.client), path).forEach(function(file) { return output.push(wrap.htmlTag.css('/_serveDev/css/' + file + '?ts=' + client.id + '&client=' + client.name)); }); }); @@ -93,7 +93,7 @@ module.exports = function(ss, client, options, cb) { // Send Application Code client.paths.code.forEach(function(path) { - return magicPath.files(pathlib.join(ss.root, options.dirs.code), path).forEach(function(file) { + return magicPath.files(pathlib.join(ss.root, options.dirs.client), path).forEach(function(file) { var url = '/_serveDev/code/' + file + '?ts=' + client.id + '&client=' + client.name; if (! options.globalModules) url += '&pathPrefix=' + path; return output.push(wrap.htmlTag.js(url)); From a08f775352b021de3f04842735e098c06f43da0e Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 17:10:30 +0100 Subject: [PATCH 21/59] bug(merge): Fixed merge errors --- lib/client/index.js | 1 - lib/client/serve/dev.js | 3 ++- lib/client/view.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index 4ca1b709..a0d56a74 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -12,7 +12,6 @@ var fs = require('fs'), path = require('path'), log = require('../utils/log'), systemAssets = require('./system'), - bundler = require('./bundler/index'); // Determine if assets should be (re)packed on startup diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index 84d8ba57..464c1b0d 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -6,7 +6,8 @@ var url = require('url'), qs = require('querystring'), system = require('../system'), - utils = require('./utils'); + utils = require('./utils'), + bundler = require('../bundler/index'); // Expose asset server as the public API // diff --git a/lib/client/view.js b/lib/client/view.js index 6246e4a7..671b27bf 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -20,7 +20,7 @@ module.exports = function(ss, client, options, cb) { compress: options.packedAssets, filename: client.paths.view }; - return bundler.asset.html(client.paths.client, htmlOptions, cb); + return bundler.asset.html(client.paths.view, htmlOptions, cb); // When packing assets the default path to the CSS or JS file can be overridden // either with a string or a function, typically pointing to an resource on a CDN @@ -44,8 +44,9 @@ module.exports = function(ss, client, options, cb) { } else { return defaultPath; } - }; - templates = function() { + } + + function templates() { var dir, files, output; dir = pathlib.join(ss.root, options.dirs.templates); output = []; From 8788aeec9aa422b5dc8b620489d8d4ce4c323a94 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 20:23:17 +0100 Subject: [PATCH 22/59] feat(bundler): tmpl: '*' Templates are in '/client/templates' by default, but can be outside using paths starting with './' --- lib/client/view.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/client/view.js b/lib/client/view.js index 671b27bf..d5f4e7b7 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -47,13 +47,18 @@ module.exports = function(ss, client, options, cb) { } function templates() { - var dir, files, output; - dir = pathlib.join(ss.root, options.dirs.templates); - output = []; + var dir = pathlib.join(ss.root, options.dirs.client); + var output = []; if (client.paths.tmpl) { - files = []; + var files = []; client.paths.tmpl.forEach(function(tmpl) { - files = files.concat(magicPath.files(dir, tmpl)); + if (tmpl.substring(tmpl.length-2) == '/*') { + var matching = magicPath.files(pathlib.join(dir,tmpl.substring(0,tmpl.length-2)), '*'); + files = files.concat(matching.map(function(p) { return pathlib.join(tmpl.substring(0,tmpl.length-2),p); })); + } + else { + files = files.concat(magicPath.files(dir, tmpl)); + } }); templateEngine.generate(dir, files, function(templateHTML) { return output.push(templateHTML); From bb3133eb61c27e453389a065c36e89548be2b475 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 20:23:48 +0100 Subject: [PATCH 23/59] bug(ondemand): Call bundler --- lib/client/serve/ondemand.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index 074f6ee1..ab1ad832 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -4,21 +4,18 @@ // ---------------------- // Serves assets to browsers on demand, caching responses in production mode -var magicPath, pathlib, queryCache, utils, log; - require('colors'); -pathlib = require('path'); - -magicPath = require('../magic_path'); - -log = require('../../utils/log'); - -utils = require('./utils'); +var url = require('url'), + qs = require('querystring'), + pathlib = require('path'), + magicPath = require('../magic_path'), + log = require('../../utils/log'), + utils = require('./utils'); // When packing assets, cache responses to each query in RAM to avoid // having to re-compile and minify assets. TODO: Add limits/purging -queryCache = {}; +var queryCache = {}; module.exports = function(ss, router, options) { function serve(processor) { @@ -38,15 +35,17 @@ module.exports = function(ss, router, options) { // Async Code Loading function code(request, response, path, cb) { var output = [], + thisUrl = url.parse(request.url), + params = qs.parse(thisUrl.query), dir = pathlib.join(ss.root, options.dirs.client), files = magicPath.files(dir, [path]); - //TODO determine client and get bundler return files.forEach(function(file) { var description; try { - //TODO return bundler.js() - return asset.js(file, { + return bundler.get(ss,params,options).asset.js(file, { + client: params.client, + clientId: params.ts, pathPrefix: options.globalModules? null : path, compress: options.packAssets }, function(js) { @@ -64,8 +63,10 @@ module.exports = function(ss, router, options) { // Web Workers function worker(request, response, path, cb) { - //TODO return bundler.js() - return asset.worker(path, { + var thisUrl = url.parse(request.url), + params = qs.parse(thisUrl.query); + + return bundler.get(ss,params,options).asset.worker(path, { compress: options.packAssets }, cb); } @@ -74,3 +75,4 @@ module.exports = function(ss, router, options) { router.on('/_serve/code?*', serve(code)); return router.on('/_serve/worker?*', serve(worker)); }; + From 94b86e7b4750c4d92b62f250b593dbbef5eb74f0 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Feb 2015 20:24:36 +0100 Subject: [PATCH 24/59] feat(bundler): client.includes needed for now --- lib/client/bundler/webpack.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 375eff7b..617a2562 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -36,6 +36,7 @@ module.exports = function(webpack) { // Define new client object client.paths = ss.bundler.sourcePaths(ss,paths,options); + client.includes = {}; //TODO post-define default? return ss.bundler.destsFor(ss,client,options); } From e6cb07b9ef511adeed39f39a5f1a8752f97669c2 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Feb 2015 20:37:46 +0100 Subject: [PATCH 25/59] feat(bundler): ss.bundler.asset.launch --- lib/client/bundler/default.js | 6 ++++++ lib/client/bundler/index.js | 7 +++++++ lib/client/bundler/webpack.js | 6 ++++++ lib/client/index.js | 4 ++++ lib/client/serve/dev.js | 17 +++++++++++------ 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index b3c78feb..e55a7ace 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -25,6 +25,7 @@ module.exports = function(ss,client,options){ asset: { js: assetJS, worker: assetWorker, + launch: assetLaunch, css: assetCSS, html: assetHTML }, @@ -74,6 +75,11 @@ module.exports = function(ss,client,options){ }); } + function assetLaunch(cb) { + var output = ss.bundler.launchCode(ss, client); + return cb(output); + } + function assetCSS(path, opts, cb) { return ss.bundler.loadFile(ss, options.dirs.client, path, 'css', opts, cb); } diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index bcb7f972..5b2b080a 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -202,6 +202,13 @@ exports.destsFor = function(ss,client,options) { }; } +exports.launchCode = function(ss, client) { + var initCode = system.serve.initCode(), + entryInit = 'require("/entry");'; + initCode = initCode.replace(entryInit, 'require(' + client.entryInitPath + ');') + return initCode; +}; + exports.packAssetSet = function packAssetSet(assetType, dir, client, bundler, postProcess) { var filePaths, prefix, diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 617a2562..8c612d24 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -18,6 +18,7 @@ module.exports = function(webpack) { html: assetHTML, js: assetJS, worker: assetWorker, + launch: assetLaunch, css: assetCSS } }; @@ -60,6 +61,11 @@ module.exports = function(webpack) { } + function assetLaunch(cb) { + var output = ss.bundler.launchCode(ss, client); + return cb(output); + } + function assetWorker(path, opts, cb) { webpack({}, function() { cb('//'); diff --git a/lib/client/index.js b/lib/client/index.js index a0d56a74..6f27b43c 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -139,6 +139,10 @@ module.exports = function(ss, router) { var client = clients[name] = { name: name }; client.id = Number(Date.now()); bundler.define(ss,client,arguments,options); + + //TODO reconsider relative paths of all these + client.entryInitPath = '/code/' + client.name + '/entry'; + return client; }, diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index 464c1b0d..a295e74d 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -22,10 +22,10 @@ module.exports = function (ss, router, options) { // Listen for requests for application client code router.on('/_serveDev/code?*', function(request, response) { - var params, path, thisUrl; - thisUrl = url.parse(request.url); - params = qs.parse(thisUrl.query); - path = utils.parseUrl(request.url); + var thisUrl = url.parse(request.url), + params = qs.parse(thisUrl.query), + path = utils.parseUrl(request.url); + return bundler.get(ss,params,options).asset.js(path, { client: params.client, clientId: params.ts, @@ -35,8 +35,13 @@ module.exports = function (ss, router, options) { }); }); router.on('/_serveDev/start?*', function(request, response) { - //TODO initCode for a client with ts=.. - return utils.serve.js(system.serve.initCode(), response); + var thisUrl = url.parse(request.url), + params = qs.parse(thisUrl.query); + + bundler.get(ss,params,options).asset.launch(function(output) { + return utils.serve.js(output, response); + }); + }); // CSS From d3dcc2df06c2b7b7d10a736807e30ad4b2176d72 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Feb 2015 20:38:25 +0100 Subject: [PATCH 26/59] chore(declare): var and function declarations updated --- lib/client/system/index.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/client/system/index.js b/lib/client/system/index.js index e7ba376b..8a32b171 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -4,20 +4,15 @@ // ------------- // Loads system libraries and modules for the client. Also exposes an internal API // which other modules can use to send system assets to the client -var assets, fs, fsUtils, minifyJS, pathlib, send, uglifyjs, wrap; -fs = require('fs'); - -pathlib = require('path'); - -uglifyjs = require('uglify-js'); - -wrap = require('../wrap'); - -fsUtils = require('../../utils/file'); +var fs = require('fs'), + pathlib = require('path'), + uglifyjs = require('uglify-js'), + wrap = require('../wrap'), + fsUtils = require('../../utils/file'); // Allow internal modules to deliver assets to the browser -assets = { +var assets = { shims: [], libs: [], modules: {}, @@ -35,10 +30,11 @@ function pushUniqueAsset(listName,asset) { } // API to add new System Library or Module -exports.send = send = function (type, name, content, options) { +var send = exports.send = function (type, name, content, options) { if (options === null || options === undefined) { options = {}; } + switch (type) { case 'code': return assets.initCode.push(content); @@ -165,7 +161,7 @@ exports.serve = { }; // Private -minifyJS = function(originalCode) { +function minifyJS(originalCode) { var ast, jsp, pro; jsp = uglifyjs.parser; pro = uglifyjs.uglify; @@ -173,4 +169,4 @@ minifyJS = function(originalCode) { ast = pro.ast_mangle(ast); ast = pro.ast_squeeze(ast); return pro.gen_code(ast) + ';'; -}; +} From 4c4313e66f0a500b16baa25d8964cd45ff0dc529 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Feb 2015 21:58:06 +0100 Subject: [PATCH 27/59] feat(bundler): Improved bundler api --- lib/client/bundler/default.js | 16 +- lib/client/bundler/index.js | 479 +++++++++++++++++----------------- lib/client/bundler/webpack.js | 12 +- lib/client/index.js | 9 +- lib/client/pack.js | 2 +- lib/client/serve/dev.js | 11 +- lib/client/serve/ondemand.js | 7 +- lib/client/view.js | 2 +- 8 files changed, 276 insertions(+), 262 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index e55a7ace..8247b31c 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -47,10 +47,10 @@ module.exports = function(ss,client,options){ } // Define new client object - client.paths = ss.bundler.sourcePaths(ss,paths,options); + client.paths = ss.bundler.sourcePaths(paths); client.includes = includeFlags(paths.includes); - return ss.bundler.destsFor(ss,client,options); + return ss.bundler.destsFor(client); } function load() { @@ -58,8 +58,8 @@ module.exports = function(ss,client,options){ } function assetJS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'js', opts, function(output) { - output = ss.bundler.wrapCode(output, path, opts.pathPrefix, options); + return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { + output = ss.bundler.wrapCode(output, path, opts.pathPrefix); if (opts.compress && path.indexOf('.min') === -1) { output = ss.bundler.minifyJSFile(output, path); } @@ -67,7 +67,7 @@ module.exports = function(ss,client,options){ }); } function assetWorker(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'js', opts, function(output) { + return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { if (opts.compress) { output = ss.bundler.minifyJSFile(output, path); } @@ -76,16 +76,16 @@ module.exports = function(ss,client,options){ } function assetLaunch(cb) { - var output = ss.bundler.launchCode(ss, client); + var output = ss.bundler.launchCode(client); return cb(output); } function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'css', opts, cb); + return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); } function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'html', opts, cb); + return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } function packJS(postProcess) { diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 5b2b080a..85398d01 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -19,291 +19,302 @@ var fs = require('fs'), */ var bundlers = {}; -/** - * Define the bundler for a client - * @param client object to store the definition in - * @param args arguments passed to define - */ -exports.define = function defineBundler(ss,client,args,options) { +module.exports = function(ss,options) { - var name = args[0], - pathsOrFunc = args[1]; + function getBundler(client){ - if (typeof pathsOrFunc === "function") { - bundlers[name] = pathsOrFunc(ss,options); - bundlers[name].dests = bundlers[name].define(client, args[2], args[3], args[4], args[5]); - } else { - bundlers[name] = require('./default')(ss,client,options); - bundlers[name].dests = bundlers[name].define(args[1]); - } -}; - -/** - * Determine the bundler for a client - * @param client Query params with client=name or an actual client object - */ -function getBundler(ss,client,options){ + if (client.bundler) { return client.bundler; } - if (client.bundler) { return client.bundler; } + if (typeof client.client === "string") { + return bundlers[client.client]; + } + if (typeof client.name === "string") { + return bundlers[client.name]; + } - if (typeof client.client === "string") { - return bundlers[client.client]; - } - if (typeof client.name === "string") { - return bundlers[client.name]; + throw new Error('Unknow client '+(client.name || client.client) ); } - throw new Error('Unknow client '+(client.name || client.client) ); -} -exports.get = getBundler; + return { -exports.load = function() { - for(var n in bundlers) { - bundlers[n].load(); - } -}; + /** + * Define the bundler for a client + * @param client object to store the definition in + * @param args arguments passed to define + */ + define: function defineBundler(client,args) { -exports.pack = function pack(ss, client, options) { - client.pack = true; + var name = args[0], + pathsOrFunc = args[1]; - // the concrete bundler for the client - var bundler = getBundler(ss, client, options); + if (typeof pathsOrFunc === "function") { + bundlers[name] = pathsOrFunc(ss,options); + bundlers[name].dests = bundlers[name].define(client, args[2], args[3], args[4], args[5]); + } else { + bundlers[name] = require('./default')(ss,client,options); + bundlers[name].dests = bundlers[name].define(args[1]); + } - /* PACKER */ + //console.warn('client defined',name,client,bundlers[name].dests); + }, - log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow); - - // Prepare folder - mkdir(bundler.dests.containerDir); - mkdir(bundler.dests.dir); - if (!(options.packedAssets && options.packedAssets.keepOldFiles)) { - deleteOldFiles(bundler.dests.dir); - } + /** + * Determine the bundler for a client + * @param client Query params with client=name or an actual client object + */ + get: getBundler, - // Output CSS - bundler.pack.css(function(files) { - var minified, original; - original = files.join('\n'); - minified = cleanCSS.process(original); - log.info((' Minified CSS from ' + (exports.formatKb(original.length)) + ' to ' + (exports.formatKb(minified.length))).grey); - return minified; - }); - - // Output JS - bundler.pack.js(function(files) { - var parts = []; - if (client.includes.system) { - parts.push( system.serve.js({ compress:true }) ); - } - parts = parts.concat(files); - if (client.includes.initCode) { - parts.push( system.serve.initCode() ); - } + load: function() { + for(var n in bundlers) { + bundlers[n].load(); + } + }, - return parts.join(";"); - }); + pack: function pack(client) { + client.pack = true; - // Output HTML view - return view(ss, client, options, function(html) { - fs.writeFileSync(bundler.dests.paths.html, html); - return log.info('✓'.green, 'Created and cached HTML file ' + bundler.dests.relPaths.html); - }); -}; + // the concrete bundler for the client + var bundler = getBundler(client); + /* PACKER */ -function deleteOldFiles(clientDir) { - var numFilesDeleted = 0, - filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { - return fs.unlinkSync(path.join(clientDir, fileName)); + log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow); + + // Prepare folder + mkdir(bundler.dests.containerDir); + mkdir(bundler.dests.dir); + if (!(options.packedAssets && options.packedAssets.keepOldFiles)) { + deleteOldFiles(bundler.dests.dir); + } + + // Output CSS + bundler.pack.css(function(files) { + var minified, original; + original = files.join('\n'); + minified = cleanCSS.process(original); + log.info((' Minified CSS from ' + (exports.formatKb(original.length)) + ' to ' + (exports.formatKb(minified.length))).grey); + return minified; }); - return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted'); -} -function mkdir(dir) { - if (!fs.existsSync(dir)) { - return fs.mkdirSync(dir); - } -} + // Output JS + bundler.pack.js(function(files) { + var parts = []; + if (client.includes.system) { + parts.push( system.serve.js({ compress:true }) ); + } + parts = parts.concat(files); + if (client.includes.initCode) { + parts.push( system.serve.initCode() ); + } -// API for implementing bundlers + return parts.join(";"); + }); -exports.loadFile = function loadFile(ss, dir, fileName, type, options, cb) { - dir = path.join(ss.root, dir); - var p = path.join(dir, fileName); - var extension = path.extname(p); - extension = extension && extension.substring(1); // argh! - var formatter = ss.client.formatters[extension]; - if (p.substr(0, dir.length) !== dir) { - throw new Error('Invalid path. Request for ' + p + ' must not live outside ' + dir); - } - if (!formatter) { - throw new Error('Unsupported file extension \'.' + extension + '\' when we were expecting some type of ' + (type.toUpperCase()) + ' file. Please provide a formatter for ' + (p.substring(ss.root.length)) + ' or move it to /client/static'); - } - if (formatter.assetType !== type) { - throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); - } - return formatter.compile(p.replace(/\\/g, '/'), options, cb); -}; + // Output HTML view + return view(ss, client, options, function(html) { + fs.writeFileSync(bundler.dests.paths.html, html); + return log.info('✓'.green, 'Created and cached HTML file ' + bundler.dests.relPaths.html); + }); + }, -exports.minifyJSFile = function minifyJSFile(originalCode, fileName) { - var ast = jsp.parse(originalCode); - ast = pro.ast_mangle(ast); - ast = pro.ast_squeeze(ast); - var minifiedCode = pro.gen_code(ast); - log.info((' Minified ' + fileName + ' from ' + (exports.formatKb(originalCode.length)) + ' to ' + (exports.formatKb(minifiedCode.length))).grey); - return minifiedCode; -}; -// input is decorated and returned -exports.sourcePaths = function(ss,paths,options) { + // API for implementing bundlers - function entries(from, dirType) { - if (from == null) { - return []; - } - var list = (from instanceof Array)? from : [from]; + loadFile: function loadFile(dir, fileName, type, options, cb) { + dir = path.join(ss.root, dir); + var p = path.join(dir, fileName); + var extension = path.extname(p); + extension = extension && extension.substring(1); // argh! + var formatter = ss.client.formatters[extension]; + if (p.substr(0, dir.length) !== dir) { + throw new Error('Invalid path. Request for ' + p + ' must not live outside ' + dir); + } + if (!formatter) { + throw new Error('Unsupported file extension \'.' + extension + '\' when we were expecting some type of ' + (type.toUpperCase()) + ' file. Please provide a formatter for ' + (p.substring(ss.root.length)) + ' or move it to /client/static'); + } + if (formatter.assetType !== type) { + throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); + } + return formatter.compile(p.replace(/\\/g, '/'), options, cb); + }, - return list.map(function(value) { - var relClient = './' + path.relative(options.dirs.client, options.dirs[dirType]); - return value.substring(0,2) === './'? value : path.join(relClient, value); - }); - } + minifyJSFile: function minifyJSFile(originalCode, fileName) { + var ast = jsp.parse(originalCode); + ast = pro.ast_mangle(ast); + ast = pro.ast_squeeze(ast); + var minifiedCode = pro.gen_code(ast); + log.info((' Minified ' + fileName + ' from ' + (exports.formatKb(originalCode.length)) + ' to ' + (exports.formatKb(minifiedCode.length))).grey); + return minifiedCode; + }, - paths.css = entries(paths.css, 'css'); - paths.code = entries(paths.code, 'code'); - paths.tmpl = entries(paths.tmpl || paths.templates, 'templates'); + // input is decorated and returned + sourcePaths: function(paths) { - var relClient = './' + path.relative(options.dirs.client, options.dirs['views']); - paths.view = paths.view.substring(0,2) === './'? paths.view : path.join(relClient, paths.view); + function entries(from, dirType) { + if (from == null) { + return []; + } + var list = (from instanceof Array)? from : [from]; - return paths; -}; + return list.map(function(value) { + var relClient = './' + path.relative(options.dirs.client, options.dirs[dirType]); + return value.substring(0,2) === './'? value : path.join(relClient, value); + }); + } -exports.destsFor = function(ss,client,options) { - var containerDir = path.join(ss.root, options.dirs.assets); - var clientDir = path.join(containerDir, client.name); + paths.css = entries(paths.css, 'css'); + paths.code = entries(paths.code, 'code'); + paths.tmpl = entries(paths.tmpl || paths.templates, 'templates'); - return { + var relClient = './' + path.relative(options.dirs.client, options.dirs['views']); + paths.view = paths.view.substring(0,2) === './'? paths.view : path.join(relClient, paths.view); - //TODO perhaps mixin the abs versions by SS - paths: { - html: path.join(clientDir, client.id + '.html'), - js: path.join(clientDir, client.id + '.js'), - css: path.join(clientDir, client.id + '.css') + return paths; }, - relPaths: { - html: path.join(options.dirs.assets, client.name, client.id + '.html'), - js: path.join(options.dirs.assets, client.name, client.id + '.js'), - css: path.join(options.dirs.assets, client.name, client.id + '.css') + + destsFor: function(client) { + var containerDir = path.join(ss.root, options.dirs.assets); + var clientDir = path.join(containerDir, client.name); + + return { + + //TODO perhaps mixin the abs versions by SS + paths: { + html: path.join(clientDir, client.id + '.html'), + js: path.join(clientDir, client.id + '.js'), + css: path.join(clientDir, client.id + '.css') + }, + relPaths: { + html: path.join(options.dirs.assets, client.name, client.id + '.html'), + js: path.join(options.dirs.assets, client.name, client.id + '.js'), + css: path.join(options.dirs.assets, client.name, client.id + '.css') + }, + dir: clientDir, + containerDir: containerDir + }; }, - dir: clientDir, - containerDir: containerDir - }; -} -exports.launchCode = function(ss, client) { - var initCode = system.serve.initCode(), - entryInit = 'require("/entry");'; - initCode = initCode.replace(entryInit, 'require(' + client.entryInitPath + ');') - return initCode; -}; + launchCode: function(client) { + var initCode = system.serve.initCode(), + entryInit = 'require("/entry");'; + initCode = initCode.replace(entryInit, 'require(' + client.entryInitPath + ');') + return initCode; + }, -exports.packAssetSet = function packAssetSet(assetType, dir, client, bundler, postProcess) { - var filePaths, - prefix, - paths = client.paths[assetType]; + packAssetSet: function packAssetSet(assetType, dir, client, bundler, postProcess) { + var filePaths, + prefix, + paths = client.paths[assetType]; - function writeFile(fileContents) { - var fileName = bundler.dests.paths[assetType]; - fs.writeFileSync(fileName, postProcess(fileContents)); - return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.dests.relPaths[assetType]); - } + function writeFile(fileContents) { + var fileName = bundler.dests.paths[assetType]; + fs.writeFileSync(fileName, postProcess(fileContents)); + return log.info('✓'.green, 'Packed', filePaths.length, 'files into', bundler.dests.relPaths[assetType]); + } - function processFiles(fileContents, i) { - var file, path, _ref; - if (!fileContents) { - fileContents = []; - } - if (!i) { - i = 0; - } - _ref = filePaths[i], path = _ref.path, file = _ref.file; - return bundler.asset[assetType](file, { - pathPrefix: path, - compress: true - }, function(output) { - fileContents.push(output); - if (filePaths[++i]) { - return processFiles(fileContents, i); - } else { - return writeFile(fileContents); + function processFiles(fileContents, i) { + var file, path, _ref; + if (!fileContents) { + fileContents = []; + } + if (!i) { + i = 0; + } + _ref = filePaths[i], path = _ref.path, file = _ref.file; + return bundler.asset[assetType](file, { + pathPrefix: path, + compress: true + }, function(output) { + fileContents.push(output); + if (filePaths[++i]) { + return processFiles(fileContents, i); + } else { + return writeFile(fileContents); + } + }); } - }); - } - // Expand any dirs into real files - if (paths && paths.length > 0) { - filePaths = []; - prefix = path.join(ss.root, dir); - paths.forEach(function(path) { - return magicPath.files(prefix, path).forEach(function(file) { - return filePaths.push({ - path: path, - file: file + // Expand any dirs into real files + if (paths && paths.length > 0) { + filePaths = []; + prefix = path.join(ss.root, dir); + paths.forEach(function(path) { + return magicPath.files(prefix, path).forEach(function(file) { + return filePaths.push({ + path: path, + file: file + }); + }); }); - }); - }); - return processFiles(); - } -} + return processFiles(); + } + }, -exports.formatKb = function formatKb(size) { - return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; -}; + formatKb: function formatKb(size) { + return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; + }, -// Before client-side code is sent to the browser any file which is NOT a library (e.g. /client/code/libs) -// is wrapped in a module wrapper (to keep vars local and allow you to require() one file in another). -// The 'system' directory is a special case - any module placed in this dir will not have a leading slash -exports.wrapCode = function wrapCode(code, path, pathPrefix, options) { - var pathAry = path.split('/'); + // Before client-side code is sent to the browser any file which is NOT a library (e.g. /client/code/libs) + // is wrapped in a module wrapper (to keep vars local and allow you to require() one file in another). + // The 'system' directory is a special case - any module placed in this dir will not have a leading slash + wrapCode: function wrapCode(code, path, pathPrefix) { + var pathAry = path.split('/'); - // Don't touch the code if it's in a 'libs' directory - if (pathAry.indexOf('libs') >= 0) { - return code; - } + // Don't touch the code if it's in a 'libs' directory + if (pathAry.indexOf('libs') >= 0) { + return code; + } - if (pathAry.indexOf('entry.js') === -1 && options && options.browserifyExcludePaths) { - //TODO is this an array? should be revised - for(var p in options.browserifyExcludePaths) { - if (options.browserifyExcludePaths.hasOwnProperty(p)) { - if ( path.split( options.browserifyExcludePaths[p] )[0] === '' ) { - return code; + if (pathAry.indexOf('entry.js') === -1 && options && options.browserifyExcludePaths) { + //TODO is this an array? should be revised + for(var p in options.browserifyExcludePaths) { + if (options.browserifyExcludePaths.hasOwnProperty(p)) { + if ( path.split( options.browserifyExcludePaths[p] )[0] === '' ) { + return code; + } + } } } - } - } - // Don't add a leading slash if this is a 'system' module - if (pathAry.indexOf('system') >= 0) { - return wrap.module(pathAry[pathAry.length - 1], code); - } else { + // Don't add a leading slash if this is a 'system' module + if (pathAry.indexOf('system') >= 0) { + return wrap.module(pathAry[pathAry.length - 1], code); + } else { - // Otherwise treat as a regular module - var modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/'); + // Otherwise treat as a regular module + var modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/'); - // Work out namespace for module - if (pathPrefix) { + // Work out namespace for module + if (pathPrefix) { - // Ignore any filenames in the path - if (pathPrefix.indexOf('.') > 0) { - var sp = pathPrefix.split('/'); - sp.pop(); - pathPrefix = sp.join('/'); + // Ignore any filenames in the path + if (pathPrefix.indexOf('.') > 0) { + var sp = pathPrefix.split('/'); + sp.pop(); + pathPrefix = sp.join('/'); + } + modPath = path.substr(pathPrefix.length + 1); + } + return wrap.module('/' + modPath, code); } - modPath = path.substr(pathPrefix.length + 1); } - return wrap.module('/' + modPath, code); - } + + }; }; + +function deleteOldFiles(clientDir) { + var numFilesDeleted = 0, + filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { + return fs.unlinkSync(path.join(clientDir, fileName)); + }); + return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted'); +} + +function mkdir(dir) { + if (!fs.existsSync(dir)) { + return fs.mkdirSync(dir); + } +} + diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 8c612d24..261a8bfc 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -36,10 +36,10 @@ module.exports = function(webpack) { bundler.client = client; // Define new client object - client.paths = ss.bundler.sourcePaths(ss,paths,options); + client.paths = ss.bundler.sourcePaths(paths); client.includes = {}; //TODO post-define default? - return ss.bundler.destsFor(ss,client,options); + return ss.bundler.destsFor(client); } function load() { @@ -47,11 +47,11 @@ module.exports = function(webpack) { } function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'css', opts, cb); + return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); } function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(ss, options.dirs.client, path, 'html', opts, cb); + return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } function assetJS(path, opts, cb) { @@ -62,7 +62,7 @@ module.exports = function(webpack) { } function assetLaunch(cb) { - var output = ss.bundler.launchCode(ss, client); + var output = ss.bundler.launchCode(client); return cb(output); } @@ -74,7 +74,7 @@ module.exports = function(webpack) { } function packCSS(postProcess) { - ss.bundler.packAssetSet('css', options.dirs.client, client, bundler, postProcess); + ss.bundler.packAssetSet('css', options.dirs.client, bundler.client, bundler, postProcess); } function packJS() { diff --git a/lib/client/index.js b/lib/client/index.js index 6f27b43c..e94fee42 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -11,8 +11,7 @@ require('colors'); var fs = require('fs'), path = require('path'), log = require('../utils/log'), - systemAssets = require('./system'), - bundler = require('./bundler/index'); + systemAssets = require('./system'); // Determine if assets should be (re)packed on startup var packAssets = process.env['SS_PACK']; @@ -39,7 +38,7 @@ var clients = {}; module.exports = function(ss, router) { // make bundler methods available for default and other implementations - ss.bundler = bundler; + var bundler = ss.bundler = require('./bundler/index')(ss,options); // Require sub modules var templateEngine = require('./template_engine')(ss), @@ -138,7 +137,7 @@ module.exports = function(ss, router) { // if a function is used construct a bundler with it otherwise use default bundler var client = clients[name] = { name: name }; client.id = Number(Date.now()); - bundler.define(ss,client,arguments,options); + bundler.define(client,arguments); //TODO reconsider relative paths of all these client.entryInitPath = '/code/' + client.name + '/entry'; @@ -193,7 +192,7 @@ module.exports = function(ss, router) { if (packAssets) { for (name in clients) { if (clients.hasOwnProperty(name)) { - bundler.pack(ss, clients[name], options); + bundler.pack(clients[name]); } } } diff --git a/lib/client/pack.js b/lib/client/pack.js index 18f9e107..83d8b471 100644 --- a/lib/client/pack.js +++ b/lib/client/pack.js @@ -15,7 +15,7 @@ var fs = require('fs'), log = require('../utils/log'); module.exports = function(ss, client, options) { - var bundler = require('./bundler/index').get(ss, client, options); + var bundler = ss.bundler.get(client); var clientDir, containerDir; //TODO client.pack = true; diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index a295e74d..92699f42 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -6,13 +6,14 @@ var url = require('url'), qs = require('querystring'), system = require('../system'), - utils = require('./utils'), - bundler = require('../bundler/index'); + utils = require('./utils'); // Expose asset server as the public API // module.exports = function (ss, router, options) { + var bundler = require('../bundler/index')(ss,options); + // JAVASCRIPT // Serve system libraries and modules @@ -26,7 +27,7 @@ module.exports = function (ss, router, options) { params = qs.parse(thisUrl.query), path = utils.parseUrl(request.url); - return bundler.get(ss,params,options).asset.js(path, { + return bundler.get(params).asset.js(path, { client: params.client, clientId: params.ts, pathPrefix: params.pathPrefix @@ -38,7 +39,7 @@ module.exports = function (ss, router, options) { var thisUrl = url.parse(request.url), params = qs.parse(thisUrl.query); - bundler.get(ss,params,options).asset.launch(function(output) { + bundler.get(params).asset.launch(function(output) { return utils.serve.js(output, response); }); @@ -52,7 +53,7 @@ module.exports = function (ss, router, options) { thisUrl = url.parse(request.url); params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); - return bundler.get(ss,params,options).asset.css(path, { + return bundler.get(params).asset.css(path, { client: params.client, clientId: params.ts }, function(output) { diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index ab1ad832..21a8fc29 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -18,6 +18,9 @@ var url = require('url'), var queryCache = {}; module.exports = function(ss, router, options) { + + var bundler = require('../bundler/index')(ss,options); + function serve(processor) { return function(request, response) { var path = utils.parseUrl(request.url); @@ -43,7 +46,7 @@ module.exports = function(ss, router, options) { return files.forEach(function(file) { var description; try { - return bundler.get(ss,params,options).asset.js(file, { + return bundler.get(params).asset.js(file, { client: params.client, clientId: params.ts, pathPrefix: options.globalModules? null : path, @@ -66,7 +69,7 @@ module.exports = function(ss, router, options) { var thisUrl = url.parse(request.url), params = qs.parse(thisUrl.query); - return bundler.get(ss,params,options).asset.worker(path, { + return bundler.get(params).asset.worker(path, { compress: options.packAssets }, cb); } diff --git a/lib/client/view.js b/lib/client/view.js index d5f4e7b7..d40502ae 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -9,7 +9,7 @@ var pathlib = require('path'), module.exports = function(ss, client, options, cb) { var templateEngine = require('./template_engine')(ss), - bundler = require('./bundler/index').get(ss, client, options); + bundler = ss.bundler.get(client); // Add links to CSS and JS files var includes = headers().concat(templates()); From 6f0b391bd288363a74915a125ce57e6c2881b910 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 5 Feb 2015 18:10:26 +0100 Subject: [PATCH 28/59] feat(bundler): Dropping global-require Require path starting with ./ is relative to /client, this must also be passed in the dev time URL. --- docs/js/docs-setup.js | 2 +- docs/partials/tutorials/client_side_code.html | 4 ---- lib/client/serve/ondemand.js | 2 +- src/docs/tutorials/en/client_side_code.ngdoc | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 8d556272..0b168bf7 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -30,7 +30,7 @@ NG_DOCS={ "type": "overview", "moduleName": "Client-Side Code", "shortDescription": "Client-Side Code", - "keywords": "ability absolutely access accessed add allowing allows alphanumerically amd amount app apply approaches as-is automatically aware awesome backbone background bear better blank boiler-plate brought browser browserify browserifyexcludepaths cache called calls case cases catch change chaos choice clean client client-code client-side code coffee coming command common components connection console copy create created critical critically default define demand depend dependencies depends developer developers difference directores directory disable distinction doesn don easily ensure entry entrymodulename error established exactly example exceptions exclude excluded excluding execuring execute executed exist expect experience explicitly export file files folder forget form full functions future globalmodules goal going great handle head history hopes https inbuilt info instantly internally javascript jquery js killing leading legacy libraries library libs lightweight list live lives ll load loading long magic major making manage managing manually md mess mind modification modify module modules namespacing node null onus options order org overview packaging path paths performs point position problem problems project querystring reconnecting reference regular relative require required requires send serve served server server-side set share sharing side single slash small socketstream solution solutions solves special ss stack statement string structure subdirectories substack syntax system top track treated tricky true tutorials type typically underscore unique unstructured url values variable view views wade wanted web websocket window work works write writing" + "keywords": "ability absolutely access accessed add allowing allows alphanumerically amd amount app apply approaches as-is automatically aware awesome backbone background bear better blank boiler-plate brought browser browserify browserifyexcludepaths cache called calls case cases catch change chaos choice clean client client-code client-side code coffee coming command common components connection console copy create created critical critically default define demand depend dependencies depends developer developers difference directores directory disable distinction doesn don easily ensure entry entrymodulename error established exactly example exceptions exclude excluded excluding execuring execute executed exist expect experience explicitly export file files folder forget form functions future goal going great handle head history hopes https inbuilt info instantly internally javascript jquery js killing leading legacy libraries library libs lightweight list live lives ll load loading long magic major making manage managing manually md mess mind modification modify module modules namespacing node null onus options order org overview packaging path paths performs point position problem problems project querystring reconnecting reference regular relative require required requires send serve served server server-side set share sharing side single slash small socketstream solution solutions solves special ss stack statement string structure subdirectories substack syntax system top track treated tricky tutorials type typically underscore unique unstructured url values variable view views wade wanted web websocket window work works write writing" }, { "section": "tutorials", diff --git a/docs/partials/tutorials/client_side_code.html b/docs/partials/tutorials/client_side_code.html index 49a94c7d..acdb8c20 100644 --- a/docs/partials/tutorials/client_side_code.html +++ b/docs/partials/tutorials/client_side_code.html @@ -38,10 +38,6 @@

Options

 ss.client.set({ entryModuleName: null });
 
-
  • globalModules {boolean} - set true to load client side modules using their full path relative to client/code. If for example your app is my the entry module can be accessed with require('/my/entry'). -
    -ss.client.set({ globalModules: true });
    -
  • Note, that paths for excluding should be relative to client/code/ and that file client/code/app/entry.js could not be excluded in any cases.

    If you need to exclude from automatically packaging certain file, just specify the file's relative path: diff --git a/lib/client/serve/ondemand.js b/lib/client/serve/ondemand.js index 21a8fc29..0ada457e 100644 --- a/lib/client/serve/ondemand.js +++ b/lib/client/serve/ondemand.js @@ -49,7 +49,7 @@ module.exports = function(ss, router, options) { return bundler.get(params).asset.js(file, { client: params.client, clientId: params.ts, - pathPrefix: options.globalModules? null : path, + //pathPrefix: options.globalModules? null : path, compress: options.packAssets }, function(js) { output.push(js); diff --git a/src/docs/tutorials/en/client_side_code.ngdoc b/src/docs/tutorials/en/client_side_code.ngdoc index 811abf02..40b7773e 100644 --- a/src/docs/tutorials/en/client_side_code.ngdoc +++ b/src/docs/tutorials/en/client_side_code.ngdoc @@ -70,10 +70,6 @@ code for the entry module. If null or a blank string is given no entry module is

     ss.client.set({ entryModuleName: null });
     
    -- **globalModules {boolean} ** - set true to load client side modules using their full path relative to `client/code`. If for example your app is `my` the entry module can be accessed with `require('/my/entry')`. -
    -ss.client.set({ globalModules: true });
    -
    **Note**, that paths for excluding should be relative to `client/code/` and that file `client/code/app/entry.js` could not be excluded in any cases. From bd7ab276c0a94884304a42656bae62083a5b0c7e Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Thu, 5 Feb 2015 18:11:07 +0100 Subject: [PATCH 29/59] feat(shim): Dropping JSON shim #485 --- lib/client/system/shims/json.min.js | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 lib/client/system/shims/json.min.js diff --git a/lib/client/system/shims/json.min.js b/lib/client/system/shims/json.min.js deleted file mode 100644 index 73be3910..00000000 --- a/lib/client/system/shims/json.min.js +++ /dev/null @@ -1,18 +0,0 @@ -if(!this.JSON){JSON=function(){function f(n){return n<10?'0'+n:n;} -Date.prototype.toJSON=function(){return this.getUTCFullYear()+'-'+ -f(this.getUTCMonth()+1)+'-'+ -f(this.getUTCDate())+'T'+ -f(this.getUTCHours())+':'+ -f(this.getUTCMinutes())+':'+ -f(this.getUTCSeconds())+'Z';};var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function stringify(value,whitelist){var a,i,k,l,r=/["\\\x00-\x1f\x7f-\x9f]/g,v;switch(typeof value){case'string':return r.test(value)?'"'+value.replace(r,function(a){var c=m[a];if(c){return c;} -c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+ -(c%16).toString(16);})+'"':'"'+value+'"';case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} -if(typeof value.toJSON==='function'){return stringify(value.toJSON());} -a=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){l=value.length;for(i=0;i Date: Sun, 8 Feb 2015 02:01:23 +0100 Subject: [PATCH 30/59] feat(bundler): Pack & System Assets serving in bundler --- lib/client/bundler/default.js | 70 ++++++++++++++---- lib/client/bundler/index.js | 132 +++++++++++++++++++++------------- lib/client/bundler/webpack.js | 45 +++++++++--- lib/client/index.js | 35 ++++----- lib/client/pack.js | 85 ---------------------- lib/client/serve/dev.js | 8 ++- lib/client/system/index.js | 82 ++------------------- lib/client/view.js | 29 +++----- lib/client/wrap.js | 17 ----- 9 files changed, 214 insertions(+), 289 deletions(-) delete mode 100644 lib/client/pack.js delete mode 100644 lib/client/wrap.js diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 8247b31c..a5f3e263 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -3,7 +3,8 @@ var fs = require('fs'), path = require('path'), - log = require('../../utils/log'); + log = require('../../utils/log'), + systemAssets = require('../system').assets; function includeFlags(overrides) { var includes = { @@ -22,16 +23,17 @@ module.exports = function(ss,client,options){ var bundler = { define: define, load: load, + toMinifiedCSS: toMinifiedCSS, + toMinifiedJS: toMinifiedJS, asset: { + includeSystemLib: includeSystemLib, + includeSystemModule: includeSystemModule, + entries: entries, js: assetJS, worker: assetWorker, launch: assetLaunch, css: assetCSS, html: assetHTML - }, - pack: { - js: packJS, - css: packCSS } }; @@ -57,8 +59,29 @@ module.exports = function(ss,client,options){ } + function includeSystemLib(name,content,options) { + switch(name) { + case "browserify": + } + return true; + } + + function includeSystemModule(name,content,options) { + switch(name) { + case "eventemitter2": + case "socketstream": + } + return true; + } + + // list of entries for an asset type relative to the client directory + function entries(assetType) { + return ss.bundler.entries(client, assetType); + } + function assetJS(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { + //TODO with options compress saved to avoid double compression output = ss.bundler.wrapCode(output, path, opts.pathPrefix); if (opts.compress && path.indexOf('.min') === -1) { output = ss.bundler.minifyJSFile(output, path); @@ -75,9 +98,8 @@ module.exports = function(ss,client,options){ }); } - function assetLaunch(cb) { - var output = ss.bundler.launchCode(client); - return cb(output); + function assetLaunch() { + return ss.bundler.launchCode(client); } function assetCSS(path, opts, cb) { @@ -88,12 +110,36 @@ module.exports = function(ss,client,options){ return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } - function packJS(postProcess) { - ss.bundler.packAssetSet('js', options.dirs.client, client, bundler, postProcess); + function toMinifiedCSS(files) { + return ss.bundler.minifyCSS(files); } - function packCSS(postProcess) { - ss.bundler.packAssetSet('css', options.dirs.client, client, bundler, postProcess); + function toMinifiedJS(files) { + // Libs + var libs = systemAssets.libs.map(function(lib) { + return bundler.asset.includeSystemLib(lib.name, lib.content, lib.options)? lib : null; + }).filter(function(content) { + return !!content; + }); + + // Modules + var mods = [], + _ref = systemAssets.modules; + for (var name in _ref) { + if (_ref.hasOwnProperty(name)) { + var mod = _ref[name]; + if (bundler.asset.includeSystemModule(mod.name,mod.content,mod.options)) { + var code = ss.bundler.wrapModule(name, mod.content); + mods.push({ name:mod.name, content:code,options:mod.options,type:mod.type }); + } + } + } + + var initCode = bundler.asset.launch(); + if (initCode) { + //parts.push(initCode); + } + return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(systemAssets.initCode)); } }; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 85398d01..ed7829c8 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -7,7 +7,6 @@ var fs = require('fs'), cleanCSS = require('clean-css'), system = require('../system'), view = require('../view'), - wrap = require('../wrap'), magicPath = require('../magic_path'), uglifyjs = require('uglify-js'), jsp = uglifyjs.parser, @@ -89,31 +88,14 @@ module.exports = function(ss,options) { } // Output CSS - bundler.pack.css(function(files) { - var minified, original; - original = files.join('\n'); - minified = cleanCSS.process(original); - log.info((' Minified CSS from ' + (exports.formatKb(original.length)) + ' to ' + (exports.formatKb(minified.length))).grey); - return minified; - }); + ss.bundler.packAssetSet('css', client, bundler.toMinifiedCSS); // Output JS - bundler.pack.js(function(files) { - var parts = []; - if (client.includes.system) { - parts.push( system.serve.js({ compress:true }) ); - } - parts = parts.concat(files); - if (client.includes.initCode) { - parts.push( system.serve.initCode() ); - } - - return parts.join(";"); - }); + ss.bundler.packAssetSet('js', client, bundler.toMinifiedJS); // Output HTML view return view(ss, client, options, function(html) { - fs.writeFileSync(bundler.dests.paths.html, html); + fs.writeFileSync(getBundler(client).dests.paths.html, html); return log.info('✓'.green, 'Created and cached HTML file ' + bundler.dests.relPaths.html); }); }, @@ -139,12 +121,26 @@ module.exports = function(ss,options) { return formatter.compile(p.replace(/\\/g, '/'), options, cb); }, + minifyCSS: function minifyCSS(files) { + var original = files.join('\n'); + var minified = cleanCSS().minify(original); + log.info((' Minified CSS from ' + (formatKb(original.length)) + ' to ' + (formatKb(minified.length))).grey); + return minified; + }, + + minifyJS: function minifyJS_(files) { + var min = files.map(function(js) { + return js.options.minified ? js.content : minifyJS(js.content); + }); + return min.join('\n'); + }, + minifyJSFile: function minifyJSFile(originalCode, fileName) { var ast = jsp.parse(originalCode); ast = pro.ast_mangle(ast); ast = pro.ast_squeeze(ast); var minifiedCode = pro.gen_code(ast); - log.info((' Minified ' + fileName + ' from ' + (exports.formatKb(originalCode.length)) + ' to ' + (exports.formatKb(minifiedCode.length))).grey); + log.info((' Minified ' + fileName + ' from ' + (formatKb(originalCode.length)) + ' to ' + (formatKb(minifiedCode.length))).grey); return minifiedCode; }, @@ -196,16 +192,25 @@ module.exports = function(ss,options) { }, launchCode: function(client) { - var initCode = system.serve.initCode(), - entryInit = 'require("/entry");'; - initCode = initCode.replace(entryInit, 'require(' + client.entryInitPath + ');') + var initCode = system.assets.initCode.map(function(ic) { return ic.content; }).join('\n'), + entryInit = options.defaultEntryInit, + realInit = 'require(' + client.entryInitPath + ');'; + + if (typeof options.entryModuleName === 'string' || options.entryModuleName === null) { + realInit = options.entryModuleName? 'require("/'+options.entryModuleName+'");' : ''; + } + + initCode = initCode.replace(entryInit, realInit) return initCode; }, - packAssetSet: function packAssetSet(assetType, dir, client, bundler, postProcess) { - var filePaths, - prefix, - paths = client.paths[assetType]; + packAssetSet: function packAssetSet(assetType, client, postProcess) { + var bundler = getBundler(client), + filePaths = bundler.asset.entries(assetType), + dir = options.dirs.client; + + return processFiles(); + function writeFile(fileContents) { var fileName = bundler.dests.paths[assetType]; @@ -214,19 +219,18 @@ module.exports = function(ss,options) { } function processFiles(fileContents, i) { - var file, path, _ref; if (!fileContents) { fileContents = []; } if (!i) { i = 0; } - _ref = filePaths[i], path = _ref.path, file = _ref.file; + var _ref = filePaths[i], path = _ref.importedBy, file = _ref.file; return bundler.asset[assetType](file, { pathPrefix: path, compress: true }, function(output) { - fileContents.push(output); + fileContents.push({content:output,options:{}}); if (filePaths[++i]) { return processFiles(fileContents, i); } else { @@ -234,26 +238,40 @@ module.exports = function(ss,options) { } }); } + }, - // Expand any dirs into real files - if (paths && paths.length > 0) { - filePaths = []; - prefix = path.join(ss.root, dir); - paths.forEach(function(path) { - return magicPath.files(prefix, path).forEach(function(file) { - return filePaths.push({ - path: path, - file: file - }); - }); - }); - return processFiles(); + // css, js, worker, + entries: function entries(client, assetType) { + + var entries = [], + pathType; + switch(assetType) { + case 'css': pathType = 'css'; break; + case 'js': pathType = 'code'; break; + case 'worker': pathType = 'code'; break; } + client.paths[pathType].forEach(function(from) { + return magicPath.files(path.join(ss.root, options.dirs.client), from).forEach(function(file) { + return entries.push({file:file,importedBy:from}); + }); + }); + return entries; + }, + formatKb: formatKb, - formatKb: function formatKb(size) { - return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; + htmlTag : { + css: function(path) { + return ''; + }, + js: function(path) { + return ''; + } + }, + + wrapModule: function(modPath, code) { + return 'require.define("' + modPath + '", function (require, module, exports, __dirname, __filename){\n' + code + '\n});'; }, // Before client-side code is sent to the browser any file which is NOT a library (e.g. /client/code/libs) @@ -280,11 +298,11 @@ module.exports = function(ss,options) { // Don't add a leading slash if this is a 'system' module if (pathAry.indexOf('system') >= 0) { - return wrap.module(pathAry[pathAry.length - 1], code); + return ss.bundler.wrapModule(pathAry[pathAry.length - 1], code); } else { // Otherwise treat as a regular module - var modPath = options.globalModules? pathAry.join("/") : pathAry.slice(1).join('/'); + var modPath = /*options.globalModules? pathAry.join("/") :*/ pathAry.slice(1).join('/'); // Work out namespace for module if (pathPrefix) { @@ -297,7 +315,7 @@ module.exports = function(ss,options) { } modPath = path.substr(pathPrefix.length + 1); } - return wrap.module('/' + modPath, code); + return ss.bundler.wrapModule('/' + modPath, code); } } @@ -318,3 +336,17 @@ function mkdir(dir) { } } +function formatKb(size) { + return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; +} + +// Private +function minifyJS(originalCode) { + var ast, jsp, pro; + jsp = uglifyjs.parser; + pro = uglifyjs.uglify; + ast = jsp.parse(originalCode); + ast = pro.ast_mangle(ast); + ast = pro.ast_squeeze(ast); + return pro.gen_code(ast) + ';'; +} diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 261a8bfc..235391eb 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -10,12 +10,15 @@ module.exports = function(webpack) { var bundler = { define: define, load: load, - pack: { - js: packJS, - css: packCSS - }, + toMinifiedCSS: toMinifiedCSS, + toMinifiedJS: toMinifiedJS, asset: { + includeSystemLib: includeSystemLib, + includeSystemModule: includeSystemModule, + entries: entries, + html: assetHTML, + system: assetSystem, js: assetJS, worker: assetWorker, launch: assetLaunch, @@ -37,7 +40,6 @@ module.exports = function(webpack) { // Define new client object client.paths = ss.bundler.sourcePaths(paths); - client.includes = {}; //TODO post-define default? return ss.bundler.destsFor(client); } @@ -46,6 +48,26 @@ module.exports = function(webpack) { } + function includeSystemLib(name,content,options) { + switch(name) { + case "browserify": + } + return true; + } + + function includeSystemModule(name,content,options) { + switch(name) { + case "eventemitter2": + case "socketstream": + } + return true; + } + + // list of entries for an asset type relative to the client directory + function entries(assetType) { + return ss.bundler.entries(bundler.client, assetType); + } + function assetCSS(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); } @@ -54,6 +76,10 @@ module.exports = function(webpack) { return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } + function assetSystem(cb) { + cb(''); + } + function assetJS(path, opts, cb) { webpack({}, function() { cb('//'); @@ -70,15 +96,14 @@ module.exports = function(webpack) { webpack({}, function() { cb('//'); }); - } - function packCSS(postProcess) { - ss.bundler.packAssetSet('css', options.dirs.client, bundler.client, bundler, postProcess); + function toMinifiedCSS(files) { + return ss.bundler.minifyCSS(files.join('\n')); } - function packJS() { - + function toMinifiedJS(files) { + return '// minified JS for '+bundler.client.name; } }; diff --git a/lib/client/index.js b/lib/client/index.js index e94fee42..fafaa4cd 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -20,6 +20,7 @@ var packAssets = process.env['SS_PACK']; var options = { packedAssets: packAssets || false, liveReload: ['code', 'css', 'static', 'templates', 'views'], + defaultEntryInit: 'require("/entry");', dirs: { client: '/client', code: '/client/code', @@ -38,7 +39,7 @@ var clients = {}; module.exports = function(ss, router) { // make bundler methods available for default and other implementations - var bundler = ss.bundler = require('./bundler/index')(ss,options); + ss.bundler = require('./bundler/index')(ss,options); // Require sub modules var templateEngine = require('./template_engine')(ss), @@ -137,19 +138,25 @@ module.exports = function(ss, router) { // if a function is used construct a bundler with it otherwise use default bundler var client = clients[name] = { name: name }; client.id = Number(Date.now()); - bundler.define(client,arguments); + client.paths = {}; + client.includes = { + css: true, + html: true, + system: true, + initCode: true + }; //TODO reconsider relative paths of all these client.entryInitPath = '/code/' + client.name + '/entry'; + ss.bundler.define(client,arguments); + return client; }, // Listen and serve incoming asset requests load: function() { - var client, id, name, pack, entryInit; - - bundler.load(); + ss.bundler.load(); // Cache instances of code formatters and template engines here // This may change in the future as I don't like hanging system objects @@ -159,13 +166,7 @@ module.exports = function(ss, router) { ss.client.templateEngines = templateEngine.load(); // Code to execute once everything is loaded - entryInit = 'require("/entry");'; - if (typeof options.entryModuleName === 'string' || options.entryModuleName === null) { - entryInit = options.entryModuleName? 'require("/'+options.entryModuleName+'");' : ''; - } - if (entryInit) { - systemAssets.send('code', 'init', entryInit); - } + systemAssets.send('code', 'init', options.defaultEntryInit); if (options.packedAssets) { @@ -173,10 +174,10 @@ module.exports = function(ss, router) { // If unsuccessful, assets will be re-packed automatically if (!packAssets) { log.info('i'.green, 'Attempting to find pre-packed assets... (force repack with SS_PACK=1)'.grey); - for (name in clients) { + for (var name in clients) { if (clients.hasOwnProperty(name)) { - client = clients[name]; - id = options.packedAssets.id || determineLatestId(client); + var client = clients[name], + id = options.packedAssets.id || determineLatestId(client); if (id) { client.id = id; log.info('✓'.green, ('Serving client \'' + client.name + '\' using pre-packed assets (ID ' + client.id + ')').grey); @@ -190,9 +191,9 @@ module.exports = function(ss, router) { // Pack Assets if (packAssets) { - for (name in clients) { + for (var name in clients) { if (clients.hasOwnProperty(name)) { - bundler.pack(clients[name]); + ss.bundler.pack(clients[name]); } } } diff --git a/lib/client/pack.js b/lib/client/pack.js deleted file mode 100644 index 83d8b471..00000000 --- a/lib/client/pack.js +++ /dev/null @@ -1,85 +0,0 @@ -// Asset Packer -// ------------ -// Packs all CSS, JS and HTML assets declared in the ss.client.define() call to be sent upon initial connection -// Other code modules can still be served asynchronously later on -'use strict'; - -require('colors'); - -var fs = require('fs'), - pathlib = require('path'), - cleanCSS = require('clean-css'), - magicPath = require('./magic_path'), - system = require('./system'), - view = require('./view'), - log = require('../utils/log'); - -module.exports = function(ss, client, options) { - var bundler = ss.bundler.get(client); - var clientDir, containerDir; //TODO - client.pack = true; - - /* PACKER */ - - log(('Pre-packing and minifying the \'' + client.name + '\' client...').yellow); - - var description = ss.client.describeAssets(client.name); - - // Prepare folder - description.ensureDirs(); - if (!(options.packedAssets && options.packedAssets.keepOldFiles)) { - deleteOldFiles(description.dir); - } - - // Output CSS - bundler.packCSS(client, function(files) { - var minified, original; - original = files.join('\n'); - minified = cleanCSS.process(original); - log.info((' Minified CSS from ' + (formatKb(original.length)) + ' to ' + (formatKb(minified.length))).grey); - return minified; - }); - - // Output JS - bundler.packJS(client, function(files) { - var parts = []; - if (client.includes.system) { - parts.push( system.serve.js({ compress:true }) ); - } - parts = parts.concat(files); - if (client.includes.initCode) { - parts.push( system.serve.initCode() ); - } - - return parts.join(';'); - }); - - // Output HTML view - return view(ss, client, options, function(html) { - var fileName; - fileName = pathlib.join(clientDir, client.id + '.html'); - fs.writeFileSync(fileName, html); - return log.info('✓'.green, 'Created and cached HTML file ' + fileName.substr(ss.root.length)); - }); -}; - -// PRIVATE - -function formatKb(size) { - return '' + (Math.round((size / 1024) * 1000) / 1000) + ' KB'; -} - -function mkdir(dir) { - if (!fs.existsSync(dir)) { - return fs.mkdirSync(dir); - } -} - -function deleteOldFiles(clientDir) { - var filesDeleted, numFilesDeleted; - numFilesDeleted = 0; - filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { - return fs.unlinkSync(pathlib.join(clientDir, fileName)); - }); - return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted'); -} diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index 92699f42..bb4f9908 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -18,7 +18,12 @@ module.exports = function (ss, router, options) { // Serve system libraries and modules router.on('/_serveDev/system?*', function(request, response) { - return utils.serve.js(system.serve.js(), response); + var thisUrl = url.parse(request.url), + params = qs.parse(thisUrl.query); + + return bundler.get(params).asset.system(function(output) { + return utils.serve.js(output, response); + }); }); // Listen for requests for application client code @@ -42,7 +47,6 @@ module.exports = function (ss, router, options) { bundler.get(params).asset.launch(function(output) { return utils.serve.js(output, response); }); - }); // CSS diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 8a32b171..6be49b5f 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -8,12 +8,10 @@ var fs = require('fs'), pathlib = require('path'), uglifyjs = require('uglify-js'), - wrap = require('../wrap'), fsUtils = require('../../utils/file'); // Allow internal modules to deliver assets to the browser -var assets = { - shims: [], +var assets = exports.assets = { libs: [], modules: {}, initCode: [] @@ -37,17 +35,12 @@ var send = exports.send = function (type, name, content, options) { switch (type) { case 'code': - return assets.initCode.push(content); - case 'shim': - return pushUniqueAsset('shims',{ - name: name, - content: content, - options: options - }); + return assets.initCode.push({content:content,options:options}); case 'lib': case 'library': return pushUniqueAsset('libs',{ name: name, + type: type, content: content, options: options }); @@ -57,6 +50,8 @@ var send = exports.send = function (type, name, content, options) { throw new Error('System module name \'' + name + '\' already exists'); } else { assets.modules[name] = { + name: name, + type: type, content: content, options: options }; @@ -66,7 +61,6 @@ var send = exports.send = function (type, name, content, options) { }; exports.unload = function() { - assets.shims = []; assets.libs = []; assets.modules = {}; assets.initCode = []; @@ -76,22 +70,6 @@ exports.unload = function() { exports.load = function() { var modDir; - // System shims for backwards compatibility with all browsers. - // Load order is not important - modDir = pathlib.join(__dirname, '/shims'); - fsUtils.readDirSync(modDir).files.forEach(function(fileName) { - var code, extension, modName, sp, preMinified; - code = fs.readFileSync(fileName, 'utf8'); - sp = fileName.split('.'); - extension = sp[sp.length - 1]; - preMinified = fileName.indexOf('.min') >= 0; - modName = fileName.substr(modDir.length + 1); - return send('shim', modName, code, { - minified: preMinified, - coffee: extension === 'coffee' - }); - }); - // System Libs. Including browserify client code // Load order is not important modDir = pathlib.join(__dirname, '/libs'); @@ -120,53 +98,3 @@ exports.load = function() { }); }); }; - -// Serve system assets -exports.serve = { - js: function (options) { - var code, mod, name, output, _ref; - if (options === null || options === undefined) { - options = {}; - } - - // Shims - output = assets.shims.map(function(code) { - return options.compress && !code.options.minified && minifyJS(code.content) || code.content; - }); - - // Libs - output = output.concat(assets.libs.map(function(code) { - return options.compress && !code.options.minified && minifyJS(code.content) || code.content; - })); - - // Modules - _ref = assets.modules; - for (name in _ref) { - if (_ref.hasOwnProperty(name)) { - - mod = _ref[name]; - code = wrap.module(name, mod.content); - if (options.compress && !mod.options.minified) { - code = minifyJS(code); - } - output.push(code); - - } - } - return output.join('\n'); - }, - initCode: function() { - return assets.initCode.join(' '); - } -}; - -// Private -function minifyJS(originalCode) { - var ast, jsp, pro; - jsp = uglifyjs.parser; - pro = uglifyjs.uglify; - ast = jsp.parse(originalCode); - ast = pro.ast_mangle(ast); - ast = pro.ast_squeeze(ast); - return pro.gen_code(ast) + ';'; -} diff --git a/lib/client/view.js b/lib/client/view.js index d40502ae..91d5b1c1 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -4,8 +4,7 @@ 'use strict'; var pathlib = require('path'), - magicPath = require('./magic_path'), - wrap = require('./wrap'); + magicPath = require('./magic_path'); module.exports = function(ss, client, options, cb) { var templateEngine = require('./template_engine')(ss), @@ -77,38 +76,30 @@ module.exports = function(ss, client, options, cb) { if (options.packedAssets) { css = resolveAssetLink('css'); js = resolveAssetLink('js'); - output.push(wrap.htmlTag.css(css)); - output.push(wrap.htmlTag.js(js)); + output.push(ss.bundler.htmlTag.css(css)); + output.push(ss.bundler.htmlTag.js(js)); } else { // Otherwise, in development, list all files individually so debugging is easier // SocketStream system libs and modules if (client.includes.system) { - output.push(wrap.htmlTag.js('/_serveDev/system?ts=' + client.id)); + output.push(ss.bundler.htmlTag.js('/_serveDev/system?ts=' + client.id)); } // Send all CSS if (client.includes.css) { - client.paths.css.forEach(function(path) { - return magicPath.files(pathlib.join(ss.root, options.dirs.client), path).forEach(function(file) { - return output.push(wrap.htmlTag.css('/_serveDev/css/' + file + '?ts=' + client.id + '&client=' + client.name)); - }); - }); + output.concat( bundler.asset.entries('css') + .map(function(entry) { return ss.bundler.htmlTag.css('/_serverDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); } // Send Application Code - client.paths.code.forEach(function(path) { - return magicPath.files(pathlib.join(ss.root, options.dirs.client), path).forEach(function(file) { - var url = '/_serveDev/code/' + file + '?ts=' + client.id + '&client=' + client.name; - if (! options.globalModules) url += '&pathPrefix=' + path; - return output.push(wrap.htmlTag.js(url)); - }); - }); + output.concat( bundler.asset.entries('js') + .map(function(entry) { return ss.bundler.htmlTag.js('/_serverDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); - // Start your app and connect to SocketStream + // Start your app and connect to SocketStream if (client.includes.initCode) { - output.push(wrap.htmlTag.js('/_serveDev/start?ts=' + client.id)); + output.push(ss.bundler.htmlTag.js('/_serveDev/start?ts=' + client.id)); } } return output; diff --git a/lib/client/wrap.js b/lib/client/wrap.js deleted file mode 100644 index c91e2046..00000000 --- a/lib/client/wrap.js +++ /dev/null @@ -1,17 +0,0 @@ -// Simple wrapper for modules -'use strict'; - -//TODO review if this should be isolated in bundler API - -exports.module = function(modPath, code) { - return 'require.define("' + modPath + '", function (require, module, exports, __dirname, __filename){\n' + code + '\n});'; -}; - -exports.htmlTag = { - css: function(path) { - return ''; - }, - js: function(path) { - return ''; - } -}; \ No newline at end of file From 4359210e0c2d903eaf3baa74f7bc06c20dd93992 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 02:53:21 +0100 Subject: [PATCH 31/59] chore(lint): Down to some obscure obstacle Couldn't figure out how to silence inner functions warning --- .jshintrc | 2 - lib/client/bundler/default.js | 79 ++++++++++++++++++++++++++----- lib/client/bundler/index.js | 14 +++--- lib/client/bundler/webpack.js | 88 +++++++++++++++++++++++++++++++---- 4 files changed, 153 insertions(+), 30 deletions(-) diff --git a/.jshintrc b/.jshintrc index 33d439aa..904eb73b 100644 --- a/.jshintrc +++ b/.jshintrc @@ -3,7 +3,6 @@ "curly" : true, "eqeqeq" : true, "eqnull" : true, - "forin" : true, "immed" : true, "latedef" : "nofunc", "newcap" : true, @@ -20,7 +19,6 @@ "sub" : true, "supernew" : true, "node" : true, - "onevar" : true, "unused" : true, "multistr" : true, "smarttabs": true, diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index a5f3e263..b7ff903f 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -1,10 +1,7 @@ // Default bundler implementation 'use strict'; -var fs = require('fs'), - path = require('path'), - log = require('../../utils/log'), - systemAssets = require('../system').assets; +var systemAssets = require('../system').assets; function includeFlags(overrides) { var includes = { @@ -59,14 +56,28 @@ module.exports = function(ss,client,options){ } - function includeSystemLib(name,content,options) { + /** + * + * @param name + * @param content + * @param options + * @returns {boolean} + */ + function includeSystemLib(name) { switch(name) { case "browserify": } return true; } - function includeSystemModule(name,content,options) { + /** + * + * @param name + * @param content + * @param options + * @returns {boolean} + */ + function includeSystemModule(name) { switch(name) { case "eventemitter2": case "socketstream": @@ -74,11 +85,23 @@ module.exports = function(ss,client,options){ return true; } - // list of entries for an asset type relative to the client directory + /** + * list of entries for an asset type relative to the client directory + * + * @param assetType + * @returns {*} + */ function entries(assetType) { return ss.bundler.entries(client, assetType); } + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetJS(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { //TODO with options compress saved to avoid double compression @@ -89,6 +112,14 @@ module.exports = function(ss,client,options){ return cb(output); }); } + + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetWorker(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { if (opts.compress) { @@ -98,22 +129,50 @@ module.exports = function(ss,client,options){ }); } + /** + * + * @returns {*} + */ function assetLaunch() { return ss.bundler.launchCode(client); } + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetCSS(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); } + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetHTML(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } + /** + * + * @param files + * @returns {*} + */ function toMinifiedCSS(files) { return ss.bundler.minifyCSS(files); } + /** + * + * @param files + * @returns {*} + */ function toMinifiedJS(files) { // Libs var libs = systemAssets.libs.map(function(lib) { @@ -136,10 +195,8 @@ module.exports = function(ss,client,options){ } var initCode = bundler.asset.launch(); - if (initCode) { - //parts.push(initCode); - } - return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(systemAssets.initCode)); + + return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(initCode)); } }; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index ed7829c8..6a58db62 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -201,13 +201,12 @@ module.exports = function(ss,options) { } initCode = initCode.replace(entryInit, realInit) - return initCode; + return { content:initCode }; }, packAssetSet: function packAssetSet(assetType, client, postProcess) { var bundler = getBundler(client), - filePaths = bundler.asset.entries(assetType), - dir = options.dirs.client; + filePaths = bundler.asset.entries(assetType); return processFiles(); @@ -243,7 +242,7 @@ module.exports = function(ss,options) { // css, js, worker, entries: function entries(client, assetType) { - var entries = [], + var _entries = [], pathType; switch(assetType) { case 'css': pathType = 'css'; break; @@ -252,10 +251,10 @@ module.exports = function(ss,options) { } client.paths[pathType].forEach(function(from) { return magicPath.files(path.join(ss.root, options.dirs.client), from).forEach(function(file) { - return entries.push({file:file,importedBy:from}); + return _entries.push({file:file,importedBy:from}); }); }); - return entries; + return _entries; }, @@ -323,8 +322,7 @@ module.exports = function(ss,options) { }; function deleteOldFiles(clientDir) { - var numFilesDeleted = 0, - filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { + var filesDeleted = fs.readdirSync(clientDir).map(function(fileName) { return fs.unlinkSync(path.join(clientDir, fileName)); }); return filesDeleted.length > 1 && log('✓'.green, '' + filesDeleted.length + ' previous packaged files deleted'); diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 235391eb..8b5d913a 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -1,9 +1,9 @@ // Webpack bundler implementation 'use strict'; -var fs = require('fs'), - path = require('path'), - log = require('../../utils/log'); +//var fs = require('fs'), +// path = require('path'), +// log = require('../../utils/log'); module.exports = function(webpack) { return function(ss,options){ @@ -27,6 +27,12 @@ module.exports = function(webpack) { }; return bundler; + /** + * + * @param client + * @param paths + * @returns {*} + */ function define(client, paths) { if (typeof paths.view !== 'string') { @@ -48,14 +54,28 @@ module.exports = function(webpack) { } - function includeSystemLib(name,content,options) { + /** + * + * @param name + * @param content + * @param options + * @returns {boolean} + */ + function includeSystemLib(name) { switch(name) { case "browserify": } return true; } - function includeSystemModule(name,content,options) { + /** + * + * @param name + * @param content + * @param options + * @returns {boolean} + */ + function includeSystemModule(name) { switch(name) { case "eventemitter2": case "socketstream": @@ -63,23 +83,52 @@ module.exports = function(webpack) { return true; } - // list of entries for an asset type relative to the client directory + /** + * list of entries for an asset type relative to the client directory + * + * @param assetType + * @returns {*} + */ function entries(assetType) { return ss.bundler.entries(bundler.client, assetType); } + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetCSS(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); } + /** + * + * @param path + * @param opts + * @param cb + * @returns {*} + */ function assetHTML(path, opts, cb) { return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); } + /** + * + * @param cb + */ function assetSystem(cb) { cb(''); } + /** + * + * @param path + * @param opts + * @param cb + */ function assetJS(path, opts, cb) { webpack({}, function() { cb('//'); @@ -87,22 +136,43 @@ module.exports = function(webpack) { } + /** + * + * @param cb + * @returns {*} + */ function assetLaunch(cb) { - var output = ss.bundler.launchCode(client); + var output = ss.bundler.launchCode(bundler.client); return cb(output); } + /** + * + * @param path + * @param opts + * @param cb + */ function assetWorker(path, opts, cb) { webpack({}, function() { cb('//'); }); } + /** + * + * @param files + * @returns {*} + */ function toMinifiedCSS(files) { - return ss.bundler.minifyCSS(files.join('\n')); + return ss.bundler.minifyCSS(files); } - function toMinifiedJS(files) { + /** + * + * @param files + * @returns {string} + */ + function toMinifiedJS() { return '// minified JS for '+bundler.client.name; } }; From 4ad8085c15ea4535cdbe9f69eecc50cf64e9dbf1 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 10:42:10 +0100 Subject: [PATCH 32/59] chore(lint): bundler pass on jshint --- lib/client/bundler/default.js | 4 ++-- lib/client/bundler/index.js | 5 ++--- lib/client/bundler/webpack.js | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index b7ff903f..d58ec501 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -34,8 +34,6 @@ module.exports = function(ss,client,options){ } }; - return bundler; - function define(paths) { if (typeof paths.view !== 'string') { @@ -198,5 +196,7 @@ module.exports = function(ss,client,options){ return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(initCode)); } + + return bundler; }; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 6a58db62..3c6b75e1 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -208,9 +208,6 @@ module.exports = function(ss,options) { var bundler = getBundler(client), filePaths = bundler.asset.entries(assetType); - return processFiles(); - - function writeFile(fileContents) { var fileName = bundler.dests.paths[assetType]; fs.writeFileSync(fileName, postProcess(fileContents)); @@ -237,6 +234,8 @@ module.exports = function(ss,options) { } }); } + + return processFiles(); }, // css, js, worker, diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 8b5d913a..72415369 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -25,7 +25,6 @@ module.exports = function(webpack) { css: assetCSS } }; - return bundler; /** * @@ -175,6 +174,8 @@ module.exports = function(webpack) { function toMinifiedJS() { return '// minified JS for '+bundler.client.name; } + + return bundler; }; }; From fc93d394d4405cdfa57c005dff38eb1ebef42424 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 16:49:34 +0100 Subject: [PATCH 33/59] feat(assets): URL scheme for assets First draft of a document explaining the URL rationale in development and production --- docs/js/docs-setup.js | 9 +++++++++ docs/partials/tutorials/url_scheme.html | 22 ++++++++++++++++++++++ src/docs/tutorials/en/url_scheme.ngdoc | 0 3 files changed, 31 insertions(+) create mode 100644 docs/partials/tutorials/url_scheme.html create mode 100644 src/docs/tutorials/en/url_scheme.ngdoc diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 0b168bf7..3655b0ae 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -185,6 +185,15 @@ NG_DOCS={ "shortDescription": "Template Engine Wrappers", "keywords": "add app as-is attach automatically based bearing behavior best browser building bytes call called calls case catch client client-side clientcode closing code command compile compiled compiledtemplate compiling config consider console contents create default defaultformatter directly dot easy engine engines error example exports extension false file files find folder formatter formatters fs function functions github good guides handles hasownproperty hogan hogan-template ht html idea include init inside jade join js key language large lib library loads log map message mind module modules needed occasionally opening overview overwrite party pass passing path performance pick points pre-compile pre-processing preferred prefix process prototype prove readfilesync ready red reference relative render require return returns selectformatter selecting selects send sending server side simply socketstream ss suffix supports tag template templates templating text third throw time tips tmpl trade transform transforming true tutorials twitter type uncommon utf8 var variety vm wide wire wrap wrapper wrappers written" }, + { + "section": "tutorials", + "id": "url_scheme", + "shortName": "URL Scheme", + "type": "overview", + "moduleName": "URL Scheme", + "shortDescription": "URL Scheme", + "keywords": "ad assets calling change choose client code common completely consider considered contents css current demand development directory equivalent exported fetching files fly form future handled hoc html ideally js level loading middleware minified module modules on-demand open overview packed partially path paths production relative root saved scheme sense serve serveclient served serving static support time tutorials url urls view views whitelist work" + }, { "section": "tutorials", "id": "using_emberjs", diff --git a/docs/partials/tutorials/url_scheme.html b/docs/partials/tutorials/url_scheme.html new file mode 100644 index 00000000..ba29c523 --- /dev/null +++ b/docs/partials/tutorials/url_scheme.html @@ -0,0 +1,22 @@ +

    +
    +
    +

    +

    URL Scheme

    +

    The common URL for a view is its name at the root level, but you can choose whatever you will calling serveClient(..).

    +

    Assets

    +

    The contents of the client assets directory will be served under /assets.

    +

    When views are packed for production they are saved under the client assets directory. This will change in the future +to make relative URLs work the same in development and production.

    +

    Middleware

    +

    At development time middleware is added to serve HTML, JS and CSS on the fly.

    +

    Serving CSS

    +

    CSS files are served under /assets//123.css in production. When served ad hoc in development, and on-demand in +production all CSS must be served on the same level and ideally in an equivalent URL.

    +

    JS Module Paths

    +

    On Demand Loading

    +

    The current on-demand fetching of JS is handled by middleware. It should be possible to do it using static files.

    +

    In production it would make sense to support a path like /assets/require/...

    +

    We will have to consider whether all client code is considered completely open, or only partially. Should all client +modules be exported in minified form, or only those in a whitelist.

    +
    diff --git a/src/docs/tutorials/en/url_scheme.ngdoc b/src/docs/tutorials/en/url_scheme.ngdoc new file mode 100644 index 00000000..e69de29b From 0e82774f443705366e90eb2de04be3b1d835078f Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 16:50:50 +0100 Subject: [PATCH 34/59] feat(assets): missing file --- src/docs/tutorials/en/url_scheme.ngdoc | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/docs/tutorials/en/url_scheme.ngdoc b/src/docs/tutorials/en/url_scheme.ngdoc index e69de29b..1618772f 100644 --- a/src/docs/tutorials/en/url_scheme.ngdoc +++ b/src/docs/tutorials/en/url_scheme.ngdoc @@ -0,0 +1,37 @@ +@ngdoc overview +@name URL Scheme + +@description +# URL Scheme + +The common URL for a view is its name at the root level, but you can choose whatever you will calling `serveClient(..)`. + +## Assets + +The contents of the client assets directory will be served under `/assets`. + + +When views are packed for production they are saved under the client assets directory. This will change in the future +to make relative URLs work the same in development and production. + +## Middleware + +At development time middleware is added to serve HTML, JS and CSS on the fly. + + +## Serving CSS + +CSS files are served under /assets//123.css in production. When served ad hoc in development, and on-demand in +production all CSS must be served on the same level and ideally in an equivalent URL. + +## JS Module Paths + + +## On Demand Loading + +The current on-demand fetching of JS is handled by middleware. It should be possible to do it using static files. + +In production it would make sense to support a path like `/assets/require/..`. + +We will have to consider whether all client code is considered completely open, or only partially. Should all client +modules be exported in minified form, or only those in a whitelist. \ No newline at end of file From ddc6bc58829a168abd5e7322782bb1920cc1e049 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 18:24:58 +0100 Subject: [PATCH 35/59] chore(simplify): client dir is always base for assets --- lib/client/bundler/default.js | 8 ++++---- lib/client/bundler/index.js | 10 ++++------ lib/client/bundler/webpack.js | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index d58ec501..9ba4725f 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -101,7 +101,7 @@ module.exports = function(ss,client,options){ * @returns {*} */ function assetJS(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { + return ss.bundler.loadFile(path, 'js', opts, function(output) { //TODO with options compress saved to avoid double compression output = ss.bundler.wrapCode(output, path, opts.pathPrefix); if (opts.compress && path.indexOf('.min') === -1) { @@ -119,7 +119,7 @@ module.exports = function(ss,client,options){ * @returns {*} */ function assetWorker(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'js', opts, function(output) { + return ss.bundler.loadFile(path, 'js', opts, function(output) { if (opts.compress) { output = ss.bundler.minifyJSFile(output, path); } @@ -143,7 +143,7 @@ module.exports = function(ss,client,options){ * @returns {*} */ function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); + return ss.bundler.loadFile(path, 'css', opts, cb); } /** @@ -154,7 +154,7 @@ module.exports = function(ss,client,options){ * @returns {*} */ function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); + return ss.bundler.loadFile(path, 'html', opts, cb); } /** diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 3c6b75e1..5f7d68a6 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -54,8 +54,6 @@ module.exports = function(ss,options) { bundlers[name] = require('./default')(ss,client,options); bundlers[name].dests = bundlers[name].define(args[1]); } - - //console.warn('client defined',name,client,bundlers[name].dests); }, /** @@ -95,7 +93,7 @@ module.exports = function(ss,options) { // Output HTML view return view(ss, client, options, function(html) { - fs.writeFileSync(getBundler(client).dests.paths.html, html); + fs.writeFileSync(bundler.dests.paths.html, html); return log.info('✓'.green, 'Created and cached HTML file ' + bundler.dests.relPaths.html); }); }, @@ -103,8 +101,8 @@ module.exports = function(ss,options) { // API for implementing bundlers - loadFile: function loadFile(dir, fileName, type, options, cb) { - dir = path.join(ss.root, dir); + loadFile: function loadFile(fileName, type, opts, cb) { + var dir = path.join(ss.root, options.dirs.client); var p = path.join(dir, fileName); var extension = path.extname(p); extension = extension && extension.substring(1); // argh! @@ -118,7 +116,7 @@ module.exports = function(ss,options) { if (formatter.assetType !== type) { throw new Error('Unable to render \'' + fileName + '\' as this appears to be a ' + (formatter.assetType.toUpperCase()) + ' file. Expecting some type of ' + (type.toUpperCase()) + ' file in ' + (dir.substr(ss.root.length)) + ' instead'); } - return formatter.compile(p.replace(/\\/g, '/'), options, cb); + return formatter.compile(p.replace(/\\/g, '/'), opts, cb); }, minifyCSS: function minifyCSS(files) { diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 72415369..f674e5f2 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -100,7 +100,7 @@ module.exports = function(webpack) { * @returns {*} */ function assetCSS(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'css', opts, cb); + return ss.bundler.loadFile(path, 'css', opts, cb); } /** @@ -111,7 +111,7 @@ module.exports = function(webpack) { * @returns {*} */ function assetHTML(path, opts, cb) { - return ss.bundler.loadFile(options.dirs.client, path, 'html', opts, cb); + return ss.bundler.loadFile(path, 'html', opts, cb); } /** From 510fb53e4a53e7be36d4b91282526d0792dfefba Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 18:43:56 +0100 Subject: [PATCH 36/59] feat(bundler): view with tags --- lib/client/view.js | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/client/view.js b/lib/client/view.js index 91d5b1c1..7755f2b6 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -10,17 +10,6 @@ module.exports = function(ss, client, options, cb) { var templateEngine = require('./template_engine')(ss), bundler = ss.bundler.get(client); - // Add links to CSS and JS files - var includes = headers().concat(templates()); - - // Output HTML - var htmlOptions = { - headers: includes.join(''), - compress: options.packedAssets, - filename: client.paths.view - }; - return bundler.asset.html(client.paths.view, htmlOptions, cb); - // When packing assets the default path to the CSS or JS file can be overridden // either with a string or a function, typically pointing to an resource on a CDN function resolveAssetLink(type) { @@ -79,8 +68,7 @@ module.exports = function(ss, client, options, cb) { output.push(ss.bundler.htmlTag.css(css)); output.push(ss.bundler.htmlTag.js(js)); } else { - - // Otherwise, in development, list all files individually so debugging is easier + // Otherwise, in development, list all files individually so debugging is easier // SocketStream system libs and modules if (client.includes.system) { @@ -89,12 +77,12 @@ module.exports = function(ss, client, options, cb) { // Send all CSS if (client.includes.css) { - output.concat( bundler.asset.entries('css') + output = output.concat( bundler.asset.entries('css') .map(function(entry) { return ss.bundler.htmlTag.css('/_serverDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); } - // Send Application Code - output.concat( bundler.asset.entries('js') + // Send Application Code + output = output.concat( bundler.asset.entries('js') .map(function(entry) { return ss.bundler.htmlTag.js('/_serverDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); // Start your app and connect to SocketStream @@ -104,4 +92,15 @@ module.exports = function(ss, client, options, cb) { } return output; } + + // Add links to CSS and JS files + var includes = headers().concat(templates()); + + // Output HTML + var htmlOptions = { + headers: includes.join(''), + compress: options.packedAssets, + filename: client.paths.view + }; + return bundler.asset.html(client.paths.view, htmlOptions, cb); }; From 7fd3833ceb6fb86ccd8ef83e0f56b38af0b4ed1b Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 8 Feb 2015 19:01:10 +0100 Subject: [PATCH 37/59] feat(bundler): system modules are named without JS extensions --- lib/client/system/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 6be49b5f..85e442f3 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -92,7 +92,7 @@ exports.load = function() { code = fs.readFileSync(fileName, 'utf8'); sp = fileName.split('.'); extension = sp[sp.length - 1]; - modName = fileName.substr(modDir.length + 1); + modName = fileName.substr(modDir.length + 1).replace('.js','').replace('.min.js',''); return send('mod', modName, code, { coffee: extension === 'coffee' }); From 9707a63a18404e3ee9ff3626dcc4075454507fa6 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Mon, 9 Feb 2015 09:08:23 +0100 Subject: [PATCH 38/59] chore(lint): var refactor --- lib/client/http.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/client/http.js b/lib/client/http.js index b8e1e25e..49ec4fa1 100644 --- a/lib/client/http.js +++ b/lib/client/http.js @@ -30,9 +30,9 @@ module.exports = function(ss, clients, options) { // Append the 'serveClient' method to the HTTP Response object res.serveClient = function(name) { - var client, fileName, self, sendHTML; - self = this; - sendHTML = function(html, code) { + var self = this; + + function sendHTML(html, code) { if (!code) { code = 200; } @@ -45,9 +45,10 @@ module.exports = function(ss, clients, options) { self.setHeader('Content-Length', Buffer.byteLength(html)); self.setHeader('Content-Type', 'text/html; charset=UTF-8'); self.end(html); - }; + } + try { - client = typeof name === 'string' && clients[name]; + var client = typeof name === 'string' && clients[name]; if (!client) { throw new Error('Unable to find single-page client: ' + name); } @@ -57,7 +58,7 @@ module.exports = function(ss, clients, options) { // Return from in-memory cache if possible if (!cache[name]) { - fileName = pathlib.join(ss.root, options.dirs.assets, client.name, client.id + '.html'); + var fileName = pathlib.join(ss.root, options.dirs.assets, client.name, client.id + '.html'); cache[name] = fs.readFileSync(fileName, 'utf8'); } From fda53a0f18710d96af466c5c70971ed90edfd540 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Mon, 9 Feb 2015 09:08:52 +0100 Subject: [PATCH 39/59] chore(lint): extracted getBundle function --- lib/client/bundler/index.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 5f7d68a6..1e23c185 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -18,21 +18,21 @@ var fs = require('fs'), */ var bundlers = {}; -module.exports = function(ss,options) { +function getBundler(client){ - function getBundler(client){ + if (client.bundler) { return client.bundler; } - if (client.bundler) { return client.bundler; } + if (typeof client.client === "string") { + return bundlers[client.client]; + } + if (typeof client.name === "string") { + return bundlers[client.name]; + } - if (typeof client.client === "string") { - return bundlers[client.client]; - } - if (typeof client.name === "string") { - return bundlers[client.name]; - } + throw new Error('Unknow client '+(client.name || client.client) ); +} - throw new Error('Unknow client '+(client.name || client.client) ); - } +module.exports = function(ss,options) { return { From 694aa2213819a6ffc47326f5e9bcb2c32c7476ae Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 10 Feb 2015 20:22:56 +0100 Subject: [PATCH 40/59] fix(bundler): fix _serveDev typo --- lib/client/bundler/default.js | 4 ++-- lib/client/view.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 9ba4725f..543a8b89 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -192,9 +192,9 @@ module.exports = function(ss,client,options){ } } - var initCode = bundler.asset.launch(); + var initCodes = bundler.asset.launch(); - return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(initCode)); + return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(initCodes)); } return bundler; diff --git a/lib/client/view.js b/lib/client/view.js index 7755f2b6..e6bcc8c1 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -78,12 +78,12 @@ module.exports = function(ss, client, options, cb) { // Send all CSS if (client.includes.css) { output = output.concat( bundler.asset.entries('css') - .map(function(entry) { return ss.bundler.htmlTag.css('/_serverDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); + .map(function(entry) { return ss.bundler.htmlTag.css('/_serveDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); } // Send Application Code output = output.concat( bundler.asset.entries('js') - .map(function(entry) { return ss.bundler.htmlTag.js('/_serverDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); + .map(function(entry) { return ss.bundler.htmlTag.js('/_serveDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); // Start your app and connect to SocketStream if (client.includes.initCode) { From ecfe64f2287e5eb843087e4caca06a12c46ad593 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 10 Feb 2015 23:34:43 +0100 Subject: [PATCH 41/59] fix(bundler): packing initCode --- lib/client/bundler/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 1e23c185..f937888d 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -199,7 +199,7 @@ module.exports = function(ss,options) { } initCode = initCode.replace(entryInit, realInit) - return { content:initCode }; + return { content:initCode, options: {} }; }, packAssetSet: function packAssetSet(assetType, client, postProcess) { From 295f72bc91a005152cb3781c3cfd63d098251731 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Wed, 11 Feb 2015 20:30:46 +0100 Subject: [PATCH 42/59] system modules + libs iteration Run through them the same way when packing and adding html tags. --- lib/client/bundler/default.js | 70 +++++++++++------------------------ lib/client/bundler/index.js | 70 ++++++++++++++++++++++++++++++++--- lib/client/bundler/webpack.js | 63 +++++++++++++------------------ lib/client/serve/dev.js | 31 +++++++++++----- lib/client/system/index.js | 7 ++-- lib/client/view.js | 28 ++++++++------ 6 files changed, 153 insertions(+), 116 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 543a8b89..92fe5f29 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -23,12 +23,13 @@ module.exports = function(ss,client,options){ toMinifiedCSS: toMinifiedCSS, toMinifiedJS: toMinifiedJS, asset: { - includeSystemLib: includeSystemLib, - includeSystemModule: includeSystemModule, entries: entries, + + loader: assetLoader, + systemModule: systemModule, js: assetJS, worker: assetWorker, - launch: assetLaunch, + start: assetStart, css: assetCSS, html: assetHTML } @@ -55,17 +56,17 @@ module.exports = function(ss,client,options){ } /** + * list of entries for an asset type relative to the client directory * - * @param name - * @param content - * @param options - * @returns {boolean} + * @param assetType + * @returns {*} */ - function includeSystemLib(name) { - switch(name) { - case "browserify": - } - return true; + function entries(assetType) { + return ss.bundler.entries(client, assetType); + } + + function assetLoader() { + return client.includes.system? ss.bundler.systemLibs() : null; } /** @@ -75,22 +76,15 @@ module.exports = function(ss,client,options){ * @param options * @returns {boolean} */ - function includeSystemModule(name) { + function systemModule(name) { switch(name) { case "eventemitter2": case "socketstream": + default: + if (client.includes.system) { + return ss.bundler.systemModule(name) + } } - return true; - } - - /** - * list of entries for an asset type relative to the client directory - * - * @param assetType - * @returns {*} - */ - function entries(assetType) { - return ss.bundler.entries(client, assetType); } /** @@ -131,8 +125,8 @@ module.exports = function(ss,client,options){ * * @returns {*} */ - function assetLaunch() { - return ss.bundler.launchCode(client); + function assetStart() { + return client.includes.initCode? ss.bundler.startCode(client) : null; } /** @@ -172,29 +166,7 @@ module.exports = function(ss,client,options){ * @returns {*} */ function toMinifiedJS(files) { - // Libs - var libs = systemAssets.libs.map(function(lib) { - return bundler.asset.includeSystemLib(lib.name, lib.content, lib.options)? lib : null; - }).filter(function(content) { - return !!content; - }); - - // Modules - var mods = [], - _ref = systemAssets.modules; - for (var name in _ref) { - if (_ref.hasOwnProperty(name)) { - var mod = _ref[name]; - if (bundler.asset.includeSystemModule(mod.name,mod.content,mod.options)) { - var code = ss.bundler.wrapModule(name, mod.content); - mods.push({ name:mod.name, content:code,options:mod.options,type:mod.type }); - } - } - } - - var initCodes = bundler.asset.launch(); - - return ss.bundler.minifyJS(libs.concat(mods).concat(files).concat(initCodes)); + return ss.bundler.minifyJS(files); } return bundler; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index f937888d..a70c6a81 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -189,8 +189,39 @@ module.exports = function(ss,options) { }; }, - launchCode: function(client) { - var initCode = system.assets.initCode.map(function(ic) { return ic.content; }).join('\n'), + systemLibs: function() { + var names = []; + return { + type: 'loader', + names: names, + content: system.assets.libs.map(function(lib) { names.push(lib.name); return lib.content; }).join('\n') + }; + }, + + systemModule: function(name) { + var mod = system.assets.modules[name]; + if (mod) { + var code = ss.bundler.wrapModule(name, mod.content); + return { + file: mod.name, + name: mod.name, + content: code, + options: mod.options, + type: mod.type + }; + } + }, + + /** + * Default start/init codes to load the client view. + * + * Called in default bundler startCode. + * + * @param client Client Object + * @returns {{content: *, options: {}}} Single Entry for inclusion in entries() + */ + startCode: function(client) { + var startCode = system.assets.startCode.map(function(ic) { return ic.content; }).join('\n'), entryInit = options.defaultEntryInit, realInit = 'require(' + client.entryInitPath + ');'; @@ -198,8 +229,8 @@ module.exports = function(ss,options) { realInit = options.entryModuleName? 'require("/'+options.entryModuleName+'");' : ''; } - initCode = initCode.replace(entryInit, realInit) - return { content:initCode, options: {} }; + startCode = startCode.replace(entryInit, realInit) + return { content:startCode, options: {}, type: 'start' }; }, packAssetSet: function packAssetSet(assetType, client, postProcess) { @@ -236,23 +267,50 @@ module.exports = function(ss,options) { return processFiles(); }, - // css, js, worker, + /** + * Make a list of asset entries for JS/CSS bundle. + * + * @param client + * @param assetType + * @returns {Array} + */ entries: function entries(client, assetType) { var _entries = [], + bundler = getBundler(client), pathType; switch(assetType) { case 'css': pathType = 'css'; break; case 'js': pathType = 'code'; break; case 'worker': pathType = 'code'; break; } + if (pathType === 'code') { + // Libs + var libs = [bundler.asset.loader()]; + + // Modules + var mods = [], + _ref = system.assets.modules; + for (var name in _ref) { + if (_ref.hasOwnProperty(name)) { + mods.push( bundler.asset.systemModule(name) ); + } + } + _entries = _entries.concat(libs).concat(mods); + } client.paths[pathType].forEach(function(from) { return magicPath.files(path.join(ss.root, options.dirs.client), from).forEach(function(file) { return _entries.push({file:file,importedBy:from}); }); }); - return _entries; + if (pathType === 'code') { + _entries.push(bundler.asset.start()); + } + // entries with blank ones stripped out + return _entries.filter(function(entry) { + return !!entry; + }); }, formatKb: formatKb, diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index f674e5f2..67e6dd3e 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -13,15 +13,14 @@ module.exports = function(webpack) { toMinifiedCSS: toMinifiedCSS, toMinifiedJS: toMinifiedJS, asset: { - includeSystemLib: includeSystemLib, - includeSystemModule: includeSystemModule, entries: entries, html: assetHTML, - system: assetSystem, + loader: assetLoader, + includeSystemModule: includeSystemModule, js: assetJS, worker: assetWorker, - launch: assetLaunch, + start: assetStart, css: assetCSS } }; @@ -53,35 +52,6 @@ module.exports = function(webpack) { } - /** - * - * @param name - * @param content - * @param options - * @returns {boolean} - */ - function includeSystemLib(name) { - switch(name) { - case "browserify": - } - return true; - } - - /** - * - * @param name - * @param content - * @param options - * @returns {boolean} - */ - function includeSystemModule(name) { - switch(name) { - case "eventemitter2": - case "socketstream": - } - return true; - } - /** * list of entries for an asset type relative to the client directory * @@ -118,10 +88,29 @@ module.exports = function(webpack) { * * @param cb */ - function assetSystem(cb) { - cb(''); + function assetLoader() { + return { type: 'loader', content: ';/* loader */' }; + } + + /** + * + * @param name + * @param content + * @param options + * @returns {boolean} + */ + function systemModule(name) { + switch(name) { + case "eventemitter2": + case "socketstream": + default: + //if (client.includes.system) { + return ss.bundler.systemModule(name) + //} + } } + /** * * @param path @@ -140,8 +129,8 @@ module.exports = function(webpack) { * @param cb * @returns {*} */ - function assetLaunch(cb) { - var output = ss.bundler.launchCode(bundler.client); + function assetStart(cb) { + var output = ss.bundler.startCode(bundler.client); return cb(output); } diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index bb4f9908..e3a306dd 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -12,27 +12,38 @@ var url = require('url'), // module.exports = function (ss, router, options) { - var bundler = require('../bundler/index')(ss,options); - - // JAVASCRIPT + // JAVASCRIPT // Serve system libraries and modules router.on('/_serveDev/system?*', function(request, response) { var thisUrl = url.parse(request.url), - params = qs.parse(thisUrl.query); + params = qs.parse(thisUrl.query), + moduleName = utils.parseUrl(request.url); - return bundler.get(params).asset.system(function(output) { - return utils.serve.js(output, response); - }); + // no module name (probably ts=..) + if (moduleName.indexOf('=') >= 0) { + var loader = ss.bundler.get(params).asset.loader() || {}, + namesComment = '/* ' + loader.names.join(',') + ' */'; + utils.serve.js(namesComment+'\n'+loader.content || '', response); + } + + // module + else { + var module = ss.bundler.get(params).asset.systemModule(moduleName) || {}; + utils.serve.js(module.content || '', response); + } }); + //TODO bundler calculates entries. view builds according to entries. formatter is predetermined + // Listen for requests for application client code router.on('/_serveDev/code?*', function(request, response) { var thisUrl = url.parse(request.url), params = qs.parse(thisUrl.query), path = utils.parseUrl(request.url); - return bundler.get(params).asset.js(path, { + return ss.bundler.get(params).asset.js(path, { + //TODO formatter: params.formatter, client: params.client, clientId: params.ts, pathPrefix: params.pathPrefix @@ -44,7 +55,7 @@ module.exports = function (ss, router, options) { var thisUrl = url.parse(request.url), params = qs.parse(thisUrl.query); - bundler.get(params).asset.launch(function(output) { + ss.bundler.get(params).asset.launch(function(output) { return utils.serve.js(output, response); }); }); @@ -57,7 +68,7 @@ module.exports = function (ss, router, options) { thisUrl = url.parse(request.url); params = qs.parse(thisUrl.query); path = utils.parseUrl(request.url); - return bundler.get(params).asset.css(path, { + return ss.bundler.get(params).asset.css(path, { client: params.client, clientId: params.ts }, function(output) { diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 85e442f3..0759c72f 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -14,7 +14,7 @@ var fs = require('fs'), var assets = exports.assets = { libs: [], modules: {}, - initCode: [] + startCode: [] }; function pushUniqueAsset(listName,asset) { @@ -34,8 +34,9 @@ var send = exports.send = function (type, name, content, options) { } switch (type) { + case 'start': case 'code': - return assets.initCode.push({content:content,options:options}); + return assets.startCode.push({content:content,options:options, type:'start'}); case 'lib': case 'library': return pushUniqueAsset('libs',{ @@ -63,7 +64,7 @@ var send = exports.send = function (type, name, content, options) { exports.unload = function() { assets.libs = []; assets.modules = {}; - assets.initCode = []; + assets.startCode = []; }; // Load all system libs and modules diff --git a/lib/client/view.js b/lib/client/view.js index e6bcc8c1..67910cdc 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -70,25 +70,31 @@ module.exports = function(ss, client, options, cb) { } else { // Otherwise, in development, list all files individually so debugging is easier - // SocketStream system libs and modules - if (client.includes.system) { - output.push(ss.bundler.htmlTag.js('/_serveDev/system?ts=' + client.id)); - } - // Send all CSS if (client.includes.css) { output = output.concat( bundler.asset.entries('css') - .map(function(entry) { return ss.bundler.htmlTag.css('/_serveDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); + .map(function(entry) { + return ss.bundler.htmlTag.css('/_serveDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); } // Send Application Code output = output.concat( bundler.asset.entries('js') - .map(function(entry) { return ss.bundler.htmlTag.js('/_serveDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); + .map(function(entry) { + switch(entry.type) { + case 'loader': + return ss.bundler.htmlTag.js('/_serveDev/system?ts=' + client.id + '&client=' + client.name); - // Start your app and connect to SocketStream - if (client.includes.initCode) { - output.push(ss.bundler.htmlTag.js('/_serveDev/start?ts=' + client.id)); - } + case 'mod': + case 'module': + return ss.bundler.htmlTag.js('/_serveDev/system/' + entry.file + '?ts=' + client.id + '&client=' + client.name); + + case 'start': + return ss.bundler.htmlTag.js('/_serveDev/start?ts=' + client.id + '&client=' + client.name); + + default: + return ss.bundler.htmlTag.js('/_serveDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); + } + }) ); } return output; } From c5adac7e0a7c2bade6e826cb79f4c8f746fa2555 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Wed, 11 Feb 2015 20:48:52 +0100 Subject: [PATCH 43/59] feat(id): unique client id can be used to look up bundler --- lib/client/bundler/index.js | 20 ++++++++++++++------ lib/client/index.js | 3 ++- lib/client/view.js | 10 +++++----- package.json | 1 + 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index a70c6a81..9c91aa05 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -16,12 +16,18 @@ var fs = require('fs'), * Bundler by client name * @type {{}} */ -var bundlers = {}; +var bundlers = {}, + bundlerById = {}; function getBundler(client){ if (client.bundler) { return client.bundler; } + if (client.ts) { + if (bundlerById[client.ts]) { + return bundlerById[client.ts]; + } + } if (typeof client.client === "string") { return bundlers[client.client]; } @@ -45,15 +51,17 @@ module.exports = function(ss,options) { define: function defineBundler(client,args) { var name = args[0], - pathsOrFunc = args[1]; + pathsOrFunc = args[1], + bundler; if (typeof pathsOrFunc === "function") { - bundlers[name] = pathsOrFunc(ss,options); - bundlers[name].dests = bundlers[name].define(client, args[2], args[3], args[4], args[5]); + bundler = bundlers[name] = pathsOrFunc(ss,options); + bundler.dests = bundler.define(client, args[2], args[3], args[4], args[5]); } else { - bundlers[name] = require('./default')(ss,client,options); - bundlers[name].dests = bundlers[name].define(args[1]); + bundler = bundlers[name] = require('./default')(ss,client,options); + bundler.dests = bundler.define(args[1]); } + bundlerById[client.id] = bundler; }, /** diff --git a/lib/client/index.js b/lib/client/index.js index fafaa4cd..521e44f5 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -10,6 +10,7 @@ require('colors'); var fs = require('fs'), path = require('path'), + shortid = require('shortid'), log = require('../utils/log'), systemAssets = require('./system'); @@ -137,7 +138,7 @@ module.exports = function(ss, router) { } // if a function is used construct a bundler with it otherwise use default bundler var client = clients[name] = { name: name }; - client.id = Number(Date.now()); + client.id = shortid.generate(); client.paths = {}; client.includes = { css: true, diff --git a/lib/client/view.js b/lib/client/view.js index 67910cdc..8953ae68 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -74,7 +74,7 @@ module.exports = function(ss, client, options, cb) { if (client.includes.css) { output = output.concat( bundler.asset.entries('css') .map(function(entry) { - return ss.bundler.htmlTag.css('/_serveDev/css/' + entry.file + '?ts=' + client.id + '&client=' + client.name); }) ); + return ss.bundler.htmlTag.css('/_serveDev/css/' + entry.file + '?ts=' + client.id); }) ); } // Send Application Code @@ -82,17 +82,17 @@ module.exports = function(ss, client, options, cb) { .map(function(entry) { switch(entry.type) { case 'loader': - return ss.bundler.htmlTag.js('/_serveDev/system?ts=' + client.id + '&client=' + client.name); + return ss.bundler.htmlTag.js('/_serveDev/system?ts=' + client.id); case 'mod': case 'module': - return ss.bundler.htmlTag.js('/_serveDev/system/' + entry.file + '?ts=' + client.id + '&client=' + client.name); + return ss.bundler.htmlTag.js('/_serveDev/system/' + entry.file + '?ts=' + client.id); case 'start': - return ss.bundler.htmlTag.js('/_serveDev/start?ts=' + client.id + '&client=' + client.name); + return ss.bundler.htmlTag.js('/_serveDev/start?ts=' + client.id); default: - return ss.bundler.htmlTag.js('/_serveDev/code/' + entry.file + '?ts=' + client.id + '&client=' + client.name); + return ss.bundler.htmlTag.js('/_serveDev/code/' + entry.file + '?ts=' + client.id); } }) ); } diff --git a/package.json b/package.json index 5e9b8d6f..40c5b067 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "redis": "= 0.12.1", "semver": "= 4.2.0", "send": "0.11.0", + "shortid": "^2.1.3", "uglify-js": "= 1.3.3", "uid2": "0.0.3", "utils-merge": "1.0.0" From f7ed22a932437a9de8a6bc9c633dbe20c1802add Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Wed, 11 Feb 2015 21:21:03 +0100 Subject: [PATCH 44/59] feat(webpack): webpack bundler exceptions fixed --- lib/client/bundler/webpack.js | 8 ++++---- lib/client/serve/dev.js | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 67e6dd3e..3589b0a5 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -17,7 +17,7 @@ module.exports = function(webpack) { html: assetHTML, loader: assetLoader, - includeSystemModule: includeSystemModule, + systemModule: systemModule, js: assetJS, worker: assetWorker, start: assetStart, @@ -89,7 +89,7 @@ module.exports = function(webpack) { * @param cb */ function assetLoader() { - return { type: 'loader', content: ';/* loader */' }; + return { type: 'loader', names: [], content: ';/* loader */' }; } /** @@ -129,9 +129,9 @@ module.exports = function(webpack) { * @param cb * @returns {*} */ - function assetStart(cb) { + function assetStart() { var output = ss.bundler.startCode(bundler.client); - return cb(output); + return output; } /** diff --git a/lib/client/serve/dev.js b/lib/client/serve/dev.js index e3a306dd..6ca319c8 100644 --- a/lib/client/serve/dev.js +++ b/lib/client/serve/dev.js @@ -55,9 +55,8 @@ module.exports = function (ss, router, options) { var thisUrl = url.parse(request.url), params = qs.parse(thisUrl.query); - ss.bundler.get(params).asset.launch(function(output) { - return utils.serve.js(output, response); - }); + var start = ss.bundler.get(params).asset.start() || {}; + return utils.serve.js(start.content || '', response); }); // CSS From 4999e76725519269790fde3323b1f80d8c95a1a2 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Wed, 18 Feb 2015 22:12:48 +0100 Subject: [PATCH 45/59] feat(bundler): pass system.assets to entries --- lib/client/bundler/default.js | 73 +++++++++++++++++++++++++++++------ lib/client/bundler/index.js | 2 +- lib/client/bundler/webpack.js | 3 +- lib/client/view.js | 3 +- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 92fe5f29..3b3ff0b9 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -16,7 +16,22 @@ function includeFlags(overrides) { return includes; } +/** + * @typedef { name:string, path:string, content:string, options:string, type:string } AssetEntry + */ + module.exports = function(ss,client,options){ + + /** + * @ngdoc service + * @name client.bundler:default + * @function + * + * @description + * The default bundler of HTML, CSS & JS + * + * @type {{define: define, load: load, toMinifiedCSS: toMinifiedCSS, toMinifiedJS: toMinifiedJS, asset: {entries: entries, loader: assetLoader, systemModule: systemModule, js: assetJS, worker: assetWorker, start: assetStart, css: assetCSS, html: assetHTML}}} + */ var bundler = { define: define, load: load, @@ -51,30 +66,60 @@ module.exports = function(ss,client,options){ return ss.bundler.destsFor(client); } + /** + * + * @returns {{a: string, b: string}} + */ function load() { - + return { + a:'a', + b:'b' + } } /** - * list of entries for an asset type relative to the client directory + * @ngdoc method + * @name client.bundler:default#entries + * @methodOf client.bundler:default + * @function + * @description + * Provides the view and the pack functions with a + * list of entries for an asset type relative to the client directory. + * The default implementation is used. * - * @param assetType - * @returns {*} + * @param {String} assetType js/css + * @param {Object} systemAssets Collection of libs, modules, initCode + * @returns {[AssetEntry]} List of output entries */ - function entries(assetType) { - return ss.bundler.entries(client, assetType); + function entries(assetType,systemAssets) { + return ss.bundler.entries(client, assetType, systemAssets); } + /** + * @ngdoc method + * @name client.bundler:default#assetLoader + * @methodOf client.bundler:default + * @function + * @description + * Return entry for the JS loader depending on the includes.system client config. + * + * @returns {AssetEntry} Loader resource + */ function assetLoader() { return client.includes.system? ss.bundler.systemLibs() : null; } /** + * @ngdoc method + * @name client.bundler:default#systemModule + * @methodOf client.bundler:default + * @function + * @description + * Return the resource for a registered system module by the given name. It uses + * the default wrapCode for module registration with require. * - * @param name - * @param content - * @param options - * @returns {boolean} + * @param {String} name Logical Module Name + * @returns {AssetEntry} Module resource */ function systemModule(name) { switch(name) { @@ -122,8 +167,14 @@ module.exports = function(ss,client,options){ } /** + * @ngdoc method + * @name client.bundler:default#assetStart + * @methodOf client.bundler:default + * @function + * @description + * Return the resource for starting the view. It is code for immediate execution at the end of the page. * - * @returns {*} + * @returns {AssetEntry} Start Script resource */ function assetStart() { return client.includes.initCode? ss.bundler.startCode(client) : null; diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 9c91aa05..9202b9ec 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -243,7 +243,7 @@ module.exports = function(ss,options) { packAssetSet: function packAssetSet(assetType, client, postProcess) { var bundler = getBundler(client), - filePaths = bundler.asset.entries(assetType); + filePaths = bundler.asset.entries(assetType,system.assets); function writeFile(fileContents) { var fileName = bundler.dests.paths[assetType]; diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 3589b0a5..9b027516 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -56,9 +56,10 @@ module.exports = function(webpack) { * list of entries for an asset type relative to the client directory * * @param assetType + * @param systemAssets * @returns {*} */ - function entries(assetType) { + function entries(assetType,systemAssets) { return ss.bundler.entries(bundler.client, assetType); } diff --git a/lib/client/view.js b/lib/client/view.js index 8953ae68..df77f2e7 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -4,6 +4,7 @@ 'use strict'; var pathlib = require('path'), + system = require('./system'), magicPath = require('./magic_path'); module.exports = function(ss, client, options, cb) { @@ -78,7 +79,7 @@ module.exports = function(ss, client, options, cb) { } // Send Application Code - output = output.concat( bundler.asset.entries('js') + output = output.concat( bundler.asset.entries('js',system.assets) .map(function(entry) { switch(entry.type) { case 'loader': From ee1dd954b083e131d160952d248ffcfbc4cc8522 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Fri, 20 Feb 2015 22:42:10 +0100 Subject: [PATCH 46/59] feat(bundler): better systemModule logical path/dir for system modules and libraries optional wrap system modules --- lib/client/bundler/index.js | 14 ++++++++++++-- lib/client/system/index.js | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 9202b9ec..69553a1f 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -206,13 +206,23 @@ module.exports = function(ss,options) { }; }, - systemModule: function(name) { + /** + * @ngdoc + * @method + * + * @param {String} name Name of the system module to return in a descriptor + * @param {boolean} wrap Shall the content be wrapped in `require.define`. Default is true. + * @returns {{file: *, name: *, path: *, dir: *, content: (code|*), options: *, type: *}} + */ + systemModule: function(name,wrap) { var mod = system.assets.modules[name]; if (mod) { - var code = ss.bundler.wrapModule(name, mod.content); + var code = wrap===false? mod.content: ss.bundler.wrapModule(name, mod.content); return { file: mod.name, name: mod.name, + path: mod.path, + dir: mod.dir, content: code, options: mod.options, type: mod.type diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 0759c72f..725833ce 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -42,6 +42,8 @@ var send = exports.send = function (type, name, content, options) { return pushUniqueAsset('libs',{ name: name, type: type, + dir: pathlib.join(__dirname,'libs'), + path: pathlib.join(__dirname,'libs',name + '.js'), content: content, options: options }); @@ -53,6 +55,8 @@ var send = exports.send = function (type, name, content, options) { assets.modules[name] = { name: name, type: type, + dir: pathlib.join(__dirname,'modules'), + path: pathlib.join(__dirname,'modules',name + '.js'), content: content, options: options }; From 41b515dff2d345de936574ba17dde9bcc7308dfe Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sat, 21 Feb 2015 19:00:52 +0100 Subject: [PATCH 47/59] docs(bundler): revised documentation --- docs/js/docs-setup.js | 108 ++++++++++++++++-- .../partials/api/bundler.default.default.html | 38 ++++++ docs/partials/api/bundler.html | 8 ++ .../partials/api/bundler.webpack.webpack.html | 21 ++++ docs/partials/api/ss.add.html | 18 +++ docs/partials/api/ss.bundler.bundler.html | 25 ++++ docs/partials/api/ss.client.client.html | 26 +++++ docs/partials/api/ss.env.html | 10 ++ docs/partials/api/ss.html | 7 ++ .../{utils.log.log.html => ss.log.log.html} | 22 ++-- docs/partials/api/ss.root.html | 13 +++ docs/partials/api/ss.version.html | 10 ++ .../tutorials/client_side_development.html | 3 +- lib/client/bundler/default.js | 38 +++--- lib/client/bundler/index.js | 42 ++++++- lib/client/bundler/webpack.js | 28 ++++- lib/client/index.js | 10 ++ lib/client/system/index.js | 13 ++- lib/socketstream.js | 38 +++++- lib/utils/log.js | 28 ++--- .../en/client_side_development.ngdoc | 5 +- 21 files changed, 445 insertions(+), 66 deletions(-) create mode 100644 docs/partials/api/bundler.default.default.html create mode 100644 docs/partials/api/bundler.html create mode 100644 docs/partials/api/bundler.webpack.webpack.html create mode 100644 docs/partials/api/ss.add.html create mode 100644 docs/partials/api/ss.bundler.bundler.html create mode 100644 docs/partials/api/ss.client.client.html create mode 100644 docs/partials/api/ss.env.html create mode 100644 docs/partials/api/ss.html rename docs/partials/api/{utils.log.log.html => ss.log.log.html} (54%) create mode 100644 docs/partials/api/ss.root.html create mode 100644 docs/partials/api/ss.version.html diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 3655b0ae..97832ab0 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -39,7 +39,7 @@ NG_DOCS={ "type": "overview", "moduleName": "Client-Side Development", "shortDescription": "Client-Side Development", - "keywords": "asset assets break browser bundle bundler caching client client-side css determine development directory entry file fly html http injected js overview path pattern relative separate separately served timestamp tutorials type url view" + "keywords": "amount asset assets break browser bundle bundler bundlers caching client client-side css determine development directory entry file files fly generally html http injected js overview path pattern reduce relative separate separately served timestamp tutorials type url view watch work" }, { "section": "tutorials", @@ -221,6 +221,33 @@ NG_DOCS={ "shortDescription": "Custom Request Responder", "keywords": "access action add adding additional aimed allow allowing allows annotated api apitree app appending application applications arbitrary argument arguments assigned assuming async automatically backbone basic basically best bi-directional browser build built-in bundled byte bytes calculate call callback callbacks called case channel character choice choose clicks client client-side clone code combination comfortable commas complete complex concepts config connection conscious continually control coordinate core create creating custom data default define defined desire destined develop developer developers digits directions directly documentation easier easily echo efficient ember encountered entirely errors event events exactly example experiment experimental explore extend external feature fewer files find flexibility flowing format freenode function functionally future gaming git github going groups handler handles heartbeat help hesitate high-speed http https ideas implement implementing incoming increase info inspiration interfaces internal introduction investigate invoked io irc issue javascript js json json-serialized left length libraries ll load loaded log lot low-bandwidth main message messages method middleware mode model models module modules mongodb-esq mouse movements moving namespacing node non-blocking note notice npm number numeric object objects online optional org outgoing overhead overview packet-sniff packing param1 param2 pass passed passing persistent pick pipe prepended presence process production protocol pub publish receives recommend register registerapi registering regular repl request requests responder responderid responders result rpc rpc-style runs schema second send sending sends separated sequential serialization serialize server server-side simple simply socket socketstream source ss ss-console ss-echo-responder stable stack stage started status store streaming streams string strings suite support synching takes test third-party time touch transforms transports tutorials types typical ultimate ultra ultra-simple unique unlimited user users ve versa vice view virtual ways website websocket wire world write writing zeromq" }, + { + "section": "api", + "id": "bundler", + "shortName": "bundler", + "type": "service", + "moduleName": "bundler", + "shortDescription": "Bundlers included.", + "keywords": "api bundler bundlers included service" + }, + { + "section": "api", + "id": "bundler.default:default", + "shortName": "default", + "type": "service", + "moduleName": "bundler", + "shortDescription": "The default bundler of HTML, CSS & JS", + "keywords": "api asset assetcss assethtml assetjs assetloader assetstart assettype assetworker bundler client code collection config css default define depending directory entries entry execution functions html implementation includes initcode js libs list load loader logical method module modules output pack registered registration relative require resource return script service start starting system systemassets systemmodule tominifiedcss tominifiedjs type view worker wrapcode" + }, + { + "section": "api", + "id": "bundler.webpack:webpack", + "shortName": "webpack", + "type": "service", + "moduleName": "bundler", + "shortDescription": "The webpack bundler of HTML, CSS & JS", + "keywords": "api asset assettype bundler client collection concept css custom demonstration directory entries functions html improved initcode js libs list method modules output pack purposes relative service systemassets type validate view webpack" + }, { "section": "api", "id": "http.index:index", @@ -239,6 +266,76 @@ NG_DOCS={ "shortDescription": "Right now the router is simply an EventEmitter. This may change in the future", "keywords": "allows api callback cb change clients ee eventemitter exists fall find fully function future html5 http instance mock multiple object original passed pushstate recursively req request res respond route router routing service simply single-page speed support url" }, + { + "section": "api", + "id": "ss", + "shortName": "ss", + "type": "overview", + "moduleName": "ss", + "shortDescription": "Internal API object which is passed to sub-modules and can be used within your app", + "keywords": "access add api app env exports function internal log object overview passed require root session socketstream ss string sub-modules var" + }, + { + "section": "api", + "id": "ss.add", + "shortName": "ss.add", + "type": "function", + "moduleName": "ss", + "shortDescription": "Call from your app to safely extend the 'ss' internal API object passed through to your /server code", + "keywords": "add api app call code extend fn function internal key object passed safely ss" + }, + { + "section": "api", + "id": "ss.bundler:bundler", + "shortName": "bundler", + "type": "service", + "moduleName": "ss", + "shortDescription": "Client bundling API", + "keywords": "api bundler bundling client content custom default define describe descriptor entry implementing libraries method module require return service single ss system systemlibs systemmodule true wrap wrapped" + }, + { + "section": "api", + "id": "ss.client:client", + "shortName": "client", + "type": "service", + "moduleName": "ss", + "shortDescription": "Client serving, bundling, development, building.", + "keywords": "add allow allows api assets building bundling client clients code coffee compress content css defined development file flags format function html js lib library libs module options production require send served service serving single ss system type" + }, + { + "section": "api", + "id": "ss.env", + "shortName": "ss.env", + "type": "property", + "moduleName": "ss", + "keywords": "api change default development env environment execution node_env property set ss ss_env type variable" + }, + { + "section": "api", + "id": "ss.log:log", + "shortName": "log", + "type": "service", + "moduleName": "ss", + "shortDescription": "Contains method stubs for logging to console (by default) or", + "keywords": "api assigning choose console debug default error fairly function happened info informed keeping level log logging method override parameters provider require service socketstream ss stubs sysadmin takes time trivial unexpected var wakeup warn winston" + }, + { + "section": "api", + "id": "ss.root", + "shortName": "ss.root", + "type": "property", + "moduleName": "ss", + "shortDescription": "By default the project root is the current working directory", + "keywords": "api current default directory project property root ss working" + }, + { + "section": "api", + "id": "ss.version", + "shortName": "ss.version", + "type": "property", + "moduleName": "ss", + "keywords": "api major minor property ss version" + }, { "section": "api", "id": "utils", @@ -257,15 +354,6 @@ NG_DOCS={ "shortDescription": "This is used to maintain lists of userIds to socketIds and channelIds to socketIds", "keywords": "absolute adapted api array basename basepath channelids contents css dir directory dorectory eror extension file filepath files find findextforbase findextforbasepath github givven https isdir jade json lists loadpackagejson loads lookup maintain matching mode null object package parse path readdirsync reads returns root service socketids socketstream start synchronous thorws true unable userids utils views" }, - { - "section": "api", - "id": "utils.log:log", - "shortName": "log", - "type": "service", - "moduleName": "utils", - "shortDescription": "Contains method stubs for logging to console (by default) or", - "keywords": "api assigning choose console debug default error fairly function happened info informed keeping level log logging method override parameters provider require service socketstream ss stubs sysadmin takes time trivial unexpected utils var wakeup warn winston" - }, { "section": "api", "id": "utils.misc:misc", diff --git a/docs/partials/api/bundler.default.default.html b/docs/partials/api/bundler.default.default.html new file mode 100644 index 00000000..da34af5e --- /dev/null +++ b/docs/partials/api/bundler.default.default.html @@ -0,0 +1,38 @@ +

    default +
    service in module bundler + +
    +

    +

    Description

    +

    The default bundler of HTML, CSS & JS

    +
    +

    Methods

    +
    • assetLoader()

      +

      Return entry for the JS loader depending on the includes.system client config.

      +
      Returns
      AssetEntry

      Loader resource

      +
      +
    • +
    • assetStart()

      +

      Return the resource for starting the view. It is code for immediate execution at the end of the page.

      +
      Returns
      AssetEntry

      Start Script resource

      +
      +
    • +
    • entries(assetType, systemAssets)

      +

      Provides the view and the pack functions with a +list of entries for an asset type relative to the client directory. +The default implementation is used.

      +
      Parameters
      ParamTypeDetails
      assetTypeString

      js/css

      +
      systemAssetsObject

      Collection of libs, modules, initCode

      +
      Returns
      [AssetEntry]

      List of output entries

      +
      +
    • +
    • systemModule(name)

      +

      Return the resource for a registered system module by the given name. It uses +the default wrapCode for module registration with require.

      +
      Parameters
      ParamTypeDetails
      nameString

      Logical Module Name

      +
      Returns
      AssetEntry

      Module resource

      +
      +
    • +
    +
    +
    diff --git a/docs/partials/api/bundler.html b/docs/partials/api/bundler.html new file mode 100644 index 00000000..965c70f0 --- /dev/null +++ b/docs/partials/api/bundler.html @@ -0,0 +1,8 @@ +

    bundler +
    +
    +

    +

    Description

    +

    Bundlers included.

    +
    +
    diff --git a/docs/partials/api/bundler.webpack.webpack.html b/docs/partials/api/bundler.webpack.webpack.html new file mode 100644 index 00000000..5e98c6d4 --- /dev/null +++ b/docs/partials/api/bundler.webpack.webpack.html @@ -0,0 +1,21 @@ +

    webpack +
    service in module bundler + +
    +

    +

    Description

    +

    The webpack bundler of HTML, CSS & JS

    +

    This is just for demonstration purposes and to validate the custom bundler concept. It can be improved.

    +
    +

    Methods

    +
    • entries(assetType, systemAssets)

      +

      Provides the view and the pack functions with a +list of entries for an asset type relative to the client directory.

      +
      Parameters
      ParamTypeDetails
      assetTypeString

      js/css

      +
      systemAssetsObject

      Collection of libs, modules, initCode

      +
      Returns
      [AssetEntry]

      List of output entries

      +
      +
    • +
    +
    +
    diff --git a/docs/partials/api/ss.add.html b/docs/partials/api/ss.add.html new file mode 100644 index 00000000..f3f5b8fc --- /dev/null +++ b/docs/partials/api/ss.add.html @@ -0,0 +1,18 @@ +

    add +
    service in module ss + +
    +

    +

    Description

    +

    Call from your app to safely extend the 'ss' internal API object passed through to your /server code

    +
    +

    Usage

    +
    add(name, fn);
    +

    Parameters

    ParamTypeDetails
    namestring
      +
    • Key in the ss API.
    • +
    +
    fnfunctionnumberbooleanstring
      +
    • value or function
    • +
    +
    +
    diff --git a/docs/partials/api/ss.bundler.bundler.html b/docs/partials/api/ss.bundler.bundler.html new file mode 100644 index 00000000..df3263cf --- /dev/null +++ b/docs/partials/api/ss.bundler.bundler.html @@ -0,0 +1,25 @@ +

    bundler +
    service in module ss + +
    +

    +

    Description

    +

    Client bundling API

    +

    Client bundling API for implementing a custom bundler.

    +
    +

    Methods

    +
    • systemLibs()

      +

      A single entry for all system libraries.

      +
      Returns
      AssetEntry

      Entry

      +
      +
    • +
    • systemModule(name, wrap)

      +

      Describe a system module.

      +
      Parameters
      ParamTypeDetails
      nameString

      Name of the system module to return in a descriptor

      +
      wrapboolean

      Shall the content be wrapped in require.define. Default is true.

      +
      Returns
      AssetEntry

      Entry

      +
      +
    • +
    +
    +
    diff --git a/docs/partials/api/ss.client.client.html b/docs/partials/api/ss.client.client.html new file mode 100644 index 00000000..067c7aec --- /dev/null +++ b/docs/partials/api/ss.client.client.html @@ -0,0 +1,26 @@ +

    client +
    service in module ss + +
    +

    +

    Description

    +

    Client serving, bundling, development, building.

    +

    One or more clients are defined and will be served in production as a single HTML, CSS, and JS file.

    +
    +

    Methods

    +
    • send(name, content, options)

      +

      Allow other libs to send assets to the client. add new System Library or Module

      +
      Parameters
      ParamTypeDetails
      namestring
        +
      • Module name for require.
      • +
      +
      contentstring
        +
      • The JS code
      • +
      +
      optionsObject
        +
      • Allows you to specify compress and coffee format flags.
      • +
      +
      +
    • +
    +
    +
    diff --git a/docs/partials/api/ss.env.html b/docs/partials/api/ss.env.html new file mode 100644 index 00000000..96cec4e5 --- /dev/null +++ b/docs/partials/api/ss.env.html @@ -0,0 +1,10 @@ +

    env +
    service in module ss + +
    +

    +

    Usage

    +
    ss.env
    +

    Returns

    string

    Execution environment type. To change set environment variable NODE_ENV or SS_ENV. 'development' by default.

    +
    +
    diff --git a/docs/partials/api/ss.html b/docs/partials/api/ss.html new file mode 100644 index 00000000..e41031f5 --- /dev/null +++ b/docs/partials/api/ss.html @@ -0,0 +1,7 @@ +

    +
    +
    +

    +

    Internal API object which is passed to sub-modules and can be used within your app

    +

    To access it without it being passed var ss = require('socketstream').api;

    +
    diff --git a/docs/partials/api/utils.log.log.html b/docs/partials/api/ss.log.log.html similarity index 54% rename from docs/partials/api/utils.log.log.html rename to docs/partials/api/ss.log.log.html index da0e297f..1921c46e 100644 --- a/docs/partials/api/utils.log.log.html +++ b/docs/partials/api/ss.log.log.html @@ -1,47 +1,47 @@

    log -
    service in module utils +
    service in module ss

    Description

    -

    Contains method stubs for logging to console (by default) or +

    Contains method stubs for logging to console (by default) or whatever logging provider you choose.

    Methods

    • debug()

      -

      Debug level logging, uses console.log by default. Override by assigning a +

      Debug level logging, uses console.log by default. Override by assigning a function that takes the same parameters as console.log:

      var ss = require('socketstream');
       ss.api.log.debug = console.log;
       

      Example

      -
      ss.api.log.debug("Something fairly trivial happened");
      +
      ss.log.debug("Something fairly trivial happened");
       
    • error()

      -

      Error level logging, uses console.error by default. Override by assigning a +

      Error level logging, uses console.error by default. Override by assigning a function that takes the same parameters as console.error.

      Example

      -
      ss.api.log.error("Time to wakeup the sysadmin");
      +
      ss.log.error("Time to wakeup the sysadmin");
       
    • info()

      -

      Info level logging, uses console.log by default. Override by assigning a +

      Info level logging, uses console.log by default. Override by assigning a function that takes the same parameters as console.log.

      Example

      -
      ss.api.log.info("Just keeping you informed");
      +
      ss.log.info("Just keeping you informed");
       
    • warn()

      -

      Warn level logging, uses console.log by default. Override by assigning a +

      Warn level logging, uses console.log by default. Override by assigning a function that takes the same parameters as console.log:

      var ss = require('socketstream'),
           winston = require('winston');
      -ss.api.log.warn = winston.warn;
      +ss.log.warn = winston.warn;
       

      Example

      -
      ss.api.log.warn("Something unexpected happened!");
      +
      ss.log.warn("Something unexpected happened!");
       
    • diff --git a/docs/partials/api/ss.root.html b/docs/partials/api/ss.root.html new file mode 100644 index 00000000..7b0c645d --- /dev/null +++ b/docs/partials/api/ss.root.html @@ -0,0 +1,13 @@ +

      root +
      service in module ss + +
      +

      +

      Description

      +

      By default the project root is the current working directory

      +
      +

      Usage

      +
      ss.root
      +

      Returns

      string

      Project root

      +
      +
      diff --git a/docs/partials/api/ss.version.html b/docs/partials/api/ss.version.html new file mode 100644 index 00000000..b37b755a --- /dev/null +++ b/docs/partials/api/ss.version.html @@ -0,0 +1,10 @@ +

      version +
      service in module ss + +
      +

      +

      Usage

      +
      ss.version
      +

      Returns

      number

      major.minor

      +
      +
      diff --git a/docs/partials/tutorials/client_side_development.html b/docs/partials/tutorials/client_side_development.html index dc64043b..dce4704d 100644 --- a/docs/partials/tutorials/client_side_development.html +++ b/docs/partials/tutorials/client_side_development.html @@ -11,5 +11,6 @@

    • The asset type is specified in the URL to determine the bundler.
    • A timestamp is given in the URL to break any caching.
    -

    The URL pattern is http:///_serveDev/<type>/<relative path>?ts=<id>&client=<client>.

    +

    The URL pattern is http:///_serveDev/<type>/<relative path>?ts=<id>.

    +

    Bundlers generally work within the client directory to reduce the amount of files to watch.

    diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 3b3ff0b9..536eed00 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -17,21 +17,21 @@ function includeFlags(overrides) { } /** - * @typedef { name:string, path:string, content:string, options:string, type:string } AssetEntry + * @typedef { name:string, path:string, dir:string, content:string, options:string, type:string } AssetEntry */ +/** + * @ngdoc service + * @name bundler.default:default + * @function + * + * @description + * The default bundler of HTML, CSS & JS + * + * @type {{define: define, load: load, toMinifiedCSS: toMinifiedCSS, toMinifiedJS: toMinifiedJS, asset: {entries: entries, loader: assetLoader, systemModule: systemModule, js: assetJS, worker: assetWorker, start: assetStart, css: assetCSS, html: assetHTML}}} + */ module.exports = function(ss,client,options){ - /** - * @ngdoc service - * @name client.bundler:default - * @function - * - * @description - * The default bundler of HTML, CSS & JS - * - * @type {{define: define, load: load, toMinifiedCSS: toMinifiedCSS, toMinifiedJS: toMinifiedJS, asset: {entries: entries, loader: assetLoader, systemModule: systemModule, js: assetJS, worker: assetWorker, start: assetStart, css: assetCSS, html: assetHTML}}} - */ var bundler = { define: define, load: load, @@ -79,8 +79,8 @@ module.exports = function(ss,client,options){ /** * @ngdoc method - * @name client.bundler:default#entries - * @methodOf client.bundler:default + * @name bundler.default:default#entries + * @methodOf bundler.default:default * @function * @description * Provides the view and the pack functions with a @@ -97,8 +97,8 @@ module.exports = function(ss,client,options){ /** * @ngdoc method - * @name client.bundler:default#assetLoader - * @methodOf client.bundler:default + * @name bundler.default:default#assetLoader + * @methodOf bundler.default:default * @function * @description * Return entry for the JS loader depending on the includes.system client config. @@ -111,8 +111,8 @@ module.exports = function(ss,client,options){ /** * @ngdoc method - * @name client.bundler:default#systemModule - * @methodOf client.bundler:default + * @name bundler.default:default#systemModule + * @methodOf bundler.default:default * @function * @description * Return the resource for a registered system module by the given name. It uses @@ -168,8 +168,8 @@ module.exports = function(ss,client,options){ /** * @ngdoc method - * @name client.bundler:default#assetStart - * @methodOf client.bundler:default + * @name bundler.default:default#assetStart + * @methodOf bundler.default:default * @function * @description * Return the resource for starting the view. It is code for immediate execution at the end of the page. diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 69553a1f..a381ee04 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -12,6 +12,10 @@ var fs = require('fs'), jsp = uglifyjs.parser, pro = uglifyjs.uglify; +/** + * @typedef { name:string, path:string, dir:string, content:string, options:string, type:string } AssetEntry + */ + /** * Bundler by client name * @type {{}} @@ -38,6 +42,24 @@ function getBundler(client){ throw new Error('Unknow client '+(client.name || client.client) ); } +/** + * @ngdoc service + * @name bundler + * @function + * @description + * Bundlers included. + */ + +/** + * @ngdoc service + * @name ss.bundler:bundler + * @function + * + * @description + * Client bundling API + * ----------- + * Client bundling API for implementing a custom bundler. + */ module.exports = function(ss,options) { @@ -197,6 +219,16 @@ module.exports = function(ss,options) { }; }, + /** + * @ngdoc method + * @name ss.bundler:bundler#systemLibs + * @methodOf ss.bundler:bundler + * @function + * @description + * A single entry for all system libraries. + * + * @returns {AssetEntry} Entry + */ systemLibs: function() { var names = []; return { @@ -207,12 +239,16 @@ module.exports = function(ss,options) { }, /** - * @ngdoc - * @method + * @ngdoc method + * @name ss.bundler:bundler#systemModule + * @methodOf ss.bundler:bundler + * @function + * @description + * Describe a system module. * * @param {String} name Name of the system module to return in a descriptor * @param {boolean} wrap Shall the content be wrapped in `require.define`. Default is true. - * @returns {{file: *, name: *, path: *, dir: *, content: (code|*), options: *, type: *}} + * @returns {AssetEntry} Entry */ systemModule: function(name,wrap) { var mod = system.assets.modules[name]; diff --git a/lib/client/bundler/webpack.js b/lib/client/bundler/webpack.js index 9b027516..8c82a64f 100644 --- a/lib/client/bundler/webpack.js +++ b/lib/client/bundler/webpack.js @@ -5,6 +5,20 @@ // path = require('path'), // log = require('../../utils/log'); +/** + * @typedef { name:string, path:string, dir:string, content:string, options:string, type:string } AssetEntry + */ + +/** + * @ngdoc service + * @name bundler.webpack:webpack + * @function + * + * @description + * The webpack bundler of HTML, CSS & JS + * + * This is just for demonstration purposes and to validate the custom bundler concept. It can be improved. + */ module.exports = function(webpack) { return function(ss,options){ var bundler = { @@ -53,11 +67,17 @@ module.exports = function(webpack) { } /** - * list of entries for an asset type relative to the client directory + * @ngdoc method + * @name bundler.webpack:default#entries + * @methodOf bundler.webpack:webpack + * @function + * @description + * Provides the view and the pack functions with a + * list of entries for an asset type relative to the client directory. * - * @param assetType - * @param systemAssets - * @returns {*} + * @param {String} assetType js/css + * @param {Object} systemAssets Collection of libs, modules, initCode + * @returns {[AssetEntry]} List of output entries */ function entries(assetType,systemAssets) { return ss.bundler.entries(bundler.client, assetType); diff --git a/lib/client/index.js b/lib/client/index.js index 521e44f5..f3976440 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -37,6 +37,16 @@ var options = { // Store each client as an object var clients = {}; +/** + * @ngdoc service + * @name ss.client:client + * @function + * + * @description + * Client serving, bundling, development, building. + * ----------- + * One or more clients are defined and will be served in production as a single HTML, CSS, and JS file. + */ module.exports = function(ss, router) { // make bundler methods available for default and other implementations diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 725833ce..93c0f446 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -27,7 +27,18 @@ function pushUniqueAsset(listName,asset) { return list.push(asset); } -// API to add new System Library or Module +/** + * @ngdoc function + * @name ss.client:client#send + * @methodOf ss.client:client + * @parma {'code','lib','module'} type - `code`, `lib`, `module`. + * @param {string} name - Module name for require. + * @param {string} content - The JS code + * @param {Object} options - Allows you to specify `compress` and `coffee` format flags. + * @description + * Allow other libs to send assets to the client. add new System Library or Module + */ + var send = exports.send = function (type, name, content, options) { if (options === null || options === undefined) { options = {}; diff --git a/lib/socketstream.js b/lib/socketstream.js index 82356e18..18a9cf53 100644 --- a/lib/socketstream.js +++ b/lib/socketstream.js @@ -32,16 +32,50 @@ var session = exports.session = require('./session'); // logging var log = require('./utils/log'); -// Create an internal API object which is passed to sub-modules and can be used within your app +/** + * @ngdoc overview + * @name ss + * @description + * Internal API object which is passed to sub-modules and can be used within your app + * + * To access it without it being passed `var ss = require('socketstream').api;` + * + * @type {{version: *, root: *, env: string, log: (*|exports), session: exports, add: Function}} + */ var api = exports.api = { + /** + * @ngdoc property + * @name ss.version + * @returns {number} major.minor + */ version: version, + /** + * @ngdoc property + * @name ss.root + * @description + * By default the project root is the current working directory + * @returns {string} Project root + */ root: root, + /** + * @ngdoc property + * @name ss.env + * @returns {string} Execution environment type. To change set environment variable `NODE_ENV` or `SS_ENV`. 'development' by default. + */ env: env, + log: log, session: session, - // Call ss.api.add('name_of_api', value_or_function) from your app to safely extend the 'ss' internal API object passed through to your /server code + /** + * @ngdoc function + * @name ss.add + * @param {string} name - Key in the `ss` API. + * @param {function|number|boolean|string} fn - value or function + * @description + * Call from your app to safely extend the 'ss' internal API object passed through to your /server code + */ add: function(name, fn) { if (api[name]) { throw new Error('Unable to register internal API extension \'' + name + '\' as this name has already been taken'); diff --git a/lib/utils/log.js b/lib/utils/log.js index fabe53c4..a0065568 100644 --- a/lib/utils/log.js +++ b/lib/utils/log.js @@ -1,7 +1,7 @@ 'use strict'; /** * @ngdoc service - * @name utils.log:log + * @name ss.log:log * @function * * @description @@ -11,8 +11,8 @@ /** * @ngdoc service - * @name utils.log#debug - * @methodOf utils.log:log + * @name ss.log#debug + * @methodOf ss.log:log * @function * * @description @@ -25,14 +25,14 @@ * * @example * ``` - * ss.api.log.debug("Something fairly trivial happened"); + * ss.log.debug("Something fairly trivial happened"); * ``` */ /** * @ngdoc service - * @name utils.log#info - * @methodOf utils.log:log + * @name ss.log#info + * @methodOf ss.log:log * @function * * @description @@ -41,14 +41,14 @@ * * @example * ``` - * ss.api.log.info("Just keeping you informed"); + * ss.log.info("Just keeping you informed"); * ``` */ /** * @ngdoc service - * @name utils.log#warn - * @methodOf utils.log:log + * @name ss.log#warn + * @methodOf ss.log:log * @function * * @description @@ -57,19 +57,19 @@ * ``` * var ss = require('socketstream'), * winston = require('winston'); - * ss.api.log.warn = winston.warn; + * ss.log.warn = winston.warn; * ``` * * @example * ``` - * ss.api.log.warn("Something unexpected happened!"); + * ss.log.warn("Something unexpected happened!"); * ``` */ /** * @ngdoc service - * @name utils.log#error - * @methodOf utils.log:log + * @name ss.log#error + * @methodOf ss.log:log * @function * * @description @@ -78,7 +78,7 @@ * * @example * ``` - * ss.api.log.error("Time to wakeup the sysadmin"); + * ss.log.error("Time to wakeup the sysadmin"); * ``` */ module.exports = (function() { diff --git a/src/docs/tutorials/en/client_side_development.ngdoc b/src/docs/tutorials/en/client_side_development.ngdoc index 6eeac654..69ea5bac 100644 --- a/src/docs/tutorials/en/client_side_development.ngdoc +++ b/src/docs/tutorials/en/client_side_development.ngdoc @@ -13,5 +13,8 @@ Each entry is served separately to the browser injected in the HTML on the fly. * The asset type is specified in the URL to determine the bundler. * A timestamp is given in the URL to break any caching. -The URL pattern is `http:///_serveDev//?ts=&client=`. +The URL pattern is `http:///_serveDev//?ts=`. + +Bundlers generally work within the client directory to reduce the amount of files to watch. + From 60883a422dd743fbb7193cdb511673a2f4a3b952 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 22 Feb 2015 16:01:53 +0100 Subject: [PATCH 48/59] fix(error): Better error --- lib/client/bundler/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index a381ee04..be1352dc 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -39,7 +39,7 @@ function getBundler(client){ return bundlers[client.name]; } - throw new Error('Unknow client '+(client.name || client.client) ); + throw new Error('Unknown client '+(client.name || client.client || client.ts) ); } /** From 3d1b463f196b773e6558e92a305cff551095c7f7 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 22 Feb 2015 16:07:17 +0100 Subject: [PATCH 49/59] dummy unit test file --- test/unit/client/bundler/default.test.js | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/unit/client/bundler/default.test.js diff --git a/test/unit/client/bundler/default.test.js b/test/unit/client/bundler/default.test.js new file mode 100644 index 00000000..f4043bec --- /dev/null +++ b/test/unit/client/bundler/default.test.js @@ -0,0 +1,25 @@ +'use strict'; + + + +describe('default bundler', function () { + + + + describe('#entries', function () { + + + + it('should return entries for everything needed in view'); + + + + it('should return be affected by includes flags'); + + + + }); + + + +}); \ No newline at end of file From 505e195bf01cc8fac51a7c60ea3aa89f4446fa27 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 22 Feb 2015 17:58:36 +0100 Subject: [PATCH 50/59] doc(events): server:start --- docs/js/docs-setup.js | 18 ++++++++++++++++++ docs/partials/api/events.html | 10 ++++++++++ docs/partials/api/start.html | 12 ++++++++++++ lib/socketstream.js | 20 +++++++++++++++++--- 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 docs/partials/api/events.html create mode 100644 docs/partials/api/start.html diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 97832ab0..9ef7cfc3 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -248,6 +248,15 @@ NG_DOCS={ "shortDescription": "The webpack bundler of HTML, CSS & JS", "keywords": "api asset assettype bundler client collection concept css custom demonstration directory entries functions html improved initcode js libs list method modules output pack purposes relative service systemassets type validate view webpack" }, + { + "section": "api", + "id": "events", + "shortName": "events", + "type": "service", + "moduleName": "events", + "shortDescription": "Internal Event bus.", + "keywords": "api assets bus emitted event events expended idea internal module note production saved server service socketstream ss-console starts" + }, { "section": "api", "id": "http.index:index", @@ -336,6 +345,15 @@ NG_DOCS={ "moduleName": "ss", "keywords": "api major minor property ss version" }, + { + "section": "api", + "id": "start", + "shortName": "start", + "type": "function", + "moduleName": "start", + "shortDescription": "Starts the development or production server", + "keywords": "api development function http instance module production server start starts" + }, { "section": "api", "id": "utils", diff --git a/docs/partials/api/events.html b/docs/partials/api/events.html new file mode 100644 index 00000000..065c603c --- /dev/null +++ b/docs/partials/api/events.html @@ -0,0 +1,10 @@ +

    events +
    +
    +

    +

    Description

    +

    Internal Event bus.

    +

    Note: only used by the ss-console module for now. This idea will be expended upon in SocketStream 0.4

    +

    'server:start' is emitted when the server starts. If in production the assets will be saved before the event.

    +
    +
    diff --git a/docs/partials/api/start.html b/docs/partials/api/start.html new file mode 100644 index 00000000..9ad7a0bc --- /dev/null +++ b/docs/partials/api/start.html @@ -0,0 +1,12 @@ +

    start +
    +
    +

    +

    Description

    +

    Starts the development or production server

    +
    +

    Usage

    +
    start(server);
    +

    Parameters

    ParamTypeDetails
    serverHTTPServer

    Instance of the server from the http module

    +
    +
    diff --git a/lib/socketstream.js b/lib/socketstream.js index 18a9cf53..750a7ff9 100644 --- a/lib/socketstream.js +++ b/lib/socketstream.js @@ -86,8 +86,16 @@ var api = exports.api = { } }; -// Create internal Events bus -// Note: only used by the ss-console module for now. This idea will be expended upon in SocketStream 0.4 +/** + * @ngdoc service + * @name events + * @description + * Internal Event bus. + * + * Note: only used by the ss-console module for now. This idea will be expended upon in SocketStream 0.4 + * + * 'server:start' is emitted when the server starts. If in production the assets will be saved before the event. + */ var events = exports.events = new EventEmitter2(); // Publish Events @@ -111,7 +119,13 @@ var ws = exports.ws = require('./websocket/index')(api, responders); // Only one instance of the server can be started at once var serverInstance = null; -// Public API +/** + * @ngdoc function + * @name start + * @param {HTTPServer} server Instance of the server from the http module + * @description + * Starts the development or production server + */ function start(httpServer) { // Load SocketStream server instance From 148369b63074dc6c825b6622afa95088a21185ea Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Mar 2015 01:33:04 +0100 Subject: [PATCH 51/59] fix(tests): system assets --- test/unit/client/system/index.test.js | 40 ++++++++------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/test/unit/client/system/index.test.js b/test/unit/client/system/index.test.js index 8d5e447c..dae8d6c3 100644 --- a/test/unit/client/system/index.test.js +++ b/test/unit/client/system/index.test.js @@ -1,13 +1,12 @@ 'use strict'; var path = require('path'), - ss = require( path.join(process.env.PWD, 'lib/socketstream') ); + ss = require( path.join(process.env.PWD, 'lib/socketstream')); describe('client system library', function () { - describe('#send', function () { beforeEach(function() { @@ -15,44 +14,29 @@ describe('client system library', function () { ss.client.assets.load(); }); - it('should extend shims',function() { - - var jsBefore, jsAfter; - - jsBefore = ss.client.assets.serve.js(); - ss.client.assets.send('shim','extra.js','var extra = 0;'); - jsAfter = ss.client.assets.serve.js(); - jsAfter.should.have.length(jsBefore.length + 1 + 14); - }); - - it('should replace shims',function() { - - var jsBefore, jsAfter; - - jsBefore = ss.client.assets.serve.js(); - ss.client.assets.send('shim','json.min.js',''); - jsAfter = ss.client.assets.serve.js(); - jsAfter.should.have.length(jsBefore.length - 1886); - }); - it('should extend libs',function() { var jsBefore, jsAfter; - jsBefore = ss.client.assets.serve.js(); + jsBefore = ss.api.bundler.systemLibs(); + jsBefore.should.be.type('object'); + jsBefore.type.should.be.equal('loader'); ss.client.assets.send('lib','extra.js','var extra = 0;'); - jsAfter = ss.client.assets.serve.js(); - jsAfter.should.have.length(jsBefore.length + 1 + 14); + jsAfter = ss.api.bundler.systemLibs(); + jsAfter.should.be.type('object'); + jsAfter.content.should.have.length(jsBefore.content.length + 1 + 14); }); it('should replace libs',function() { var jsBefore, jsAfter; - jsBefore = ss.client.assets.serve.js(); + jsBefore = ss.api.bundler.systemLibs(); + jsBefore.should.be.type('object'); + jsBefore.type.should.be.equal('loader'); ss.client.assets.send('lib','browserify.js',''); - jsAfter = ss.client.assets.serve.js(); - jsAfter.should.have.length(jsBefore.length - 8854); + jsAfter = ss.api.bundler.systemLibs(); + jsAfter.content.should.have.length(jsBefore.content.length - 8854); }); }); From 588cf84c91bd27ce3abc095b46b26beb135bbf4f Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Mar 2015 02:20:39 +0100 Subject: [PATCH 52/59] fix(tests): send modules --- lib/client/bundler/index.js | 1 + lib/client/system/index.js | 1 + .../system/modules/socketstream.test.js | 44 ++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index be1352dc..09a9614e 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -251,6 +251,7 @@ module.exports = function(ss,options) { * @returns {AssetEntry} Entry */ systemModule: function(name,wrap) { + name = name.replace(/\.js$/,''); var mod = system.assets.modules[name]; if (mod) { var code = wrap===false? mod.content: ss.bundler.wrapModule(name, mod.content); diff --git a/lib/client/system/index.js b/lib/client/system/index.js index 93c0f446..fa82128b 100644 --- a/lib/client/system/index.js +++ b/lib/client/system/index.js @@ -63,6 +63,7 @@ var send = exports.send = function (type, name, content, options) { if (assets.modules[name]) { throw new Error('System module name \'' + name + '\' already exists'); } else { + name = name.replace(/\.js$/,''); assets.modules[name] = { name: name, type: type, diff --git a/test/unit/client/system/modules/socketstream.test.js b/test/unit/client/system/modules/socketstream.test.js index 860c85a7..ace8f4bf 100644 --- a/test/unit/client/system/modules/socketstream.test.js +++ b/test/unit/client/system/modules/socketstream.test.js @@ -1,5 +1,7 @@ 'use strict'; +var path = require('path'), + ss = require( path.join(process.env.PWD, 'lib/socketstream')); describe('socketstream client library', function () { @@ -24,8 +26,48 @@ describe('socketstream client library', function () { describe('#send', function () { + it('should extend mods',function() { + + ss.client.assets.send('mod','extra.js','var extra = 0;'); + var extra = ss.api.bundler.systemModule('extra.js',false); + extra.should.be.type('object'); + extra.name.should.be.equal('extra'); + extra.file.should.be.equal('extra'); + extra.path.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules/','extra.js')); + extra.dir.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules')); + extra.content.should.be.equal('var extra = 0;'); + + var extra = ss.api.bundler.systemModule('extra.js'); + extra.should.be.type('object'); + extra.name.should.be.equal('extra'); + extra.file.should.be.equal('extra'); + extra.path.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules/','extra.js')); + extra.dir.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules')); + extra.content.should.be.equal('require.define("extra", function (require, module, exports, __dirname, __filename){\n' + + 'var extra = 0;\n});'); + }); - + it('should replace mods',function() { + + ss.client.assets.send('mod','extra.js','var extra = 0;'); + ss.client.assets.send('mod','extra.js','var extra2 = 100;'); + var extra = ss.api.bundler.systemModule('extra.js',false); + extra.should.be.type('object'); + extra.name.should.be.equal('extra'); + extra.file.should.be.equal('extra'); + extra.path.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules/','extra.js')); + extra.dir.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules')); + extra.content.should.be.equal('var extra2 = 100;'); + + var extra = ss.api.bundler.systemModule('extra.js'); + extra.should.be.type('object'); + extra.name.should.be.equal('extra'); + extra.file.should.be.equal('extra'); + extra.path.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules/','extra.js')); + extra.dir.should.be.equal(path.join(process.env.PWD,'lib/client/system/modules')); + extra.content.should.be.equal('require.define("extra", function (require, module, exports, __dirname, __filename){\n' + + 'var extra2 = 100;\n});'); + }); }); From 795448539d9def98185cbc7a9e626eeb01688bb6 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Mar 2015 21:41:49 +0100 Subject: [PATCH 53/59] fix(tests): system assets --- lib/client/bundler/index.js | 6 ++-- lib/client/index.js | 2 +- test/unit/client/system/index.test.js | 44 +++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 09a9614e..1d8518ff 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -278,13 +278,15 @@ module.exports = function(ss,options) { startCode: function(client) { var startCode = system.assets.startCode.map(function(ic) { return ic.content; }).join('\n'), entryInit = options.defaultEntryInit, - realInit = 'require(' + client.entryInitPath + ');'; + realInit = client.entryInitPath? 'require("' + client.entryInitPath + '");' : null; if (typeof options.entryModuleName === 'string' || options.entryModuleName === null) { realInit = options.entryModuleName? 'require("/'+options.entryModuleName+'");' : ''; } - startCode = startCode.replace(entryInit, realInit) + if (realInit !== null) { + startCode = startCode.replace(entryInit, realInit); + } return { content:startCode, options: {}, type: 'start' }; }, diff --git a/lib/client/index.js b/lib/client/index.js index f3976440..f75d3aad 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -158,7 +158,7 @@ module.exports = function(ss, router) { }; //TODO reconsider relative paths of all these - client.entryInitPath = '/code/' + client.name + '/entry'; + client.entryInitPath = './code/' + client.name + '/entry'; ss.bundler.define(client,arguments); diff --git a/test/unit/client/system/index.test.js b/test/unit/client/system/index.test.js index dae8d6c3..47c027f2 100644 --- a/test/unit/client/system/index.test.js +++ b/test/unit/client/system/index.test.js @@ -1,15 +1,19 @@ 'use strict'; var path = require('path'), - ss = require( path.join(process.env.PWD, 'lib/socketstream')); - + should = require('should'), + ss = require( path.join(process.env.PWD, 'lib/socketstream')), + options = ss.client.options; describe('client system library', function () { + var origDefaultEntryInit = options.defaultEntryInit; describe('#send', function () { beforeEach(function() { + options.defaultEntryInit = origDefaultEntryInit; + ss.client.assets.unload(); ss.client.assets.load(); }); @@ -38,6 +42,42 @@ describe('client system library', function () { jsAfter = ss.api.bundler.systemLibs(); jsAfter.content.should.have.length(jsBefore.content.length - 8854); }); + + it('should replace init code', function() { + + //ss.client.options.entryModuleName = + var expected = 'require("./entry");',//ss.client.options.defaultEntryInit, + client = { + entryInitPath: './entry' + }; + + // Code to execute once everything is loaded + ss.client.assets.send('code', 'init', options.defaultEntryInit); + + var start = ss.api.bundler.startCode(client); + start.should.be.type('object'); + start.type.should.be.equal('start'); + start.content.should.be.equal(expected); + // client.entryInitPath + }); + + it('should allow startCode for the client to be configured', function(){ + var expected = 'require("./startCode");', + client = {}; + + options.defaultEntryInit = 'require("./startCode");'; + + // Code to execute once everything is loaded + ss.client.assets.send('code', 'init', options.defaultEntryInit); + + var start = ss.api.bundler.startCode(client); + start.should.be.type('object'); + start.type.should.be.equal('start'); + start.content.should.be.equal(expected); + }); + + //TODO options.entryModuleName + //TODO options.defaultEntryInit }); From 059917308de0885d03d4a5675b92df5fd5490ee3 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Sun, 1 Mar 2015 23:01:51 +0100 Subject: [PATCH 54/59] test(bundler): default bundler entries --- lib/client/bundler/index.js | 1 + lib/client/magic_path.js | 5 +- test/fixtures/project/client/abc/index.js | 0 test/unit/client/bundler/default.test.js | 57 +++++++++++++++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/project/client/abc/index.js diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 1d8518ff..6f579f39 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -24,6 +24,7 @@ var bundlers = {}, bundlerById = {}; function getBundler(client){ + if (typeof client === "string") { return bundlers[client]; } if (client.bundler) { return client.bundler; } diff --git a/lib/client/magic_path.js b/lib/client/magic_path.js index 8e156374..2d86d11d 100644 --- a/lib/client/magic_path.js +++ b/lib/client/magic_path.js @@ -7,6 +7,7 @@ require('colors'); var fileUtils = require('../utils/file'), + pathlib = require('path'), log = require('../utils/log'); exports.files = function(prefix, paths) { @@ -23,11 +24,11 @@ exports.files = function(prefix, paths) { var dir, sp, tree; sp = path.split('/'); if (sp[sp.length - 1].indexOf('.') > 0) { - return files.push(path); + return files.push(path); // explicit (seems like a very weak test) } else { dir = prefix; if (path !== '*') { - dir += '/' + path; + dir = pathlib.join(dir, path); } tree = fileUtils.readDirSync(dir); if (tree) { diff --git a/test/fixtures/project/client/abc/index.js b/test/fixtures/project/client/abc/index.js new file mode 100644 index 00000000..e69de29b diff --git a/test/unit/client/bundler/default.test.js b/test/unit/client/bundler/default.test.js index f4043bec..6216b34a 100644 --- a/test/unit/client/bundler/default.test.js +++ b/test/unit/client/bundler/default.test.js @@ -1,21 +1,70 @@ 'use strict'; - +var path = require('path'), + should = require('should'), + ss = require( path.join(process.env.PWD, 'lib/socketstream')), + options = ss.client.options; describe('default bundler', function () { + var origDefaultEntryInit = options.defaultEntryInit; + + describe('#entries', function () { + + beforeEach(function() { + + options.defaultEntryInit = origDefaultEntryInit; + + //ss.client.assets.unload(); + //ss.client.assets.load(); + }); + + + it('should return entries for everything needed in view', function() { + + //TODO set project root function + ss.root = ss.api.root = path.join(__dirname, '../../../fixtures/project'); + + var client = ss.client.define('abc', { + code: './abc/index.js', + view: './abc.html' + }); + + ss.client.load(); + + var bundler = ss.api.bundler.get('abc'), + entriesCSS = bundler.asset.entries('css'), + entriesJS = bundler.asset.entries('js'); + + entriesCSS.should.have.lengthOf(0); + entriesJS.should.have.lengthOf(5); + // libs + entriesJS[0].names.should.have.lengthOf(1); + entriesJS[0].names[0].should.be.equal('browserify.js'); - describe('#entries', function () { + // mod + entriesJS[1].name.should.be.equal('eventemitter2'); + entriesJS[1].type.should.be.equal('mod'); + // mod + entriesJS[2].name.should.be.equal('socketstream'); + entriesJS[2].type.should.be.equal('mod'); + // mod TODO + entriesJS[3].file.should.be.equal('./abc/index.js'); + //entriesJS[3].type.should.be.equal('mod'); - it('should return entries for everything needed in view'); + // start TODO + entriesJS[4].content.should.be.equal('require("./code/abc/entry");'); + entriesJS[4].type.should.be.equal('start'); + //entriesJS.should.be.equal([{ path:'./abc.js'}]); + }); - it('should return be affected by includes flags'); + it('should return be affected by includes flags'); }); From 48492c1bd9b0685db2546e5d37de08438880d1ec Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Mon, 2 Mar 2015 19:12:22 +0100 Subject: [PATCH 55/59] test(client): client.define --- test/fixtures/project/client/abc/abc.html | 4 ++ test/fixtures/project/client/abc/index.js | 1 + test/fixtures/project/client/abc/style.css | 1 + test/unit/client/bundler/default.test.js | 52 ++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 test/fixtures/project/client/abc/abc.html create mode 100644 test/fixtures/project/client/abc/style.css diff --git a/test/fixtures/project/client/abc/abc.html b/test/fixtures/project/client/abc/abc.html new file mode 100644 index 00000000..4e35a57b --- /dev/null +++ b/test/fixtures/project/client/abc/abc.html @@ -0,0 +1,4 @@ + +ABC +

    ABC

    + \ No newline at end of file diff --git a/test/fixtures/project/client/abc/index.js b/test/fixtures/project/client/abc/index.js index e69de29b..09d4352e 100644 --- a/test/fixtures/project/client/abc/index.js +++ b/test/fixtures/project/client/abc/index.js @@ -0,0 +1 @@ +// test diff --git a/test/fixtures/project/client/abc/style.css b/test/fixtures/project/client/abc/style.css new file mode 100644 index 00000000..c84ecefc --- /dev/null +++ b/test/fixtures/project/client/abc/style.css @@ -0,0 +1 @@ +/* */ \ No newline at end of file diff --git a/test/unit/client/bundler/default.test.js b/test/unit/client/bundler/default.test.js index 6216b34a..34c11661 100644 --- a/test/unit/client/bundler/default.test.js +++ b/test/unit/client/bundler/default.test.js @@ -9,6 +9,58 @@ describe('default bundler', function () { var origDefaultEntryInit = options.defaultEntryInit; + describe('define', function() { + + it('should support default css/code/view/tmpl locations'); + + it('should support relative css/code/view/tmpl locations'); + + it('should set up client and bundler', function() { + + //TODO set project root function + ss.root = ss.api.root = path.join(__dirname, '../../../fixtures/project'); + + var client = ss.client.define('abc', { + css: './abc/style.css', + code: './abc/index.js', + view: './abc/abc.html' + }); + + client.id.should.be.type('string'); + + client.paths.should.be.type('object'); + client.paths.css.should.be.eql(['./abc/style.css']); + client.paths.code.should.be.eql(['./abc/index.js']); + client.paths.view.should.be.eql('./abc/abc.html'); + client.paths.tmpl.should.be.eql([]); + + client.includes.should.be.type('object'); + client.includes.css.shoud.be.equal(true); + client.includes.html.shoud.be.equal(true); + client.includes.system.shoud.be.equal(true); + client.includes.initCode.shoud.be.equal(true); + client.entryInitPath.should.be.equal('./code/abc/entry'); + + client.dests.paths.html.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.html') ); + client.dests.paths.css.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.css') ); + client.dests.paths.js.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.js') ); + + client.dests.relPaths.html.should.be.equal( path.join( 'assets', 'abc', client.id + '.html') ); + client.dests.relPaths.css.should.be.equal( path.join( 'assets', 'abc', client.id + '.css') ); + client.dests.relPaths.js.should.be.equal( path.join( 'assets', 'abc', client.id + '.js') ); + + client.dests.dir.should.be.equal( path.join(ss.root,'assets', client.id) ); + client.dests.containerDir.should.be.equal( path.join(ss.root,'assets') ); + + + //client.id = shortid.generate(); + }); + }); + + afterEach(function() { + ss.client.forget(); + }); + describe('#entries', function () { beforeEach(function() { From e0d018334e968e6a9fdb631661b8e2e384ab1ef2 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Mar 2015 08:55:54 +0100 Subject: [PATCH 56/59] fix(packed): Pack 0 assets --- lib/client/bundler/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index 6f579f39..c7fcc04e 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -308,6 +308,10 @@ module.exports = function(ss,options) { if (!i) { i = 0; } + if (filePaths.length === 0) { + return writeFile([]); + } + var _ref = filePaths[i], path = _ref.importedBy, file = _ref.file; return bundler.asset[assetType](file, { pathPrefix: path, From 25b6d1607e69ab1d1e44226bcea228b3a4227dea Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Mar 2015 09:10:36 +0100 Subject: [PATCH 57/59] test(client): File locations --- lib/client/bundler/default.js | 2 +- test/unit/client/bundler/default.test.js | 28 +++++++++++++----------- test/unit/client/index.test.js | 4 ++++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/client/bundler/default.js b/lib/client/bundler/default.js index 536eed00..79d31d86 100644 --- a/lib/client/bundler/default.js +++ b/lib/client/bundler/default.js @@ -64,7 +64,7 @@ module.exports = function(ss,client,options){ client.includes = includeFlags(paths.includes); return ss.bundler.destsFor(client); - } + } /** * diff --git a/test/unit/client/bundler/default.test.js b/test/unit/client/bundler/default.test.js index 34c11661..6314ab82 100644 --- a/test/unit/client/bundler/default.test.js +++ b/test/unit/client/bundler/default.test.js @@ -2,7 +2,7 @@ var path = require('path'), should = require('should'), - ss = require( path.join(process.env.PWD, 'lib/socketstream')), + ss = require( '../../../../lib/socketstream'), options = ss.client.options; describe('default bundler', function () { @@ -35,22 +35,24 @@ describe('default bundler', function () { client.paths.tmpl.should.be.eql([]); client.includes.should.be.type('object'); - client.includes.css.shoud.be.equal(true); - client.includes.html.shoud.be.equal(true); - client.includes.system.shoud.be.equal(true); - client.includes.initCode.shoud.be.equal(true); + client.includes.css.should.be.equal(true); + client.includes.html.should.be.equal(true); + client.includes.system.should.be.equal(true); + client.includes.initCode.should.be.equal(true); client.entryInitPath.should.be.equal('./code/abc/entry'); - client.dests.paths.html.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.html') ); - client.dests.paths.css.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.css') ); - client.dests.paths.js.should.be.equal( path.join(ss.root, 'assets', 'abc', client.id + '.js') ); + var bundler = ss.api.bundler.get('abc'); - client.dests.relPaths.html.should.be.equal( path.join( 'assets', 'abc', client.id + '.html') ); - client.dests.relPaths.css.should.be.equal( path.join( 'assets', 'abc', client.id + '.css') ); - client.dests.relPaths.js.should.be.equal( path.join( 'assets', 'abc', client.id + '.js') ); + bundler.dests.paths.html.should.be.equal( path.join(ss.root,'client','static', 'assets', 'abc', client.id + '.html') ); + bundler.dests.paths.css.should.be.equal( path.join(ss.root,'client','static', 'assets', 'abc', client.id + '.css') ); + bundler.dests.paths.js.should.be.equal( path.join(ss.root,'client','static', 'assets', 'abc', client.id + '.js') ); - client.dests.dir.should.be.equal( path.join(ss.root,'assets', client.id) ); - client.dests.containerDir.should.be.equal( path.join(ss.root,'assets') ); + bundler.dests.relPaths.html.should.be.equal( path.join('/client','static', 'assets', 'abc', client.id + '.html') ); + bundler.dests.relPaths.css.should.be.equal( path.join('/client','static', 'assets', 'abc', client.id + '.css') ); + bundler.dests.relPaths.js.should.be.equal( path.join('/client','static', 'assets', 'abc', client.id + '.js') ); + + bundler.dests.dir.should.be.equal( path.join(ss.root,'client','static','assets', client.name) ); + bundler.dests.containerDir.should.be.equal( path.join(ss.root,'client','static','assets') ); //client.id = shortid.generate(); diff --git a/test/unit/client/index.test.js b/test/unit/client/index.test.js index 751f0008..755f8c51 100644 --- a/test/unit/client/index.test.js +++ b/test/unit/client/index.test.js @@ -1,5 +1,9 @@ 'use strict'; +var path = require('path'), + should = require('should'), + ss = require( '../../../lib/socketstream'), + options = ss.client.options; describe('client asset manager index', function () { From 7eec2aff7871fe9ddf22843bc5cc8015c6bbc7fe Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Mar 2015 09:56:04 +0100 Subject: [PATCH 58/59] doc(bundler): define, get, destsFor Plus load/unload tweaks --- docs/js/docs-setup.js | 2 +- docs/partials/api/ss.bundler.bundler.html | 21 +++++++++- lib/client/bundler/index.js | 50 ++++++++++++++++++++--- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/docs/js/docs-setup.js b/docs/js/docs-setup.js index 9ef7cfc3..3a8a76f9 100644 --- a/docs/js/docs-setup.js +++ b/docs/js/docs-setup.js @@ -300,7 +300,7 @@ NG_DOCS={ "type": "service", "moduleName": "ss", "shortDescription": "Client bundling API", - "keywords": "api bundler bundling client content custom default define describe descriptor entry implementing libraries method module require return service single ss system systemlibs systemmodule true wrap wrapped" + "keywords": "actual api args arguments assets bundler bundlers bundling call client containerdir content custom default define definition describe describing descriptor destinations destsfor determine dir directly entry entry-points file implementing libraries locations method module object offer params passed paths query relpaths replace require return service single ss store system systemlibs systemmodule true tweak wrap wrapped" }, { "section": "api", diff --git a/docs/partials/api/ss.bundler.bundler.html b/docs/partials/api/ss.bundler.bundler.html index df3263cf..63fe8ffb 100644 --- a/docs/partials/api/ss.bundler.bundler.html +++ b/docs/partials/api/ss.bundler.bundler.html @@ -8,7 +8,26 @@

    bundler

    Client bundling API for implementing a custom bundler.

    Methods

    -
    • systemLibs()

      +
      • define(client, args)

        +
        Parameters
        ParamTypeDetails
        clientstring

        object to store the definition in

        +
        argsobject

        arguments passed to define

        +
        +
      • +
      • destsFor(client)

        +

        The define client method of all bundlers must return the file locations for the client.

        +
        return ss.bundler.destsFor(client);
        +

        To offer a very different way to define the entry-points for assets the bundler can tweak +the paths or replace them.

        +
        Parameters
        ParamTypeDetails
        clientobject

        Object describing the client.

        +
        Returns
        object

        Destinations paths, relPaths, dir, containerDir

        +
        +
      • +
      • get(client)

        +

        Determine the bundler for a client

        +
        Parameters
        ParamTypeDetails
        clientobjectstring

        Query params with client=name or an actual client object

        +
        +
      • +
      • systemLibs()

        A single entry for all system libraries.

        Returns
        AssetEntry

        Entry

        diff --git a/lib/client/bundler/index.js b/lib/client/bundler/index.js index c7fcc04e..eef2e495 100644 --- a/lib/client/bundler/index.js +++ b/lib/client/bundler/index.js @@ -67,9 +67,13 @@ module.exports = function(ss,options) { return { /** - * Define the bundler for a client - * @param client object to store the definition in - * @param args arguments passed to define + * @ngdoc method + * @name ss.bundler:bundler#define + * @methodOf ss.bundler:bundler + * @function + * [Internal] Define the bundler for a client (do not call directly) + * @param {string} client object to store the definition in + * @param {object} args arguments passed to define */ define: function defineBundler(client,args) { @@ -88,17 +92,38 @@ module.exports = function(ss,options) { }, /** + * @ngdoc method + * @name ss.bundler:bundler#get + * @methodOf ss.bundler:bundler + * @function + * @description * Determine the bundler for a client - * @param client Query params with client=name or an actual client object + * @param {object|string} client Query params with client=name or an actual client object */ get: getBundler, load: function() { for(var n in bundlers) { - bundlers[n].load(); + if (bundlers[n].load) { + bundlers[n].load(); + } } }, + unload: function() { + for(var n in bundlers) { + if (bundlers[n].unload) { + bundlers[n].unload(); + bundlers[n].unload = null; + } + } + }, + + forget: function() { + bundlerById = {}; + bundlers = {}; + }, + pack: function pack(client) { client.pack = true; @@ -198,6 +223,21 @@ module.exports = function(ss,options) { return paths; }, + /** + * @ngdoc method + * @name ss.bundler:bundler#destsFor + * @methodOf ss.bundler:bundler + * @function + * @description + * The define client method of all bundlers must return the file locations for the client. + * + * return ss.bundler.destsFor(client); + * + * To offer a very different way to define the entry-points for assets the bundler can tweak + * the paths or replace them. + * @param {object} client Object describing the client. + * @returns {object} Destinations paths, relPaths, dir, containerDir + */ destsFor: function(client) { var containerDir = path.join(ss.root, options.dirs.assets); var clientDir = path.join(containerDir, client.name); From d1beae0417f232db0fdf2fd2d79b707288c70267 Mon Sep 17 00:00:00 2001 From: Henrik Vendelbo Date: Tue, 3 Mar 2015 10:06:03 +0100 Subject: [PATCH 59/59] feat(client): unload and forget calls Used in tests and to clean up before unloading/reloading process --- .gitignore | 1 + lib/client/index.js | 11 +++++++++++ test/fixtures/project/client/static/assets/info.txt | 1 + 3 files changed, 13 insertions(+) create mode 100644 test/fixtures/project/client/static/assets/info.txt diff --git a/.gitignore b/.gitignore index d6eaaea2..82903334 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules dump.rdb npm-debug.log *.tgz +test/fixtures/project/client/static/assets/abc diff --git a/lib/client/index.js b/lib/client/index.js index f75d3aad..eaf55193 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -65,6 +65,8 @@ module.exports = function(ss, router) { // Very basic check to see if we can find pre-packed assets // TODO: Improve to test for complete set + //TODO: Update for new id scheme + //TODO: move to bundler function determineLatestId(client) { var files, id, latestId; try { @@ -217,6 +219,15 @@ module.exports = function(ss, router) { } // Listen out for requests to async load new assets return require('./serve/ondemand')(ss, router, options); + }, + + unload: function() { + + ss.bundler.unload(); + }, + + forget: function() { + clients = {}; } }; }; diff --git a/test/fixtures/project/client/static/assets/info.txt b/test/fixtures/project/client/static/assets/info.txt new file mode 100644 index 00000000..4b2944cc --- /dev/null +++ b/test/fixtures/project/client/static/assets/info.txt @@ -0,0 +1 @@ +saving assets here during tests