From a48310df8cea539ebd238e37822f3a99c1e0911d Mon Sep 17 00:00:00 2001 From: Austin King <shout@ozten.com> Date: Thu, 9 Feb 2012 14:43:51 -0800 Subject: [PATCH] Refactored dialog_layout to use connect-cachify. Fixes Issue 620. --- bin/browserid | 14 ++- lib/browserid/views.js | 1 + lib/static_resources.js | 192 ++++++++++++++++++++---------- package.json | 2 + resources/views/dialog_layout.ejs | 70 +---------- resources/views/layout.ejs | 58 +-------- tests/static-resource-test.js | 68 +++++++++++ 7 files changed, 217 insertions(+), 188 deletions(-) create mode 100755 tests/static-resource-test.js diff --git a/bin/browserid b/bin/browserid index 1c07ce581..5802ba36d 100755 --- a/bin/browserid +++ b/bin/browserid @@ -13,6 +13,8 @@ urlparse = require('urlparse'), express = require('express'); const +assets = require('../lib/static_resources').all, +cachify = require('connect-cachify'), i18n = require('../lib/i18n.js'), wsapi = require('../lib/wsapi.js'), httputils = require('../lib/httputils.js'), @@ -131,6 +133,16 @@ app.use(function(req, resp, next) { return next(); }); +var static_root = path.join(__dirname, "..", "resources", "static"); + +app.use(cachify.setup(assets(config.get('supported_languages')), + { + prefix: 'v', + production: config.get('use_minified_resources'), + root: static_root, + })); + + // #7 - perform response substitution to support local/dev/beta environments // (specifically, this replaces URLs in responses, e.g. https://browserid.org // with https://diresworb.org) @@ -168,7 +180,7 @@ app.use(function(req, res, next) { next(); }); -app.use(express.static(path.join(__dirname, "..", "resources", "static"))); +app.use(express.static(static_root)); // open the databse db.open(config.get('database'), function (error) { diff --git a/lib/browserid/views.js b/lib/browserid/views.js index d87269bd1..ce496c908 100644 --- a/lib/browserid/views.js +++ b/lib/browserid/views.js @@ -9,6 +9,7 @@ logger = require('../logging.js').logger, fs = require('fs'), connect = require('connect'), config = require('../configuration.js'), +und = require('underscore'), util = require('util'); // all templated content, redirects, and renames are handled here. diff --git a/lib/static_resources.js b/lib/static_resources.js index a3d542d3e..5ef651474 100644 --- a/lib/static_resources.js +++ b/lib/static_resources.js @@ -1,82 +1,148 @@ +var i18n = require('./i18n'), + und = require('underscore'); -const resources = { +/** + * Module for managing all the known static assets in browserid. + * In filenames/paths below, you may use ``:locale`` as a url + * variable to be expanded later. + * + * These settings affect usage of cachify and eventually our + * asset build steps. + * + * Be careful editing common_js, as it will affect all + * minified scripts that depend on that variable. IE re-ordering + * the list or removing a script. + */ + +// Common to browserid.js dialog.js +var common_js = [ + '/lib/jquery-1.7.1.min.js', + '/lib/winchan.js', + '/lib/underscore-min.js', + '/lib/vepbundle.js', + '/lib/ejs.js', + '/shared/javascript-extensions.js', + '/i18n/:locale/client.json', + '/shared/gettext.js', + '/shared/browserid.js', + '/lib/hub.js', + '/lib/dom-jquery.js', + '/lib/module.js', + '/lib/jschannel.js', + '/shared/templates.js', + '/shared/renderer.js', + '/shared/class.js', + '/shared/mediator.js', + '/shared/tooltip.js', + '/shared/validation.js', + '/shared/helpers.js', + '/shared/screens.js', + '/shared/browser-support.js', + '/shared/wait-messages.js', + '/shared/error-messages.js', + '/shared/error-display.js', + '/shared/storage.js', + '/shared/xhr.js', + '/shared/network.js', + '/shared/provisioning.js', + '/shared/user.js', + '/shared/modules/page_module.js', + '/shared/modules/xhr_delay.js', + '/shared/modules/xhr_disable_form.js', + '/shared/modules/cookie_check.js' +]; + +var browserid_min_js = '/production/:locale/browserid.js'; +var browserid_js = und.flatten([ + common_js, + [ + '/pages/page_helpers.js', + '/pages/index.js', + '/pages/start.js', + '/pages/add_email_address.js', + '/pages/verify_email_address.js', + '/pages/forgot.js', + '/pages/manage_account.js', + '/pages/signin.js', + '/pages/signup.js' + ] +]); + +var dialog_min_js = '/production/:locale/dialog.js'; +var dialog_js = und.flatten([ + common_js, + [ + '/shared/command.js', + '/shared/history.js', + '/shared/state_machine.js', + + '/dialog/resources/internal_api.js', + '/dialog/resources/helpers.js', + '/dialog/resources/state.js', + + '/dialog/controllers/actions.js', + '/dialog/controllers/dialog.js', + '/dialog/controllers/authenticate.js', + '/dialog/controllers/forgot_password.js', + '/dialog/controllers/check_registration.js', + '/dialog/controllers/pick_email.js', + '/dialog/controllers/add_email.js', + '/dialog/controllers/required_email.js', + '/dialog/controllers/verify_primary_user.js', + '/dialog/controllers/provision_primary_user.js', + '/dialog/controllers/primary_user_provisioned.js', + '/dialog/controllers/email_chosen.js', + + '/dialog/start.js' + ]]); + +exports.resources = resources = { '/production/dialog.css': [ '/css/common.css', '/dialog/css/popup.css', '/dialog/css/m.css' ], - '/production/:locale:/dialog.js': [ - "/lib/jquery-1.7.1.min.js", - "/lib/winchan.js", - "/lib/underscore-min.js", - "/lib/vepbundle.js", - "/lib/ejs.js", - "/shared/javascript-extensions.js", - "/i18n/:locale:/client.json", - "/shared/gettext.js", - "/shared/browserid.js", - "/lib/hub.js", - "/lib/dom-jquery.js", - "/lib/module.js", - "/lib/jschannel.js", - "/shared/templates.js", - "/shared/renderer.js", - "/shared/class.js", - "/shared/mediator.js", - "/shared/tooltip.js", - "/shared/validation.js", - "/shared/helpers.js", - "/shared/screens.js", - "/shared/browser-support.js", - "/shared/wait-messages.js", - "/shared/error-messages.js", - "/shared/error-display.js", - "/shared/storage.js", - "/shared/xhr.js", - "/shared/network.js", - "/shared/provisioning.js", - "/shared/user.js", - "/shared/command.js", - "/shared/history.js", - "/shared/state_machine.js", - "/shared/modules/page_module.js", - "/shared/modules/xhr_delay.js", - "/shared/modules/xhr_disable_form.js", - "/shared/modules/cookie_check.js", - "/dialog/resources/internal_api.js", - "/dialog/resources/helpers.js", - "/dialog/resources/state.js", - "/dialog/controllers/actions.js", - "/dialog/controllers/dialog.js", - "/dialog/controllers/authenticate.js", - "/dialog/controllers/forgot_password.js", - "/dialog/controllers/check_registration.js", - "/dialog/controllers/pick_email.js", - "/dialog/controllers/add_email.js", - "/dialog/controllers/required_email.js", - "/dialog/controllers/verify_primary_user.js", - "/dialog/controllers/provision_primary_user.js", - "/dialog/controllers/primary_user_provisioned.js", - "/dialog/controllers/email_chosen.js", - "/dialog/start.js" + '/production/browserid.css': [ + '/css/common.css', + '/css/style.css', + '/css/m.css' ] }; +resources[dialog_min_js] = dialog_js; +resources[browserid_min_js] = browserid_js; -// return all filenames of static resources we serve -// locales is an array of supported locales -exports.all = function(locales) { +var replace = function(path, locale) { return path.replace(':locale', locale); }; +/** + * Returns all filenames of static resources + * in a connect-cachify compatible format. + * + * @langs - array of languages we support + * @return { minified_file: [dependent, files] } + * + * Languages will be converted to locales. Filenames and list of files + * will be expanded to match all the permutations. + */ +exports.all = function(langs) { + var res = {}; + for (var f in resources) { + langs.forEach(function (lang) { + var l = i18n.localeFrom(lang); + res[replace(f, l)] = getResources(f, l); + }); + } + return res; }; -// get all resource urls for a specified resource -exports.getResources(path, locale) { +/** + * Get all resource urls for a specified resource based on the locale + */ +exports.getResources = getResources = function(path, locale) { var res = []; - path = path.replace(locale, ':locale:'); if (resources[path]) { resources[path].forEach(function(r) { - res.push(r.replace(':locale:', r)); + res.push(replace(r, locale)); }); } return res; }; - diff --git a/package.json b/package.json index 6eb5f6ddf..62188227f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "convict": "0.0.6", "cjson": "0.0.6", "client-sessions": "0.0.3", + "connect-cachify": "0.0.4", "connect-cookie-session": "0.0.2", "connect-logger-statsd": "0.0.1", "ejs": "0.4.3", @@ -27,6 +28,7 @@ "temp": "0.2.0", "uglify-js": "1.0.6", "uglifycss": "0.0.4", + "underscore": "1.3.1", "urlparse": "0.0.1", "winston": "0.5.6" }, diff --git a/resources/views/dialog_layout.ejs b/resources/views/dialog_layout.ejs index edefc45f3..5fff6b14c 100644 --- a/resources/views/dialog_layout.ejs +++ b/resources/views/dialog_layout.ejs @@ -12,13 +12,7 @@ <!--[if lt IE 9]> <script src="/lib/html5shim.js"></script> <![endif]--> - <% if (production) { %> - <link href="/production/dialog.css" rel="stylesheet" type="text/css"> - <% } else { %> - <link href="/css/common.css" rel="stylesheet" type="text/css"> - <link href="/dialog/css/popup.css" rel="stylesheet" type="text/css"> - <link href="/dialog/css/m.css" rel="stylesheet" type="text/css"> - <% } %> + <%- cachify_css('/production/dialog.css') %> <link href="https://fonts.googleapis.com/css?family=Droid+Serif:400,400italic,700,700italic" rel="stylesheet" type="text/css"> <title><%= gettext('BrowserID') %></title> </head> @@ -52,67 +46,7 @@ </div> <% if (useJavascript !== false) { %> - <% if (production) { %> - <script src="/production/<%= locale %>/dialog.js"></script> - <% } else { %> - <script src="/lib/jquery-1.7.1.min.js"></script> - <script src="/lib/winchan.js"></script> - <script src="/lib/underscore-min.js"></script> - <script src="/lib/vepbundle.js"></script> - <script src="/lib/ejs.js"></script> - <script src="/shared/javascript-extensions.js"></script> - <script src="/i18n/<%= locale %>/client.json"></script> - <script src="/shared/gettext.js"></script> - <script src="/shared/browserid.js"></script> - <script src="/lib/hub.js"></script> - <script src="/lib/dom-jquery.js"></script> - <script src="/lib/module.js"></script> - <script src="/lib/jschannel.js"></script> - - <script src="/shared/templates.js"></script> - <script src="/shared/renderer.js"></script> - <script src="/shared/class.js"></script> - <script src="/shared/mediator.js"></script> - <script src="/shared/tooltip.js"></script> - <script src="/shared/validation.js"></script> - <script src="/shared/helpers.js"></script> - <script src="/shared/screens.js"></script> - <script src="/shared/browser-support.js"></script> - <script src="/shared/wait-messages.js"></script> - <script src="/shared/error-messages.js"></script> - <script src="/shared/error-display.js"></script> - <script src="/shared/storage.js"></script> - <script src="/shared/xhr.js"></script> - <script src="/shared/network.js"></script> - <script src="/shared/provisioning.js"></script> - <script src="/shared/user.js"></script> - <script src="/shared/command.js"></script> - <script src="/shared/history.js"></script> - <script src="/shared/state_machine.js"></script> - - <script src="/shared/modules/page_module.js"></script> - <script src="/shared/modules/xhr_delay.js"></script> - <script src="/shared/modules/xhr_disable_form.js"></script> - <script src="/shared/modules/cookie_check.js"></script> - - <script src="/dialog/resources/internal_api.js"></script> - <script src="/dialog/resources/helpers.js"></script> - <script src="/dialog/resources/state.js"></script> - - <script src="/dialog/controllers/actions.js"></script> - <script src="/dialog/controllers/dialog.js"></script> - <script src="/dialog/controllers/authenticate.js"></script> - <script src="/dialog/controllers/forgot_password.js"></script> - <script src="/dialog/controllers/check_registration.js"></script> - <script src="/dialog/controllers/pick_email.js"></script> - <script src="/dialog/controllers/add_email.js"></script> - <script src="/dialog/controllers/required_email.js"></script> - <script src="/dialog/controllers/verify_primary_user.js"></script> - <script src="/dialog/controllers/provision_primary_user.js"></script> - <script src="/dialog/controllers/primary_user_provisioned.js"></script> - <script src="/dialog/controllers/email_chosen.js"></script> - <script src="/dialog/start.js"></script> - <% } %> + <%- cachify_js(util.format('/production/%s/dialog.js', locale)) %> <% } %> </body> </html> diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs index 118292f33..6aa1d55f8 100644 --- a/resources/views/layout.ejs +++ b/resources/views/layout.ejs @@ -11,62 +11,8 @@ <script src="/lib/html5shim.js"></script> <![endif]--> <link href='https://fonts.googleapis.com/css?family=Droid+Serif:400,400italic,700,700italic' rel='stylesheet' type='text/css'> - <% if (production) { %> - <link rel="stylesheet" type="text/css" href="/production/browserid.css"> - <script src="/production/<%= locale %>/browserid.js"></script> - <% } else { %> - <link rel="stylesheet" href="/css/common.css" type="text/css" media="screen"> - <link rel="stylesheet" href="/css/style.css" type="text/css" media="screen"> - <link rel="stylesheet" href="/css/m.css" type="text/css" media="screen"> - - <script src="/lib/vepbundle.js"></script> - <script src="/lib/jquery-1.7.1.min.js"></script> - <script src="/lib/underscore-min.js"></script> - <script src="/lib/ejs.js"></script> - <script src="/shared/javascript-extensions.js"></script> - <script src="/i18n/<%= locale %>/client.json"></script> - <script src="/shared/gettext.js"></script> - <script src="/shared/browserid.js"></script> - <script src="/lib/dom-jquery.js"></script> - <script src="/lib/module.js"></script> - <script src="/lib/jschannel.js"></script> - <script src="/lib/winchan.js"></script> - <script src="/lib/hub.js"></script> - - <script src="/shared/templates.js"></script> - <script src="/shared/renderer.js"></script> - <script src="/shared/class.js"></script> - <script src="/shared/mediator.js"></script> - <script src="/shared/tooltip.js"></script> - <script src="/shared/validation.js"></script> - <script src="/shared/helpers.js"></script> - <script src="/shared/screens.js"></script> - <script src="/shared/browser-support.js"></script> - <script src="/shared/wait-messages.js"></script> - <script src="/shared/error-messages.js"></script> - <script src="/shared/error-display.js"></script> - <script src="/shared/mediator.js"></script> - <script src="/shared/storage.js"></script> - <script src="/shared/xhr.js"></script> - <script src="/shared/network.js"></script> - <script src="/shared/provisioning.js"></script> - <script src="/shared/user.js"></script> - - <script src="/shared/modules/page_module.js"></script> - <script src="/shared/modules/xhr_delay.js"></script> - <script src="/shared/modules/xhr_disable_form.js"></script> - <script src="/shared/modules/cookie_check.js"></script> - - <script src="/pages/page_helpers.js"></script> - <script src="/pages/index.js"></script> - <script src="/pages/start.js"></script> - <script src="/pages/add_email_address.js"></script> - <script src="/pages/verify_email_address.js"></script> - <script src="/pages/forgot.js"></script> - <script src="/pages/manage_account.js"></script> - <script src="/pages/signin.js"></script> - <script src="/pages/signup.js"></script> - <% } %> + <%- cachify_css('/production/browserid.css') %> + <%- cachify_js(util.format('/production/%s/browserid.js', locale)) %> <title><%= format(gettext("BrowserID: %s"), [title]) %></title> </head> <body> diff --git a/tests/static-resource-test.js b/tests/static-resource-test.js new file mode 100755 index 000000000..f7fa6603d --- /dev/null +++ b/tests/static-resource-test.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +require('./lib/test_env.js'); + +const assert = require('assert'), + vows = require('vows'), + resources = require('../lib/static_resources'); + +var suite = vows.describe('cache header tests'); +suite.options.error = false; + +var locales = ['ar', 'de', 'en_US', 'fr']; +suite.addBatch({ + "All resources expand": { + topic: function () { + this.callback(resources.all(locales)); + }, + "We get stuff": function (files) { + var res = resources.resources; + assert.ok(files['/production/dialog.css'].length >= 3); + // Get ride of non-localized asset bundles + ['/production/dialog.css', '/production/browserid.css'].forEach( + function (nonLocaleAsset) { + delete res[nonLocaleAsset]; + delete files[nonLocaleAsset]; + }); + + // Keys expand + // files ['/production/:locale/dialog.js'] + // becomes ['/production/ar/dialog.js', 'production/de/dialog.js', ...] + assert.equal(Object.keys(files).length, + Object.keys(res).length * locales.length); + + // Let's use the first bundle + var minFile = Object.keys(files)[0]; + var minRes = Object.keys(res)[0]; + + // Number of files underneath stay the same + assert.equal(files[minFile].length, + res[minRes].length); + // Non-localized files underneath stay the same + [0, 1, 2, 3, 4, 5, 7].forEach(function (nonLocalizedIndex) { + assert.equal(files[minFile][nonLocalizedIndex], + res[minRes][nonLocalizedIndex]); + }); + // Fragile - filename with :locale... + // When fixing this test case... console.log(res[Object.keys(res)[0]]); + var localeIndex = 6; + assert.notEqual(files[minFile][localeIndex], + res[minRes][localeIndex]); + var counter = 0; + for (var key in res) { + res[key].forEach(function (item) { + counter++; + }); + } + assert.ok(counter > 90); + } + } +}); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module); -- GitLab