Skip to content
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.

Commit

Permalink
Merge pull request #616 from caridy/loader-meta
Browse files Browse the repository at this point in the history
loader metadata per lang

- new centralized configuration through application.json->yui->config for loader on server, loader on client, combo handler and application in general.

- new experimental yui-sandbox to be able to isolate processes (like client side metadata computation)

- enhance loader-app-base and loader-app-full to be lang aware for performance reasons

- define a way to configure the metadata url through application.json->yui-config->url
  • Loading branch information
caridy committed Oct 12, 2012
2 parents 135ed5a + 000013f commit 45358c1
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 125 deletions.
68 changes: 20 additions & 48 deletions lib/app/addons/ac/deploy.server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/


/*jslint anon:true, sloppy:true, nomen:true, stupid:true*/
/*jslint anon:true, sloppy:true, nomen:true, node: true*/
/*global YUI*/


Expand All @@ -14,21 +14,9 @@
*/
YUI.add('mojito-deploy-addon', function(Y, NAME) {

var fs = require('fs'),
minify;


// TODO: [Issue 64] improve this, it's a poor man's minify.
// a. build minification into static handler?
// b. build minification into prod-build script ?
// c. build minification into server-start?
minify = function(str) {
// Remove comment blocks /* ... */ and
// remove white space at the start of lines
return str.replace(/\/\*[\s\S]*?\*\//g, '').
replace(/^[ \t\r\n]+/gm, '');
};
'use strict';

var fs = require('fs');

/**
* <strong>Access point:</strong> <em>ac.deploy.*</em>
Expand Down Expand Up @@ -75,62 +63,46 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) {
contextClient,
appConfigClient,
yuiConfig = {},
fwConfig,
yuiConfigEscaped,
yuiConfigStr,
yuiModules,
yuiCombo,
yuiJsUrls = [],
yuiCssUrls = [],
viewId,
binder,
i,
id,
clientConfig = {},
clientConfigEscaped,
clientConfigStr,
initialModuleList = {},
initializer, // script for YUI initialization
type,
module,
path,
pathToRoot;

contextClient = Y.mojito.util.copy(contextServer);
contextClient.runtime = 'client';
appConfigClient = store.getAppConfig(contextClient);
clientConfig.context = contextClient;

appConfigClient.yui = appConfigClient.yui || {};
appConfigClient.yui.config = appConfigClient.yui.config || {};
yuiConfig = Y.merge({

yuiConfig = appConfigClient.yui.config;
yuiConfig.lang = contextServer.lang; // same as contextClient.lang
fetchCSS: true,
combine: true,
base: "/combo?",
comboBase: "/combo?",
root: "",
url: "/combo?yui-base/yui-base.js&loader-base/loader-base.js&" +
"loader-app-base{langPath}.js"

// If we have a "base" for YUI use it
if (appConfigClient.yui.base) {
yuiConfig.base = appConfigClient.yui.base;
yuiConfig.combine = !!appConfigClient.yui.combine;
} else {
yuiConfig.combine = true;
yuiConfig.comboBase = "/combo?";
yuiConfig.root = "";
}
}, ((appConfigClient.yui && appConfigClient.yui.config) || {}), {
lang: contextServer.lang // same as contextClient.lang
});

// adjusting the url based on {langPath} to facilitate
// the customization of the combo url.
yuiConfig.url = yuiConfig.url.replace('{langPath}',
(contextServer.lang ? '_' + contextServer.lang : ''));

clientConfig.store = store.serializeClientStore(contextClient);

// Set the YUI URL to use on the client (This has to be done
// before any other scripts are added)
assetHandler.addAsset('js', 'top',
(appConfigClient.yui.url ||
'/combo?yui-base/yui-base.js&loader-base/loader-base.js&loader-app-base.js'));

// defaults to true if missing
if (!yuiConfig.hasOwnProperty('fetchCSS') || yuiConfig.fetchCSS) {
for (i = 0; i < yuiCssUrls.length; i += 1) {
assetHandler.addCss(yuiCssUrls[i], 'top');
}
}
assetHandler.addAsset('js', 'top', yuiConfig.url);

// adding the default module for the Y.use statement in the client
initialModuleList['mojito-client'] = true;
Expand Down
178 changes: 136 additions & 42 deletions lib/app/middleware/mojito-combo-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies computations efficiently.
var libfs = require('fs'),
mime = require('mime'),
libpath = require('path'),
YUI = require(libpath.join(__dirname, '..', '..', 'yui-sandbox.js')).getYUI(),
parseUrl = require('url').parse,
logger,
NAME = 'ComboHandler',
Expand All @@ -46,6 +47,10 @@ var libfs = require('fs'),
MODULE_META_PRIVATE_ENTRIES = ['after', 'expanded', 'supersedes', 'ext', '_parsed', '_inspected',
'skinCache', 'langCache'],

REGEX_LANG_TOKEN = /\"\{langToken\}\"/g,
REGEX_LANG_PATH = /\{langPath\}/g,
REGEX_LOCALE = /\_([a-z]{2}(-[A-Z]{2})?)$/,

DEFAULT_HEADERS = {
'.js': {
'Content-Type': 'application/javascript; charset=utf-8'
Expand Down Expand Up @@ -177,23 +182,54 @@ function clearCache(key) {
}
}

function processMeta(resolvedMods, modules, expanded_modules, conditions) {
function processMeta(resolvedMods, modules, expanded_modules, langs, conditions) {
var m,
l,
i,
module;
module,
name,
mod,
lang,
bundle;

for (m in resolvedMods) {
if (resolvedMods.hasOwnProperty(m)) {
module = resolvedMods[m];

mod = name = module.name;
bundle = name.indexOf('lang/') === 0;
lang = bundle && REGEX_LOCALE.exec(name);

if (lang) {
mod = mod.slice(0, lang.index); // eg. lang/foo_en-US -> lang/foo
lang = lang[1];
// TODO: validate lang
langs.push(lang); // eg. en-US
}
mod = bundle ? mod.slice(5) : mod; // eg. lang/foo -> foo

// language manipulation
// TODO: this routine is very restrictive, and we might want to
// make it optional later on.
if (module.lang) {
module.lang = ['{langToken}'];
}
if (bundle) {
module.owner = mod;
// applying some extra optimizations
module.langPack = lang || '*';
module.intl = true;
delete module.expanded_map;
}

if (module.condition && module.condition.test) {
conditions[module.name] = module.condition.test.toString();
module.condition.test = "{" + module.name + "}";
}

modules[module.name] = {};
if (module.type === 'css') {
modules[module.name] = 'css';
modules[module.name].type = 'css';
}
for (i = 0; i < MODULE_META_ENTRIES.length; i += 1) {
if (module[MODULE_META_ENTRIES[i]]) {
Expand All @@ -211,6 +247,25 @@ function processMeta(resolvedMods, modules, expanded_modules, conditions) {
}


function produceMeta(meta, name) {
var token = '',
path = '',
lang = REGEX_LOCALE.exec(name);

if (lang && lang[1] && meta[lang[1]]) {
lang = lang[1]; // eg. en-US
token = '"' + lang + '"';
path = '_' + lang;
meta = meta[lang];
} else {
meta = meta['*']; // default in case they use invalid lang
}
return LOADER_MODULE_TEMPLATE
.replace('{metadata}', meta)
.replace(REGEX_LANG_TOKEN, token)
.replace(REGEX_LANG_PATH, path);
}

/*
* Static file server.
*
Expand All @@ -226,59 +281,103 @@ function processMeta(resolvedMods, modules, expanded_modules, conditions) {
* @return {Function}
* @api public
*/
function staticProvider(store, globalLogger, Y) {
logger = globalLogger;
var appConfig = store.getStaticAppConfig(),
function staticProvider(store, globalLogger) {
var appConfig = store.getAppConfig(store.getStaticContext()),
options = appConfig.staticHandling || {},
cache = options.cache,
maxAge = options.maxAge,
urls = store.getAllModulesURLs(),
lang,

// collecting client side metadata
mojits = store.yui.getConfigAllMojits('client', {}),
shared = store.yui.getConfigShared('client', {}, false),
modules_config = Y.merge((mojits.modules || {}), (shared.modules || {})),
modules_config,
Y,
loader,
resolved,
appMetaData,
appResolvedMetaData,
appMetaData = {},
appResolvedMetaData = {},

// other structures
langs = ['*'], // language wildcard
expanded_modules = {}, // expanded meta (including fullpaths)
modules = {}, // regular meta (a la loader-yui3)
conditions = {}, // hash to store conditional functions
name;
name,
i;

logger = globalLogger;

Y = YUI({
fetchCSS: true,
combine: true,
base: "/combo?",
comboBase: "/combo?",
root: ""
}, ((appConfig.yui && appConfig.yui.config && appConfig.yui.config.config) || {}));

modules_config = Y.merge((mojits.modules || {}), (shared.modules || {}));
Y.applyConfig({
modules: modules_config,
useSync: true
});
Y.use('loader');

// using the loader at the server side to compute the loader metadata
// to avoid loading the whole thing on demand.
loader = new Y.Loader(Y.merge({
ignoreRegistered: true,
modules: modules_config
}, {
loader = new Y.Loader({
require: Y.Object.keys(modules_config)
}));
});
resolved = loader.resolve(true);

// Need to make a copy otherwise the changes we make deep in this structure
// will bleed into the loader, especially causing condition.test to fail.
resolved = Y.mojito.util.copy(resolved);

if (cache && !maxAge) {
maxAge = cache;
}
maxAge = maxAge || 0;

processMeta(resolved.jsMods, modules, expanded_modules, conditions);
processMeta(resolved.cssMods, modules, expanded_modules, conditions);
processMeta(resolved.jsMods, modules, expanded_modules, langs, conditions);
processMeta(resolved.cssMods, modules, expanded_modules, langs, conditions);

for (i = 0; i < langs.length; i += 1) {
lang = langs[i];

appMetaData[lang] = {};
appResolvedMetaData[lang] = {};

for (name in expanded_modules) {
if (expanded_modules.hasOwnProperty(name)) {
if (expanded_modules[name].owner &&
!expanded_modules[expanded_modules[name].owner]) {
// if there is not a module corresponding with the lang pack
// that means the controller doesn't have client affinity,
// in that case, we don't need to ship it.
continue;
}
if ((lang === '*') ||
(expanded_modules[name].langPack === '*') ||
(!expanded_modules[name].langPack) ||
(lang === expanded_modules[name].langPack)) {

appMetaData[lang][name] = modules[name];
appResolvedMetaData[lang][name] = expanded_modules[name];

}
}
}

appMetaData = JSON.stringify(modules);
appResolvedMetaData = JSON.stringify(expanded_modules);
appMetaData[lang] = JSON.stringify(appMetaData[lang]);
appResolvedMetaData[lang] = JSON.stringify(appResolvedMetaData[lang]);

for (name in conditions) {
if (conditions.hasOwnProperty(name)) {
appMetaData = appMetaData.replace('"{' + name + '}"', conditions[name]);
appResolvedMetaData = appResolvedMetaData.replace('"{' + name + '}"', conditions[name]);
for (name in conditions) {
if (conditions.hasOwnProperty(name)) {
appMetaData[lang] = appMetaData[lang]
.replace('"{' + name + '}"', conditions[name]);
appResolvedMetaData[lang] = appResolvedMetaData[lang]
.replace('"{' + name + '}"', conditions[name]);
}
}

}


Expand All @@ -303,7 +402,7 @@ function staticProvider(store, globalLogger, Y) {
return next();
}

logger.log('serving static path: ' + url.pathname, 'debug', 'static-handler');
logger.log('serving combo url: ' + url.query, 'debug', NAME);

// YIV might be messing around with the querystring params
// trying to formalize them by adding = and transforming /
Expand Down Expand Up @@ -363,19 +462,21 @@ function staticProvider(store, globalLogger, Y) {
// so errors can be found early on.
for (i = 0; i < files.length; i += 1) {

// something like foo/bar-min.js should become just "bar"
module = libpath.basename(files[i], ext).
replace(/\-(min|debug)$/, '');
// something like:
// - foo/bar-min.js becomes "bar"
// - foo/lang/bar_en-US.js becomes "lang/bar_en-US"
module = (files[i].indexOf('/lang/') >= 0 ? 'lang/' : '') +
libpath.basename(files[i], ext).replace(/\-(min|debug)$/, '');

if (module === 'loader-app-base') {
if (module.indexOf('loader-app-base') === 0) {
result[i] = {
fullpath: module,
content: LOADER_MODULE_TEMPLATE.replace('{metadata}', appMetaData)
content: produceMeta(appMetaData, module)
};
} else if (module === 'loader-app-full') {
} else if (module.indexOf('loader-app-full') === 0) {
result[i] = {
fullpath: module,
content: LOADER_MODULE_TEMPLATE.replace('{metadata}', appResolvedMetaData)
content: produceMeta(appResolvedMetaData, module)
};
} else if (urls[module]) {
result[i] = {
Expand Down Expand Up @@ -418,13 +519,6 @@ function staticProvider(store, globalLogger, Y) {
}
}
}
} else {
logger.log('Error producing combo: ' + files, 'error', NAME);
// this should never happen, because an invalid
// module error should happen before reaching this.
res.writeHead(400);
res.end(undefined);
return;
}

};
Expand Down
Loading

0 comments on commit 45358c1

Please sign in to comment.