From b249f77b21fc2d3b79bbe4f7733a3b9186ee0102 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 23 Jun 2014 21:59:28 -0700 Subject: [PATCH 01/19] Never add two sourceMappingURL notes to a file (eg, when roundtripping through JsImage.readFromDisk) Pre-0.9.0 this just means excessive rebuilds; on the packaging branch, this means excessive version number bumps! --- tools/bundler.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/bundler.js b/tools/bundler.js index 04025de4ed..d9bf8ca144 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -1158,6 +1158,10 @@ _.extend(JsImage.prototype, { ); var sourceMapFileName = path.basename(loadItem.sourceMap); + // Remove any existing sourceMappingURL line. (eg, if roundtripping + // through JsImage.readFromDisk, don't end up with two!) + item.source = item.source.replace( + /\n\/\/# sourceMappingURL=.+\n?$/, ''); item.source += "\n//# sourceMappingURL=" + sourceMapFileName + "\n"; loadItem.sourceMapRoot = item.sourceMapRoot; } From da356dad0f7b621aab4c2b34f287aeaca798b425 Mon Sep 17 00:00:00 2001 From: Emily Stark Date: Tue, 24 Jun 2014 08:33:02 -0700 Subject: [PATCH 02/19] Remove sentence about SRP from docs --- docs/client/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/client/api.js b/docs/client/api.js index e1d5d24746..b389f4fad9 100644 --- a/docs/client/api.js +++ b/docs/client/api.js @@ -1087,7 +1087,7 @@ Template.api.loginWithPassword = { { name: "password", type: "String", - descr: "The user's password. This is __not__ sent in plain text over the wire — it is secured with [SRP](http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol)." + descr: "The user's password." }, { name: "callback", From 53c3143285e035dd3039ff92323f68ce5f9c59df Mon Sep 17 00:00:00 2001 From: Matt DeBergalis Date: Tue, 24 Jun 2014 09:29:52 -0700 Subject: [PATCH 03/19] jobs note --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1cd7553747..df1861c3e8 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,7 @@ Interested in contributing to Meteor? * Core framework design mailing list: https://groups.google.com/group/meteor-core * Contribution guidelines: https://github.com/meteor/meteor/tree/devel/Contributing.md + +We are hiring! Visit https://www.meteor.com/jobs/working-at-meteor to +learn more about working full-time on the Meteor project. + From 9a100f0dcaf6ad65490042aa5287f0d534b790f1 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Tue, 24 Jun 2014 21:21:35 -0700 Subject: [PATCH 04/19] note that dev bundle 0.3.39 will shortly be in use --- meteor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meteor b/meteor index 93ddb8cc13..0414a5a4bb 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/bin/bash -BUNDLE_VERSION=0.3.38 +BUNDLE_VERSION=0.3.38 # warning! 0.3.39 used on packaging branch # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. From 6d2408f52b174372294f8e1584acb3a24f83e467 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Wed, 25 Jun 2014 14:55:01 -0700 Subject: [PATCH 05/19] copy-dev-bundle-from-jenkins: don't overwrite! --- scripts/admin/copy-dev-bundle-from-jenkins.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/admin/copy-dev-bundle-from-jenkins.sh b/scripts/admin/copy-dev-bundle-from-jenkins.sh index f7ebb5c92d..4f1e71a4c0 100755 --- a/scripts/admin/copy-dev-bundle-from-jenkins.sh +++ b/scripts/admin/copy-dev-bundle-from-jenkins.sh @@ -37,5 +37,12 @@ echo Found build $DIRNAME s3cmd ls s3://com.meteor.jenkins/$DIRNAME/ | \ perl -nle 'if (/\.tar\.gz/) { ++$TAR } else { die "something weird" } END { exit !($TAR == 3) }' +for FILE in $(s3cmd ls s3://com.meteor.jenkins/$DIRNAME/ | perl -nlaF/ -e 'print $F[-1]'); do + if s3cmd info $TARGET$FILE >/dev/null 2>&1; then + echo "$TARGET$FILE already exists (maybe from another branch?)" + exit 1 + fi +done + echo Copying to $TARGET s3cmd -P cp -r s3://com.meteor.jenkins/$DIRNAME/ $TARGET From 8b74a7d253f86fc8e2c102c79aa1b093804cf4b3 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Wed, 25 Jun 2014 23:51:33 -0700 Subject: [PATCH 06/19] more dev bundle warnings --- meteor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meteor b/meteor index 0414a5a4bb..4ce7672a42 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/bin/bash -BUNDLE_VERSION=0.3.38 # warning! 0.3.39 used on packaging branch +BUNDLE_VERSION=0.3.38 # warning! 0.3.39-40 used on packaging branch # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. From b76f3e67c880c132895975a963745f61ebab8fac Mon Sep 17 00:00:00 2001 From: Emily Stark Date: Thu, 19 Jun 2014 11:23:50 -0700 Subject: [PATCH 07/19] Move reload safety belt into an opt-in package --- docs/.meteor/packages | 1 + packages/galaxy-reload-safetybelt/.gitignore | 1 + packages/galaxy-reload-safetybelt/package.js | 9 ++ .../reload-safety-belt.js | 12 ++ packages/webapp/boilerplate.html | 12 +- packages/webapp/webapp_server.js | 19 +-- tools/tests/galaxy-reload-safetybelt.js | 138 ++++++++++++++++++ tools/tests/list-sites.js | 1 - 8 files changed, 174 insertions(+), 19 deletions(-) create mode 100644 packages/galaxy-reload-safetybelt/.gitignore create mode 100644 packages/galaxy-reload-safetybelt/package.js create mode 100644 packages/galaxy-reload-safetybelt/reload-safety-belt.js create mode 100644 tools/tests/galaxy-reload-safetybelt.js diff --git a/docs/.meteor/packages b/docs/.meteor/packages index 46c52f81e7..e72e891e45 100644 --- a/docs/.meteor/packages +++ b/docs/.meteor/packages @@ -12,3 +12,4 @@ jquery-waypoints less spiderable appcache +galaxy-reload-safetybelt diff --git a/packages/galaxy-reload-safetybelt/.gitignore b/packages/galaxy-reload-safetybelt/.gitignore new file mode 100644 index 0000000000..677a6fc263 --- /dev/null +++ b/packages/galaxy-reload-safetybelt/.gitignore @@ -0,0 +1 @@ +.build* diff --git a/packages/galaxy-reload-safetybelt/package.js b/packages/galaxy-reload-safetybelt/package.js new file mode 100644 index 0000000000..d1d78a07b0 --- /dev/null +++ b/packages/galaxy-reload-safetybelt/package.js @@ -0,0 +1,9 @@ +Package.describe({ + summary: "Reload safety belt for galaxy apps", + internal: true +}); + +Package.on_use(function (api) { + api.add_files("reload-safety-belt.js", "server"); + api.export("ReloadSafetyBelt", "server"); +}); diff --git a/packages/galaxy-reload-safetybelt/reload-safety-belt.js b/packages/galaxy-reload-safetybelt/reload-safety-belt.js new file mode 100644 index 0000000000..e16d3402ed --- /dev/null +++ b/packages/galaxy-reload-safetybelt/reload-safety-belt.js @@ -0,0 +1,12 @@ +// The reload safetybelt is some js that will be loaded after everything else in +// the HTML. In some multi-server deployments, when you update, you have a +// chance of hitting an old server for the HTML and the new server for the JS or +// CSS. This prevents you from displaying the page in that case, and instead +// reloads it, presumably all on the new version now. + +ReloadSafetyBelt = "\n" + + "if (typeof Package === 'undefined' ||\n" + + " ! Package.webapp ||\n" + + " ! Package.webapp.WebApp ||\n" + + " ! Package.webapp.WebApp._isCssLoaded())\n" + + " document.location.reload(); \n"; diff --git a/packages/webapp/boilerplate.html b/packages/webapp/boilerplate.html index e71c29c5ba..baf56a3343 100644 --- a/packages/webapp/boilerplate.html +++ b/packages/webapp/boilerplate.html @@ -9,10 +9,14 @@ {{/if}} {{#each js}} {{/each}} -{{#if inlineScriptsAllowed}} - -{{else}} - +{{#if reloadSafetyBelt}} + {{#if inlineScriptsAllowed}} + + {{else}} + + {{/if}} {{/if}} {{{head}}} diff --git a/packages/webapp/webapp_server.js b/packages/webapp/webapp_server.js index 5b2429cefd..aec8e62c82 100644 --- a/packages/webapp/webapp_server.js +++ b/packages/webapp/webapp_server.js @@ -19,18 +19,6 @@ WebAppInternals = {}; var bundledJsCssPrefix; -// The reload safetybelt is some js that will be loaded after everything else in -// the HTML. In some multi-server deployments, when you update, you have a -// chance of hitting an old server for the HTML and the new server for the JS or -// CSS. This prevents you from displaying the page in that case, and instead -// reloads it, presumably all on the new version now. -var RELOAD_SAFETYBELT = "\n" + - "if (typeof Package === 'undefined' ||\n" + - " ! Package.webapp ||\n" + - " ! Package.webapp.WebApp ||\n" + - " ! Package.webapp.WebApp._isCssLoaded())\n" + - " document.location.reload(); \n"; - // Keepalives so that when the outer server dies unceremoniously and // doesn't kill us, we quit ourselves. A little gross, but better than // pidfiles. @@ -345,8 +333,10 @@ var runWebAppServer = function () { JSON.stringify(__meteor_runtime_config__) + ";"); return; } else if (pathname === "/meteor_reload_safetybelt.js" && + Package["galaxy-reload-safetybelt"] && + Package["galaxy-reload-safetybelt"].ReloadSafetyBelt && ! WebAppInternals.inlineScriptsAllowed()) { - serveStaticJs(RELOAD_SAFETYBELT); + serveStaticJs(Package["galaxy-reload-safetybelt"].ReloadSafetyBelt); return; } @@ -609,7 +599,8 @@ var runWebAppServer = function () { body: '', inlineScriptsAllowed: WebAppInternals.inlineScriptsAllowed(), meteorRuntimeConfig: JSON.stringify(__meteor_runtime_config__), - reloadSafetyBelt: RELOAD_SAFETYBELT, + reloadSafetyBelt: Package["galaxy-reload-safetybelt"] && + Package["galaxy-reload-safetybelt"].ReloadSafetyBelt, rootUrlPathPrefix: __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '', bundledJsCssPrefix: bundledJsCssPrefix || __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '' diff --git a/tools/tests/galaxy-reload-safetybelt.js b/tools/tests/galaxy-reload-safetybelt.js new file mode 100644 index 0000000000..f3f5cf7644 --- /dev/null +++ b/tools/tests/galaxy-reload-safetybelt.js @@ -0,0 +1,138 @@ +var httpHelpers = require('../http-helpers.js'); +var selftest = require('../selftest.js'); +var Sandbox = selftest.Sandbox; +var unipackage = require('../unipackage.js'); +var release = require('../release.js'); +var utils = require("../utils.js"); + +var fetchPage = function (path) { + var result = httpHelpers.request("http://localhost:3000" + (path || "")); + if (result.response.statusCode !== 200) { + throw new Error("Response status code: ", result.response.statusCode); + } + return result; +}; + +var runApp = function (s) { + var run = s.run(); + run.waitSecs(5); + run.match("Started your app"); + return run; +}; + +var scriptTagSafetyBelt = + ' {{/each}} -{{#if reloadSafetyBelt}} - {{#if inlineScriptsAllowed}} +{{#each additionalStaticJs}} + {{#if ../inlineScriptsAllowed}} {{else}} - + {{/if}} -{{/if}} +{{/each}} + {{{head}}} diff --git a/packages/webapp/webapp_server.js b/packages/webapp/webapp_server.js index 2b60479441..00afdc795a 100644 --- a/packages/webapp/webapp_server.js +++ b/packages/webapp/webapp_server.js @@ -332,11 +332,9 @@ var runWebAppServer = function () { serveStaticJs("__meteor_runtime_config__ = " + JSON.stringify(__meteor_runtime_config__) + ";"); return; - } else if (pathname === "/meteor_reload_safetybelt.js" && - Package["reload-safetybelt"] && - Package["reload-safetybelt"].ReloadSafetyBelt && + } else if (_.has(additionalStaticJs, pathname) && ! WebAppInternals.inlineScriptsAllowed()) { - serveStaticJs(Package["reload-safetybelt"].ReloadSafetyBelt); + serveStaticJs(additionalStaticJs[pathname]); return; } @@ -440,7 +438,7 @@ var runWebAppServer = function () { // Will be updated by main before we listen. var boilerplateTemplate = null; var boilerplateBaseData = null; - var boilerplateByAttributes = {}; + var memoizedBoilerplate = {}; app.use(function (req, res, next) { if (! appUrl(req.url)) return next(); @@ -472,19 +470,26 @@ var runWebAppServer = function () { var htmlAttributes = getHtmlAttributes(request); - // The only thing that changes from request to request (for now) are the - // HTML attributes (used by, eg, appcache), so we can memoize based on that. - var attributeKey = JSON.stringify(htmlAttributes); - if (!_.has(boilerplateByAttributes, attributeKey)) { + // The only thing that changes from request to request (for now) are + // the HTML attributes (used by, eg, appcache) and whether inline + // scripts are allowed, so we can memoize based on that. + var boilerplateKey = JSON.stringify({ + inlineScriptsAllowed: inlineScriptsAllowed, + htmlAttributes: htmlAttributes + }); + + if (! _.has(memoizedBoilerplate, boilerplateKey)) { try { - var boilerplateData = _.extend({htmlAttributes: htmlAttributes}, - boilerplateBaseData); + var boilerplateData = _.extend({ + htmlAttributes: htmlAttributes, + inlineScriptsAllowed: WebAppInternals.inlineScriptsAllowed() + }, boilerplateBaseData); var boilerplateInstance = boilerplateTemplate.extend({ data: boilerplateData }); var boilerplateHtmlJs = boilerplateInstance.render(); - boilerplateByAttributes[attributeKey] = "\n" + - HTML.toHTML(boilerplateHtmlJs, boilerplateInstance); + memoizedBoilerplate[boilerplateKey] = "\n" + + HTML.toHTML(boilerplateHtmlJs, boilerplateInstance); } catch (e) { Log.error("Error running template: " + e); res.writeHead(500, headers); @@ -494,7 +499,7 @@ var runWebAppServer = function () { } res.writeHead(200, headers); - res.write(boilerplateByAttributes[attributeKey]); + res.write(memoizedBoilerplate[boilerplateKey]); res.end(); return undefined; }); @@ -593,14 +598,23 @@ var runWebAppServer = function () { var expectKeepalives = _.contains(argv, '--keepalive'); boilerplateBaseData = { + // 'htmlAttributes' and 'inlineScriptsAllowed' are set at render + // time, because they are allowed to change from request to + // request. css: [], js: [], head: '', body: '', - inlineScriptsAllowed: WebAppInternals.inlineScriptsAllowed(), + additionalStaticJs: _.map( + additionalStaticJs, + function (contents, pathname) { + return { + pathname: pathname, + contents: contents + }; + } + ), meteorRuntimeConfig: JSON.stringify(__meteor_runtime_config__), - reloadSafetyBelt: Package["reload-safetybelt"] && - Package["reload-safetybelt"].ReloadSafetyBelt, rootUrlPathPrefix: __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '', bundledJsCssPrefix: bundledJsCssPrefix || __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '' @@ -1002,3 +1016,12 @@ WebAppInternals.setInlineScriptsAllowed = function (value) { WebAppInternals.setBundledJsCssPrefix = function (prefix) { bundledJsCssPrefix = prefix; }; + +// Packages can call `WebAppInternals.addStaticJs` to specify static +// JavaScript to be included in the app. This static JS will be inlined, +// unless inline scripts have been disabled, in which case it will be +// served under `/`. +var additionalStaticJs = {}; +WebAppInternals.addStaticJs = function (contents) { + additionalStaticJs["/" + sha1(contents) + ".js"] = contents; +}; diff --git a/packages/webapp/webapp_tests.js b/packages/webapp/webapp_tests.js index 1f555c362c..9b837eb2df 100644 --- a/packages/webapp/webapp_tests.js +++ b/packages/webapp/webapp_tests.js @@ -1,4 +1,11 @@ var url = Npm.require("url"); +var crypto = Npm.require("crypto"); + +var additionalScript = "(function () { var foo = 1; })"; +WebAppInternals.addStaticJs(additionalScript); +var hash = crypto.createHash('sha1'); +hash.update(additionalScript); +var additionalScriptPathname = hash.digest('hex') + ".js"; Tinytest.add("webapp - content-type header", function (test) { var cssResource = _.find( @@ -21,3 +28,28 @@ Tinytest.add("webapp - content-type header", function (test) { test.equal(resp.headers["content-type"].toLowerCase(), "application/javascript; charset=utf-8"); }); + +Tinytest.add("webapp - additional static javascript", function (test) { + var origInlineScriptsAllowed = WebAppInternals.inlineScriptsAllowed(); + + WebAppInternals.setInlineScriptsAllowed(true); + var resp = HTTP.get(Meteor.absoluteUrl()); + // When inline scripts are allowed, the script should be inlined. + test.isTrue(resp.content.indexOf(additionalScript) !== -1); + // And the script should not be served as its own separate resource + // (so it will serve the app). + resp = HTTP.get(Meteor.absoluteUrl() + "/" + additionalScriptPathname); + test.isTrue(resp.content.indexOf("__meteor_runtime_config__") !== -1); + + WebAppInternals.setInlineScriptsAllowed(false); + resp = HTTP.get(Meteor.absoluteUrl()); + // When inline scripts are disallowed, the script body should not be + // inlined, and the script should be included in a