From fb61e884dbf4fa3edde6895385fac58339bf78b8 Mon Sep 17 00:00:00 2001 From: Avital Oliver Date: Tue, 23 Apr 2013 15:58:40 -0700 Subject: [PATCH] Don't create new var scope for js files in client/compatibility/ --- docs/client/concepts.html | 6 ++++++ packages/meteor/package.js | 5 +++-- tools/bundler.js | 25 +++++++++++++++++++------ tools/packages.js | 8 +++++++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/docs/client/concepts.html b/docs/client/concepts.html index cd26c5ef26..49c02bcfed 100644 --- a/docs/client/concepts.html +++ b/docs/client/concepts.html @@ -43,6 +43,12 @@ client. It minifies this bundle and serves it to each new client. You're free to use a single JavaScript file for your entire application, or create a nested tree of separate files, or anything in between. +Some JavaScript libraries only work when placed in the +`client/compatibility` subdirectory. Files in this directory are +executed without being wrapped in a new variable scope. This means +that each top-level `var` defines a global variable. In addition, +these files are executed before other client-side JavaScript files. + Files outside the `client`, `server` and `tests` subdirectories are loaded on both the client and the server! That's the place for model definitions and other functions. Meteor provides the variables [`isClient`](#meteor_isclient) and diff --git a/packages/meteor/package.js b/packages/meteor/package.js index dafe07c090..01946c1b9d 100644 --- a/packages/meteor/package.js +++ b/packages/meteor/package.js @@ -6,12 +6,13 @@ Package.describe({ }); Package.register_extension( - "js", function (bundle, source_path, serve_path, where) { + "js", function (bundle, source_path, serve_path, where, opt) { bundle.add_resource({ type: "js", path: serve_path, source_file: source_path, - where: where + where: where, + compatibility: opt.compatibility }); } ); diff --git a/tools/bundler.js b/tools/bundler.js index 5dbab04c24..c0f51ab055 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -115,7 +115,7 @@ var PackageBundlingInfo = function (pkg, bundle) { }); }, - add_files: function (paths, where) { + add_files: function (paths, where, opt) { if (!(paths instanceof Array)) paths = paths ? [paths] : []; if (!(where instanceof Array)) @@ -123,7 +123,7 @@ var PackageBundlingInfo = function (pkg, bundle) { _.each(where, function (w) { _.each(paths, function (rel_path) { - self.add_file(rel_path, w); + self.add_file(rel_path, w, opt); }); }); }, @@ -188,8 +188,11 @@ _.extend(PackageBundlingInfo.prototype, { return candidates[0]; }, - add_file: function (rel_path, where) { + // opt {Object} + // - compatibility {Boolean} In case this is a JS file, don't wrap in a closure. + add_file: function (rel_path, where, opt) { var self = this; + opt = opt || {}; if (self.files[where][rel_path]) return; @@ -210,7 +213,8 @@ _.extend(PackageBundlingInfo.prototype, { handler(self.bundle.api, sourcePath, path.join(self.pkg.serve_root, rel_path), - where); + where, + opt); } else { // If we don't have an extension handler, serve this file // as a static resource. @@ -302,6 +306,10 @@ var Bundle = function () { * * data: the data to send. overrides source_file if present. you * must still set path (except for "head" and "body".) + * + * compatibility: (only for js files) when set, don't wrap code in + * a closure. used for client-side javascript libraries that use + * the `function foo()` or `var foo =` syntax to define globals. */ add_resource: function (options) { var source_file = options.source_file || options.path; @@ -337,11 +345,16 @@ var Bundle = function () { // scope (eg, file-level vars are file-scoped). On the server, this // is done in server/server.js to inject the Npm symbol. // + // Some client-side Javascript libraries define globals with `var foo =` or + // `function bar()` which only work if loaded directly from a script tag. If + // `options.compatibility` is set, don't wrap in a closure to enable using + // such libraries. + // // The ".call(this)" allows you to do a top-level "this.foo = " // to define global variables when using "use strict" // (http://es5.github.io/#x15.3.4.4); this is the only way to do // it in CoffeeScript. - if (w === "client") { + if (w === "client" && !options.compatibility) { wrapped = Buffer.concat([ new Buffer("(function(){ "), data, @@ -673,7 +686,7 @@ _.extend(Bundle.prototype, { contents = self.files.client[file]; delete self.files.client[file]; self.files.client_cacheable[file] = contents; - url = file + '?' + sha1(contents) + url = file + '?' + sha1(contents); } else throw new Error('unable to find file: ' + file); diff --git a/tools/packages.js b/tools/packages.js index 8e98150976..4930b52f0f 100644 --- a/tools/packages.js +++ b/tools/packages.js @@ -238,7 +238,13 @@ _.extend(Package.prototype, { api.use(project.get_packages(app_dir)); // -- Source files -- - api.add_files(sources_except(api, "server"), "client"); + var inCompatibilityMode = function (filename) { + return filename.indexOf(path.sep + 'client' + path.sep + 'compatibility' + path.sep) !== -1; + }; + var clientFiles = sources_except(api, "server"); + api.add_files(_.filter(clientFiles, inCompatibilityMode), "client", {compatibility: true}); + api.add_files(_.reject(clientFiles, inCompatibilityMode), "client"); + api.add_files(sources_except(api, "client"), "server"); });