diff --git a/addons/ac/shaker.server.js b/addons/ac/shaker.server.js
index 1525a58..397899b 100644
--- a/addons/ac/shaker.server.js
+++ b/addons/ac/shaker.server.js
@@ -8,25 +8,15 @@
YUI.add('mojito-shaker-addon', function (Y, NAME) {
'use strict';
-
- function FakeAssetsAddon() {
- this.assets = {js: [], css: [], blob: []};
- }
-
- FakeAssetsAddon.prototype = {
- getAssets: function () {
- return this.assets;
- },
- addAsset: function (type, location, url) {
- this.assets[type].push(url);
- }
- };
+ var self,
+ pagePositions = ['top', 'shakerTop', 'bottom', 'shakerBottom', 'shakerInlineCss', 'shakerInlineJs'];
function ShakerAddon(command, adapter, ac) {
- this._ac = ac; // the future action context of the mojit (not attached yet if mojit created dynamically)
- this._adapter = adapter; // where the functions done and error live before attach them to the ac.
- this._command = command; //all the configuration for the mojit
- this._init(ac, adapter);
+ this.ac = ac;
+ this.context = ac.context;
+ this.route = ac.url.find(adapter.req.url, adapter.req.method);
+ this._hookDone(ac, adapter);
+ self = this;
}
ShakerAddon.prototype = {
@@ -35,182 +25,216 @@ YUI.add('mojito-shaker-addon', function (Y, NAME) {
setStore: function (rs) {
this.rs = rs;
+ this.title = rs.shaker.title;
+ this.meta = rs.shaker.meta;
+ this.settings = this.meta.settings;
+ this.posl = rs.selector.getPOSLFromContext(this.context);
+ this.poslStr = this.posl.join("-")
+ this.appResources = this.meta.app[this.poslStr].app.assets;
+ this.currentLocation = this.meta.currentLocation;
+ this.rollups = this.currentLocation && this.route ? this.meta.app[this.poslStr].rollups[this.route.name] : null;
+ this.inline = this.settings.inline ? this.meta.inline : null;
},
- getStore: function () {
- if (this.rs) {
- return this.rs;
- } else {
- // dirty fallback version to access the store
- this.rs = this._adapter.req.app.store;
- return this.rs;
- }
+ run: function (assets) {
+ var start = new Date().getTime();
+ this.isHTMLFrame = true;
+ this._getTitle(assets);
+ this._initializeAssets(assets);
+ this._addAppResources(assets);
+ this._addRouteRollups(assets);
+ this._filterAndUpdate(assets);
+ console.log("time: " + ((new Date().getTime()) - start));
},
- _getFakeYUIBootstrap: function () {
- var store = this.getStore(),
- shakerRS = store.shaker,
- fakeBS = shakerRS.fakeYUIBootstrap;
-
- if (fakeBS) {
- return '';
- } else {
- Y.log('[SHAKER] Error getting the Fake YUI BootStrap', 'error');
- }
+ setTitle: function (title) {
+ this.ac.assets.addBlob(title, 'shakerTitle');
},
- createOptimizedBootstrapBlob: function (jsList) {
- var urls,
- tmpl;
-
- if (jsList && jsList.length) {
- urls = jsList.join('","');
- tmpl = '';
- return tmpl;
+ _getTitle: function (assets) {
+ // if the title was set, choose the last title that was set
+ if (assets.shakerTitle && assets.shakerTitle.blob) {
+ this.ac.instance.config.title = assets.shakerTitle.blob[assets.shakerTitle.blob.length - 1];
}
},
- _init: function (ac, adapter) {
- // initialize will return the shaker metadata
- if (this._initializeShaker()) {
- this._augmentAppAssets(ac);
- } else {
- Y.log('[SHAKER] Metadata not found. Application running without Shaker...', 'error');
- }
- },
-
- _initializeShaker: function () {
- var store = this.getStore(),
- shakerRS = store.shaker,
- staticContext = store.getStaticContext(),
- appConfig = store.getAppConfig(staticContext),
- shakerConfig = appConfig && appConfig.shaker,
- shakerMeta = shakerRS.meta;
- this.shakerConfig = shakerConfig;
- this._meta = shakerMeta;
-
- return shakerMeta;
+ _initializeAssets: function (assets) {
+ Y.Array.each(pagePositions, function (pagePosition) {
+ assets[pagePosition] = assets[pagePosition] || {};
+ assets[pagePosition].css = assets[pagePosition].css || [];
+ assets[pagePosition].js = assets[pagePosition].js || [];
+ assets[pagePosition].blob = assets[pagePosition].blob || [];
+ });
},
- _augmentAppAssets: function (ac) {
- var instance = ac.command.instance,
- action = instance.action || ac.command.action || 'index',
- viewObj = instance.views[action] || {},
- actionAssets = viewObj && viewObj.assets;
-
- ac.assets.addAssets(actionAssets);
- delete viewObj.assets;
+ _addAppResources: function (assets) {
+ Y.Array.each(pagePositions, function (pagePosition) {
+ Y.Object.each(self.appResources[pagePosition], function (typeResources, type) {
+ [].push.apply(assets[pagePosition][type], typeResources || []);
+ });
+ });
},
- checkRouteBundling: function () {
- var ac = this._ac,
- adapter = this._adapter,
- assets = ac.assets.getAssets(),
- core = this._meta.core,
- command = this._command,
- store = adapter.req.app.store,
- url = adapter.req.url,
- method = adapter.req.method,
- //get the triggered route
- route = ac.url.find(url, method),
- //get context
- strContext = store.selector.getPOSLFromContext(ac.context).join('-'),
- //check if we have a bundle for that route
- shakerApp = this._meta.app[strContext],
- shakerBundle = shakerApp.routesBundle[route.name];
-
- if (shakerBundle) {
- Y.log('Bundling entry point!', 'shaker');
- // If is empty for some reason...
- assets.topShaker = assets.topShaker || {js: [], css: [], blob: []};
- assets.bottomShaker = assets.bottomShaker || {js: [], css: [], blob: []};
-
- // Attach the assets we collect during dispatching...
- assets.topShaker.css = shakerBundle.css;
- assets.bottomShaker.js = core.concat(shakerBundle.js);
- return true;
+ _addRouteRollups: function (assets) {
+ // add route rollups
+ // do not add rollups is current location is default
+ if (!self.rollups) {
+ return;
}
+ Y.Array.each(pagePositions, function (pagePosition) {
+ Y.Object.each(self.rollups.assets[pagePosition], function (typeResources, type) {
+ [].push.apply(assets[pagePosition][type], typeResources || []);
+ });
+ });
},
- clientDeployment: function (meta) {
- var ac = this._ac,
- assets = ac.assets,
- store = this.getStore(),
- shakerConfig = this.shakerConfig || {},
- //collect assets
- mAssets = assets.getAssets(),
- collectedJSAssets = (mAssets.bottomShaker && mAssets.bottomShaker.js) || [],
- // if we have the optimizeBootstrap enabled
- // create a fake asset addon to collect the Mojito original generated deployment assets
- assetsAddon = shakerConfig.optimizeBootstrap ? new FakeAssetsAddon() : assets,
- binders = meta.binders,
- inlineDynamicaLoader,
- deployedFake;
-
- // If we are deploying to the client get all the assets required
- if (ac.config.get('deploy') === true) {
- ac.deploy.constructMojitoClientRuntime(assetsAddon, binders);
- }
-
- if (shakerConfig.optimizeBootstrap) {
- deployedFake = assetsAddon.getAssets();
- collectedJSAssets = collectedJSAssets.concat(deployedFake.js);
-
- inlineDynamicaLoader = this.createOptimizedBootstrapBlob(collectedJSAssets);
- delete mAssets.bottomShaker;
-
- assets.addAsset('blob', 'bottomShaker', this._getFakeYUIBootstrap());
- assets.addAsset('blob', 'bottomShaker', inlineDynamicaLoader);
- assets.addAsset('blob', 'bottomShaker', deployedFake.blob);
-
- }
+ _filterAndUpdate: function (assets) {
+ Y.Object.each(assets, function (positionResources, position) {
+ Y.Object.each(positionResources, function (typeResources, type) {
+ var i = 0,
+ newLocation,
+ isRollup = false,
+ inlineElement = "",
+ comboLocalTypeResources = [],
+ comboLocationTypeResources = [],
+ comboLoad;
+
+ if (type === "blob") {
+ type = position === "shakerInlineCss" ? "css" : position === "shakerInlineJs" ? "js" : type;
+ }
+
+ comboLoad = (type === "js" && self.settings.serveJs.combo)
+ || (type === "css" && self.settings.serveCss.combo);
+
+ while (i < typeResources.length) {
+ // remove resource if found in rollup
+ if (self.rollups && self.rollups[type] && self.rollups[type].resources[typeResources[i]]) {
+ typeResources.splice(i, 1);
+ } else if (self.inline && self.inline[typeResources[i]] !== undefined) {
+ // resource is to be inlined
+ inlineElement += self.inline[typeResources[i]].trim();
+ typeResources.splice(i, 1);
+ } else {
+ // replace asset with new location if available
+ newLocation = self.currentLocation && self.currentLocation && self.currentLocation.resources[typeResources[i]];
+ isRollup = self.rollups && self.rollups[type] && self.rollups[type].rollups.indexOf(typeResources[i]) !== -1;
+
+ // don't combo load rollups
+ if (comboLoad && !isRollup) {
+ // get local resource to comboload
+ if (self.settings.serveLocation === "local" || !newLocation) {
+ comboLocalTypeResources.push(newLocation || typeResources[i]);
+ } else {
+ // get location resources to comboload
+ comboLocationTypeResources.push(newLocation);
+ }
+ // remove resource since it will appear comboloaded
+ typeResources.splice(i, 1);
+ } else {
+ typeResources[i] = newLocation || typeResources[i];
+ i++;
+ }
+ }
+ }
+
+ // create inline asset
+ if (position === "shakerInlineCss" && inlineElement) {
+ typeResources.push("");
+ return;
+ } else if (position === "shakerInlineJs" && inlineElement) {
+ typeResources.push("");
+ return;
+ }
+
+ // add comboload resources
+ if (comboLoad && comboLocalTypeResources.length !== 0) {
+ typeResources.push(self._comboload(comboLocalTypeResources, true));
+ }
+ if (comboLoad && comboLocationTypeResources.length !== 0) {
+ typeResources.push(self._comboload(comboLocationTypeResources, false));
+ }
+ });
+ });
},
- run: function (meta) {
- var routeFound;
- if (this._meta.app) {
- routeFound = this.checkRouteBundling();
+ _comboload: function (resourcesArray, isLocal) {
+ var comboSep = "~",
+ comboBase = "/combo~",
+ locationComboConfig = self.currentLocation && self.currentLocation.yuiConfig && self.currentLocation.yuiConfig.groups &&
+ self.currentLocation.yuiConfig.app;
+ if (!isLocal) {
+ comboBase = locationComboConfig && locationComboConfig.comboBase || comboBase;
+ comboSep = locationComboConfig && locationComboConfig.comboSep || comboSep;
}
- this.clientDeployment(meta);
+ // if just one resource return it, otherwise return combo url
+ return resourcesArray.length === 1 ? resourcesArray[0] : comboBase + resourcesArray.join(comboSep);
},
- renderListAsHtmlAssets: function (list, type) {
- var i,
- data = '',
- url;
-
- if ('js' === type) {
- for (i = 0; i < list.length; i += 1) {
- // TODO: Fuly escape any HTML chars in the URL to avoid trivial
- // attribute injection attacks. See owasp-esapi reference impl.
- url = encodeURI(list[i]);
- data += '\n';
- }
- } else if ('css' === type) {
- for (i = 0; i < list.length; i += 1) {
- // TODO: Escape any HTML chars in the URL to avoid trivial
- // attribute injection attacks. See owasp-esapi reference impl.
- url = encodeURI(list[i]);
- data += '\n';
- }
- } else if ('blob' === type) {
- for (i = 0; i < list.length; i += 1) {
- // NOTE: Giant security hole...but used by everyone who uses
- // Mojito so there's not much we can do except tell authors of
- // Mojito applications to _never_ use user input to generate
- // blob content or populate config data. Whatever goes in here
- // can't be easily encoded without the likelihood of corruption.
- data += list[i] + '\n';
- }
- } else {
- Y.log('Unknown asset type "' + type + '". Skipped.', 'warn', NAME);
+ _hookDone: function (ac, adapter) {
+ console.log("hook done");
+ var self = this,
+ originalDone = adapter.done;
+
+ adapter.done = function () {
+ // We don't know for sure how many arguments we have,
+ // so we have to pass through the hook references plus all the original arguments.
+ self._shakerDone.apply(self, [this, originalDone].concat([].slice.apply(arguments)));
+ };
+ },
+ /*
+ * The first two arguments are the real context and method of Mojito, that we pass artificially on @_hookDoneMethod
+ * The rest are the original arguments that are being passed by Mojito.
+ * We have to do like this since we need to modify the arguments but we don't know how many we got.
+ */
+ _shakerDone: function (selfContext, done, data, meta) {
+ var self = this,
+ args;
+
+ if (!self.isHTMLFrame && self.inline) {
+ Y.Array.each(["shakerInlineCss", "shakerInlineJs"], function (position) {
+ var positionResources = meta.assets[position],
+ inlineElement = "",
+ type = position === "shakerInlineCss" ? "css" : "js";
+
+ Y.Array.each(positionResources && positionResources.blob, function (resource) {
+ // do not add inline asset if already in rollup
+ if (self.rollups && self.rollups[type] && self.rollups[type].resources[resource]) {
+ return;
+ }
+ if (self.inline[resource]) {
+ inlineElement += self.inline[resource];
+ }
+ });
+
+ if (type === "css" && inlineElement) {
+ inlineElement = "";
+ //console.log(inlineElement);
+ if (typeof data === 'string') {
+ data = inlineElement + data;
+ } else if (data instanceof Array) {
+ data.splice(0, 0, inlineElement);
+ }
+ } else if (type === "js" && inlineElement) {
+ inlineElement = "";
+ //console.log(inlineElement);
+ if (typeof data === 'string') {
+ data += inlineElement;
+ } else if (data instanceof Array) {
+ data.push(inlineElement);
+ }
+ }
+ // empty inline resources
+ // do not delete this position (causes resources to appear twice for some reason)
+ meta.assets[position].blob = [];
+ });
}
- return data;
- }
+ // Restore the original arguments and call the real ac.done with the modified data.
+ args = [].slice.apply(arguments).slice(2);
+ args[0] = data;
+ done.apply(selfContext, args);
+ }
};
Y.mojito.addons.ac.shaker = ShakerAddon;
@@ -223,4 +247,4 @@ YUI.add('mojito-shaker-addon', function (Y, NAME) {
'mojito-deploy-addon',
'mojito-url-addon'
]
-});
+});
\ No newline at end of file
diff --git a/addons/rs/shaker.server.js b/addons/rs/shaker.server.js
index 85b1e89..acf7329 100644
--- a/addons/rs/shaker.server.js
+++ b/addons/rs/shaker.server.js
@@ -1,22 +1,8 @@
-/*
- * Copyright (c) 2012-2013, Yahoo! Inc. All rights reserved.
- * Copyrights licensed under the New BSD License.
- * See the accompanying LICENSE file for terms.
- */
-
-/*jslint anon:true, sloppy:true, nomen:true*/
-/*global YUI*/
-
YUI.add('addon-rs-shaker', function (Y, NAME) {
var libpath = require('path'),
libfs = require('fs'),
- //bootstrap
- BOOTSTRAP_DIR = '../../lib/bootstrap/',
- BOOTSTRAP_YUI_OVERRIDE = 'yui-bootstrap-override',
- BOOTSTRAP_YUI_INLINE = 'yui-bootstrap-inline-min',
- //inline (defined in core as well)
- INLINE_SELECTOR = 'shaker-inline';
+ self;
function RSAddonShaker() {
RSAddonShaker.superclass.constructor.apply(this, arguments);
@@ -28,181 +14,225 @@ YUI.add('addon-rs-shaker', function (Y, NAME) {
Y.extend(RSAddonShaker, Y.Plugin.Base, {
initializer: function (config) {
+ self = this;
this.rs = config.host;
this._poslCache = {}; // context: POSL
this.appRoot = config.appRoot;
this.mojitoRoot = config.mojitoRoot;
this.appConfig = config.host.getStaticAppConfig() || {};
- this.shakerConfig = this.appConfig.shaker || {};
+ //this.shakerConfig = this.appConfig.shaker || {};
var yuiRS = this.rs.yui,
store = this.rs,
shakerConfig = this.shakerConfig;
- if (!this.initilized) {
- //first read the shaker metadata
- this.meta = this.rs.config.readConfigSimple(libpath.join(this.appRoot, 'shaker-meta.json'));
+ // Augments the view with assets
+ if (!process.shakerCompiler) {
+ if (!this.initilized) {
+ //first read the shaker metadata
+ this.meta = this.rs.config.readConfigSimple(libpath.join(this.appRoot, 'shaker-meta.json'));
+
+ if (this.meta && !Y.Object.isEmpty(this.meta)) {
+ Y.log('Metadata loaded correctly.', 'info', 'Shaker');
+ Y.log('Preloading store', 'info', 'mojito-store');
+ } else {
+ Y.log('Metadata not found.', 'warn', 'Shaker');
+ }
+ }
- if (this.meta && !Y.Object.isEmpty(this.meta)) {
- Y.log('Metadata loaded correctly.', 'info', 'Shaker');
- Y.log('Preloading store', 'info', 'mojito-store');
- } else {
- Y.log('Metadata not found.', 'warn', 'Shaker');
+ // get settings
+ // TODO: validate settings
+ this.meta.settings = this.appConfig.shaker && this.appConfig.shaker.settings;
+ // get location
+ this.meta.currentLocation = this.meta.locations[this.meta.settings.serveLocation];
+
+ // if the current location is set to something other than default
+ // hook into getAppConfig in order to set custom yui configuration
+ if (this.meta.currentLocation) {
+ this.rs._appConfigCache = {};
+ this.beforeHostMethod('getAppConfig', this.getAppConfig, this);
}
- }
- /*
- * AOP HOOKS:
- * We need to hook some events on the store,
- * but we will have to do different hooks depending if we are on build time or in runtime
- * The reason is that there are some hook that are not needeed on runtime or viceversa
- */
+ // populate meta data with app and rollup resources for each posl
+ var routes = Y.Object.keys(self.meta.app["*"].rollups);debugger;
+ Y.Object.each(self.meta.app, function (poslResources, poslStr) {
+ var posl = poslStr.split("-");
+ poslResources.app = poslResources.app || {};
+ poslResources.app.assets = self._positionResources(self.getAppResources(self.meta, posl));
+ Y.Array.each(routes, function (route) {
+ poslResources.rollups = poslResources.rollups || {};
+ poslResources.rollups[route] = self.getRollupResources(self.meta, posl, route);
+ var typeResources = {};
+ typeResources.js = poslResources.rollups[route].js && poslResources.rollups[route].js.rollups || [];
+ typeResources.css = poslResources.rollups[route].css && poslResources.rollups[route].css.rollups || [];
+ poslResources.rollups[route].assets = self._positionResources(typeResources);
+ });
+ });
- if (shakerConfig.optimizeBootstrap) {
- this.beforeHostMethod('makeResourceVersions', this.makeResourceVersions, this);
- }
- //on build time we need this to reconfigure the url of where the assets come from...
- if (shakerConfig.comboCDN) {
- this.beforeHostMethod('resolveResourceVersions', this.resolveResourceVersions, this);
+ this.onHostEvent('resolveMojitDetails', this.resolveMojitDetails, this);
}
+ this.beforeHostMethod('resolveResourceVersions', this.resolveResourceVersions, this);
- this.beforeHostMethod('parseResourceVersion', this.parseResourceVersion, this);
- // This hooks are for runtime
- if (!process.shakerCompile) {
+ },
- //alter the url for the seeds or augment it if necesary...
- if (shakerConfig.comboCDN || shakerConfig.optimizeBootstrap) {
- Y.Do.after(this.alterAppSeedFiles, yuiRS, 'getAppSeedFiles', this);
- }
- //alter bootstrap config
- //Y.Do.after(function (){console.log(Y.Do.currentRetVal);}, yuiRS, 'getAppGroupConfig', this);
+ _positionResources: function (resources) {
+ var positionedResources = {
+ top: {},
+ shakerTop: {},
+ bottom: {}
+ },
+ shakerResources = {},
+ shakerInline = {},
+ resource;
+
+ // separate inline resources
+ // dont separate inline resources if no inline resources or no current location
+ // allow inline if default location with inline specified
+ if (!Y.Object.isEmpty(self.meta.inline) && self.meta.settings.inline) {
+ Y.Object.each(resources, function (typeResources, type) {
+ shakerResources[type] = [];
+
+ if (!self.meta.settings.serveCss && type === "css" ||
+ !self.meta.settings.serveJs && type === "js") {
+ return;
+ }
- // Augments the view with assets
- this.onHostEvent('resolveMojitDetails', this.resolveMojitDetails, this);
+ shakerInline[type] = {
+ blob: []
+ };
+ Y.Array.each(typeResources, function (resource) {
+ if (self.meta.inline[resource] !== undefined) {
+ shakerInline[type].blob.push(resource);
+ } else {
+ shakerResources[type].push(resource);
+ }
+ });
+ });
+ positionedResources.shakerInlineCss = shakerInline.css;
+ positionedResources.shakerInlineJs = shakerInline.js;
+ } else {
+ shakerResources = resources;
}
- },
- destructor: function () {
- // TODO: needed to break cycle so we don't leak memory?
- this.rs = null;
- },
+ // add css assets to proper position
+ if (self.meta.settings.serveCss) {
+ positionedResources[self.meta.settings.serveCss.position].css = shakerResources.css || [];
+ }
- /*
- * We need to add the synthetic bootstrap items
- */
- makeResourceVersions: function () {
- var store = this.rs,
- yuiRS = store.yui;
- this.addOptimizedBootstrap(store, yuiRS);
+ // add js assets to proper position
+ if (self.meta.settings.serveJs) {
+ positionedResources[self.meta.settings.serveJs.position].js = shakerResources.js || [];
+ }
+ return positionedResources;
},
- /*
- * Add the synthetic resources for the optimized bootstrap
- * On runtime we can access the new synthethic files
- */
- addOptimizedBootstrap: function (store, yuiRS) {
- var relativePath = libpath.join(__dirname, BOOTSTRAP_DIR),
- bootstrapResources = [
- BOOTSTRAP_YUI_OVERRIDE,
- BOOTSTRAP_YUI_INLINE
- ];
-
- Y.Array.each(bootstrapResources, function (item) {
- var content = libfs.readFileSync(relativePath + item + '.js', 'utf8'),
- res = {
- source: {},
- mojit: 'shared',
- type: 'yui-module',
- subtype: 'synthetic',
- name: item,
- affinity: 'client',
- selector: '*',
- yui: {
- name: item
- }
- };
+ getAppConfig: function (ctx) {
+ var appConfig,
+ key,
+ ycb;
- // this is how mojito creates synthetic resources when the server start, so wejust replicate it.
- res.id = [res.type, res.subtype, res.name].join('-');
- res.source.pkg = store.getAppPkgMeta();
- res.source.fs = store.makeResourceFSMeta(__dirname, 'app', '../../lib/bootstrap/', item + '.js', true);
+ ctx = this.rs.blendStaticContext(ctx);
+ key = JSON.stringify(ctx || {});
+
+ if (this.rs._appConfigCache[key]) {
+ return JSON.parse(this.rs._appConfigCache[key]);
+ }
- yuiRS.resContents[item] = content;
- store.addResourceVersion(res);
+ ycb = this.rs._appConfigYCB.read(ctx);
- // We save in the shaker addon the content of the hook
- // because on runtime we want to pick it syncronously
- if (item === BOOTSTRAP_YUI_INLINE) {
- this.fakeYUIBootstrap = content;
+ appConfig = Y.mojito.util.blend(this.rs._fwConfig.appConfigBase, this.rs._config.appConfig);
+ appConfig = Y.mojito.util.blend(appConfig, ycb);
+
+ Y.mix(appConfig.yui.config, self.meta.currentLocation.yuiConfig, true, null, 0, true);/*{
+ "groups": {
+ "app": {
+ "base": "/",
+ "comboBase": "/combo~",
+ "comboSep": "~",
+ "combine": true,
+ "root": "/static/app1/assets/compiled/"
+ }
}
+ }, true, null, 0, true);*/
+
+ this.rs._appConfigCache[key] = JSON.stringify(appConfig);
- }, this);
+ return appConfig;
},
- _resolveSeedResourceURL: function (moduleList) {
- var store = this.rs,
- yuiRS = store.yui,
- seed = moduleList,
- files,
- i;
-
- for (i = 0; i < seed.length; i += 1) {
- if (yuiRS.yuiModulesDetails.hasOwnProperty(seed[i])) {
- seed[i] = yuiRS.yuiModulesDetails[seed[i]].url;
- } else if (yuiRS.appModulesDetails.hasOwnProperty(seed[i])) {
- seed[i] = yuiRS.appModulesDetails[seed[i]].url;
- } else {
- Y.log('Couldnt find module for seed. Optmized bootstrap may fail', 'warn', 'Shaker');
- }
- }
+ getMojitResources: function (meta, posl, mojit, action) {
+ return this._getResources(posl, function (poslStr) {
+ return meta.app[poslStr].mojits && meta.app[poslStr].mojits[mojit] && meta.app[poslStr].mojits[mojit][action];
+ });
+ },
- return seed;
+ getAppResources: function (meta, posl) {
+ return this._getResources(posl, function (poslStr) {
+ return meta.app[poslStr].app;
+ });
},
- /*
- * When comboLoad is active we need to change the seed to point to the CDN...
- * We rely on the mapping we have on the Shaker metadata.
- * Also if optimizeBootstrap is set to true we need to augment the seed files to create our own seed.
- *
- * NOTE:
- * I have to implement _resolveSeedResourceURL, because the original method @getAppSeedFiles
- * pulls the seed directly from configuration or it creates it's own,
- * so we cannot hook directly those files.
- */
- alterAppSeedFiles: function () {
- var i,
- newUrl,
- resources,
- shakerConfig = this.shakerConfig,
- cdnUrls = this.meta && this.meta.cdnModules,
- currentSeed = Y.Do.currentRetVal;
-
- if (shakerConfig.optimizeBootstrap) {
-
- resources = this._resolveSeedResourceURL([BOOTSTRAP_YUI_OVERRIDE]);
- //the first element has to be the yui-override
- currentSeed.unshift(resources[0]);
- }
+ getRollupResources: function (meta, posl, route) {
+ return this._getResources(posl, function (poslStr) {
+ return meta.app[poslStr].rollups && meta.app[poslStr].rollups[route];
+ });
+ },
- // We need to change the url to point to the generated in CDN...
- if (shakerConfig.comboCDN && cdnUrls) {
- for (i in currentSeed) {
- if (currentSeed.hasOwnProperty(i)) {
- newUrl = cdnUrls[currentSeed[i]];
- if (newUrl) {
- currentSeed[i] = newUrl;
+ _getResources: function (posl, getMetaResources) {
+ // TODO remove resources type based on config/shaker runtime settings
+ var poslStr = posl.join("-"),
+ resources = {
+ css: null,
+ js: null
+ },
+ continueSearching = true,
+ metaResources;
+
+ // if posl does not exist in meta then use default
+ // posl should always exist in meta to ensure a more general posl is used instead of just jumping to '*'
+ poslStr = this.meta.app[poslStr] ? poslStr : "*";
+
+ while (continueSearching) {
+ // get app or mojit resources for the posl inside the metadata
+ metaResources = getMetaResources(poslStr);
+ if (metaResources) {
+ // assume metaResources contains all the type resources required
+ continueSearching = false;
+ Y.Object.each(resources, function (typeResources, type) {
+ // this resource has not been found
+ if (!typeResources) {
+ resources[type] = metaResources[type];
}
- }
+ // type resource not found so must continue searching for it
+ if (!resources[type]) {
+ continueSearching = true;
+ return;
+ }
+ });
+ }
+ if (posl.length === 1) {
+ break;
}
+ // remove the minor selector from the posl and search within more general posl
+ posl.splice(posl.length-2, 1);
+ poslStr = posl.join('-');
}
+ // remove type resources if empty
+ Y.Object.each(resources, function (typeResources, type) {
+ if (!typeResources) {
+ //delete resources[type];
+ resources[type] = [];
+ }
+ });
+ return resources;
},
/*
* Change the URL's of the Store so we get the comboLoad from CDN.
*/
- resolveResourceVersions: function (cdnUrls) {
+ resolveResourceVersions: function (urlMap) {
var r,
res,
ress,
@@ -212,10 +242,9 @@ YUI.add('addon-rs-shaker', function (Y, NAME) {
meta,
urls = {};
- //get the CDN URL mapping
- cdnUrls = cdnUrls || this.meta.cdnModules;
+ urlMap = urlMap || this.meta && this.meta.currentLocation && this.meta.currentLocation.resources;
- if (!cdnUrls) {
+ if(!urlMap) {
return;
}
@@ -225,93 +254,43 @@ YUI.add('addon-rs-shaker', function (Y, NAME) {
for (m = 0; m < mojits.length; m += 1) {
mojit = mojits[m];
+
ress = this.rs.getResourceVersions({mojit: mojit});
for (r = 0; r < ress.length; r += 1) {
res = ress[r];
//Change the url
- if (res.yui && cdnUrls[res.url]) {
- res.url = cdnUrls[res.url];
+ if (res.yui && urlMap[res.url]) {
+ res.url = urlMap[res.url];
}
}
}
},
- /*
- * Converting inlines files to be readable by the store.
- * parseResourceVersion:
- * AOP hook!
- */
- parseResourceVersion: function (source, type, subtype) {
- var basename,
- tmpBasename,
- inline;
-
- if (type === 'asset') {
- basename = source.fs.basename.split('.');
- inline = basename.pop();
-
- if (inline === INLINE_SELECTOR) {
- // Add the inline property to source, since we don't have access to the resource itself yet.
- source.inline = true;
- // put back the basename without the INLINE_SELECTOR so mojito doesnt skip the file.
- basename[0] = basename[0] + '-' + INLINE_SELECTOR;
- source.fs.basename = basename.join('.');
- }
- }
- },
-
/*
* Augment the view spec with the Shaker computed assets.
* Will be merged on the action-context module (either on the client or in the server).
*/
resolveMojitDetails: function (e) {
- var env = e.args.env,
+ var mojit = e.args.type,
+ views = e.mojitDetails.views,
posl = e.args.posl,
- mojitName = e.args.type,
- ress = e.args.ress,
- details = e.mojitDetails,
- strContext = posl.join('-'),
- isFrame = mojitName.indexOf('ShakerHTMLFrameMojit') !== -1,
- shakerMeta = this.meta,
- shakerBase,
- frameActionMeta,
- actionMeta,
- css,
- resource,
- i;
-
- if (Y.Object.isEmpty(this.meta)) {
+ self = this;
+
+ // skip mojits not in metadata
+ if (!this.meta.app["*"].mojits[mojit]) {
return;
}
- // If the mojit is the ShakerHTMLFrame, we are going to put the common assets there.
- if (isFrame) {
- shakerBase = shakerMeta.app[strContext];
- shakerBase = shakerBase && shakerBase.app;
- frameActionMeta = {
- css: shakerBase
- };
+ Y.Object.each(views, function (view, action) {
+ var resources = self.getMojitResources(self.meta, posl, mojit, action);
- } else {
- // Check if on the nested meta we have all the info we need...
- shakerBase = shakerMeta.app[strContext];
- shakerBase = shakerBase && shakerBase.mojits[mojitName];
- }
-
- Y.Object.each(details.views, function(view, name) {
- actionMeta = (isFrame ? frameActionMeta : shakerBase && shakerBase[name]) || {css: [], blob: []};
- view.assets = {
- topShaker: {
- css: actionMeta.css
- },
- inlineShaker: {
- blob: actionMeta.blob
- }
- };
+ view.assets = self._positionResources(resources);
});
+
}
});
+ RSAddonShaker.appResources = {};
Y.namespace('mojito.addons.rs').shaker = RSAddonShaker;
}, '0.0.1', {
diff --git a/bin/mojito-shake b/bin/mojito-shake
index beb26ce..565912c 100755
--- a/bin/mojito-shake
+++ b/bin/mojito-shake
@@ -1,15 +1,4 @@
#!/usr/bin/env node
-/*
- * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
- * Copyrights licensed under the New BSD License.
- * See the accompanying LICENSE file for terms.
- */
-
-/*jslint anon:true, sloppy:true*/
-
-var libmojito = require('mojito'),
- utils = require('mojito/lib/management/utils'),
- libpath = require('path');
/*
* A command is expected to export the following:
* run - The function that executes the command. The signature is:
@@ -24,7 +13,6 @@ var libmojito = require('mojito'),
* hasValue - True is this option requires a value. Optional; default false.
*/
-
/*
* Creates a map keyed by both short and long option names, to simplify lookup
* of option info from command line args.
@@ -88,20 +76,14 @@ function parseArgs(args, optionInfo) {
return { params: params, options: options, errors: errors };
}
-// ---------- Start of mainline code ----------
+
function main() {
- var args = process.argv.slice(2),
- command,
+ var utils,
+ command = require('../commands/shake'),
+ args = process.argv.slice(2),
argInfo;
- try {
- command = require('../commands/shake');
- } catch (e) {
- utils.error('Invalid command!');
- return;
- }
-
if (args.length === 0) {
argInfo = { command: 'help', params: [] };
} else {
@@ -109,23 +91,18 @@ function main() {
}
if (argInfo.errors && argInfo.errors.length > 0) {
+ utils = require('mojito/lib/management/utils')
argInfo.errors.forEach(function(e) {
utils.log(e);
});
- utils.error('Invalid command line.', "Try 'mojito help '.");
+ //utils.error('Invalid command line.', "Try 'mojito help '.");
return;
}
- command.run(argInfo.params, argInfo.options, function(err) {
- if (err) {
- utils.error(err);
- } else {
- utils.success('mojito done.');
- }
- });
+ command.run(argInfo.params, argInfo.options);
}
// Execute the main() function. Note that this occurs as part of the
-// require/import process so simply importing/require()ing the cli.js will cause
+// require/import process so simply importing/requiring the cli.js will cause
// the main() operation to be invoked.
main();
diff --git a/commands/shake.js b/commands/shake.js
index f3606de..1108ae6 100755
--- a/commands/shake.js
+++ b/commands/shake.js
@@ -4,8 +4,10 @@
* See the accompanying LICENSE file for terms.
*/
-start = require('mojito/lib/app/commands/start');
-Shaker = require('mojito-shaker/lib/shaker').Shaker;
+var utils = require('mojito/lib/management/utils'),
+ mojitoStart = require('mojito/lib/app/commands/start'),
+ ShakerCompiler = require('../lib/compiler').ShakerCompiler;
+
/**
* Convert a CSV string into a context object.
* @param {string} s A string of the form: 'key1:value1,key2:value2'.
@@ -35,10 +37,9 @@ function contextCsvToObject(s) {
* Standard usage string export.
*/
exports.usage = '\nShaker Options:\n' +
- '\t--context A comma-separated list of key:value pairs that define the' +
- ' base\n' +
- '\t context used to read configuration files\n' +
- '\t--run Run Mojito Server after running Shaker\n';
+ ' --context A comma-separated list of key:value pairs that define the base\n' +
+ ' context used to read configuration files (e.g. "environment:dev")\n' +
+ ' --run Run the Mojito server after running the Shaker compiler\n';
/**
* Standard options list export.
@@ -73,7 +74,7 @@ exports.options = [
* @param {object} opts Options/flags for the command.
* @param {function} callback An optional callback to invoke on completion.
*/
-exports.run = function(params, options, callback) {
+exports.run = function(params, options) {
options = options || {};
var context = {},
debug = options.debug || 0;
@@ -86,15 +87,26 @@ exports.run = function(params, options, callback) {
console.log(this.usage);
return;
}
-
- var shaker = new Shaker({context: context, debugLevel: debug});
- shaker.run(function (err, data) {
- if (options.run) {
- delete options.run;
- start.run(params, options, callback);
+
+ // shaker compiler
+ process.shakerCompiler = true;
+
+ var compiler = new ShakerCompiler(context);
+ compiler.compile(function (err) {
+ if (err) {
+ utils.error(err);
} else {
- callback(err, data);
+ utils.success('Shaker done.');
+ if (options.run) {
+ delete options.run;
+ mojitoStart.run(params, options, function (err) {
+ if (err) {
+ utils.error(err);
+ } else {
+ utils.success('Mojito done.');
+ }
+ });
+ }
}
});
- //if we do async stuff move callback
};
diff --git a/index.js b/index.js
index ace0a92..3d81f41 100644
--- a/index.js
+++ b/index.js
@@ -1 +1 @@
-module.exports = require('./lib/shaker');
\ No newline at end of file
+module.exports = require('./lib/compiler');
diff --git a/lib/compiler.js b/lib/compiler.js
index 3144413..22e391d 100644
--- a/lib/compiler.js
+++ b/lib/compiler.js
@@ -9,8 +9,7 @@ var validateConfig = require('./config.js').validateConfig,
mojitrollup: require('./rollups/mojitrollup.js').mojitrollup
},
locations: {
- local: require('./locations/local.js').local,
- mobstor: require('shaker-mobstor').mobstor
+ local: require('./locations/local.js').location
}
},
typeHierarchy = {
@@ -23,13 +22,23 @@ var validateConfig = require('./config.js').validateConfig,
};
function ShakerCompiler(context) {
-debugger;
this.compile = function (done) {
var resources = new ShakerResources(context),
config = resources.shakerConfig;
config.appConfig = resources.appConfig;
+ // initialize locations
+ Y.Object.each(config.locations, function (locationConfig, location) {
+ if (!modules.locations[location]) {
+ try {
+ modules.locations[location] = require('mojito-shaker-' + location).location;
+ } catch (e) {
+ console.log(e);
+ }
+ }
+ });
+
applyTasks(config, resources.appResources, function (err) {
applyRollups(config, resources.appResources, resources.organizedResources, function (err) {
applyLocations(config.locations, config.appConfig, resources.appResources, function (err) {
@@ -480,7 +489,7 @@ debugger;
// TODO check if write files, sometimes no permission
// TODO check it can write file first before compiling
-require('fs').writeFile("shakerResources.json", JSON.stringify(organizedResources));
+require('fs').writeFile("shaker-meta.json", JSON.stringify(organizedResources));
}
@@ -507,7 +516,7 @@ require('fs').writeFile("shakerResources.json", JSON.stringify(organizedResource
locations[locationName].resources[resource.url] = resource.locations[locationName];
//}
});
-
+debugger;
resources.resolveResourceVersions(locations[locationName].resources);
loaderResources = resources.getLoaderResources();
diff --git a/lib/locations/local.js b/lib/locations/local.js
index 9a856e2..73d61bf 100644
--- a/lib/locations/local.js
+++ b/lib/locations/local.js
@@ -3,7 +3,7 @@ var fs = require('fs');
var basedir = 'assets/compiled';
-exports.local = function (config) {
+exports.location = function (config) {
var root = "/" + config.appConfig.prefix + "/" + config.appConfig.appName + "/" + basedir + "/";
this.yuiConfig = {
"groups": {
diff --git a/lib/resources.js b/lib/resources.js
index 13bc2cc..79f7fe9 100644
--- a/lib/resources.js
+++ b/lib/resources.js
@@ -40,8 +40,8 @@ function ShakerResources(context) {
// TODO remove posl if no resoruces
- require('fs').writeFile("appResources.json", JSON.stringify(self.appResources));
- require('fs').writeFile("organizedResources.json", JSON.stringify(self.organizedResources));
+ //require('fs').writeFile("appResources.json", JSON.stringify(self.appResources));
+ //require('fs').writeFile("organizedResources.json", JSON.stringify(self.organizedResources));
@@ -387,8 +387,7 @@ function ShakerResources(context) {
function getShakerConfig(context) {
var config = store.getAppConfig(context || {});
- // TODO: change to shaker
- return config.shaker2 || {};
+ return config.shaker || {};
};
function getShakerConfigByContext(mojitName, context) {
@@ -416,7 +415,6 @@ function ShakerResources(context) {
}
function getStoreConfigs() {
- debugger;
var appConfig = store.getAppConfig(context);
return {
diff --git a/lib/rollups/mojitrollup.js b/lib/rollups/mojitrollup.js
index 452d937..16faf90 100644
--- a/lib/rollups/mojitrollup.js
+++ b/lib/rollups/mojitrollup.js
@@ -95,6 +95,5 @@ exports.mojitrollup = function (config, resources) {
});
});
});
- debugger;
return rollups;
}
diff --git a/mojits/ShakerHTMLFrameMojit/controller.server.js b/mojits/ShakerHTMLFrameMojit/controller.server.js
index 2778aee..44378a0 100644
--- a/mojits/ShakerHTMLFrameMojit/controller.server.js
+++ b/mojits/ShakerHTMLFrameMojit/controller.server.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
+ * Copyright (c) 2011-2013, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
@@ -9,17 +9,74 @@
/*global YUI*/
-YUI.add('ShakerHTMLFrameMojit', function(Y, NAME) {
+YUI.add('HTMLFrameMojit', function (Y, NAME) {
+ 'use strict';
Y.namespace('mojito.controllers')[NAME] = {
- index: function(ac) {
+ index: function (ac) {
this.__call(ac);
},
- __call: function(ac) {
+ __call: function (ac) {
+ this._renderChild(ac, function (data, meta) {
+
+ ac.shaker.run(meta.assets);
+
+ // meta.assets from child should be piped into
+ // the frame's assets before doing anything else.
+ ac.assets.addAssets(meta.assets);
+
+ if (ac.config.get('deploy') === true) {
+ ac.deploy.constructMojitoClientRuntime(ac.assets,
+ meta.binders);
+ }
+
+ // we don't care much about the views specified in childs
+ // and for the parent, we have a fixed one.
+
+ meta.view = meta.view || {};
+
+ meta.view.name = 'index';
+
+ // 1. mixing bottom and top fragments from assets into
+ // the template data, along with title and mojito version.
+ // 2. mixing meta with child metas, along with some extra
+ // headers.
+
+ ac.done(
+ Y.merge(data, ac.assets.renderLocations(), {
+
+ title: ac.shaker.title || ac.config.get('title') || 'Powered by Mojito',
+ mojito_version: Y.mojito.version
+
+ }),
+ Y.mojito.util.metaMerge(meta, {
+
+ http: {
+ headers: {
+ 'content-type': 'text/html; charset="utf-8"'
+ }
+ }
+
+ }, true)
+ );
+
+ });
+
+ },
+
+ /**
+ * Renders a child mojit based on a config called "child" and
+ * the "assets" collection specified in the specs.
+ * @method _renderChild
+ * @protected
+ * @param {Object} ac Action Context Object.
+ * @param {Function} callback The callback.
+ */
+ _renderChild: function (ac, callback) {
// Grab the "child" from the config an add it as the
// only item in the "children" map.
var child = ac.config.get('child'),
@@ -37,55 +94,19 @@ YUI.add('ShakerHTMLFrameMojit', function(Y, NAME) {
assets: ac.config.get('assets')
};
- Y.log('executing ShakerHTMLFrameMojit child', 'mojito', 'qeperf');
// Now execute the child as a composite
- ac.composite.execute(cfg, function(data, meta) {
-
- // Make sure we have meta
- meta.http = meta.http || {};
- meta.http.headers = meta.http.headers || {};
-
- // Make sure our Content-type is HTML
- meta.http.headers['content-type'] =
- 'text/html; charset="utf-8"';
-
- // Set the default data
- data.title = ac.config.get('title') ||
- 'Powered by Mojito ' + Y.mojito.version;
- data.mojito_version = Y.mojito.version;
-
- data.enableDynamicTitle = ac.config.get('enableDynamicTitle');
-
- // Add all the assets we have been given to our local store
- ac.assets.addAssets(meta.assets);
-
- // SHAKER RUNTIME!
- // NOTE: We move the deployment of the client to within Shaker addon...
- ac.shaker.run(meta);
-
- // Attach assets found in the "meta" to the page
- Y.Object.each(ac.assets.getAssets(), function(types, location) {
- if (!data[location]) {
- data[location] = '';
- }
- Y.Object.each(types, function(assets, type) {
- data[location] += ac.shaker.renderListAsHtmlAssets(assets, type);
- });
- });
-
- meta.view = {name: 'index'};
-
- Y.log('ShakerHTMLFrameMojit done()', 'mojito', 'qeperf');
-
- ac.done(data, meta);
- });
+ ac.composite.execute(cfg, callback);
}
+
};
}, '0.1.0', {requires: [
- 'mojito-composite-addon',
+ 'mojito',
+ 'mojito-util',
'mojito-assets-addon',
+ 'mojito-deploy-addon',
'mojito-config-addon',
+ 'mojito-composite-addon',
'mojito-shaker-addon'
]});
diff --git a/mojits/ShakerHTMLFrameMojit/definition.json b/mojits/ShakerHTMLFrameMojit/definition.json
index 4ec523a..df95660 100644
--- a/mojits/ShakerHTMLFrameMojit/definition.json
+++ b/mojits/ShakerHTMLFrameMojit/definition.json
@@ -1,8 +1,5 @@
[
{
- "settings" : ["master"],
- "yui": {
- "sharedYUIInstance" : true
- }
+ "settings" : ["master"]
}
]
diff --git a/mojits/ShakerHTMLFrameMojit/views/index.hb.html b/mojits/ShakerHTMLFrameMojit/views/index.hb.html
new file mode 100644
index 0000000..2dc0c9b
--- /dev/null
+++ b/mojits/ShakerHTMLFrameMojit/views/index.hb.html
@@ -0,0 +1,27 @@
+
+
+
+
+{{#meta}}
+
+{{/meta}}
+{{^meta}}
+
+{{/meta}}
+
+{{title}}
+
+{{{shakerInlineCss}}}
+{{{top}}}
+{{{shakerTop}}}
+
+
+
+
+{{{child}}}
+
+{{{shakerInlineJs}}}
+{{{bottom}}}
+{{{shakerBottom}}}
+
+
diff --git a/mojits/ShakerHTMLFrameMojit/views/index.iphone.hb.html b/mojits/ShakerHTMLFrameMojit/views/index.iphone.hb.html
new file mode 100644
index 0000000..3156b6b
--- /dev/null
+++ b/mojits/ShakerHTMLFrameMojit/views/index.iphone.hb.html
@@ -0,0 +1,24 @@
+
+
+
+
+{{#meta}}
+
+{{/meta}}
+{{^meta}}
+
+{{/meta}}
+
+{{title}}
+
+{{{top}}}
+
+
+
+
+{{{child}}}
+
+{{{bottom}}}
+
+
+
diff --git a/package.json b/package.json
index aa083d8..cf8d123 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,14 @@
{
"name": "mojito-shaker",
- "version": "3.1.3",
+ "version": "4.0.0",
"description": "Compiles and deploys asset rollups for Mojito applications.",
"author": "shaker-users@yahoo-inc.com",
"contributors": [
+ "Albert Jimenez ",
"Diego Ferreiro ",
- "Stephen Murphy ",
- "Brett Stimmerman ",
"Julien Lecomte ",
- "Albert Jimenez "
+ "Stephen Murphy ",
+ "Brett Stimmerman "
],
"dependencies": {
"async": "0.1.x",